diff --git a/bundles/org.openhab.io.openhabcloud/pom.xml b/bundles/org.openhab.io.openhabcloud/pom.xml
index dc347e243dd32..3e742ab68008c 100644
--- a/bundles/org.openhab.io.openhabcloud/pom.xml
+++ b/bundles/org.openhab.io.openhabcloud/pom.xml
@@ -36,13 +36,13 @@
org.openhab.osgiify
io.socket.socket.io-client
- 1.0.0
+ 1.0.1
compile
org.openhab.osgiify
io.socket.engine.io-client
- 1.0.0
+ 1.0.1
compile
diff --git a/bundles/org.openhab.io.openhabcloud/src/main/feature/feature.xml b/bundles/org.openhab.io.openhabcloud/src/main/feature/feature.xml
index c6971bc30a15f..8dfbe53750d49 100644
--- a/bundles/org.openhab.io.openhabcloud/src/main/feature/feature.xml
+++ b/bundles/org.openhab.io.openhabcloud/src/main/feature/feature.xml
@@ -9,8 +9,8 @@
mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.jsr305/3.0.2_1
mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.okhttp/3.8.1_1
mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.okio/1.13.0_1
- mvn:org.openhab.osgiify/io.socket.socket.io-client/1.0.0
- mvn:org.openhab.osgiify/io.socket.engine.io-client/1.0.0
+ mvn:org.openhab.osgiify/io.socket.socket.io-client/1.0.1
+ mvn:org.openhab.osgiify/io.socket.engine.io-client/1.0.1
mvn:org.openhab.addons.bundles/org.openhab.io.openhabcloud/${project.version}
diff --git a/bundles/org.openhab.io.openhabcloud/src/main/java/org/openhab/io/openhabcloud/internal/CloudClient.java b/bundles/org.openhab.io.openhabcloud/src/main/java/org/openhab/io/openhabcloud/internal/CloudClient.java
index 54b6e1ae93b25..5473fec5ecdc2 100644
--- a/bundles/org.openhab.io.openhabcloud/src/main/java/org/openhab/io/openhabcloud/internal/CloudClient.java
+++ b/bundles/org.openhab.io.openhabcloud/src/main/java/org/openhab/io/openhabcloud/internal/CloudClient.java
@@ -41,11 +41,13 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import io.socket.backo.Backoff;
import io.socket.client.IO;
import io.socket.client.Manager;
import io.socket.client.Socket;
import io.socket.emitter.Emitter;
import io.socket.engineio.client.Transport;
+import io.socket.thread.EventThread;
/**
* This class provides communication between openHAB and the openHAB Cloud service.
@@ -122,6 +124,11 @@ public class CloudClient {
private boolean remoteAccessEnabled;
private Set exposedItems;
+ /**
+ * Back-off strategy for reconnecting when manual reconnection is needed
+ */
+ private final Backoff reconnectBackoff = new Backoff();
+
/**
* Constructor of CloudClient
*
@@ -139,6 +146,9 @@ public CloudClient(HttpClient httpClient, String uuid, String secret, String bas
this.remoteAccessEnabled = remoteAccessEnabled;
this.exposedItems = exposedItems;
this.jettyClient = httpClient;
+ reconnectBackoff.setMin(1000);
+ reconnectBackoff.setMax(30_000);
+ reconnectBackoff.setJitter(0.5);
}
/**
@@ -174,6 +184,25 @@ public void call(Object... args) {
}
});
}
+ }).on(Manager.EVENT_CONNECT_ERROR, new Emitter.Listener() {
+
+ @Override
+ public void call(Object... args) {
+ if (args.length > 0) {
+ if (args[0] instanceof Exception) {
+ Exception e = (Exception) args[0];
+ logger.debug(
+ "Error connecting to the openHAB Cloud instance: {} {}. Should reconnect automatically.",
+ e.getClass().getSimpleName(), e.getMessage());
+ } else {
+ logger.debug(
+ "Error connecting to the openHAB Cloud instance: {}. Should reconnect automatically.",
+ args[0]);
+ }
+ } else {
+ logger.debug("Error connecting to the openHAB Cloud instance. Should reconnect automatically.");
+ }
+ }
});
socket.on(Socket.EVENT_CONNECT, new Emitter.Listener() {
@Override
@@ -182,20 +211,86 @@ public void call(Object... args) {
isConnected = true;
onConnect();
}
+ }).on(Socket.EVENT_CONNECTING, new Emitter.Listener() {
+ @Override
+ public void call(Object... args) {
+ logger.debug("Socket.IO connecting");
+ }
+ }).on(Socket.EVENT_RECONNECTING, new Emitter.Listener() {
+ @Override
+ public void call(Object... args) {
+ logger.debug("Socket.IO re-connecting (attempt {})", args[0]);
+ }
+ }).on(Socket.EVENT_RECONNECT, new Emitter.Listener() {
+ @Override
+ public void call(Object... args) {
+ logger.debug("Socket.IO re-connected successfully (attempt {})", args[0]);
+ }
+ }).on(Socket.EVENT_RECONNECT_ERROR, new Emitter.Listener() {
+ @Override
+ public void call(Object... args) {
+ if (args[0] instanceof Exception) {
+ Exception e = (Exception) args[0];
+ logger.debug("Socket.IO re-connect attempt error: {} {}", e.getClass().getSimpleName(),
+ e.getMessage());
+ } else {
+ logger.debug("Socket.IO re-connect attempt error: {}", args[0]);
+ }
+ }
+ }).on(Socket.EVENT_RECONNECT_FAILED, new Emitter.Listener() {
+ @Override
+ public void call(Object... args) {
+ logger.debug("Socket.IO re-connect attempts failed. Stopping reconnection.");
+ }
}).on(Socket.EVENT_DISCONNECT, new Emitter.Listener() {
@Override
public void call(Object... args) {
- logger.debug("Socket.IO disconnected");
+ if (args.length > 0) {
+ logger.debug("Socket.IO disconnected: {}", args[0]);
+ } else {
+ logger.debug("Socket.IO disconnected");
+ }
isConnected = false;
onDisconnect();
}
}).on(Socket.EVENT_ERROR, new Emitter.Listener() {
@Override
public void call(Object... args) {
- if (logger.isDebugEnabled()) {
- logger.error("Error connecting to the openHAB Cloud instance: {}", args[0]);
+ if (CloudClient.this.socket.connected()) {
+ if (logger.isDebugEnabled() && args.length > 0) {
+ logger.error("Error during communication: {}", args[0]);
+ } else {
+ logger.error("Error during communication");
+ }
} else {
- logger.error("Error connecting to the openHAB Cloud instance");
+ // We are not connected currently, manual reconnection is needed to keep trying to (re-)establish
+ // connection.
+ //
+ // Socket.IO 1.x java client: 'error' event is emitted from Socket on connection errors that are not
+ // retried, but also with error that are automatically retried. If we
+ //
+ // Note how this is different in Socket.IO 2.x java client, Socket emits 'connect_error' event.
+ // OBS: Don't get confused with Socket IO 2.x docs online, in 1.x connect_error is emitted also on
+ // errors that are retried by the library automatically!
+ long delay = reconnectBackoff.duration();
+ // Try reconnecting on connection errors
+ if (logger.isDebugEnabled() && args.length > 0) {
+ if (args[0] instanceof Exception) {
+ Exception e = (Exception) args[0];
+ logger.error(
+ "Error connecting to the openHAB Cloud instance: {} {}. Reconnecting after {} ms.",
+ e.getClass().getSimpleName(), e.getMessage(), delay);
+ } else {
+ logger.error(
+ "Error connecting to the openHAB Cloud instance: {}. Reconnecting after {} ms.",
+ args[0], delay);
+ }
+ } else {
+ logger.error("Error connecting to the openHAB Cloud instance. Reconnecting.");
+ }
+ socket.close();
+ sleepSocketIO(delay);
+ socket.connect();
}
}
}).on("request", new Emitter.Listener() {
@@ -224,6 +319,7 @@ public void call(Object... args) {
public void onConnect() {
logger.info("Connected to the openHAB Cloud service (UUID = {}, base URL = {})", this.uuid, this.localBaseUrl);
+ reconnectBackoff.reset();
isConnected = true;
}
@@ -578,4 +674,14 @@ private JSONObject getJSONHeaders(HttpFields httpFields) {
}
return headersJSON;
}
+
+ private void sleepSocketIO(long delay) {
+ EventThread.exec(() -> {
+ try {
+ Thread.sleep(delay);
+ } catch (InterruptedException e) {
+
+ }
+ });
+ }
}