]> git.basschouten.com Git - openhab-addons.git/commitdiff
[xmpp] Improve reconnection logic (#14397)
authorlsiepel <leosiepel@gmail.com>
Mon, 19 Aug 2024 06:21:54 +0000 (08:21 +0200)
committerGitHub <noreply@github.com>
Mon, 19 Aug 2024 06:21:54 +0000 (08:21 +0200)
* Add null annotations
* Introduce re-connect logic

Signed-off-by: lsiepel <leosiepel@gmail.com>
Signed-off-by: Leo Siepel <leosiepel@gmail.com>
14 files changed:
bundles/org.openhab.binding.xmppclient/README.md
bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/XMPPClient.java [deleted file]
bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/XMPPClientBindingConstants.java
bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/XMPPClientHandlerFactory.java
bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/action/XMPPActions.java
bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/client/XMPPClient.java [new file with mode: 0644]
bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/client/XMPPClientConfigException.java [new file with mode: 0644]
bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/client/XMPPClientEventlistener.java [new file with mode: 0644]
bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/client/XMPPClientException.java [new file with mode: 0644]
bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/handler/PublishTriggerChannel.java
bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/handler/XMPPClientConfiguration.java
bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/handler/XMPPClientHandler.java
bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/handler/XMPPClientMessageSubscriber.java
bundles/org.openhab.binding.xmppclient/src/main/resources/OH-INF/thing/thing-types.xml

index 8a8d944feec24817ae46d332da17b8d6b55eac7b..e7133375d4ccd69e98c13b7ddfd7b73f983a6376 100644 (file)
@@ -26,22 +26,23 @@ Bridge xmppclient:xmppBridge:xmpp "XMPP Client" [ host="xmpp.example.com", port=
 
 **xmppBridge** parameters:
 
-| Name     | Label              | Description                               |  Required | Default value         |
-|----------|--------------------|-------------------------------------------|-----------|-----------------------|
-| username | Username           | The XMPP username (left part of JID)      | true      | -                     |
-| domain   | Domain             | The XMPP domain name (right part of JID)  | true      | -                     |
-| password | Password           | The XMPP user password                    | true      | -                     |
-| host     | Server Hostname/IP | The IP/Hostname of the XMPP server        | false     | as "domain" parameter |
-| port     | XMPP server Port   | The typical port is 5222                  | false     | 5222                  |
+| Name         | Label              | Description                                                        | Required | Default value         |
+|--------------|--------------------|--------------------------------------------------------------------|----------|-----------------------|
+| username     | Username           | The XMPP username (left part of JID)                               | true     | -                     |
+| domain       | Domain             | The XMPP domain name (right part of JID)                           | true     | -                     |
+| password     | Password           | The XMPP user password                                             | true     | -                     |
+| host         | Server Hostname/IP | The IP/Hostname of the XMPP server                                 | false    | as "domain" parameter |
+| port         | XMPP server Port   | The typical port is 5222                                           | false    | 5222                  |
+| securityMode | Security Mode      | Sets the TLS security mode: `required`, `ifpossible` or `disabled` | false    | `required`            |
 
 ## Channels
 
 **publishTrigger** parameters:
 
-| Name      |      Label          |  Description                                                                                                                                                                                                                                       |  Required |
-|-----------|---------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------|
-| payload   | Payload condition   | An optional condition on the value                                                                                                                                                                                                                 | false     |
-| separator | Separator character | The trigger channel payload usually only contains the received text. If you define a separator character, for example '#', the sender UID and received text will be in the trigger channel payload. For example: pavel@example.com#My Message Text | false     |
+| Name      | Label               | Description                                                                                                                                                                                                                                        | Required |
+|-----------|---------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|
+| payload   | Payload condition   | An optional condition on the value                                                                                                                                                                                                                 | false    |
+| separator | Separator character | The trigger channel payload usually only contains the received text. If you define a separator character, for example '#', the sender UID and received text will be in the trigger channel payload. For example: pavel@example.com#My Message Text | false    |
 
 ## Example Rules
 
diff --git a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/XMPPClient.java b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/XMPPClient.java
deleted file mode 100644 (file)
index e95c49f..0000000
+++ /dev/null
@@ -1,178 +0,0 @@
-/**
- * Copyright (c) 2010-2024 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.xmppclient.internal;
-
-import java.io.File;
-import java.io.IOException;
-import java.net.URL;
-import java.util.HashSet;
-import java.util.Set;
-
-import org.jivesoftware.smack.AbstractXMPPConnection;
-import org.jivesoftware.smack.ConnectionListener;
-import org.jivesoftware.smack.ReconnectionManager;
-import org.jivesoftware.smack.SmackException;
-import org.jivesoftware.smack.XMPPConnection;
-import org.jivesoftware.smack.XMPPException;
-import org.jivesoftware.smack.chat2.Chat;
-import org.jivesoftware.smack.chat2.ChatManager;
-import org.jivesoftware.smack.chat2.IncomingChatMessageListener;
-import org.jivesoftware.smack.packet.Message;
-import org.jivesoftware.smack.tcp.XMPPTCPConnection;
-import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
-import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
-import org.jivesoftware.smackx.disco.packet.DiscoverInfo.Identity;
-import org.jivesoftware.smackx.httpfileupload.HttpFileUploadManager;
-import org.jxmpp.jid.EntityBareJid;
-import org.jxmpp.jid.impl.JidCreate;
-import org.jxmpp.stringprep.XmppStringprepException;
-import org.openhab.binding.xmppclient.internal.handler.XMPPClientMessageSubscriber;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The {@link XMPPClient} is lib for handling XMPP connection and messaging
- *
- * @author Pavel Gololobov - Initial contribution
- */
-public class XMPPClient implements IncomingChatMessageListener, ConnectionListener {
-    private final Logger logger = LoggerFactory.getLogger(XMPPClient.class);
-    private AbstractXMPPConnection connection;
-    private ChatManager chatManager;
-    private HttpFileUploadManager httpFileUploadManager;
-    private Set<XMPPClientMessageSubscriber> subscribers = new HashSet<>();
-
-    public void subscribe(XMPPClientMessageSubscriber channel) {
-        logger.debug("Channel {} subscribed", channel.getName());
-        subscribers.add(channel);
-    }
-
-    public void unsubscribe(XMPPClientMessageSubscriber channel) {
-        logger.debug("Channel {} unsubscribed", channel.getName());
-        subscribers.remove(channel);
-    }
-
-    public void connect(String host, Integer port, String login, String domain, String password)
-            throws XMPPException, SmackException, IOException {
-        disconnect();
-        String serverHost = domain;
-        if ((host != null) && !host.isEmpty()) {
-            serverHost = host;
-        }
-
-        XMPPTCPConnectionConfiguration config = XMPPTCPConnectionConfiguration.builder() //
-                .setHost(serverHost) //
-                .setPort(port) //
-                .setUsernameAndPassword(login, password) //
-                .setXmppDomain(domain) //
-                .build();
-
-        connection = new XMPPTCPConnection(config);
-        connection.addConnectionListener(this);
-
-        ReconnectionManager reconnectionManager = ReconnectionManager.getInstanceFor(connection);
-        reconnectionManager.enableAutomaticReconnection();
-
-        Identity identity = new Identity("client", "openHAB", "bot");
-        ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection);
-        sdm.setIdentity(identity);
-
-        try {
-            connection.connect().login();
-        } catch (InterruptedException ex) {
-        }
-
-        chatManager = ChatManager.getInstanceFor(connection);
-        chatManager.addIncomingListener(this);
-        httpFileUploadManager = HttpFileUploadManager.getInstanceFor(connection);
-    }
-
-    public void disconnect() {
-        if (connection != null) {
-            connection.disconnect();
-        }
-    }
-
-    public void sendMessage(String to, String message) {
-        if (connection == null) {
-            logger.warn("XMPP connection is null");
-            return;
-        }
-        if (chatManager == null) {
-            logger.warn("XMPP chatManager is null");
-            return;
-        }
-        try {
-            EntityBareJid jid = JidCreate.entityBareFrom(to);
-            Chat chat = chatManager.chatWith(jid);
-            chat.send(message);
-        } catch (XmppStringprepException | SmackException.NotConnectedException | InterruptedException e) {
-            logger.info("XMPP message sending error", e);
-        }
-    }
-
-    public void sendImageByHTTP(String to, String filename) {
-        if (connection == null) {
-            logger.warn("XMPP connection is null");
-            return;
-        }
-        if (httpFileUploadManager == null) {
-            logger.warn("XMPP httpFileUploadManager is null");
-            return;
-        }
-        try {
-            URL u = httpFileUploadManager.uploadFile(new File(filename));
-            // Use Stanza oob
-            this.sendMessage(to, u.toString());
-        } catch (XMPPException.XMPPErrorException | SmackException | InterruptedException | IOException e) {
-            logger.warn("XMPP HTTP image sending error", e);
-        }
-    }
-
-    @Override
-    public void newIncomingMessage(EntityBareJid from, Message message, Chat chat) {
-        logger.debug("XMPP {} says {}", from.asBareJid().toString(), message.getBody());
-        for (XMPPClientMessageSubscriber subscriber : subscribers) {
-            logger.debug("Push to subscriber {}", subscriber.getName());
-            subscriber.processMessage(from.asBareJid().toString(), message.getBody());
-        }
-    }
-
-    @Override
-    public void connected(XMPPConnection connection) {
-        logger.debug("Connected to XMPP server.");
-    }
-
-    @Override
-    public void authenticated(XMPPConnection connection, boolean resumed) {
-        logger.debug("Authenticated to XMPP server.");
-    }
-
-    @Override
-    public void connectionClosed() {
-        logger.debug("XMPP connection was closed.");
-    }
-
-    @Override
-    public void connectionClosedOnError(Exception e) {
-        logger.debug("Connection to XMPP server was lost.");
-        if (connection != null) {
-            connection.disconnect();
-            try {
-                connection.connect().login();
-            } catch (SmackException | IOException | XMPPException | InterruptedException ex) {
-                logger.info("XMPP connection error", ex);
-            }
-        }
-    }
-}
index da29d009a2cdf8208683bce999130d9866b30331..9e45e2a985fce0d55f7730d6a5a1c637d5fdfc01 100644 (file)
@@ -12,6 +12,7 @@
  */
 package org.openhab.binding.xmppclient.internal;
 
+import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.openhab.core.thing.ThingTypeUID;
 
 /**
@@ -20,6 +21,7 @@ import org.openhab.core.thing.ThingTypeUID;
  *
  * @author Pavel Gololobov - Initial contribution
  */
+@NonNullByDefault
 public class XMPPClientBindingConstants {
     private static final String BINDING_ID = "xmppclient";
 
index 098c865827401f3b390b4112cadd787000c13d24..5a92aa2b6db060e611123653245179d295ffe664 100644 (file)
@@ -14,6 +14,8 @@ package org.openhab.binding.xmppclient.internal;
 
 import java.util.Set;
 
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
 import org.openhab.binding.xmppclient.internal.handler.XMPPClientHandler;
 import org.openhab.core.thing.Bridge;
 import org.openhab.core.thing.Thing;
@@ -29,6 +31,7 @@ import org.osgi.service.component.annotations.Component;
  *
  * @author Pavel Gololobov - Initial contribution
  */
+@NonNullByDefault
 @Component(configurationPid = "binding.xmppclient", service = ThingHandlerFactory.class)
 public class XMPPClientHandlerFactory extends BaseThingHandlerFactory {
     private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set
@@ -40,7 +43,7 @@ public class XMPPClientHandlerFactory extends BaseThingHandlerFactory {
     }
 
     @Override
-    protected ThingHandler createHandler(Thing thing) {
+    protected @Nullable ThingHandler createHandler(Thing thing) {
         ThingTypeUID thingTypeUID = thing.getThingTypeUID();
 
         if (thingTypeUID.equals(XMPPClientBindingConstants.BRIDGE_TYPE_XMPP)) {
index 9fac03a885914e02262340406435d6f7877ea081..4bcc6b509e504f597c018fb20671f2a7d40e0898 100644 (file)
@@ -14,7 +14,7 @@ package org.openhab.binding.xmppclient.internal.action;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.xmppclient.internal.XMPPClient;
+import org.openhab.binding.xmppclient.internal.client.XMPPClient;
 import org.openhab.binding.xmppclient.internal.handler.XMPPClientHandler;
 import org.openhab.core.automation.annotation.ActionInput;
 import org.openhab.core.automation.annotation.RuleAction;
@@ -35,7 +35,7 @@ import org.slf4j.LoggerFactory;
 @ThingActionsScope(name = "xmppclient")
 @NonNullByDefault
 public class XMPPActions implements ThingActions {
-    private static final Logger logger = LoggerFactory.getLogger(XMPPActions.class);
+    private final Logger logger = LoggerFactory.getLogger(XMPPActions.class);
     private @Nullable XMPPClientHandler handler;
 
     @Override
@@ -58,12 +58,8 @@ public class XMPPActions implements ThingActions {
         }
 
         XMPPClient connection = clientHandler.getXMPPClient();
-        if (connection == null) {
-            logger.warn("XMPP ThingHandler connection is null");
-            return;
-        }
-        if ((to == null) || (text == null)) {
-            logger.info("Skipping XMPP messaging to {} value {}", to, text);
+        if (to == null || text == null) {
+            logger.warn("Skipping XMPP messaging to {} value {}", to, text);
             return;
         }
         connection.sendMessage(to, text);
@@ -80,11 +76,7 @@ public class XMPPActions implements ThingActions {
         }
 
         XMPPClient connection = clientHandler.getXMPPClient();
-        if (connection == null) {
-            logger.warn("XMPP ThingHandler connection is null");
-            return;
-        }
-        if ((to == null) || (filename == null)) {
+        if (to == null || filename == null) {
             logger.warn("Skipping XMPP messaging to {} value {}", to, filename);
             return;
         }
diff --git a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/client/XMPPClient.java b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/client/XMPPClient.java
new file mode 100644 (file)
index 0000000..4ca3984
--- /dev/null
@@ -0,0 +1,204 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.xmppclient.internal.client;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.jivesoftware.smack.AbstractXMPPConnection;
+import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
+import org.jivesoftware.smack.ConnectionListener;
+import org.jivesoftware.smack.ReconnectionManager;
+import org.jivesoftware.smack.SmackException;
+import org.jivesoftware.smack.XMPPConnection;
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smack.chat2.Chat;
+import org.jivesoftware.smack.chat2.ChatManager;
+import org.jivesoftware.smack.chat2.IncomingChatMessageListener;
+import org.jivesoftware.smack.packet.Message;
+import org.jivesoftware.smack.tcp.XMPPTCPConnection;
+import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
+import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
+import org.jivesoftware.smackx.disco.packet.DiscoverInfo.Identity;
+import org.jivesoftware.smackx.httpfileupload.HttpFileUploadManager;
+import org.jxmpp.jid.EntityBareJid;
+import org.jxmpp.jid.impl.JidCreate;
+import org.jxmpp.stringprep.XmppStringprepException;
+import org.openhab.binding.xmppclient.internal.handler.XMPPClientMessageSubscriber;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link XMPPClient} is lib for handling XMPP connection and messaging
+ *
+ * @author Pavel Gololobov - Initial contribution
+ * @author Leo Siepel - Add reconnection logic
+ */
+@NonNullByDefault
+public class XMPPClient implements IncomingChatMessageListener, ConnectionListener {
+    private final Logger logger = LoggerFactory.getLogger(XMPPClient.class);
+    private @Nullable AbstractXMPPConnection connection;
+    private @Nullable ChatManager chatManager;
+    private @Nullable HttpFileUploadManager httpFileUploadManager;
+    private Set<XMPPClientMessageSubscriber> subscribers = new HashSet<>();
+    private final XMPPClientEventlistener eventListener;
+
+    public XMPPClient(XMPPClientEventlistener eventListener) {
+        this.eventListener = eventListener;
+    }
+
+    public void subscribe(XMPPClientMessageSubscriber channel) {
+        logger.debug("Channel {} subscribed", channel.getName());
+        subscribers.add(channel);
+    }
+
+    public void unsubscribe(XMPPClientMessageSubscriber channel) {
+        logger.debug("Channel {} unsubscribed", channel.getName());
+        subscribers.remove(channel);
+    }
+
+    public void connect(String host, Integer port, String login, String domain, String password,
+            SecurityMode securityMode) throws XMPPClientConfigException, XMPPClientException {
+        disconnect();
+        String serverHost = domain;
+        if (!host.isBlank()) {
+            serverHost = host;
+        }
+
+        XMPPTCPConnectionConfiguration config;
+        try {
+            config = XMPPTCPConnectionConfiguration.builder() //
+                    .setHost(serverHost) //
+                    .setPort(port) //
+                    .setUsernameAndPassword(login, password) //
+                    .setXmppDomain(domain) //
+                    .setSecurityMode(securityMode)//
+                    .build();
+        } catch (XmppStringprepException e) {
+            throw new XMPPClientConfigException(Objects.requireNonNullElse(e.getMessage(), "Unknown error message"));
+        }
+
+        AbstractXMPPConnection connectionLocal = new XMPPTCPConnection(config);
+        connection = connectionLocal;
+        connectionLocal.addConnectionListener(this);
+
+        ReconnectionManager reconnectionManager = ReconnectionManager.getInstanceFor(connectionLocal);
+        reconnectionManager.enableAutomaticReconnection();
+
+        Identity identity = new Identity("client", "openHAB", "bot");
+        ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection);
+        sdm.setIdentity(identity);
+
+        try {
+            connectionLocal.connect().login();
+        } catch (InterruptedException | XMPPException | SmackException | IOException e) {
+            throw new XMPPClientException(Objects.requireNonNullElse(e.getMessage(), "Unknown error message"),
+                    e.getCause());
+        }
+
+        ChatManager chatManager = ChatManager.getInstanceFor(connection);
+        chatManager.addIncomingListener(this);
+        this.chatManager = chatManager;
+        httpFileUploadManager = HttpFileUploadManager.getInstanceFor(connection);
+    }
+
+    public void disconnect() {
+        AbstractXMPPConnection connection = this.connection;
+        if (connection != null) {
+            connection.disconnect();
+        }
+    }
+
+    public void sendMessage(String to, String message) {
+        if (connection == null) {
+            eventListener.onErrorEvent("XMPP connection is null");
+            return;
+        }
+
+        ChatManager chatManager = this.chatManager;
+        if (chatManager == null) {
+            eventListener.onErrorEvent("XMPP chatManager is null");
+            return;
+        }
+        try {
+            EntityBareJid jid = JidCreate.entityBareFrom(to);
+            Chat chat = chatManager.chatWith(jid);
+            chat.send(message);
+        } catch (XmppStringprepException | SmackException.NotConnectedException | InterruptedException e) {
+            logger.warn("XMPP message sending error", e);
+        }
+    }
+
+    public void sendImageByHTTP(String to, String filename) {
+        if (connection == null) {
+            logger.warn("XMPP connection is null");
+            return;
+        }
+        HttpFileUploadManager httpFileUploadManagerLocal = httpFileUploadManager;
+        if (httpFileUploadManagerLocal == null) {
+            logger.warn("XMPP httpFileUploadManager is null");
+            return;
+        }
+        try {
+            URL u = httpFileUploadManagerLocal.uploadFile(new File(filename));
+            // Use Stanza oob
+            this.sendMessage(to, u.toString());
+        } catch (XMPPException.XMPPErrorException | SmackException | InterruptedException | IOException e) {
+            logger.warn("XMPP HTTP image sending error", e);
+        }
+    }
+
+    @Override
+    public void newIncomingMessage(@Nullable EntityBareJid from, @Nullable Message message, @Nullable Chat chat) {
+        if (from == null || message == null || chat == null) {
+            logger.debug("newIncomingMessage with atleast one null argument, should not happen");
+            return;
+        }
+
+        logger.debug("XMPP {} says {}", from.asBareJid().toString(), message.getBody());
+        for (XMPPClientMessageSubscriber subscriber : subscribers) {
+            logger.debug("Push to subscriber {}", subscriber.getName());
+            subscriber.processMessage(from.asBareJid().toString(), message.getBody());
+        }
+    }
+
+    @Override
+    public void connected(@Nullable XMPPConnection connection) {
+        logger.debug("Connected to XMPP server.");
+        eventListener.onAllOk();
+    }
+
+    @Override
+    public void authenticated(@Nullable XMPPConnection connection, boolean resumed) {
+        logger.debug("Authenticated to XMPP server.");
+        eventListener.onAllOk();
+    }
+
+    @Override
+    public void connectionClosed() {
+        logger.debug("XMPP connection was closed.");
+        eventListener.onErrorEvent("XMPP connection was closed.");
+    }
+
+    @Override
+    public void connectionClosedOnError(@Nullable Exception e) {
+        logger.debug("Connection to XMPP server was lost.");
+        eventListener.onErrorEvent("XMPP connection was closed.");
+    }
+}
diff --git a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/client/XMPPClientConfigException.java b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/client/XMPPClientConfigException.java
new file mode 100644 (file)
index 0000000..c474c8c
--- /dev/null
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.xmppclient.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * The {@link XMPPClientConfigException} represents a binding specific {@link Exception}.
+ *
+ * @author Leo Siepel - Initial contribution
+ */
+
+@NonNullByDefault
+public class XMPPClientConfigException extends Exception {
+
+    private static final long serialVersionUID = 1L;
+
+    public XMPPClientConfigException(String message) {
+        super(message);
+    }
+
+    public XMPPClientConfigException(String message, @Nullable Throwable cause) {
+        super(message, cause);
+    }
+
+    public XMPPClientConfigException(@Nullable Throwable cause) {
+        super(cause);
+    }
+}
diff --git a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/client/XMPPClientEventlistener.java b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/client/XMPPClientEventlistener.java
new file mode 100644 (file)
index 0000000..e1fb7a2
--- /dev/null
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.xmppclient.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link XMPPClientEventlistener} is an interface for handling XMPP connection events.
+ *
+ * @author Leo Siepel - Initial Contribution
+ */
+
+@NonNullByDefault
+public interface XMPPClientEventlistener {
+
+    void onErrorEvent(String errorMessage);
+
+    void onAllOk();
+}
diff --git a/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/client/XMPPClientException.java b/bundles/org.openhab.binding.xmppclient/src/main/java/org/openhab/binding/xmppclient/internal/client/XMPPClientException.java
new file mode 100644 (file)
index 0000000..5967a39
--- /dev/null
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.xmppclient.internal.client;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * The {@link XMPPClientException} represents a binding specific {@link Exception}.
+ *
+ * @author Leo Siepel - Initial contribution
+ */
+
+@NonNullByDefault
+public class XMPPClientException extends Exception {
+
+    private static final long serialVersionUID = 1L;
+
+    public XMPPClientException(String message) {
+        super(message);
+    }
+
+    public XMPPClientException(String message, @Nullable Throwable cause) {
+        super(message, cause);
+    }
+
+    public XMPPClientException(@Nullable Throwable cause) {
+        super(cause);
+    }
+}
index 807f62ba6a997048ee0d22970819eea1d6c09587..21c19627293c56ce215962bddf942e407be2a2bc 100644 (file)
@@ -12,7 +12,8 @@
  */
 package org.openhab.binding.xmppclient.internal.handler;
 
-import org.openhab.binding.xmppclient.internal.XMPPClient;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.xmppclient.internal.client.XMPPClient;
 import org.openhab.core.thing.ChannelUID;
 
 /**
@@ -22,6 +23,7 @@ import org.openhab.core.thing.ChannelUID;
  *
  * @author Pavel Gololobov - Initial contribution
  */
+@NonNullByDefault
 public class PublishTriggerChannel implements XMPPClientMessageSubscriber {
     private final XMPPClient connection;
     private final PublishTriggerChannelConfig config;
index 62f0d5b214aadc9bb90beab5e935e39dd34a1bea..b2f0bfdef5583ddb57aa80b4233d96da3321bca3 100644 (file)
@@ -14,6 +14,7 @@ package org.openhab.binding.xmppclient.internal.handler;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
+import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
 
 /**
  * The {@link XMPPClientConfiguration} class contains fields mapping thing configuration parameters.
@@ -27,4 +28,10 @@ public class XMPPClientConfiguration {
     public String username = "";
     public String password = "";
     public String domain = "";
+    public String securityMode = SecurityMode.required.toString();
+
+    public boolean isValid() {
+        String host = this.host;
+        return !(host == null || host.isBlank());
+    }
 }
index a32c8771f3bdf5b9a4b36bbc25f291360aa46a90..3c7cfc2dbfd3d79b874fda91362bc9ebbd433fb4 100644 (file)
  */
 package org.openhab.binding.xmppclient.internal.handler;
 
-import java.io.IOException;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
-import org.jivesoftware.smack.SmackException;
-import org.jivesoftware.smack.XMPPException;
-import org.openhab.binding.xmppclient.internal.XMPPClient;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
 import org.openhab.binding.xmppclient.internal.action.XMPPActions;
+import org.openhab.binding.xmppclient.internal.client.XMPPClient;
+import org.openhab.binding.xmppclient.internal.client.XMPPClientConfigException;
+import org.openhab.binding.xmppclient.internal.client.XMPPClientEventlistener;
+import org.openhab.binding.xmppclient.internal.client.XMPPClientException;
 import org.openhab.core.thing.Bridge;
 import org.openhab.core.thing.Channel;
 import org.openhab.core.thing.ChannelUID;
@@ -40,15 +43,15 @@ import org.slf4j.LoggerFactory;
  *
  * @author Pavel Gololobov - Initial contribution
  */
-
-public class XMPPClientHandler extends BaseBridgeHandler {
+@NonNullByDefault
+public class XMPPClientHandler extends BaseBridgeHandler implements XMPPClientEventlistener {
     private final Logger logger = LoggerFactory.getLogger(XMPPClientHandler.class);
     private XMPPClient xmppClient;
-    private XMPPClientConfiguration config;
     private final Map<ChannelUID, PublishTriggerChannel> channelStateByChannelUID = new HashMap<>();
 
     public XMPPClientHandler(Bridge thing) {
         super(thing);
+        xmppClient = new XMPPClient(this);
     }
 
     public XMPPClient getXMPPClient() {
@@ -85,12 +88,22 @@ public class XMPPClientHandler extends BaseBridgeHandler {
     }
 
     private void doConnect() {
-        config = getConfigAs(XMPPClientConfiguration.class);
-        xmppClient = new XMPPClient();
+        XMPPClientConfiguration config = getConfigAs(XMPPClientConfiguration.class);
+        if (!config.isValid()) {
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Please check configuration");
+            return;
+        }
+
         try {
-            xmppClient.connect(config.host, config.port, config.username, config.domain, config.password);
-        } catch (SmackException | IOException | XMPPException e) {
-            logger.info("XMPP connection error", e);
+            xmppClient.connect(Objects.requireNonNullElse(config.host, ""), config.port, config.username, config.domain,
+                    config.password, SecurityMode.valueOf(config.securityMode));
+            updateStatus(ThingStatus.ONLINE);
+        } catch (XMPPClientConfigException e) {
+            logger.debug("XMPP connection error", e);
+            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
+            return;
+        } catch (XMPPClientException e) {
+            logger.debug("XMPP connection error", e);
             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
             return;
         }
@@ -103,7 +116,15 @@ public class XMPPClientHandler extends BaseBridgeHandler {
             logger.info("XMPP added channel {} payload {}", channel.getUID().toString(), channelConfig.payload);
         }
         channelStateByChannelUID.values().forEach(c -> c.start());
+    }
 
+    @Override
+    public void onErrorEvent(String errorMessage) {
+        updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, errorMessage);
+    }
+
+    @Override
+    public void onAllOk() {
         updateStatus(ThingStatus.ONLINE);
     }
 }
index 8765c5351238654bc2bbbf0d0fc2d41cc0db9172..a401316efc6d0b821b694bb831cac28276694a76 100644 (file)
  */
 package org.openhab.binding.xmppclient.internal.handler;
 
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
 /**
  * Subscriber interface
  * 
  * @author Pavel Gololobov - Initial contribution
  */
+@NonNullByDefault
 public interface XMPPClientMessageSubscriber {
     void processMessage(String from, String payload);
 
index b9841ddce73f11908efac59f72e665092e69e1f7..ea506032b3c2efed55cad7e8c4f9385bd681b907 100644 (file)
                        <parameter name="port" type="integer">
                                <label>XMPP Server Port</label>
                                <description>The default port is 5222.</description>
+                               <advanced>true</advanced>
+                       </parameter>
+                       <parameter name="securityMode" type="text" required="true">
+                               <label>Security Mode</label>
+                               <description>An enumeration for TLS security modes that are available when making a connection to the XMPP server.</description>
+                               <limitToOptions>true</limitToOptions>
+                               <options>
+                                       <option value="required">Required</option>
+                                       <option value="ifpossible">Optional</option>
+                                       <option value="disabled">Disabled</option>
+                               </options>
+                               <default>required</default>
+                               <advanced>true</advanced>
                        </parameter>
                </config-description>
        </bridge-type>