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

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

Initial Release

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