
org.eclipse.paho.client.mqttv3.MqttAsyncClient Maven / Gradle / Ivy
/*******************************************************************************
* Copyright (c) 2009, 2018 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* https://www.eclipse.org/legal/epl-2.0
* and the Eclipse Distribution License is available at
* https://www.eclipse.org/org/documents/edl-v10.php
*
* Contributors:
* Dave Locke - initial API and implementation and/or initial documentation
* Ian Craggs - MQTT 3.1.1 support
* Ian Craggs - per subscription message handlers (bug 466579)
* Ian Craggs - ack control (bug 472172)
* James Sutton - Bug 459142 - WebSocket support for the Java client.
* James Sutton - Automatic Reconnect & Offline Buffering.
*/
package org.eclipse.paho.client.mqttv3;
import java.util.Hashtable;
import java.util.Properties;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import javax.net.SocketFactory;
import org.eclipse.paho.client.mqttv3.internal.ClientComms;
import org.eclipse.paho.client.mqttv3.internal.ConnectActionListener;
import org.eclipse.paho.client.mqttv3.internal.DisconnectedMessageBuffer;
import org.eclipse.paho.client.mqttv3.internal.ExceptionHelper;
import org.eclipse.paho.client.mqttv3.internal.HighResolutionTimer;
import org.eclipse.paho.client.mqttv3.internal.SystemHighResolutionTimer;
import org.eclipse.paho.client.mqttv3.internal.NetworkModule;
import org.eclipse.paho.client.mqttv3.internal.NetworkModuleService;
import org.eclipse.paho.client.mqttv3.internal.wire.MqttDisconnect;
import org.eclipse.paho.client.mqttv3.internal.wire.MqttPublish;
import org.eclipse.paho.client.mqttv3.internal.wire.MqttSubscribe;
import org.eclipse.paho.client.mqttv3.internal.wire.MqttUnsubscribe;
import org.eclipse.paho.client.mqttv3.logging.Logger;
import org.eclipse.paho.client.mqttv3.logging.LoggerFactory;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence;
import org.eclipse.paho.client.mqttv3.util.Debug;
import org.eclipse.paho.client.mqttv3.IMqttToken;
/**
* Lightweight client for talking to an MQTT server using non-blocking methods
* that allow an operation to run in the background.
*
*
* This class implements the non-blocking {@link IMqttAsyncClient} client
* interface allowing applications to initiate MQTT actions and then carry on
* working while the MQTT action completes on a background thread. This
* implementation is compatible with all Java SE runtimes from 1.7 and up.
*
*
* An application can connect to an MQTT server using:
*
*
* - A plain TCP socket
*
- A secure SSL/TLS socket
*
*
*
* To enable messages to be delivered even across network and client restarts
* messages need to be safely stored until the message has been delivered at the
* requested quality of service. A pluggable persistence mechanism is provided
* to store the messages.
*
*
* By default {@link MqttDefaultFilePersistence} is used to store messages to a
* file. If persistence is set to null then messages are stored in memory and
* hence can be lost if the client, Java runtime or device shuts down.
*
*
* If connecting with {@link MqttConnectOptions#setCleanSession(boolean)} set to
* true it is safe to use memory persistence as all state is cleared when a
* client disconnects. If connecting with cleanSession set to false in order to
* provide reliable message delivery then a persistent message store such as the
* default one should be used.
*
*
* The message store interface is pluggable. Different stores can be used by
* implementing the {@link MqttClientPersistence} interface and passing it to
* the clients constructor.
*
*
* @see IMqttAsyncClient
*/
public class MqttAsyncClient implements IMqttAsyncClient {
private static final String CLASS_NAME = MqttAsyncClient.class.getName();
private Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT, CLASS_NAME);
private static final String CLIENT_ID_PREFIX = "paho";
private static final long QUIESCE_TIMEOUT = 30000; // ms
private static final long DISCONNECT_TIMEOUT = 10000; // ms
private static final char MIN_HIGH_SURROGATE = '\uD800';
private static final char MAX_HIGH_SURROGATE = '\uDBFF';
private String clientId;
private String serverURI;
protected ClientComms comms;
private Hashtable topics;
private MqttClientPersistence persistence;
private MqttCallback mqttCallback;
private MqttConnectOptions connOpts;
private Object userContext;
private Timer reconnectTimer; // Automatic reconnect timer
private static int reconnectDelay = 1000; // Reconnect delay, starts at 1
// second
private boolean reconnecting = false;
private static final Object clientLock = new Object(); // Simple lock
private ScheduledExecutorService executorService;
/**
* Create an MqttAsyncClient that is used to communicate with an MQTT
* server.
*
* The address of a server can be specified on the constructor.
* Alternatively a list containing one or more servers can be specified
* using the {@link MqttConnectOptions#setServerURIs(String[])
* setServerURIs} method on MqttConnectOptions.
*
*
* The serverURI
parameter is typically used with the the
* clientId
parameter to form a key. The key is used to store
* and reference messages while they are being delivered. Hence the
* serverURI specified on the constructor must still be specified even if a
* list of servers is specified on an MqttConnectOptions object. The
* serverURI on the constructor must remain the same across restarts of the
* client for delivery of messages to be maintained from a given client to a
* given server or set of servers.
*
*
* The address of the server to connect to is specified as a URI. Two types
* of connection are supported tcp://
for a TCP connection and
* ssl://
for a TCP connection secured by SSL/TLS. For example:
*
*
* tcp://localhost:1883
* ssl://localhost:8883
*
*
* If the port is not specified, it will default to 1883 for
* tcp://
" URIs, and 8883 for ssl://
URIs.
*
*
*
* A client identifier clientId
must be specified and be less
* that 65535 characters. It must be unique across all clients connecting to
* the same server. The clientId is used by the server to store data related
* to the client, hence it is important that the clientId remain the same
* when connecting to a server if durable subscriptions or reliable
* messaging are required.
*
* A convenience method is provided to generate a random client id that
* should satisfy this criteria - {@link #generateClientId()}. As the client
* identifier is used by the server to identify a client when it reconnects,
* the client must use the same identifier between connections if durable
* subscriptions or reliable delivery of messages is required.
*
*
* In Java SE, SSL can be configured in one of several ways, which the
* client will use in the following order:
*
*
* - Supplying an
SSLSocketFactory
-
* applications can use
* {@link MqttConnectOptions#setSocketFactory(SocketFactory)} to supply a
* factory with the appropriate SSL settings.
* - SSL Properties - applications can supply SSL
* settings as a simple Java Properties using
* {@link MqttConnectOptions#setSSLProperties(Properties)}.
* - Use JVM settings - There are a number of standard
* Java system properties that can be used to configure key and trust
* stores.
*
*
*
* In Java ME, the platform settings are used for SSL connections.
*
*
*
* An instance of the default persistence mechanism
* {@link MqttDefaultFilePersistence} is used by the client. To specify a
* different persistence mechanism or to turn off persistence, use the
* {@link #MqttAsyncClient(String, String, MqttClientPersistence)}
* constructor.
*
* @param serverURI
* the address of the server to connect to, specified as a URI.
* Can be overridden using
* {@link MqttConnectOptions#setServerURIs(String[])}
* @param clientId
* a client identifier that is unique on the server being
* connected to
* @throws IllegalArgumentException
* if the URI does not start with "tcp://", "ssl://" or
* "local://".
* @throws IllegalArgumentException
* if the clientId is null or is greater than 65535 characters
* in length
* @throws MqttException
* if any other problem was encountered
*/
public MqttAsyncClient(String serverURI, String clientId) throws MqttException {
this(serverURI, clientId, new MqttDefaultFilePersistence());
}
/**
* Create an MqttAsyncClient that is used to communicate with an MQTT
* server.
*
* The address of a server can be specified on the constructor.
* Alternatively a list containing one or more servers can be specified
* using the {@link MqttConnectOptions#setServerURIs(String[])
* setServerURIs} method on MqttConnectOptions.
*
*
* The serverURI
parameter is typically used with the the
* clientId
parameter to form a key. The key is used to store
* and reference messages while they are being delivered. Hence the
* serverURI specified on the constructor must still be specified even if a
* list of servers is specified on an MqttConnectOptions object. The
* serverURI on the constructor must remain the same across restarts of the
* client for delivery of messages to be maintained from a given client to a
* given server or set of servers.
*
*
* The address of the server to connect to is specified as a URI. Two types
* of connection are supported tcp://
for a TCP connection and
* ssl://
for a TCP connection secured by SSL/TLS. For example:
*
*
* tcp://localhost:1883
* ssl://localhost:8883
*
*
* If the port is not specified, it will default to 1883 for
* tcp://
" URIs, and 8883 for ssl://
URIs.
*
*
*
* A client identifier clientId
must be specified and be less
* that 65535 characters. It must be unique across all clients connecting to
* the same server. The clientId is used by the server to store data related
* to the client, hence it is important that the clientId remain the same
* when connecting to a server if durable subscriptions or reliable
* messaging are required.
*
* A convenience method is provided to generate a random client id that
* should satisfy this criteria - {@link #generateClientId()}. As the client
* identifier is used by the server to identify a client when it reconnects,
* the client must use the same identifier between connections if durable
* subscriptions or reliable delivery of messages is required.
*
*
* In Java SE, SSL can be configured in one of several ways, which the
* client will use in the following order:
*
*
* - Supplying an
SSLSocketFactory
-
* applications can use
* {@link MqttConnectOptions#setSocketFactory(SocketFactory)} to supply a
* factory with the appropriate SSL settings.
* - SSL Properties - applications can supply SSL
* settings as a simple Java Properties using
* {@link MqttConnectOptions#setSSLProperties(Properties)}.
* - Use JVM settings - There are a number of standard
* Java system properties that can be used to configure key and trust
* stores.
*
*
*
* In Java ME, the platform settings are used for SSL connections.
*
*
* A persistence mechanism is used to enable reliable messaging. For
* messages sent at qualities of service (QoS) 1 or 2 to be reliably
* delivered, messages must be stored (on both the client and server) until
* the delivery of the message is complete. If messages are not safely
* stored when being delivered then a failure in the client or server can
* result in lost messages. A pluggable persistence mechanism is supported
* via the {@link MqttClientPersistence} interface. An implementer of this
* interface that safely stores messages must be specified in order for
* delivery of messages to be reliable. In addition
* {@link MqttConnectOptions#setCleanSession(boolean)} must be set to false.
* In the event that only QoS 0 messages are sent or received or
* cleanSession is set to true then a safe store is not needed.
*
*
* An implementation of file-based persistence is provided in class
* {@link MqttDefaultFilePersistence} which will work in all Java SE based
* systems. If no persistence is needed, the persistence parameter can be
* explicitly set to null
.
*
*
* @param serverURI
* the address of the server to connect to, specified as a URI.
* Can be overridden using
* {@link MqttConnectOptions#setServerURIs(String[])}
* @param clientId
* a client identifier that is unique on the server being
* connected to
* @param persistence
* the persistence class to use to store in-flight message. If
* null then the default persistence mechanism is used
* @throws MqttException
* if any other problem was encountered
*/
public MqttAsyncClient(String serverURI, String clientId, MqttClientPersistence persistence) throws MqttException {
this(serverURI, clientId, persistence, new TimerPingSender());
}
public MqttAsyncClient(String serverURI, String clientId, MqttClientPersistence persistence,
MqttPingSender pingSender) throws MqttException {
this(serverURI, clientId, persistence, pingSender, null);
}
/**
* Create an MqttAsyncClient that is used to communicate with an MQTT
* server.
*
* The address of a server can be specified on the constructor.
* Alternatively a list containing one or more servers can be specified
* using the {@link MqttConnectOptions#setServerURIs(String[])
* setServerURIs} method on MqttConnectOptions.
*
*
* The serverURI
parameter is typically used with the the
* clientId
parameter to form a key. The key is used to store
* and reference messages while they are being delivered. Hence the
* serverURI specified on the constructor must still be specified even if a
* list of servers is specified on an MqttConnectOptions object. The
* serverURI on the constructor must remain the same across restarts of the
* client for delivery of messages to be maintained from a given client to a
* given server or set of servers.
*
*
* The address of the server to connect to is specified as a URI. Two types
* of connection are supported tcp://
for a TCP connection and
* ssl://
for a TCP connection secured by SSL/TLS. For example:
*
*
* tcp://localhost:1883
* ssl://localhost:8883
*
*
* If the port is not specified, it will default to 1883 for
* tcp://
" URIs, and 8883 for ssl://
URIs.
*
*
*
* A client identifier clientId
must be specified and be less
* that 65535 characters. It must be unique across all clients connecting to
* the same server. The clientId is used by the server to store data related
* to the client, hence it is important that the clientId remain the same
* when connecting to a server if durable subscriptions or reliable
* messaging are required.
*
* A convenience method is provided to generate a random client id that
* should satisfy this criteria - {@link #generateClientId()}. As the client
* identifier is used by the server to identify a client when it reconnects,
* the client must use the same identifier between connections if durable
* subscriptions or reliable delivery of messages is required.
*
*
* In Java SE, SSL can be configured in one of several ways, which the
* client will use in the following order:
*
*
* - Supplying an
SSLSocketFactory
-
* applications can use
* {@link MqttConnectOptions#setSocketFactory(SocketFactory)} to supply a
* factory with the appropriate SSL settings.
* - SSL Properties - applications can supply SSL
* settings as a simple Java Properties using
* {@link MqttConnectOptions#setSSLProperties(Properties)}.
* - Use JVM settings - There are a number of standard
* Java system properties that can be used to configure key and trust
* stores.
*
*
*
* In Java ME, the platform settings are used for SSL connections.
*
*
* A persistence mechanism is used to enable reliable messaging. For
* messages sent at qualities of service (QoS) 1 or 2 to be reliably
* delivered, messages must be stored (on both the client and server) until
* the delivery of the message is complete. If messages are not safely
* stored when being delivered then a failure in the client or server can
* result in lost messages. A pluggable persistence mechanism is supported
* via the {@link MqttClientPersistence} interface. An implementer of this
* interface that safely stores messages must be specified in order for
* delivery of messages to be reliable. In addition
* {@link MqttConnectOptions#setCleanSession(boolean)} must be set to false.
* In the event that only QoS 0 messages are sent or received or
* cleanSession is set to true then a safe store is not needed.
*
*
* An implementation of file-based persistence is provided in class
* {@link MqttDefaultFilePersistence} which will work in all Java SE based
* systems. If no persistence is needed, the persistence parameter can be
* explicitly set to null
.
*
*
* @param serverURI
* the address of the server to connect to, specified as a URI.
* Can be overridden using
* {@link MqttConnectOptions#setServerURIs(String[])}
* @param clientId
* a client identifier that is unique on the server being
* connected to
* @param persistence
* the persistence class to use to store in-flight message. If
* null then the default persistence mechanism is used
* @param pingSender
* Custom {@link MqttPingSender} implementation.
* @param executorService
* used for managing threads. If null no executor service is used.
* @throws IllegalArgumentException
* if the URI does not start with "tcp://", "ssl://" or
* "local://"
* @throws IllegalArgumentException
* if the clientId is null or is greater than 65535 characters
* in length
* @throws MqttException
* if any other problem was encountered
*/
public MqttAsyncClient(String serverURI, String clientId, MqttClientPersistence persistence,
MqttPingSender pingSender, ScheduledExecutorService executorService) throws MqttException {
this(serverURI, clientId, persistence, pingSender, executorService, null);
}
/**
* Create an MqttAsyncClient that is used to communicate with an MQTT
* server.
*
* The address of a server can be specified on the constructor.
* Alternatively a list containing one or more servers can be specified
* using the {@link MqttConnectOptions#setServerURIs(String[])
* setServerURIs} method on MqttConnectOptions.
*
*
* The serverURI
parameter is typically used with the the
* clientId
parameter to form a key. The key is used to store
* and reference messages while they are being delivered. Hence the
* serverURI specified on the constructor must still be specified even if a
* list of servers is specified on an MqttConnectOptions object. The
* serverURI on the constructor must remain the same across restarts of the
* client for delivery of messages to be maintained from a given client to a
* given server or set of servers.
*
*
* The address of the server to connect to is specified as a URI. Two types
* of connection are supported tcp://
for a TCP connection and
* ssl://
for a TCP connection secured by SSL/TLS. For example:
*
*
* tcp://localhost:1883
* ssl://localhost:8883
*
*
* If the port is not specified, it will default to 1883 for
* tcp://
" URIs, and 8883 for ssl://
URIs.
*
*
*
* A client identifier clientId
must be specified and be less
* that 65535 characters. It must be unique across all clients connecting to
* the same server. The clientId is used by the server to store data related
* to the client, hence it is important that the clientId remain the same
* when connecting to a server if durable subscriptions or reliable
* messaging are required.
*
* A convenience method is provided to generate a random client id that
* should satisfy this criteria - {@link #generateClientId()}. As the client
* identifier is used by the server to identify a client when it reconnects,
* the client must use the same identifier between connections if durable
* subscriptions or reliable delivery of messages is required.
*
*
* In Java SE, SSL can be configured in one of several ways, which the
* client will use in the following order:
*
*
* - Supplying an
SSLSocketFactory
-
* applications can use
* {@link MqttConnectOptions#setSocketFactory(SocketFactory)} to supply a
* factory with the appropriate SSL settings.
* - SSL Properties - applications can supply SSL
* settings as a simple Java Properties using
* {@link MqttConnectOptions#setSSLProperties(Properties)}.
* - Use JVM settings - There are a number of standard
* Java system properties that can be used to configure key and trust
* stores.
*
*
*
* In Java ME, the platform settings are used for SSL connections.
*
*
* A persistence mechanism is used to enable reliable messaging. For
* messages sent at qualities of service (QoS) 1 or 2 to be reliably
* delivered, messages must be stored (on both the client and server) until
* the delivery of the message is complete. If messages are not safely
* stored when being delivered then a failure in the client or server can
* result in lost messages. A pluggable persistence mechanism is supported
* via the {@link MqttClientPersistence} interface. An implementer of this
* interface that safely stores messages must be specified in order for
* delivery of messages to be reliable. In addition
* {@link MqttConnectOptions#setCleanSession(boolean)} must be set to false.
* In the event that only QoS 0 messages are sent or received or
* cleanSession is set to true then a safe store is not needed.
*
*
* An implementation of file-based persistence is provided in class
* {@link MqttDefaultFilePersistence} which will work in all Java SE based
* systems. If no persistence is needed, the persistence parameter can be
* explicitly set to null
.
*
*
* @param serverURI
* the address of the server to connect to, specified as a URI.
* Can be overridden using
* {@link MqttConnectOptions#setServerURIs(String[])}
* @param clientId
* a client identifier that is unique on the server being
* connected to
* @param persistence
* the persistence class to use to store in-flight message. If
* null then the default persistence mechanism is used
* @param pingSender
* Custom {@link MqttPingSender} implementation.
* @param executorService
* used for managing threads. If null no executor service is used.
* @param highResolutionTimer
* used for providing time values for keepalive ping scheduling. If {@code null}, a default timer is used
* @throws IllegalArgumentException
* if the URI does not start with "tcp://", "ssl://" or
* "local://"
* @throws IllegalArgumentException
* if the clientId is null or is greater than 65535 characters
* in length
* @throws MqttException
* if any other problem was encountered
*/
public MqttAsyncClient(String serverURI, String clientId, MqttClientPersistence persistence,
MqttPingSender pingSender, ScheduledExecutorService executorService,
HighResolutionTimer highResolutionTimer) throws MqttException {
final String methodName = "MqttAsyncClient";
log.setResourceName(clientId);
if (clientId == null) { // Support empty client Id, 3.1.1 standard
throw new IllegalArgumentException("Null clientId");
}
// Count characters, surrogate pairs count as one character.
int clientIdLength = 0;
for (int i = 0; i < clientId.length() - 1; i++) {
if (Character_isHighSurrogate(clientId.charAt(i)))
i++;
clientIdLength++;
}
if (clientIdLength > 65535) {
throw new IllegalArgumentException("ClientId longer than 65535 characters");
}
NetworkModuleService.validateURI(serverURI);
this.serverURI = serverURI;
this.clientId = clientId;
this.persistence = persistence;
if (this.persistence == null) {
this.persistence = new MemoryPersistence();
}
if (highResolutionTimer == null) {
highResolutionTimer = new SystemHighResolutionTimer();
}
this.executorService = executorService;
// @TRACE 101= ClientID={0} ServerURI={1} PersistenceType={2}
log.fine(CLASS_NAME, methodName, "101", new Object[] { clientId, serverURI, persistence });
this.persistence.open(clientId, serverURI);
this.comms = new ClientComms(this, this.persistence, pingSender, this.executorService, highResolutionTimer);
this.persistence.close();
this.topics = new Hashtable();
}
/**
* @param ch
* the character to check.
* @return returns 'true' if the character is a high-surrogate code unit
*/
protected static boolean Character_isHighSurrogate(char ch) {
return (ch >= MIN_HIGH_SURROGATE) && (ch <= MAX_HIGH_SURROGATE);
}
/**
* Factory method to create an array of network modules, one for each of the
* supplied URIs
*
* @param address
* the URI for the server.
* @param options
* the {@link MqttConnectOptions} for the connection.
* @return a network module appropriate to the specified address.
* @throws MqttException
* if an exception occurs creating the network Modules
* @throws MqttSecurityException
* if an issue occurs creating an SSL / TLS Socket
*/
protected NetworkModule[] createNetworkModules(String address, MqttConnectOptions options)
throws MqttException, MqttSecurityException {
final String methodName = "createNetworkModules";
// @TRACE 116=URI={0}
log.fine(CLASS_NAME, methodName, "116", new Object[] { address });
NetworkModule[] networkModules = null;
String[] serverURIs = options.getServerURIs();
String[] array = null;
if (serverURIs == null) {
array = new String[] { address };
} else if (serverURIs.length == 0) {
array = new String[] { address };
} else {
array = serverURIs;
}
networkModules = new NetworkModule[array.length];
for (int i = 0; i < array.length; i++) {
networkModules[i] = createNetworkModule(array[i], options);
}
log.fine(CLASS_NAME, methodName, "108");
return networkModules;
}
/**
* Factory method to create the correct network module, based on the
* supplied address URI.
*
* @param address the URI for the server.
* @param options Connect options
* @return a network module appropriate to the specified address.
*/
private NetworkModule createNetworkModule(String address, MqttConnectOptions options) throws MqttException, MqttSecurityException {
final String methodName = "createNetworkModule";
// @TRACE 115=URI={0}
log.fine(CLASS_NAME,methodName, "115", new Object[] {address});
NetworkModule netModule = NetworkModuleService.createInstance(address, options, clientId);
return netModule;
}
private String getHostName(String uri) {
int portIndex = uri.indexOf(':');
if (portIndex == -1) {
portIndex = uri.indexOf('/');
}
if (portIndex == -1) {
portIndex = uri.length();
}
return uri.substring(0, portIndex);
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.paho.client.mqttv3.IMqttAsyncClient#connect(java.lang.Object,
* org.eclipse.paho.client.mqttv3.IMqttActionListener)
*/
public IMqttToken connect(Object userContext, IMqttActionListener callback)
throws MqttException, MqttSecurityException {
return this.connect(new MqttConnectOptions(), userContext, callback);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#connect()
*/
public IMqttToken connect() throws MqttException, MqttSecurityException {
return this.connect(null, null);
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.paho.client.mqttv3.IMqttAsyncClient#connect(org.eclipse.paho.
* client.mqttv3.MqttConnectOptions)
*/
public IMqttToken connect(MqttConnectOptions options) throws MqttException, MqttSecurityException {
return this.connect(options, null, null);
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.paho.client.mqttv3.IMqttAsyncClient#connect(org.eclipse.paho.
* client.mqttv3.MqttConnectOptions, java.lang.Object,
* org.eclipse.paho.client.mqttv3.IMqttActionListener)
*/
public IMqttToken connect(MqttConnectOptions options, Object userContext, IMqttActionListener callback)
throws MqttException, MqttSecurityException {
final String methodName = "connect";
if (comms.isConnected()) {
throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_CONNECTED);
}
if (comms.isConnecting()) {
throw new MqttException(MqttException.REASON_CODE_CONNECT_IN_PROGRESS);
}
if (comms.isDisconnecting()) {
throw new MqttException(MqttException.REASON_CODE_CLIENT_DISCONNECTING);
}
if (comms.isClosed()) {
throw new MqttException(MqttException.REASON_CODE_CLIENT_CLOSED);
}
if (options == null) {
options = new MqttConnectOptions();
}
this.connOpts = options;
this.userContext = userContext;
final boolean automaticReconnect = options.isAutomaticReconnect();
// @TRACE 103=cleanSession={0} connectionTimeout={1} TimekeepAlive={2}
// userName={3} password={4} will={5} userContext={6} callback={7}
log.fine(CLASS_NAME, methodName, "103",
new Object[] { Boolean.valueOf(options.isCleanSession()), Integer.valueOf(options.getConnectionTimeout()),
Integer.valueOf(options.getKeepAliveInterval()), options.getUserName(),
((null == options.getPassword()) ? "[null]" : "[notnull]"),
((null == options.getWillMessage()) ? "[null]" : "[notnull]"), userContext, callback });
comms.setNetworkModules(createNetworkModules(serverURI, options));
comms.setReconnectCallback(new MqttReconnectCallback(automaticReconnect));
// Insert our own callback to iterate through the URIs till the connect
// succeeds
MqttToken userToken = new MqttToken(getClientId());
ConnectActionListener connectActionListener = new ConnectActionListener(this, persistence, comms, options,
userToken, userContext, callback, reconnecting);
userToken.setActionCallback(connectActionListener);
userToken.setUserContext(this);
// If we are using the MqttCallbackExtended, set it on the
// connectActionListener
if (this.mqttCallback instanceof MqttCallbackExtended) {
connectActionListener.setMqttCallbackExtended((MqttCallbackExtended) this.mqttCallback);
}
comms.setNetworkModuleIndex(0);
connectActionListener.connect();
return userToken;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnect(java.lang.
* Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
*/
public IMqttToken disconnect(Object userContext, IMqttActionListener callback) throws MqttException {
return this.disconnect(QUIESCE_TIMEOUT, userContext, callback);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnect()
*/
public IMqttToken disconnect() throws MqttException {
return this.disconnect(null, null);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnect(long)
*/
public IMqttToken disconnect(long quiesceTimeout) throws MqttException {
return this.disconnect(quiesceTimeout, null, null);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnect(long,
* java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
*/
public IMqttToken disconnect(long quiesceTimeout, Object userContext, IMqttActionListener callback)
throws MqttException {
final String methodName = "disconnect";
// @TRACE 104=> quiesceTimeout={0} userContext={1} callback={2}
log.fine(CLASS_NAME, methodName, "104", new Object[] { Long.valueOf(quiesceTimeout), userContext, callback });
MqttToken token = new MqttToken(getClientId());
token.setActionCallback(callback);
token.setUserContext(userContext);
MqttDisconnect disconnect = new MqttDisconnect();
try {
comms.disconnect(disconnect, quiesceTimeout, token);
} catch (MqttException ex) {
// @TRACE 105=< exception
log.fine(CLASS_NAME, methodName, "105", null, ex);
throw ex;
}
// @TRACE 108=<
log.fine(CLASS_NAME, methodName, "108");
return token;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnectForcibly()
*/
public void disconnectForcibly() throws MqttException {
disconnectForcibly(QUIESCE_TIMEOUT, DISCONNECT_TIMEOUT);
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnectForcibly(long)
*/
public void disconnectForcibly(long disconnectTimeout) throws MqttException {
disconnectForcibly(QUIESCE_TIMEOUT, disconnectTimeout);
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnectForcibly(long,
* long)
*/
public void disconnectForcibly(long quiesceTimeout, long disconnectTimeout) throws MqttException {
comms.disconnectForcibly(quiesceTimeout, disconnectTimeout);
}
/**
* Disconnects from the server forcibly to reset all the states. Could be
* useful when disconnect attempt failed.
*
* Because the client is able to establish the TCP/IP connection to a none
* MQTT server and it will certainly fail to send the disconnect packet.
*
* @param quiesceTimeout
* the amount of time in milliseconds to allow for existing work
* to finish before disconnecting. A value of zero or less means
* the client will not quiesce.
* @param disconnectTimeout
* the amount of time in milliseconds to allow send disconnect
* packet to server.
* @param sendDisconnectPacket
* if true, will send the disconnect packet to the server
* @throws MqttException
* if any unexpected error
*/
public void disconnectForcibly(long quiesceTimeout, long disconnectTimeout, boolean sendDisconnectPacket)
throws MqttException {
comms.disconnectForcibly(quiesceTimeout, disconnectTimeout, sendDisconnectPacket);
}
/*
* (non-Javadoc)
*
* @see IMqttAsyncClient#isConnected()
*/
public boolean isConnected() {
return comms.isConnected();
}
/*
* (non-Javadoc)
*
* @see IMqttAsyncClient#getClientId()
*/
public String getClientId() {
return clientId;
}
/*
* (non-Javadoc)
*
* @see IMqttAsyncClient#getServerURI()
*/
public String getServerURI() {
return serverURI;
}
/**
* Returns the currently connected Server URI Implemented due to:
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=481097
*
* Where getServerURI only returns the URI that was provided in
* MqttAsyncClient's constructor, getCurrentServerURI returns the URI of the
* Server that the client is currently connected to. This would be different
* in scenarios where multiple server URIs have been provided to the
* MqttConnectOptions.
*
* @return the currently connected server URI
*/
public String getCurrentServerURI() {
return comms.getNetworkModules()[comms.getNetworkModuleIndex()].getServerURI();
}
/**
* Get a topic object which can be used to publish messages.
*
* There are two alternative methods that should be used in preference to
* this one when publishing a message:
*
*
* - {@link MqttAsyncClient#publish(String, MqttMessage)} to publish a
* message in a non-blocking manner or
* - {@link MqttClient#publish(String, MqttMessage)} to publish a message
* in a blocking manner
*
*
* When you build an application, the design of the topic tree should take
* into account the following principles of topic name syntax and semantics:
*
*
*
* - A topic must be at least one character long.
* - Topic names are case sensitive. For example, ACCOUNTS and
* Accounts are two different topics.
* - Topic names can include the space character. For example,
* Accounts payable is a valid topic.
* - A leading "/" creates a distinct topic. For example,
* /finance is different from finance. /finance
* matches "+/+" and "/+", but not "+".
* - Do not include the null character (Unicode \x0000) in any topic.
*
*
*
* The following principles apply to the construction and content of a topic
* tree:
*
*
*
* - The length is limited to 64k but within that there are no limits to
* the number of levels in a topic tree.
* - There can be any number of root nodes; that is, there can be any
* number of topic trees.
*
*
* @param topic
* the topic to use, for example "finance/stock/ibm".
* @return an MqttTopic object, which can be used to publish messages to the
* topic.
* @throws IllegalArgumentException
* if the topic contains a '+' or '#' wildcard character.
*/
protected MqttTopic getTopic(String topic) {
MqttTopic.validate(topic, false/* wildcards NOT allowed */);
MqttTopic result = (MqttTopic) topics.get(topic);
if (result == null) {
result = new MqttTopic(topic, comms);
topics.put(topic, result);
}
return result;
}
/*
* (non-Javadoc) Check and send a ping if needed. By default, client
* sends PingReq to server to keep the connection to server. For some
* platforms which cannot use this mechanism, such as Android, developer
* needs to handle the ping request manually with this method.
*
* @throws MqttException for other errors encountered while publishing the
* message.
*/
public IMqttToken checkPing(Object userContext, IMqttActionListener callback) throws MqttException {
final String methodName = "ping";
MqttToken token;
// @TRACE 117=>
log.fine(CLASS_NAME, methodName, "117");
token = comms.checkForActivity(callback);
// @TRACE 118=<
log.fine(CLASS_NAME, methodName, "118");
return token;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang.
* String, int, java.lang.Object,
* org.eclipse.paho.client.mqttv3.IMqttActionListener)
*/
public IMqttToken subscribe(String topicFilter, int qos, Object userContext, IMqttActionListener callback)
throws MqttException {
return this.subscribe(new String[] { topicFilter }, new int[] { qos }, userContext, callback);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang.
* String, int)
*/
public IMqttToken subscribe(String topicFilter, int qos) throws MqttException {
return this.subscribe(new String[] { topicFilter }, new int[] { qos }, null, null);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang.
* String[], int[])
*/
public IMqttToken subscribe(String[] topicFilters, int[] qos) throws MqttException {
return this.subscribe(topicFilters, qos, null, null);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang.
* String[], int[], java.lang.Object,
* org.eclipse.paho.client.mqttv3.IMqttActionListener)
*/
public IMqttToken subscribe(String[] topicFilters, int[] qos, Object userContext, IMqttActionListener callback)
throws MqttException {
if (topicFilters.length != qos.length) {
throw new IllegalArgumentException();
}
// remove any message handlers for individual topics and validate topicFilter
for (String topicFilter : topicFilters) {
// Check if the topic filter is valid before subscribing
MqttTopic.validate(topicFilter, true/* allow wildcards */);
this.comms.removeMessageListener(topicFilter);
}
return this.subscribeBase(topicFilters, qos, userContext, callback);
}
private IMqttToken subscribeBase(String[] topicFilters, int[] qos, Object userContext, IMqttActionListener callback)
throws MqttException {
final String methodName = "subscribe";
// Only Generate Log string if we are logging at FINE level
if (log.isLoggable(Logger.FINE)) {
StringBuffer subs = new StringBuffer();
for (int i = 0; i < topicFilters.length; i++) {
if (i > 0) {
subs.append(", ");
}
subs.append("topic=").append(topicFilters[i]).append(" qos=").append(qos[i]);
}
// @TRACE 106=Subscribe topicFilter={0} userContext={1} callback={2}
log.fine(CLASS_NAME, methodName, "106", new Object[] { subs.toString(), userContext, callback });
}
MqttToken token = new MqttToken(getClientId());
token.setActionCallback(callback);
token.setUserContext(userContext);
token.internalTok.setTopics(topicFilters);
MqttSubscribe register = new MqttSubscribe(topicFilters, qos);
comms.sendNoWait(register, token);
// @TRACE 109=<
log.fine(CLASS_NAME, methodName, "109");
return token;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang.
* String, int, java.lang.Object,
* org.eclipse.paho.client.mqttv3.IMqttActionListener)
*/
public IMqttToken subscribe(String topicFilter, int qos, Object userContext, IMqttActionListener callback,
IMqttMessageListener messageListener) throws MqttException {
return this.subscribe(new String[] { topicFilter }, new int[] { qos }, userContext, callback,
new IMqttMessageListener[] { messageListener });
}
/*
* (non-Javadoc)
*
* @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang.
* String, int)
*/
public IMqttToken subscribe(String topicFilter, int qos, IMqttMessageListener messageListener)
throws MqttException {
return this.subscribe(new String[] { topicFilter }, new int[] { qos }, null, null,
new IMqttMessageListener[] { messageListener });
}
/*
* (non-Javadoc)
*
* @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang.
* String[], int[])
*/
public IMqttToken subscribe(String[] topicFilters, int[] qos, IMqttMessageListener[] messageListeners)
throws MqttException {
return this.subscribe(topicFilters, qos, null, null, messageListeners);
}
public IMqttToken subscribe(String[] topicFilters, int[] qos, Object userContext, IMqttActionListener callback,
IMqttMessageListener[] messageListeners) throws MqttException {
if (messageListeners != null && (messageListeners.length != qos.length) || (qos.length != topicFilters.length)) {
throw new IllegalArgumentException();
}
// add or remove message handlers to the list for this client
for (int i = 0; i < topicFilters.length; ++i) {
MqttTopic.validate(topicFilters[i], true/* allow wildcards */);
if (messageListeners == null || messageListeners[i] == null) {
this.comms.removeMessageListener(topicFilters[i]);
}
else {
this.comms.setMessageListener(topicFilters[i], messageListeners[i]);
}
}
IMqttToken token = null;
try {
token = this.subscribeBase(topicFilters, qos, userContext, callback);
} catch(Exception e) {
// if the subscribe fails, then we have to remove the message handlers
for (String topicFilter : topicFilters) {
this.comms.removeMessageListener(topicFilter);
}
throw e;
}
return token;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.paho.client.mqttv3.IMqttAsyncClient#unsubscribe(java.lang.
* String, java.lang.Object,
* org.eclipse.paho.client.mqttv3.IMqttActionListener)
*/
public IMqttToken unsubscribe(String topicFilter, Object userContext, IMqttActionListener callback)
throws MqttException {
return unsubscribe(new String[] { topicFilter }, userContext, callback);
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.paho.client.mqttv3.IMqttAsyncClient#unsubscribe(java.lang.
* String)
*/
public IMqttToken unsubscribe(String topicFilter) throws MqttException {
return unsubscribe(new String[] { topicFilter }, null, null);
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.paho.client.mqttv3.IMqttAsyncClient#unsubscribe(java.lang.
* String[])
*/
public IMqttToken unsubscribe(String[] topicFilters) throws MqttException {
return unsubscribe(topicFilters, null, null);
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.paho.client.mqttv3.IMqttAsyncClient#unsubscribe(java.lang.
* String[], java.lang.Object,
* org.eclipse.paho.client.mqttv3.IMqttActionListener)
*/
public IMqttToken unsubscribe(String[] topicFilters, Object userContext, IMqttActionListener callback)
throws MqttException {
final String methodName = "unsubscribe";
// Only Generate Log string if we are logging at FINE level
if (log.isLoggable(Logger.FINE)) {
String subs = "";
for (int i = 0; i < topicFilters.length; i++) {
if (i > 0) {
subs += ", ";
}
subs += topicFilters[i];
}
// @TRACE 107=Unsubscribe topic={0} userContext={1} callback={2}
log.fine(CLASS_NAME, methodName, "107", new Object[] { subs, userContext, callback });
}
for (String topicFilter : topicFilters) {
// Check if the topic filter is valid before unsubscribing
// Although we already checked when subscribing, but invalid
// topic filter is meanless for unsubscribing, just prohibit it
// to reduce unnecessary control packet send to broker.
MqttTopic.validate(topicFilter, true/* allow wildcards */);
}
// remove message handlers from the list for this client
for (String topicFilter : topicFilters) {
this.comms.removeMessageListener(topicFilter);
}
MqttToken token = new MqttToken(getClientId());
token.setActionCallback(callback);
token.setUserContext(userContext);
token.internalTok.setTopics(topicFilters);
MqttUnsubscribe unregister = new MqttUnsubscribe(topicFilters);
comms.sendNoWait(unregister, token);
// @TRACE 110=<
log.fine(CLASS_NAME, methodName, "110");
return token;
}
/*
* (non-Javadoc)
*
* @see IMqttAsyncClient#removeMessage(IMqttDeliveryToken)
*/
public boolean removeMessage(IMqttDeliveryToken token) throws MqttException {
return comms.removeMessage(token);
}
/*
* (non-Javadoc)
*
* @see IMqttAsyncClient#setCallback(MqttCallback)
*/
public void setCallback(MqttCallback callback) {
this.mqttCallback = callback;
comms.setCallback(callback);
}
/*
* (non-Javadoc)
*
* @see IMqttAsyncClient#setManualAcks(manualAcks)
*/
public void setManualAcks(boolean manualAcks) {
comms.setManualAcks(manualAcks);
}
public void messageArrivedComplete(int messageId, int qos) throws MqttException {
comms.messageArrivedComplete(messageId, qos);
}
/**
* Returns a randomly generated client identifier based on the the fixed
* prefix (paho) and the system time.
*
* When cleanSession is set to false, an application must ensure it uses the
* same client identifier when it reconnects to the server to resume state
* and maintain assured message delivery.
*
*
* @return a generated client identifier
* @see MqttConnectOptions#setCleanSession(boolean)
*/
public static String generateClientId() {
// length of nanoTime = 15, so total length = 19 < 65535(defined in
// spec)
return CLIENT_ID_PREFIX + System.nanoTime();
}
/*
* (non-Javadoc)
*
* @see IMqttAsyncClient#getPendingDeliveryTokens()
*/
public IMqttDeliveryToken[] getPendingDeliveryTokens() {
return comms.getPendingDeliveryTokens();
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.paho.client.mqttv3.IMqttAsyncClient#publish(java.lang.String,
* byte[], int, boolean, java.lang.Object,
* org.eclipse.paho.client.mqttv3.IMqttActionListener)
*/
public IMqttDeliveryToken publish(String topic, byte[] payload, int qos, boolean retained, Object userContext,
IMqttActionListener callback) throws MqttException, MqttPersistenceException {
MqttMessage message = new MqttMessage(payload);
message.setQos(qos);
message.setRetained(retained);
return this.publish(topic, message, userContext, callback);
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.paho.client.mqttv3.IMqttAsyncClient#publish(java.lang.String,
* byte[], int, boolean)
*/
public IMqttDeliveryToken publish(String topic, byte[] payload, int qos, boolean retained)
throws MqttException, MqttPersistenceException {
return this.publish(topic, payload, qos, retained, null, null);
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.paho.client.mqttv3.IMqttAsyncClient#publish(java.lang.String,
* org.eclipse.paho.client.mqttv3.MqttMessage)
*/
public IMqttDeliveryToken publish(String topic, MqttMessage message)
throws MqttException, MqttPersistenceException {
return this.publish(topic, message, null, null);
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.paho.client.mqttv3.IMqttAsyncClient#publish(java.lang.String,
* org.eclipse.paho.client.mqttv3.MqttMessage, java.lang.Object,
* org.eclipse.paho.client.mqttv3.IMqttActionListener)
*/
public IMqttDeliveryToken publish(String topic, MqttMessage message, Object userContext,
IMqttActionListener callback) throws MqttException, MqttPersistenceException {
final String methodName = "publish";
// @TRACE 111=< topic={0} message={1}userContext={1} callback={2}
log.fine(CLASS_NAME, methodName, "111", new Object[] { topic, userContext, callback });
// Checks if a topic is valid when publishing a message.
MqttTopic.validate(topic, false/* wildcards NOT allowed */);
MqttDeliveryToken token = new MqttDeliveryToken(getClientId());
token.setActionCallback(callback);
token.setUserContext(userContext);
token.setMessage(message);
token.internalTok.setTopics(new String[] { topic });
MqttPublish pubMsg = new MqttPublish(topic, message);
comms.sendNoWait(pubMsg, token);
// @TRACE 112=<
log.fine(CLASS_NAME, methodName, "112");
return token;
}
/**
* User triggered attempt to reconnect
*
* @throws MqttException
* if there is an issue with reconnecting
*/
public void reconnect() throws MqttException {
final String methodName = "reconnect";
// @Trace 500=Attempting to reconnect client: {0}
log.fine(CLASS_NAME, methodName, "500", new Object[] { this.clientId });
// Some checks to make sure that we're not attempting to reconnect an
// already connected client
if (comms.isConnected()) {
throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_CONNECTED);
}
if (comms.isConnecting()) {
throw new MqttException(MqttException.REASON_CODE_CONNECT_IN_PROGRESS);
}
if (comms.isDisconnecting()) {
throw new MqttException(MqttException.REASON_CODE_CLIENT_DISCONNECTING);
}
if (comms.isClosed()) {
throw new MqttException(MqttException.REASON_CODE_CLIENT_CLOSED);
}
// We don't want to spam the server
stopReconnectCycle();
attemptReconnect();
}
/**
* Attempts to reconnect the client to the server. If successful it will
* make sure that there are no further reconnects scheduled. However if the
* connect fails, the delay will double up to 128 seconds and will
* re-schedule the reconnect for after the delay.
*
* Any thrown exceptions are logged but not acted upon as it is assumed that
* they are being thrown due to the server being offline and so reconnect
* attempts will continue.
*/
private void attemptReconnect() {
final String methodName = "attemptReconnect";
// @Trace 500=Attempting to reconnect client: {0}
log.fine(CLASS_NAME, methodName, "500", new Object[] { this.clientId });
try {
connect(this.connOpts, this.userContext, new MqttReconnectActionListener(methodName));
} catch (MqttSecurityException ex) {
// @TRACE 804=exception
log.fine(CLASS_NAME, methodName, "804", null, ex);
} catch (MqttException ex) {
// @TRACE 804=exception
log.fine(CLASS_NAME, methodName, "804", null, ex);
}
}
private void startReconnectCycle() {
String methodName = "startReconnectCycle";
// @Trace 503=Start reconnect timer for client: {0}, delay: {1}
log.fine(CLASS_NAME, methodName, "503", new Object[] { this.clientId, Long.valueOf(reconnectDelay) });
reconnectTimer = new Timer("MQTT Reconnect: " + clientId);
reconnectTimer.schedule(new ReconnectTask(), reconnectDelay);
}
private void stopReconnectCycle() {
String methodName = "stopReconnectCycle";
// @Trace 504=Stop reconnect timer for client: {0}
log.fine(CLASS_NAME, methodName, "504", new Object[] { this.clientId });
synchronized (clientLock) {
if (this.connOpts.isAutomaticReconnect()) {
if (reconnectTimer != null) {
reconnectTimer.cancel();
reconnectTimer = null;
}
reconnectDelay = 1000; // Reset Delay Timer
}
}
}
private class ReconnectTask extends TimerTask {
private static final String methodName = "ReconnectTask.run";
public void run() {
// @Trace 506=Triggering Automatic Reconnect attempt.
log.fine(CLASS_NAME, methodName, "506");
attemptReconnect();
}
}
class MqttReconnectCallback implements MqttCallbackExtended {
final boolean automaticReconnect;
MqttReconnectCallback(boolean isAutomaticReconnect) {
automaticReconnect = isAutomaticReconnect;
}
public void connectionLost(Throwable cause) {
if (automaticReconnect) {
// Automatic reconnect is set so make sure comms is in resting
// state
comms.setRestingState(true);
reconnecting = true;
startReconnectCycle();
}
}
public void messageArrived(String topic, MqttMessage message) throws Exception {
}
public void deliveryComplete(IMqttDeliveryToken token) {
}
public void connectComplete(boolean reconnect, String serverURI) {
}
}
class MqttReconnectActionListener implements IMqttActionListener {
final String methodName;
MqttReconnectActionListener(String methodName) {
this.methodName = methodName;
}
public void onSuccess(IMqttToken asyncActionToken) {
// @Trace 501=Automatic Reconnect Successful: {0}
log.fine(CLASS_NAME, methodName, "501", new Object[] { asyncActionToken.getClient().getClientId() });
comms.setRestingState(false);
stopReconnectCycle();
}
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
// @Trace 502=Automatic Reconnect failed, rescheduling: {0}
log.fine(CLASS_NAME, methodName, "502", new Object[] { asyncActionToken.getClient().getClientId() });
if (reconnectDelay < connOpts.getMaxReconnectDelay()) {
reconnectDelay = reconnectDelay * 2;
}
rescheduleReconnectCycle(reconnectDelay);
}
private void rescheduleReconnectCycle(int delay) {
String reschedulemethodName = methodName + ":rescheduleReconnectCycle";
// @Trace 505=Rescheduling reconnect timer for client: {0}, delay:
// {1}
log.fine(CLASS_NAME, reschedulemethodName, "505",
new Object[] { MqttAsyncClient.this.clientId, String.valueOf(reconnectDelay) });
synchronized (clientLock) {
if (MqttAsyncClient.this.connOpts.isAutomaticReconnect()) {
if (reconnectTimer != null) {
reconnectTimer.schedule(new ReconnectTask(), delay);
} else {
// The previous reconnect timer was cancelled
reconnectDelay = delay;
startReconnectCycle();
}
}
}
}
}
/**
* Sets the DisconnectedBufferOptions for this client
*
* @param bufferOpts
* the {@link DisconnectedBufferOptions}
*/
public void setBufferOpts(DisconnectedBufferOptions bufferOpts) {
this.comms.setDisconnectedMessageBuffer(new DisconnectedMessageBuffer(bufferOpts));
}
/**
* Returns the number of messages in the Disconnected Message Buffer
*
* @return Count of messages in the buffer
*/
public int getBufferedMessageCount() {
return this.comms.getBufferedMessageCount();
}
/**
* Returns a message from the Disconnected Message Buffer
*
* @param bufferIndex
* the index of the message to be retrieved.
* @return the message located at the bufferIndex
*/
public MqttMessage getBufferedMessage(int bufferIndex) {
return this.comms.getBufferedMessage(bufferIndex);
}
/**
* Deletes a message from the Disconnected Message Buffer
*
* @param bufferIndex
* the index of the message to be deleted.
*/
public void deleteBufferedMessage(int bufferIndex) {
this.comms.deleteBufferedMessage(bufferIndex);
}
/**
* Returns the current number of outgoing in-flight messages being sent by
* the client. Note that this number cannot be guaranteed to be 100%
* accurate as some messages may have been sent or queued in the time taken
* for this method to return.
*
* @return the current number of in-flight messages.
*/
public int getInFlightMessageCount() {
return this.comms.getActualInFlight();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#close()
*/
public void close() throws MqttException {
close(false);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#close()
*/
public void close(boolean force) throws MqttException {
final String methodName = "close";
// @TRACE 113=<
log.fine(CLASS_NAME, methodName, "113");
comms.close(force);
// @TRACE 114=>
log.fine(CLASS_NAME, methodName, "114");
}
/**
* Return a debug object that can be used to help solve problems.
*
* @return the {@link Debug} object
*/
public Debug getDebug() {
return new Debug(clientId, comms);
}
}