source: src/main/java/geniusweb/profilesserver/websocket/GetProfileSocket.java@ 8

Last change on this file since 8 was 8, checked in by bart, 5 years ago

Update 28 jan 2020

File size: 5.4 KB
Line 
1package geniusweb.profilesserver.websocket;
2
3import java.io.IOException;
4import java.math.BigInteger;
5import java.util.Arrays;
6import java.util.Collections;
7import java.util.HashMap;
8import java.util.HashSet;
9import java.util.List;
10import java.util.Map;
11import java.util.Random;
12import java.util.Set;
13import java.util.stream.Collectors;
14
15import javax.websocket.OnClose;
16import javax.websocket.OnError;
17import javax.websocket.OnOpen;
18import javax.websocket.Session;
19import javax.websocket.server.PathParam;
20import javax.websocket.server.ServerEndpoint;
21
22import geniusweb.bidspace.AllBidsList;
23import geniusweb.bidspace.BidsWithUtility;
24import geniusweb.issuevalue.Bid;
25import geniusweb.profile.DefaultPartialOrdering;
26import geniusweb.profile.PartialOrdering;
27import geniusweb.profile.Profile;
28import geniusweb.profile.utilityspace.LinearAdditive;
29import geniusweb.profilesserver.Jackson;
30import geniusweb.profilesserver.ProfilesFactory;
31import geniusweb.profilesserver.events.ChangeEvent;
32import tudelft.utilities.immutablelist.FixedList;
33import tudelft.utilities.immutablelist.ImmutableList;
34import tudelft.utilities.listener.Listener;
35
36/**
37 * Returns a websocket that communicates the list of currently available domains
38 * and profiles. Every time something changes, a new list of domains and
39 * profiles is sent. For each new websocket the server will create one of this
40 * but they all share one {@link ProfilesFactory}.
41 *
42 * <p>
43 * Query string: the websocket allows query strings.
44 * <ul>
45 * <li>partial</li> a query key that is assigned a natural number N (0 or
46 * larger). If set, the profile is converted into a Partial Profile with the
47 * given number of points. If N>0, the maximum utility bid is included .If N>1,
48 * the minimum utility bid is included. The other bids are picked at random.
49 * This filtering mechanism only works if the profile is {@link LinearAdditive}
50 * space.
51 */
52@ServerEndpoint("/websocket/get/{domain}/{profile}")
53public class GetProfileSocket {
54 private Profile prof = null; // the latest that we sent to client.
55
56 // should all be final, except that we can only set them when start is
57 // called...
58 private String profilename;
59 private Listener<ChangeEvent> changeListener;
60 private Session session;
61 private Map<String, String> params = Collections.emptyMap();
62
63 @OnOpen
64 public void start(Session session, @PathParam("domain") final String domain,
65 @PathParam("profile") final String profile) throws IOException {
66 this.session = session;
67 this.profilename = domain + "/" + profile;
68
69 if (session.getQueryString() != null) {
70 List<String> paramstrings = Arrays
71 .asList(session.getQueryString().split("&"));
72 params = paramstrings.stream().map(str -> str.split("=")).collect(
73 Collectors.toMap(vals -> vals[0], vals -> vals[1]));
74 }
75 changeListener = new Listener<ChangeEvent>() {
76 @Override
77 public void notifyChange(ChangeEvent data) {
78 sendupdatedProfile();
79 }
80 };
81 sendupdatedProfile();
82 Profiles.factory.addListener(changeListener);
83 }
84
85 private void sendupdatedProfile() {
86 Profile newprof = Profiles.factory.getProfile(profilename);
87 // notice, may be null if profile does not exist/was removed.
88 if (newprof == null ? prof != null : !newprof.equals(prof)) {
89 prof = newprof;
90 try {
91 session.getBasicRemote().sendText(
92 Jackson.instance().writeValueAsString(filter(prof)));
93 } catch (Exception e) {
94 e.printStackTrace();
95 }
96 }
97 }
98
99 private Profile filter(Profile prof1) {
100 String partial = params.get("partial");
101 if (partial == null) {
102 return prof1;
103 }
104 if (!(prof1 instanceof PartialOrdering))
105 throw new IllegalArgumentException(
106 "profile must be partialordering but got " + prof);
107 PartialOrdering profile = (PartialOrdering) prof1;
108 final int numbids = Integer.parseInt(partial);
109 if (numbids < 0)
110 throw new IllegalArgumentException("parameter partial must be >=0");
111
112 ImmutableList<Bid> allbids;
113 if (profile instanceof DefaultPartialOrdering)
114 allbids = new FixedList<Bid>(
115 ((DefaultPartialOrdering) profile).getBids());
116 else
117 allbids = new AllBidsList(prof.getDomain());
118
119 if (BigInteger.valueOf(numbids).compareTo(allbids.size()) > 0)
120 throw new IllegalArgumentException("Request for " + numbids
121 + " exceeds number of bids in the space " + allbids.size());
122
123 BidsWithUtility info = new BidsWithUtility((LinearAdditive) prof);
124 Set<Bid> selected = new HashSet<>();
125 if (numbids > 0) {
126 selected.add(info.getExtremeBid(true));
127 }
128 if (numbids > 1) {
129 selected.add(info.getExtremeBid(false));
130 }
131 Random random = new Random();
132 for (int nr = 2; nr < numbids; nr++) {
133 int attempt = 0;
134 Bid bid;
135 do {
136 long i = random.nextInt(allbids.size().intValue());
137 bid = allbids.get(BigInteger.valueOf(i));
138 } while (attempt++ < 10 && selected.contains(bid));
139 selected.add(bid);
140 }
141
142 // put comparison info for these bids in the map
143 Map<Bid, Set<Bid>> isBetterMap = new HashMap<>();
144 for (Bid bid : selected) {
145 Set<Bid> worse = selected.stream().filter(
146 otherbid -> profile.isPreferredOrEqual(bid, otherbid)
147 && !profile.isPreferredOrEqual(otherbid, bid))
148 .collect(Collectors.toSet());
149 isBetterMap.put(bid, worse);
150 }
151 return new DefaultPartialOrdering(
152 prof.getName() + "-partial-" + numbids, prof.getDomain(),
153 prof.getReservationBid(), isBetterMap);
154 }
155
156 @OnClose
157 public void end() throws IOException {
158 Profiles.factory.removeListener(changeListener);
159 }
160
161 @OnError
162 public void onError(Throwable t) throws Throwable {
163 t.printStackTrace();
164 }
165
166}
Note: See TracBrowser for help on using the repository browser.