mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-15 16:51:07 +00:00
successful web socket Igtimi connection test with timestamp and heartbeat
This commit is contained in:
1 parent
1edba6922c
commit
c5c86e91d4
5 files changed
+67
-24
No files matched your search
+10
-2
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.igtimiadapter.websocket;
|
|||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
@@ -13,12 +14,14 @@ import java.util.concurrent.ExecutionException;
|
|||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.eclipse.jetty.websocket.api.Session;
|
import org.eclipse.jetty.websocket.api.Session;
|
||||||
import org.eclipse.jetty.websocket.api.StatusCode;
|
import org.eclipse.jetty.websocket.api.StatusCode;
|
||||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
|
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
|
||||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
|
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
|
||||||
|
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
|
||||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
|
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
|
||||||
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
||||||
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
||||||
@@ -79,6 +82,11 @@ public class WebSocketTest {
|
|||||||
session.close(StatusCode.NORMAL, "I'm done");
|
session.close(StatusCode.NORMAL, "I'm done");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OnWebSocketError
|
||||||
|
public void onError(Throwable cause) {
|
||||||
|
logger.log(Level.SEVERE, "Error with web socket connection", cause);
|
||||||
|
}
|
||||||
|
|
||||||
@OnWebSocketMessage
|
@OnWebSocketMessage
|
||||||
public void onMessage(String msg) {
|
public void onMessage(String msg) {
|
||||||
logger.info("Received "+msg);
|
logger.info("Received "+msg);
|
||||||
@@ -96,7 +104,7 @@ public class WebSocketTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void simpleWebSockeEchoTest() throws Exception {
|
public void simpleWebSockeEchoTest() throws Exception {
|
||||||
String destUri = "ws://echo.websocket.org";
|
String destUri = "ws://echo.websocket.org"; // wss currently doesn't seem to work with Jetty 9.0.4 WebSocket implementation
|
||||||
WebSocketClient client = new WebSocketClient();
|
WebSocketClient client = new WebSocketClient();
|
||||||
SimpleEchoTestSocket socket = new SimpleEchoTestSocket();
|
SimpleEchoTestSocket socket = new SimpleEchoTestSocket();
|
||||||
try {
|
try {
|
||||||
@@ -137,7 +145,7 @@ public class WebSocketTest {
|
|||||||
Account account = connectionFactory.registerAccountForWhichClientIsAuthorized("3b6cbd0522423bb1ac274ddb9e7e579c4b3be6667622271086c4fdbf30634ba9");
|
Account account = connectionFactory.registerAccountForWhichClientIsAuthorized("3b6cbd0522423bb1ac274ddb9e7e579c4b3be6667622271086c4fdbf30634ba9");
|
||||||
WebSocketConnectionManager manager = new WebSocketConnectionManager(connectionFactory, Collections.singleton("GA-EN-AAEJ"), account);
|
WebSocketConnectionManager manager = new WebSocketConnectionManager(connectionFactory, Collections.singleton("GA-EN-AAEJ"), account);
|
||||||
assertNotNull(manager);
|
assertNotNull(manager);
|
||||||
Thread.sleep(1000000);
|
assertTrue("Connection handshake not successful within 5s", manager.waitForConnection(5000));
|
||||||
manager.disconnect();
|
manager.disconnect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+2
-3
@@ -80,9 +80,8 @@ public interface IgtimiConnectionFactory {
|
|||||||
*
|
*
|
||||||
* @param account
|
* @param account
|
||||||
* represents this factory's client's permissions to access a user account's data
|
* represents this factory's client's permissions to access a user account's data
|
||||||
* @param unitIds
|
* @param deviceIds
|
||||||
* IDs of the transmitting units expected to be visible to the account's {@link Account#getUser() user's}
|
* IDs of the transmitting units expected to be visible to the account's {@link Account#getUser() user's}
|
||||||
* .
|
|
||||||
*/
|
*/
|
||||||
JSONObject getWebSocketConfigurationMessage(Account account, Iterable<String> unitIds);
|
JSONObject getWebSocketConfigurationMessage(Account account, Iterable<String> deviceIds);
|
||||||
}
|
}
|
||||||
+1
-1
@@ -34,7 +34,7 @@ public class FixFactory {
|
|||||||
JSONArray timePointsMillis = (JSONArray) fixesJson.get("t");
|
JSONArray timePointsMillis = (JSONArray) fixesJson.get("t");
|
||||||
int fixIndex = 0;
|
int fixIndex = 0;
|
||||||
for (Object timePointMillis : timePointsMillis) {
|
for (Object timePointMillis : timePointsMillis) {
|
||||||
TimePoint timePoint = new MillisecondsTimePoint((Long) timePointMillis);
|
TimePoint timePoint = new MillisecondsTimePoint(((Number) timePointMillis).longValue());
|
||||||
Map<Integer, Object> valuesPerSubindex = new HashMap<>();
|
Map<Integer, Object> valuesPerSubindex = new HashMap<>();
|
||||||
int i=1;
|
int i=1;
|
||||||
JSONArray values;
|
JSONArray values;
|
||||||
|
|||||||
+4
-4
@@ -128,10 +128,10 @@ public class IgtimiConnectionFactoryImpl implements IgtimiConnectionFactory {
|
|||||||
public JSONObject getWebSocketConfigurationMessage(Account account, Iterable<String> deviceIds) {
|
public JSONObject getWebSocketConfigurationMessage(Account account, Iterable<String> deviceIds) {
|
||||||
JSONObject result = new JSONObject();
|
JSONObject result = new JSONObject();
|
||||||
result.put("access_token", getAccessTokenForAccount(account));
|
result.put("access_token", getAccessTokenForAccount(account));
|
||||||
JSONArray unitIdsJson = new JSONArray();
|
JSONArray deviceIdsJson = new JSONArray();
|
||||||
result.put("devices", unitIdsJson);
|
result.put("devices", deviceIdsJson);
|
||||||
for (String unitId : deviceIds) {
|
for (String deviceId : deviceIds) {
|
||||||
unitIdsJson.add(unitId);
|
deviceIdsJson.add(deviceId);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
+50
-14
@@ -40,7 +40,7 @@ public class WebSocketConnectionManager extends WebSocketAdapter {
|
|||||||
private final ClientUpgradeRequest request;
|
private final ClientUpgradeRequest request;
|
||||||
private final JSONObject configurationMessage;
|
private final JSONObject configurationMessage;
|
||||||
private final Timer timer;
|
private final Timer timer;
|
||||||
private final Iterable<String> unitIds;
|
private final Iterable<String> deviceIds;
|
||||||
private final Account account;
|
private final Account account;
|
||||||
private final FixFactory fixFactory;
|
private final FixFactory fixFactory;
|
||||||
private boolean receivedServerHeartbeatInInterval;
|
private boolean receivedServerHeartbeatInInterval;
|
||||||
@@ -48,20 +48,39 @@ public class WebSocketConnectionManager extends WebSocketAdapter {
|
|||||||
private TimePoint igtimiServerTimepoint;
|
private TimePoint igtimiServerTimepoint;
|
||||||
private TimePoint localTimepointWhenServerTimepointWasReceived;
|
private TimePoint localTimepointWhenServerTimepointWasReceived;
|
||||||
|
|
||||||
public WebSocketConnectionManager(IgtimiConnectionFactory connectionFactory, Iterable<String> unitIds, Account account) throws Exception {
|
public WebSocketConnectionManager(IgtimiConnectionFactory connectionFactory, Iterable<String> deviceIds, Account account) throws Exception {
|
||||||
this.timer = new Timer("Timer for WebSocketConnectionManager for units "+unitIds+" and account "+account);
|
this.timer = new Timer("Timer for WebSocketConnectionManager for units "+deviceIds+" and account "+account);
|
||||||
this.unitIds = unitIds;
|
this.deviceIds = deviceIds;
|
||||||
this.account = account;
|
this.account = account;
|
||||||
this.fixFactory = new FixFactory();
|
this.fixFactory = new FixFactory();
|
||||||
this.connectionFactory = connectionFactory;
|
this.connectionFactory = connectionFactory;
|
||||||
this.liveDataListeners = new ConcurrentSkipListSet<>();
|
this.liveDataListeners = new ConcurrentSkipListSet<>();
|
||||||
client = new WebSocketClient();
|
client = new WebSocketClient();
|
||||||
configurationMessage = connectionFactory.getWebSocketConfigurationMessage(account, unitIds);
|
configurationMessage = connectionFactory.getWebSocketConfigurationMessage(account, deviceIds);
|
||||||
request = new ClientUpgradeRequest();
|
request = new ClientUpgradeRequest();
|
||||||
client.start();
|
client.start();
|
||||||
reconnect();
|
reconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waits until the web socket connection has been established and the initial configuration handshake has
|
||||||
|
* successfully completed. Technically, this means that the Igtimi server timestamp has successfully been received
|
||||||
|
* and parsed. {@link #getIgtimiServerTimePointAndWhenItWasReceived()} then holds a valid time point.
|
||||||
|
*
|
||||||
|
* @param timeoutInMillis
|
||||||
|
* use 0 to wait indefinitely
|
||||||
|
* @return <code>true</code> if the connection is established before the timeout occurred
|
||||||
|
*/
|
||||||
|
public boolean waitForConnection(long timeoutInMillis) throws InterruptedException {
|
||||||
|
long startedToWait = System.currentTimeMillis();
|
||||||
|
synchronized (this) {
|
||||||
|
while (igtimiServerTimepoint == null && System.currentTimeMillis() - startedToWait < timeoutInMillis) {
|
||||||
|
wait(timeoutInMillis);
|
||||||
|
}
|
||||||
|
return igtimiServerTimepoint != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void addLiveDataListener(LiveDataListener listener) {
|
public void addLiveDataListener(LiveDataListener listener) {
|
||||||
liveDataListeners.add(listener);
|
liveDataListeners.add(listener);
|
||||||
}
|
}
|
||||||
@@ -72,7 +91,9 @@ public class WebSocketConnectionManager extends WebSocketAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onWebSocketConnect(Session sess) {
|
public void onWebSocketConnect(Session session) {
|
||||||
|
super.onWebSocketConnect(session);
|
||||||
|
logger.info("received connection "+session+" for "+this);
|
||||||
try {
|
try {
|
||||||
getRemote().sendString(configurationMessage.toString());
|
getRemote().sendString(configurationMessage.toString());
|
||||||
startClientHeartbeat();
|
startClientHeartbeat();
|
||||||
@@ -85,7 +106,11 @@ public class WebSocketConnectionManager extends WebSocketAdapter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onWebSocketText(String message) {
|
public void onWebSocketText(String message) {
|
||||||
|
if (logger.isLoggable(Level.FINEST)) {
|
||||||
|
logger.finest("Received "+message+" in "+this);
|
||||||
|
}
|
||||||
if (message.equals("1")) {
|
if (message.equals("1")) {
|
||||||
|
logger.fine("Received server heartbeat");
|
||||||
receivedServerHeartbeatInInterval = true;
|
receivedServerHeartbeatInInterval = true;
|
||||||
} else if (message.startsWith("[")) {
|
} else if (message.startsWith("[")) {
|
||||||
List<Fix> fixes = new ArrayList<>();
|
List<Fix> fixes = new ArrayList<>();
|
||||||
@@ -94,14 +119,21 @@ public class WebSocketConnectionManager extends WebSocketAdapter {
|
|||||||
for (Object o : jsonArray) {
|
for (Object o : jsonArray) {
|
||||||
Util.addAll(fixFactory.createFixes((JSONObject) o), fixes);
|
Util.addAll(fixFactory.createFixes((JSONObject) o), fixes);
|
||||||
}
|
}
|
||||||
|
if (logger.isLoggable(Level.FINEST)) {
|
||||||
|
logger.finest("Received fixes"+fixes);
|
||||||
|
}
|
||||||
notifyListeners(fixes);
|
notifyListeners(fixes);
|
||||||
} catch (ParseException e) {
|
} catch (ParseException e) {
|
||||||
logger.log(Level.SEVERE, "Error trying to parse a web socket data package coming from Igtimi "+this, e);
|
logger.log(Level.SEVERE, "Error trying to parse a web socket data package coming from Igtimi "+this, e);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// try to parse server time stamp in response to the configuration message
|
// try to parse server time stamp in response to the configuration message
|
||||||
igtimiServerTimepoint = new MillisecondsTimePoint(Long.valueOf(message));
|
synchronized (this) {
|
||||||
localTimepointWhenServerTimepointWasReceived = MillisecondsTimePoint.now();
|
igtimiServerTimepoint = new MillisecondsTimePoint(Long.valueOf(message));
|
||||||
|
localTimepointWhenServerTimepointWasReceived = MillisecondsTimePoint.now();
|
||||||
|
logger.info("Received server timestamp "+igtimiServerTimepoint);
|
||||||
|
notifyAll();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +165,7 @@ public class WebSocketConnectionManager extends WebSocketAdapter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Web Socket Connection Manager for units "+unitIds+" and account "+account+" with web socket session "+getSession();
|
return "Web Socket Connection Manager for devices "+deviceIds+" and account "+account+" with web socket session "+getSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startListeningForServerHeartbeat() {
|
private void startListeningForServerHeartbeat() {
|
||||||
@@ -157,6 +189,7 @@ public class WebSocketConnectionManager extends WebSocketAdapter {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
|
logger.fine("Sending client heartbeat");
|
||||||
getRemote().sendString("1");
|
getRemote().sendString("1");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.log(Level.WARNING, "Couldn't send heartbeat to Igtimi web socket session in "+this, e);
|
logger.log(Level.WARNING, "Couldn't send heartbeat to Igtimi web socket session in "+this, e);
|
||||||
@@ -169,11 +202,14 @@ public class WebSocketConnectionManager extends WebSocketAdapter {
|
|||||||
IOException lastException = null;
|
IOException lastException = null;
|
||||||
for (URI uri : connectionFactory.getWebsocketServers()) {
|
for (URI uri : connectionFactory.getWebsocketServers()) {
|
||||||
try {
|
try {
|
||||||
logger.log(Level.INFO, "Trying to connect to "+uri+" for "+this);
|
if (uri.getScheme().equals("ws")) { // as Jetty 9.0.4 currently doesn't seem to support wss, explicitly
|
||||||
client.connect(this, uri, request);
|
// look for ws connectivity
|
||||||
logger.log(Level.INFO, "Successfully connected to "+uri+" for "+this);
|
logger.log(Level.INFO, "Trying to connect to " + uri + " for " + this);
|
||||||
lastException = null;
|
client.connect(this, uri, request);
|
||||||
break; // successfully connected
|
logger.log(Level.INFO, "Successfully connected to " + uri + " for " + this);
|
||||||
|
lastException = null;
|
||||||
|
break; // successfully connected
|
||||||
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.log(Level.INFO, "Couldn't connect to "+uri+" for "+this, e);
|
logger.log(Level.INFO, "Couldn't connect to "+uri+" for "+this, e);
|
||||||
lastException = e;
|
lastException = e;
|
||||||
|
|||||||
Reference in new issue
Block a user