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

Last change on this file since 16 was 16, checked in by bart, 4 years ago

Version 1.5.

File size: 5.7 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.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 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. The reservation bid is passed through unmodified.
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.repository.addListener(changeListener);
83 }
84
85 private void sendupdatedProfile() {
86 Profile newprof = Profiles.repository.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 /**
100 *
101 * @param prof1
102 * @return a filtered profile. If "partial" is not set, this returns the
103 * given profile unmodified. If partial is set, a
104 * DefaultPartialOrdering with the requested number of bids are
105 * selected
106 */
107 private Profile filter(Profile prof1) {
108 String partial = params.get("partial");
109 if (partial == null) {
110 return prof1;
111 }
112 if (!(prof1 instanceof PartialOrdering))
113 throw new IllegalArgumentException(
114 "profile must be partialordering but got " + prof);
115 PartialOrdering profile = (PartialOrdering) prof1;
116 final int numbids = Integer.parseInt(partial);
117 if (numbids < 0)
118 throw new IllegalArgumentException("parameter partial must be >=0");
119
120 ImmutableList<Bid> allbids;
121 if (profile instanceof DefaultPartialOrdering)
122 allbids = new FixedList<Bid>(
123 ((DefaultPartialOrdering) profile).getBids());
124 else
125 allbids = new AllBidsList(prof.getDomain());
126
127 if (BigInteger.valueOf(numbids).compareTo(allbids.size()) > 0)
128 throw new IllegalArgumentException("Request for " + numbids
129 + " exceeds number of bids in the space " + allbids.size());
130
131 BidsWithUtility info = new BidsWithUtility((LinearAdditive) prof);
132 Set<Bid> selected = new HashSet<>();
133 if (numbids > 0) {
134 selected.add(info.getExtremeBid(true));
135 }
136 if (numbids > 1) {
137 selected.add(info.getExtremeBid(false));
138 }
139 Random random = new Random();
140 for (int nr = 2; nr < numbids; nr++) {
141 int attempt = 0;
142 Bid bid;
143 do {
144 long i = random.nextInt(allbids.size().intValue());
145 bid = allbids.get(BigInteger.valueOf(i));
146 } while (attempt++ < 10 && selected.contains(bid));
147 selected.add(bid);
148 }
149
150 // put comparison info for these bids in the map
151 Map<Bid, Set<Bid>> isBetterMap = new HashMap<>();
152 for (Bid bid : selected) {
153 Set<Bid> worse = selected.stream().filter(
154 otherbid -> profile.isPreferredOrEqual(bid, otherbid)
155 && !profile.isPreferredOrEqual(otherbid, bid))
156 .collect(Collectors.toSet());
157 isBetterMap.put(bid, worse);
158 }
159 return new DefaultPartialOrdering(
160 prof.getName() + "-partial-" + numbids, prof.getDomain(),
161 prof.getReservationBid(), isBetterMap);
162 }
163
164 @OnClose
165 public void end() throws IOException {
166 Profiles.repository.removeListener(changeListener);
167 }
168
169 @OnError
170 public void onError(Throwable t) throws Throwable {
171 t.printStackTrace();
172 }
173
174}
Note: See TracBrowser for help on using the repository browser.