source: src/main/java/geniusweb/runserver/PartyConnection.java@ 10

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

Fixed performance issue with some computers

File size: 6.4 KB
Line 
1package geniusweb.runserver;
2
3import java.io.BufferedReader;
4import java.io.IOException;
5import java.io.InputStreamReader;
6import java.net.HttpURLConnection;
7import java.net.SocketException;
8import java.net.URI;
9import java.net.URISyntaxException;
10import java.util.logging.Level;
11
12import javax.websocket.ClientEndpoint;
13import javax.websocket.CloseReason;
14import javax.websocket.ContainerProvider;
15import javax.websocket.DeploymentException;
16import javax.websocket.OnClose;
17import javax.websocket.OnError;
18import javax.websocket.OnMessage;
19import javax.websocket.OnOpen;
20import javax.websocket.Session;
21import javax.websocket.WebSocketContainer;
22
23import com.fasterxml.jackson.databind.ObjectMapper;
24
25import geniusweb.actions.Action;
26import geniusweb.actions.PartyId;
27import geniusweb.party.inform.Inform;
28import geniusweb.protocol.partyconnection.ConnectionWithParty;
29import geniusweb.references.Reference;
30import tudelft.utilities.listener.DefaultListenable;
31import tudelft.utilities.logging.ReportToLogger;
32import tudelft.utilities.logging.Reporter;
33
34/**
35 * A websocket based {@link ConnectionWithParty}. it contains a websocket
36 * connection to a party that runs on a runserver. Must be public so that it can
37 * be accessed as ClientEndpoint by apache tomcat. This is internally used.
38 */
39@ClientEndpoint
40public class PartyConnection extends DefaultListenable<Action>
41 implements ConnectionWithParty {
42 private final static ObjectMapper jackson = new ObjectMapper();
43 private final Reporter log;
44
45 private final Reference reference;
46
47 private WebSocketContainer container;
48 private Session session = null;
49 private URI partyuri;
50 private Throwable error = null;
51
52 /**
53 * Constructor that uses default {@link Reporter}
54 *
55 * @param reference the address to to a http GET to get a websocket to a new
56 * running party.
57 */
58 public PartyConnection(Reference reference) {
59 this(reference, new ReportToLogger("runserver"));
60 }
61
62 /**
63 *
64 * @param reference the address to to a http GET to get a websocket to a new
65 * running party.
66 * @param reporter the {@link Reporter} used for logging important
67 * events/issues
68 * @throws IOException if the party does not start properly.
69 */
70 public PartyConnection(Reference reference, Reporter reporter) {
71 this.reference = reference;
72 this.log = reporter;
73
74 }
75
76 public PartyConnection init() throws IOException {
77 log.log(Level.INFO, "Trying to start up party " + reference);
78 URI partyuri = startParty(reference);
79
80 container = ContainerProvider.getWebSocketContainer();
81 log.log(Level.INFO,
82 "Trying to make websocket connection to running party "
83 + partyuri);
84 try {
85 container.connectToServer(this, partyuri);
86 } catch (DeploymentException e) {
87 throw new IOException(
88 "Failed to connect with running party at " + partyuri, e);
89 }
90 log.log(Level.INFO, "Created websocket connection at " + partyuri);
91 return this;
92 }
93
94 /**
95 *
96 * @param reference the address to to a http GET to get a websocket to a new
97 * running party.
98 * @return URI of websocket behind which is a running party
99 * @throws IOException if party did not start correctly
100 */
101 private URI startParty(Reference reference) throws IOException {
102 HttpURLConnection conn = (HttpURLConnection) reference.getURI().toURL()
103 .openConnection();
104 // if this throws, the IOException seems to contain the "retry later"
105 // message already
106 conn.setRequestMethod("GET");
107 conn.setReadTimeout(20000);
108 conn.connect();
109 BufferedReader rd;
110 try {
111 rd = new BufferedReader(
112 new InputStreamReader(conn.getInputStream()));
113 } catch (IOException e) {
114 if (conn.getErrorStream() == null) {
115 throw new IOException("Failed to connect " + reference
116 + ", no server error message", e);
117 }
118 // read the detail message from the error stream
119 rd = new BufferedReader(
120 new InputStreamReader(conn.getErrorStream()));
121 throw new IOException(rd.readLine(), e);
122 }
123 String partysocketaddress = rd.readLine();
124 log.log(Level.INFO, "startparty returned " + partysocketaddress);
125 if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
126 throw new IOException(
127 "Something went wrong connecting to the party:"
128 + conn.getResponseCode());
129 }
130 try {
131 this.partyuri = new URI(partysocketaddress);
132 } catch (URISyntaxException e1) {
133 throw new IOException("Failed to start party at " + reference, e1);
134 }
135 return partyuri;
136 }
137
138 /************* implements {@link ConnectionWithParty} ***************/
139 @Override
140 public void send(Inform info) throws IOException {
141 if (session == null)
142 return;
143 session.getBasicRemote().sendText(jackson.writeValueAsString(info));
144 }
145
146 @Override
147 public Reference getReference() {
148 return this.reference;
149 }
150
151 @Override
152 public PartyId getParty() {
153 String name = partyuri.getPath();
154 return new PartyId(name.substring(name.lastIndexOf('/') + 1));
155 }
156
157 @Override
158 public void close() {
159 log.log(Level.INFO, "Party close called");
160 setError(new SocketException("socket is closed"));
161 try {
162 session.close();
163 this.session = null;
164 } catch (IOException e) {
165 log.log(Level.SEVERE, "failed to close websocket", e);
166 }
167 }
168
169 /************* implements ClientEndpoint ***************/
170
171 @OnOpen
172 public void onOpen(Session session) {
173 log.log(Level.INFO, "Party is being connected: " + session);
174 this.session = session;
175 }
176
177 @OnClose
178 public void onClose(Session userSession, CloseReason reason) {
179 log.log(Level.INFO, "closing websocket " + reference + ":" + reason);
180 setError(new SocketException("socket has been closed"));
181
182 }
183
184 @OnMessage
185 public void processMessage(String message) {
186 try {
187 Action act = jackson.readValue(message, Action.class);
188 notifyChange(act);
189 } catch (IOException e) {
190 log.log(Level.WARNING,
191 "received bad message from client " + reference, e);
192 setError(e);
193 close();
194 }
195 }
196
197 @OnError
198 public void processError(Throwable t) {
199 log.log(Level.SEVERE, "Something went wrong internally!", t);
200 setError(t);
201 }
202
203 @Override
204 public URI getRemoteURI() {
205 return partyuri;
206 }
207
208 @Override
209 public Throwable getError() {
210 return error;
211 }
212
213 /**
214 * Sets the connection to final error state and report null to listeners.
215 * Only the first error is reported, the rest is ignored.
216 *
217 * @param err the error that occurred.
218 */
219 private void setError(Throwable err) {
220 boolean isSet = false;
221 synchronized (this) {
222 if (this.error == null) {
223 this.error = err;
224 isSet = true;
225 }
226 }
227 if (isSet) {
228 notifyChange(null);
229 }
230 }
231
232}
Note: See TracBrowser for help on using the repository browser.