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

Last change on this file since 39 was 39, checked in by bart, 3 years ago

Refactor to help reusing partiesserver.

File size: 5.9 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.Constants;
30import geniusweb.profilesserver.ProfilesRepository;
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 ProfilesRepository}.
41 *
42 * <p>
43 * Query strings: the websocket allows query strings. Allowed:
44 * <table>
45 * <caption>Query strings for GetprofileSocket</caption>
46 * <tr>
47 * <th>query name</th>
48 * <th>description</th>
49 * </tr>
50 * <tr>
51 * <td>partial=N</td>
52 * <td>a query key that is assigned a natural number N (0 or larger). If set,
53 * the profile is converted into a Partial Profile with the given number of
54 * points. If N&gt;0, the maximum utility bid is included .If N&gt;1, the
55 * minimum utility bid is included. The other bids are picked at random. This
56 * filtering mechanism only works if the profile is {@link LinearAdditive}
57 * space. The reservation bid is passed through unmodified.</td>
58 * </tr>
59 * </table>
60 */
61@ServerEndpoint("/websocket/get/{domain}/{profile}")
62public class GetProfileSocket {
63 private Profile prof = null; // the latest that we sent to client.
64
65 // should all be final, except that we can only set them when start is
66 // called...
67 private String profilename;
68 private Listener<ChangeEvent> changeListener;
69 private Session session;
70 private Map<String, String> params = Collections.emptyMap();
71
72 @OnOpen
73 public void start(Session session, @PathParam("domain") final String domain,
74 @PathParam("profile") final String profile) throws IOException {
75 this.session = session;
76 this.profilename = domain + "/" + profile;
77
78 if (session.getQueryString() != null) {
79 List<String> paramstrings = Arrays
80 .asList(session.getQueryString().split("&"));
81 params = paramstrings.stream().map(str -> str.split("=")).collect(
82 Collectors.toMap(vals -> vals[0], vals -> vals[1]));
83 }
84 changeListener = new Listener<ChangeEvent>() {
85 @Override
86 public void notifyChange(ChangeEvent data) {
87 sendupdatedProfile();
88 }
89 };
90 sendupdatedProfile();
91 Profiles.repository.addListener(changeListener);
92 }
93
94 private void sendupdatedProfile() {
95 Profile newprof = Profiles.repository.getProfile(profilename);
96 // notice, may be null if profile does not exist/was removed.
97 if (newprof == null ? prof != null : !newprof.equals(prof)) {
98 prof = newprof;
99 try {
100 session.getBasicRemote().sendText(
101 Constants.getJackson().writeValueAsString(filter(prof)));
102 } catch (Exception e) {
103 e.printStackTrace();
104 }
105 }
106 }
107
108 /**
109 *
110 * @param prof1
111 * @return a filtered profile. If "partial" is not set, this returns the
112 * given profile unmodified. If partial is set, a
113 * DefaultPartialOrdering with the requested number of bids are
114 * selected
115 */
116 private Profile filter(Profile prof1) {
117 String partial = params.get("partial");
118 if (partial == null) {
119 return prof1;
120 }
121 if (!(prof1 instanceof PartialOrdering))
122 throw new IllegalArgumentException(
123 "profile must be partialordering but got " + prof);
124 PartialOrdering profile = (PartialOrdering) prof1;
125 final int numbids = Integer.parseInt(partial);
126 if (numbids < 0)
127 throw new IllegalArgumentException("parameter partial must be >=0");
128
129 ImmutableList<Bid> allbids;
130 if (profile instanceof DefaultPartialOrdering)
131 allbids = new FixedList<Bid>(
132 ((DefaultPartialOrdering) profile).getBids());
133 else
134 allbids = new AllBidsList(prof.getDomain());
135
136 if (BigInteger.valueOf(numbids).compareTo(allbids.size()) > 0)
137 throw new IllegalArgumentException("Request for " + numbids
138 + " exceeds number of bids in the space " + allbids.size());
139
140 BidsWithUtility info = new BidsWithUtility((LinearAdditive) prof);
141 Set<Bid> selected = new HashSet<>();
142 if (numbids > 0) {
143 selected.add(info.getExtremeBid(true));
144 }
145 if (numbids > 1) {
146 selected.add(info.getExtremeBid(false));
147 }
148 Random random = new Random();
149 for (int nr = 2; nr < numbids; nr++) {
150 int attempt = 0;
151 Bid bid;
152 do {
153 long i = random.nextInt(allbids.size().intValue());
154 bid = allbids.get(BigInteger.valueOf(i));
155 } while (attempt++ < 10 && selected.contains(bid));
156 selected.add(bid);
157 }
158
159 // put comparison info for these bids in the map
160 Map<Bid, Set<Bid>> isBetterMap = new HashMap<>();
161 for (Bid bid : selected) {
162 Set<Bid> worse = selected.stream().filter(
163 otherbid -> profile.isPreferredOrEqual(bid, otherbid)
164 && !profile.isPreferredOrEqual(otherbid, bid))
165 .collect(Collectors.toSet());
166 isBetterMap.put(bid, worse);
167 }
168 return new DefaultPartialOrdering(
169 prof.getName() + "-partial-" + numbids, prof.getDomain(),
170 prof.getReservationBid(), isBetterMap);
171 }
172
173 @OnClose
174 public void end() throws IOException {
175 Profiles.repository.removeListener(changeListener);
176 }
177
178 @OnError
179 public void onError(Throwable t) throws Throwable {
180 t.printStackTrace();
181 }
182
183}
Note: See TracBrowser for help on using the repository browser.