From 57880699d3307b5536160cdde894f406de3121fa Mon Sep 17 00:00:00 2001 From: Sami Salonen Date: Tue, 24 Aug 2021 10:48:13 +0300 Subject: [PATCH 1/7] [openhabcloud] reconnect on connection errors According to documentation (albeit for 2.x Socket IO version) [1], reconnection is responsibility of the user on connect_error events. [1] Lifecycle diagram in https://socketio.github.io/socket.io-client-java/socket_instance.html Signed-off-by: Sami Salonen --- .../openhab/io/openhabcloud/internal/CloudClient.java | 11 +++++++++++ 1 file changed, 11 insertions(+) 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..9d41fa2fee46f 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 @@ -174,6 +174,17 @@ public void call(Object... args) { } }); } + }).on(Manager.EVENT_CONNECT_ERROR, new Emitter.Listener() { + @Override + public void call(Object... args) { + // Try reconnecting on connection errors + if (logger.isDebugEnabled()) { + logger.error("Error connecting to the openHAB Cloud instance: {}. Reconnecting.", args[0]); + } else { + logger.error("Error connecting to the openHAB Cloud instance. Reconnecting."); + } + socket.close().connect(); + } }); socket.on(Socket.EVENT_CONNECT, new Emitter.Listener() { @Override From f3ee8dac5daa5c25e0638c7962195d52cb601cee Mon Sep 17 00:00:00 2001 From: Sami Salonen Date: Tue, 24 Aug 2021 11:00:52 +0300 Subject: [PATCH 2/7] [openhabcloud] Update Socket IO dependency to 1.0.1 Signed-off-by: Sami Salonen --- bundles/org.openhab.io.openhabcloud/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 From 6863deb75ae540756729e14932a2b74d19df435d Mon Sep 17 00:00:00 2001 From: Sami Salonen Date: Wed, 25 Aug 2021 17:32:45 +0300 Subject: [PATCH 3/7] [openhabcloud] feature.xml updated also with socket io 1.0.1 Signed-off-by: Sami Salonen --- .../org.openhab.io.openhabcloud/src/main/feature/feature.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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} From a01a95ef24fabdc5031619b199939a7a8b857460 Mon Sep 17 00:00:00 2001 From: Sami Salonen Date: Wed, 25 Aug 2021 17:33:23 +0300 Subject: [PATCH 4/7] [openhabcloud] Re-connect manually on error events when not connected Signed-off-by: Sami Salonen --- .../io/openhabcloud/internal/CloudClient.java | 110 ++++++++++++++++-- 1 file changed, 101 insertions(+), 9 deletions(-) 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 9d41fa2fee46f..da6ffb72d632e 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; + /** + * Backoff strategy for reconnecting on connect_error events + */ + 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); } /** @@ -175,15 +185,23 @@ public void call(Object... args) { }); } }).on(Manager.EVENT_CONNECT_ERROR, new Emitter.Listener() { + @Override public void call(Object... args) { - // Try reconnecting on connection errors - if (logger.isDebugEnabled()) { - logger.error("Error connecting to the openHAB Cloud instance: {}. Reconnecting.", args[0]); + 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.error("Error connecting to the openHAB Cloud instance. Reconnecting."); + logger.debug("Error connecting to the openHAB Cloud instance. Should reconnect automatically."); } - socket.close().connect(); } }); socket.on(Socket.EVENT_CONNECT, new Emitter.Listener() { @@ -193,20 +211,83 @@ 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.warn("Socket.IO re-connect attempt error: {} {}", e.getClass().getSimpleName(), + e.getMessage()); + } else { + logger.warn("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 (logger.isDebugEnabled() && 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"); + // Socket.IO 1.x: 'error' event is emitted from Socket on connection errors that are not retried + // + // In Socket.IO 2.x, 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() { @@ -235,6 +316,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; } @@ -589,4 +671,14 @@ private JSONObject getJSONHeaders(HttpFields httpFields) { } return headersJSON; } + + private void sleepSocketIO(long delay) { + EventThread.exec(() -> { + try { + Thread.sleep(delay); + } catch (InterruptedException e) { + + } + }); + } } From 9f6e7b64063cb96f2d0f3bfad9bef54bdab71cb8 Mon Sep 17 00:00:00 2001 From: Sami Salonen Date: Wed, 25 Aug 2021 17:49:30 +0300 Subject: [PATCH 5/7] [openhabcloud] less loud logging on retries Signed-off-by: Sami Salonen --- .../org/openhab/io/openhabcloud/internal/CloudClient.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 da6ffb72d632e..e903ac1221ecd 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 @@ -231,10 +231,10 @@ public void call(Object... args) { public void call(Object... args) { if (args[0] instanceof Exception) { Exception e = (Exception) args[0]; - logger.warn("Socket.IO re-connect attempt error: {} {}", e.getClass().getSimpleName(), + logger.debug("Socket.IO re-connect attempt error: {} {}", e.getClass().getSimpleName(), e.getMessage()); } else { - logger.warn("Socket.IO re-connect attempt error: {}", args[0]); + logger.debug("Socket.IO re-connect attempt error: {}", args[0]); } } }).on(Socket.EVENT_RECONNECT_FAILED, new Emitter.Listener() { From 87049838c5b1d05187b407fb33a0bcde71bd5617 Mon Sep 17 00:00:00 2001 From: Sami Salonen Date: Sat, 28 Aug 2021 11:06:17 +0300 Subject: [PATCH 6/7] [openhabcloud] removing unnecessary conditional in logging Signed-off-by: Sami Salonen --- .../java/org/openhab/io/openhabcloud/internal/CloudClient.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 e903ac1221ecd..bd0469e93b86d 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 @@ -245,8 +245,7 @@ public void call(Object... args) { }).on(Socket.EVENT_DISCONNECT, new Emitter.Listener() { @Override public void call(Object... args) { - - if (logger.isDebugEnabled() && args.length > 0) { + if (args.length > 0) { logger.debug("Socket.IO disconnected: {}", args[0]); } else { logger.debug("Socket.IO disconnected"); From dff0bda3c1883c0c9cc0c4f9eea677c40319d083 Mon Sep 17 00:00:00 2001 From: Sami Salonen Date: Mon, 30 Aug 2021 10:55:22 +0300 Subject: [PATCH 7/7] [openhabcloud] javadoc corrections and clarifications Signed-off-by: Sami Salonen --- .../openhab/io/openhabcloud/internal/CloudClient.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) 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 bd0469e93b86d..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 @@ -125,7 +125,7 @@ public class CloudClient { private Set exposedItems; /** - * Backoff strategy for reconnecting on connect_error events + * Back-off strategy for reconnecting when manual reconnection is needed */ private final Backoff reconnectBackoff = new Backoff(); @@ -263,9 +263,13 @@ public void call(Object... args) { logger.error("Error during communication"); } } else { - // Socket.IO 1.x: 'error' event is emitted from Socket on connection errors that are not retried + // We are not connected currently, manual reconnection is needed to keep trying to (re-)establish + // connection. // - // In Socket.IO 2.x, Socket emits 'connect_error' event. + // 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();