[xmpp] Improve reconnection logic (#14397)

* Add null annotations
* Introduce re-connect logic

Signed-off-by: lsiepel <leosiepel@gmail.com>
Signed-off-by: Leo Siepel <leosiepel@gmail.com>
This commit is contained in:
lsiepel 2024-08-19 08:21:54 +02:00 committed by GitHub
parent cd398b701e
commit 31cca5ee1f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 253 additions and 74 deletions

View File

@ -26,22 +26,23 @@ Bridge xmppclient:xmppBridge:xmpp "XMPP Client" [ host="xmpp.example.com", port=
**xmppBridge** parameters: **xmppBridge** parameters:
| Name | Label | Description | Required | Default value | | Name | Label | Description | Required | Default value |
|----------|--------------------|-------------------------------------------|-----------|-----------------------| |--------------|--------------------|--------------------------------------------------------------------|----------|-----------------------|
| username | Username | The XMPP username (left part of JID) | true | - | | username | Username | The XMPP username (left part of JID) | true | - |
| domain | Domain | The XMPP domain name (right part of JID) | true | - | | domain | Domain | The XMPP domain name (right part of JID) | true | - |
| password | Password | The XMPP user password | true | - | | password | Password | The XMPP user password | true | - |
| host | Server Hostname/IP | The IP/Hostname of the XMPP server | false | as "domain" parameter | | 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 | | 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 ## Channels
**publishTrigger** parameters: **publishTrigger** parameters:
| Name | Label | Description | Required | | Name | Label | Description | Required |
|-----------|---------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------| |-----------|---------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|
| payload | Payload condition | An optional condition on the value | false | | 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 | | 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 ## Example Rules

View File

@ -12,6 +12,7 @@
*/ */
package org.openhab.binding.xmppclient.internal; package org.openhab.binding.xmppclient.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.ThingTypeUID;
/** /**
@ -20,6 +21,7 @@ import org.openhab.core.thing.ThingTypeUID;
* *
* @author Pavel Gololobov - Initial contribution * @author Pavel Gololobov - Initial contribution
*/ */
@NonNullByDefault
public class XMPPClientBindingConstants { public class XMPPClientBindingConstants {
private static final String BINDING_ID = "xmppclient"; private static final String BINDING_ID = "xmppclient";

View File

@ -14,6 +14,8 @@ package org.openhab.binding.xmppclient.internal;
import java.util.Set; 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.binding.xmppclient.internal.handler.XMPPClientHandler;
import org.openhab.core.thing.Bridge; import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing; import org.openhab.core.thing.Thing;
@ -29,6 +31,7 @@ import org.osgi.service.component.annotations.Component;
* *
* @author Pavel Gololobov - Initial contribution * @author Pavel Gololobov - Initial contribution
*/ */
@NonNullByDefault
@Component(configurationPid = "binding.xmppclient", service = ThingHandlerFactory.class) @Component(configurationPid = "binding.xmppclient", service = ThingHandlerFactory.class)
public class XMPPClientHandlerFactory extends BaseThingHandlerFactory { public class XMPPClientHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set
@ -40,7 +43,7 @@ public class XMPPClientHandlerFactory extends BaseThingHandlerFactory {
} }
@Override @Override
protected ThingHandler createHandler(Thing thing) { protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID(); ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (thingTypeUID.equals(XMPPClientBindingConstants.BRIDGE_TYPE_XMPP)) { if (thingTypeUID.equals(XMPPClientBindingConstants.BRIDGE_TYPE_XMPP)) {

View File

@ -14,7 +14,7 @@ package org.openhab.binding.xmppclient.internal.action;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; 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.binding.xmppclient.internal.handler.XMPPClientHandler;
import org.openhab.core.automation.annotation.ActionInput; import org.openhab.core.automation.annotation.ActionInput;
import org.openhab.core.automation.annotation.RuleAction; import org.openhab.core.automation.annotation.RuleAction;
@ -35,7 +35,7 @@ import org.slf4j.LoggerFactory;
@ThingActionsScope(name = "xmppclient") @ThingActionsScope(name = "xmppclient")
@NonNullByDefault @NonNullByDefault
public class XMPPActions implements ThingActions { 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; private @Nullable XMPPClientHandler handler;
@Override @Override
@ -58,12 +58,8 @@ public class XMPPActions implements ThingActions {
} }
XMPPClient connection = clientHandler.getXMPPClient(); XMPPClient connection = clientHandler.getXMPPClient();
if (connection == null) { if (to == null || text == null) {
logger.warn("XMPP ThingHandler connection is null"); logger.warn("Skipping XMPP messaging to {} value {}", to, text);
return;
}
if ((to == null) || (text == null)) {
logger.info("Skipping XMPP messaging to {} value {}", to, text);
return; return;
} }
connection.sendMessage(to, text); connection.sendMessage(to, text);
@ -80,11 +76,7 @@ public class XMPPActions implements ThingActions {
} }
XMPPClient connection = clientHandler.getXMPPClient(); XMPPClient connection = clientHandler.getXMPPClient();
if (connection == null) { if (to == null || filename == null) {
logger.warn("XMPP ThingHandler connection is null");
return;
}
if ((to == null) || (filename == null)) {
logger.warn("Skipping XMPP messaging to {} value {}", to, filename); logger.warn("Skipping XMPP messaging to {} value {}", to, filename);
return; return;
} }

View File

@ -10,15 +10,19 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.openhab.binding.xmppclient.internal; package org.openhab.binding.xmppclient.internal.client;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.util.HashSet; import java.util.HashSet;
import java.util.Objects;
import java.util.Set; 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.AbstractXMPPConnection;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.ConnectionListener; import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.ReconnectionManager; import org.jivesoftware.smack.ReconnectionManager;
import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.SmackException;
@ -44,13 +48,20 @@ import org.slf4j.LoggerFactory;
* The {@link XMPPClient} is lib for handling XMPP connection and messaging * The {@link XMPPClient} is lib for handling XMPP connection and messaging
* *
* @author Pavel Gololobov - Initial contribution * @author Pavel Gololobov - Initial contribution
* @author Leo Siepel - Add reconnection logic
*/ */
@NonNullByDefault
public class XMPPClient implements IncomingChatMessageListener, ConnectionListener { public class XMPPClient implements IncomingChatMessageListener, ConnectionListener {
private final Logger logger = LoggerFactory.getLogger(XMPPClient.class); private final Logger logger = LoggerFactory.getLogger(XMPPClient.class);
private AbstractXMPPConnection connection; private @Nullable AbstractXMPPConnection connection;
private ChatManager chatManager; private @Nullable ChatManager chatManager;
private HttpFileUploadManager httpFileUploadManager; private @Nullable HttpFileUploadManager httpFileUploadManager;
private Set<XMPPClientMessageSubscriber> subscribers = new HashSet<>(); private Set<XMPPClientMessageSubscriber> subscribers = new HashSet<>();
private final XMPPClientEventlistener eventListener;
public XMPPClient(XMPPClientEventlistener eventListener) {
this.eventListener = eventListener;
}
public void subscribe(XMPPClientMessageSubscriber channel) { public void subscribe(XMPPClientMessageSubscriber channel) {
logger.debug("Channel {} subscribed", channel.getName()); logger.debug("Channel {} subscribed", channel.getName());
@ -62,25 +73,32 @@ public class XMPPClient implements IncomingChatMessageListener, ConnectionListen
subscribers.remove(channel); subscribers.remove(channel);
} }
public void connect(String host, Integer port, String login, String domain, String password) public void connect(String host, Integer port, String login, String domain, String password,
throws XMPPException, SmackException, IOException { SecurityMode securityMode) throws XMPPClientConfigException, XMPPClientException {
disconnect(); disconnect();
String serverHost = domain; String serverHost = domain;
if ((host != null) && !host.isEmpty()) { if (!host.isBlank()) {
serverHost = host; serverHost = host;
} }
XMPPTCPConnectionConfiguration config = XMPPTCPConnectionConfiguration.builder() // XMPPTCPConnectionConfiguration config;
.setHost(serverHost) // try {
.setPort(port) // config = XMPPTCPConnectionConfiguration.builder() //
.setUsernameAndPassword(login, password) // .setHost(serverHost) //
.setXmppDomain(domain) // .setPort(port) //
.build(); .setUsernameAndPassword(login, password) //
.setXmppDomain(domain) //
.setSecurityMode(securityMode)//
.build();
} catch (XmppStringprepException e) {
throw new XMPPClientConfigException(Objects.requireNonNullElse(e.getMessage(), "Unknown error message"));
}
connection = new XMPPTCPConnection(config); AbstractXMPPConnection connectionLocal = new XMPPTCPConnection(config);
connection.addConnectionListener(this); connection = connectionLocal;
connectionLocal.addConnectionListener(this);
ReconnectionManager reconnectionManager = ReconnectionManager.getInstanceFor(connection); ReconnectionManager reconnectionManager = ReconnectionManager.getInstanceFor(connectionLocal);
reconnectionManager.enableAutomaticReconnection(); reconnectionManager.enableAutomaticReconnection();
Identity identity = new Identity("client", "openHAB", "bot"); Identity identity = new Identity("client", "openHAB", "bot");
@ -88,16 +106,20 @@ public class XMPPClient implements IncomingChatMessageListener, ConnectionListen
sdm.setIdentity(identity); sdm.setIdentity(identity);
try { try {
connection.connect().login(); connectionLocal.connect().login();
} catch (InterruptedException ex) { } catch (InterruptedException | XMPPException | SmackException | IOException e) {
throw new XMPPClientException(Objects.requireNonNullElse(e.getMessage(), "Unknown error message"),
e.getCause());
} }
chatManager = ChatManager.getInstanceFor(connection); ChatManager chatManager = ChatManager.getInstanceFor(connection);
chatManager.addIncomingListener(this); chatManager.addIncomingListener(this);
this.chatManager = chatManager;
httpFileUploadManager = HttpFileUploadManager.getInstanceFor(connection); httpFileUploadManager = HttpFileUploadManager.getInstanceFor(connection);
} }
public void disconnect() { public void disconnect() {
AbstractXMPPConnection connection = this.connection;
if (connection != null) { if (connection != null) {
connection.disconnect(); connection.disconnect();
} }
@ -105,11 +127,13 @@ public class XMPPClient implements IncomingChatMessageListener, ConnectionListen
public void sendMessage(String to, String message) { public void sendMessage(String to, String message) {
if (connection == null) { if (connection == null) {
logger.warn("XMPP connection is null"); eventListener.onErrorEvent("XMPP connection is null");
return; return;
} }
ChatManager chatManager = this.chatManager;
if (chatManager == null) { if (chatManager == null) {
logger.warn("XMPP chatManager is null"); eventListener.onErrorEvent("XMPP chatManager is null");
return; return;
} }
try { try {
@ -117,7 +141,7 @@ public class XMPPClient implements IncomingChatMessageListener, ConnectionListen
Chat chat = chatManager.chatWith(jid); Chat chat = chatManager.chatWith(jid);
chat.send(message); chat.send(message);
} catch (XmppStringprepException | SmackException.NotConnectedException | InterruptedException e) { } catch (XmppStringprepException | SmackException.NotConnectedException | InterruptedException e) {
logger.info("XMPP message sending error", e); logger.warn("XMPP message sending error", e);
} }
} }
@ -126,12 +150,13 @@ public class XMPPClient implements IncomingChatMessageListener, ConnectionListen
logger.warn("XMPP connection is null"); logger.warn("XMPP connection is null");
return; return;
} }
if (httpFileUploadManager == null) { HttpFileUploadManager httpFileUploadManagerLocal = httpFileUploadManager;
if (httpFileUploadManagerLocal == null) {
logger.warn("XMPP httpFileUploadManager is null"); logger.warn("XMPP httpFileUploadManager is null");
return; return;
} }
try { try {
URL u = httpFileUploadManager.uploadFile(new File(filename)); URL u = httpFileUploadManagerLocal.uploadFile(new File(filename));
// Use Stanza oob // Use Stanza oob
this.sendMessage(to, u.toString()); this.sendMessage(to, u.toString());
} catch (XMPPException.XMPPErrorException | SmackException | InterruptedException | IOException e) { } catch (XMPPException.XMPPErrorException | SmackException | InterruptedException | IOException e) {
@ -140,7 +165,12 @@ public class XMPPClient implements IncomingChatMessageListener, ConnectionListen
} }
@Override @Override
public void newIncomingMessage(EntityBareJid from, Message message, Chat chat) { 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()); logger.debug("XMPP {} says {}", from.asBareJid().toString(), message.getBody());
for (XMPPClientMessageSubscriber subscriber : subscribers) { for (XMPPClientMessageSubscriber subscriber : subscribers) {
logger.debug("Push to subscriber {}", subscriber.getName()); logger.debug("Push to subscriber {}", subscriber.getName());
@ -149,30 +179,26 @@ public class XMPPClient implements IncomingChatMessageListener, ConnectionListen
} }
@Override @Override
public void connected(XMPPConnection connection) { public void connected(@Nullable XMPPConnection connection) {
logger.debug("Connected to XMPP server."); logger.debug("Connected to XMPP server.");
eventListener.onAllOk();
} }
@Override @Override
public void authenticated(XMPPConnection connection, boolean resumed) { public void authenticated(@Nullable XMPPConnection connection, boolean resumed) {
logger.debug("Authenticated to XMPP server."); logger.debug("Authenticated to XMPP server.");
eventListener.onAllOk();
} }
@Override @Override
public void connectionClosed() { public void connectionClosed() {
logger.debug("XMPP connection was closed."); logger.debug("XMPP connection was closed.");
eventListener.onErrorEvent("XMPP connection was closed.");
} }
@Override @Override
public void connectionClosedOnError(Exception e) { public void connectionClosedOnError(@Nullable Exception e) {
logger.debug("Connection to XMPP server was lost."); logger.debug("Connection to XMPP server was lost.");
if (connection != null) { eventListener.onErrorEvent("XMPP connection was closed.");
connection.disconnect();
try {
connection.connect().login();
} catch (SmackException | IOException | XMPPException | InterruptedException ex) {
logger.info("XMPP connection error", ex);
}
}
} }
} }

View File

@ -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);
}
}

View File

@ -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();
}

View File

@ -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);
}
}

View File

@ -12,7 +12,8 @@
*/ */
package org.openhab.binding.xmppclient.internal.handler; 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; import org.openhab.core.thing.ChannelUID;
/** /**
@ -22,6 +23,7 @@ import org.openhab.core.thing.ChannelUID;
* *
* @author Pavel Gololobov - Initial contribution * @author Pavel Gololobov - Initial contribution
*/ */
@NonNullByDefault
public class PublishTriggerChannel implements XMPPClientMessageSubscriber { public class PublishTriggerChannel implements XMPPClientMessageSubscriber {
private final XMPPClient connection; private final XMPPClient connection;
private final PublishTriggerChannelConfig config; private final PublishTriggerChannelConfig config;

View File

@ -14,6 +14,7 @@ package org.openhab.binding.xmppclient.internal.handler;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
/** /**
* The {@link XMPPClientConfiguration} class contains fields mapping thing configuration parameters. * The {@link XMPPClientConfiguration} class contains fields mapping thing configuration parameters.
@ -27,4 +28,10 @@ public class XMPPClientConfiguration {
public String username = ""; public String username = "";
public String password = ""; public String password = "";
public String domain = ""; public String domain = "";
public String securityMode = SecurityMode.required.toString();
public boolean isValid() {
String host = this.host;
return !(host == null || host.isBlank());
}
} }

View File

@ -12,17 +12,20 @@
*/ */
package org.openhab.binding.xmppclient.internal.handler; package org.openhab.binding.xmppclient.internal.handler;
import java.io.IOException;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.jivesoftware.smack.SmackException; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.openhab.binding.xmppclient.internal.XMPPClient;
import org.openhab.binding.xmppclient.internal.action.XMPPActions; 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.Bridge;
import org.openhab.core.thing.Channel; import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ChannelUID;
@ -40,15 +43,15 @@ import org.slf4j.LoggerFactory;
* *
* @author Pavel Gololobov - Initial contribution * @author Pavel Gololobov - Initial contribution
*/ */
@NonNullByDefault
public class XMPPClientHandler extends BaseBridgeHandler { public class XMPPClientHandler extends BaseBridgeHandler implements XMPPClientEventlistener {
private final Logger logger = LoggerFactory.getLogger(XMPPClientHandler.class); private final Logger logger = LoggerFactory.getLogger(XMPPClientHandler.class);
private XMPPClient xmppClient; private XMPPClient xmppClient;
private XMPPClientConfiguration config;
private final Map<ChannelUID, PublishTriggerChannel> channelStateByChannelUID = new HashMap<>(); private final Map<ChannelUID, PublishTriggerChannel> channelStateByChannelUID = new HashMap<>();
public XMPPClientHandler(Bridge thing) { public XMPPClientHandler(Bridge thing) {
super(thing); super(thing);
xmppClient = new XMPPClient(this);
} }
public XMPPClient getXMPPClient() { public XMPPClient getXMPPClient() {
@ -85,12 +88,22 @@ public class XMPPClientHandler extends BaseBridgeHandler {
} }
private void doConnect() { private void doConnect() {
config = getConfigAs(XMPPClientConfiguration.class); XMPPClientConfiguration config = getConfigAs(XMPPClientConfiguration.class);
xmppClient = new XMPPClient(); if (!config.isValid()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Please check configuration");
return;
}
try { try {
xmppClient.connect(config.host, config.port, config.username, config.domain, config.password); xmppClient.connect(Objects.requireNonNullElse(config.host, ""), config.port, config.username, config.domain,
} catch (SmackException | IOException | XMPPException e) { config.password, SecurityMode.valueOf(config.securityMode));
logger.info("XMPP connection error", e); 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()); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
return; return;
} }
@ -103,7 +116,15 @@ public class XMPPClientHandler extends BaseBridgeHandler {
logger.info("XMPP added channel {} payload {}", channel.getUID().toString(), channelConfig.payload); logger.info("XMPP added channel {} payload {}", channel.getUID().toString(), channelConfig.payload);
} }
channelStateByChannelUID.values().forEach(c -> c.start()); 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); updateStatus(ThingStatus.ONLINE);
} }
} }

View File

@ -12,11 +12,14 @@
*/ */
package org.openhab.binding.xmppclient.internal.handler; package org.openhab.binding.xmppclient.internal.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
/** /**
* Subscriber interface * Subscriber interface
* *
* @author Pavel Gololobov - Initial contribution * @author Pavel Gololobov - Initial contribution
*/ */
@NonNullByDefault
public interface XMPPClientMessageSubscriber { public interface XMPPClientMessageSubscriber {
void processMessage(String from, String payload); void processMessage(String from, String payload);

View File

@ -30,6 +30,19 @@
<parameter name="port" type="integer"> <parameter name="port" type="integer">
<label>XMPP Server Port</label> <label>XMPP Server Port</label>
<description>The default port is 5222.</description> <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> </parameter>
</config-description> </config-description>
</bridge-type> </bridge-type>