]> git.basschouten.com Git - openhab-addons.git/commitdiff
[openhabcloud] reconnect on connection errors (#11153)
authorSami Salonen <ssalonen@gmail.com>
Fri, 12 Nov 2021 20:06:47 +0000 (22:06 +0200)
committerGitHub <noreply@github.com>
Fri, 12 Nov 2021 20:06:47 +0000 (21:06 +0100)
* [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 <ssalonen@gmail.com>
* [openhabcloud] Update Socket IO dependency to 1.0.1

Signed-off-by: Sami Salonen <ssalonen@gmail.com>
* [openhabcloud] feature.xml updated also with socket io 1.0.1

Signed-off-by: Sami Salonen <ssalonen@gmail.com>
* [openhabcloud] Re-connect manually on error events when not connected

Signed-off-by: Sami Salonen <ssalonen@gmail.com>
* [openhabcloud] less loud logging on retries

Signed-off-by: Sami Salonen <ssalonen@gmail.com>
* [openhabcloud] removing unnecessary conditional in logging

Signed-off-by: Sami Salonen <ssalonen@gmail.com>
* [openhabcloud] javadoc corrections and clarifications

Signed-off-by: Sami Salonen <ssalonen@gmail.com>
bundles/org.openhab.io.openhabcloud/pom.xml
bundles/org.openhab.io.openhabcloud/src/main/feature/feature.xml
bundles/org.openhab.io.openhabcloud/src/main/java/org/openhab/io/openhabcloud/internal/CloudClient.java

index dc347e243dd32dd83f89f4b0be211e0bd775d270..3e742ab68008cfba6eabbb0b34ea49c26f40bbf1 100644 (file)
     <dependency>
       <groupId>org.openhab.osgiify</groupId>
       <artifactId>io.socket.socket.io-client</artifactId>
-      <version>1.0.0</version>
+      <version>1.0.1</version>
       <scope>compile</scope>
     </dependency>
     <dependency>
       <groupId>org.openhab.osgiify</groupId>
       <artifactId>io.socket.engine.io-client</artifactId>
-      <version>1.0.0</version>
+      <version>1.0.1</version>
       <scope>compile</scope>
     </dependency>
   </dependencies>
index c6971bc30a15fce162ddf243277f0f285dfa7323..8dfbe53750d4923c51b1e6becb0d5009da5c740b 100644 (file)
@@ -9,8 +9,8 @@
                <bundle dependency="true">mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.jsr305/3.0.2_1</bundle>
                <bundle dependency="true">mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.okhttp/3.8.1_1</bundle>
                <bundle dependency="true">mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.okio/1.13.0_1</bundle>
-               <bundle dependency="true">mvn:org.openhab.osgiify/io.socket.socket.io-client/1.0.0</bundle>
-               <bundle dependency="true">mvn:org.openhab.osgiify/io.socket.engine.io-client/1.0.0</bundle>
+               <bundle dependency="true">mvn:org.openhab.osgiify/io.socket.socket.io-client/1.0.1</bundle>
+               <bundle dependency="true">mvn:org.openhab.osgiify/io.socket.engine.io-client/1.0.1</bundle>
                <bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.io.openhabcloud/${project.version}</bundle>
        </feature>
 </features>
index 54b6e1ae93b251234f2d0e1ec21954181cb5893e..5473fec5ecdc27ef79b9a84f36bc95eaae456f9f 100644 (file)
@@ -41,11 +41,13 @@ import org.openhab.core.OpenHAB;
 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<String> 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 class CloudClient {
         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 class CloudClient {
                     }
                 });
             }
+        }).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 class CloudClient {
                 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 class CloudClient {
 
     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 @@ public class CloudClient {
         }
         return headersJSON;
     }
+
+    private void sleepSocketIO(long delay) {
+        EventThread.exec(() -> {
+            try {
+                Thread.sleep(delay);
+            } catch (InterruptedException e) {
+
+            }
+        });
+    }
 }