diff --git a/org.eclipse.paho.client.mqttv3.internal.traceformat/pom.xml b/org.eclipse.paho.client.mqttv3.internal.traceformat/pom.xml new file mode 100644 index 0000000..4b25058 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3.internal.traceformat/pom.xml @@ -0,0 +1,26 @@ + + + + org.eclipse.paho + paho-parent + 0.9.0 + + + 4.0.0 + paho-mqtt-client-traceformatter + 0.9.0 + jar + Paho :: MQTT Client trace formatter + A trace formatter for the MQTT Client library. It currently outputs HTML files. + ${paho.url} + + + + org.eclipse.paho + paho-mqtt-client + ${project.version} + compile + + + \ No newline at end of file diff --git a/org.eclipse.paho.client.mqttv3.internal.traceformat/src/main/java/org/eclipse/paho/client/mqttv3/internal/logBuilder/LogMessageExtractor.java b/org.eclipse.paho.client.mqttv3.internal.traceformat/src/main/java/org/eclipse/paho/client/mqttv3/internal/logBuilder/LogMessageExtractor.java new file mode 100644 index 0000000..27f5b3b --- /dev/null +++ b/org.eclipse.paho.client.mqttv3.internal.traceformat/src/main/java/org/eclipse/paho/client/mqttv3/internal/logBuilder/LogMessageExtractor.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal.logBuilder; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.PrintStream; +import java.util.HashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Scan all Paho source files and extract NLSable trace and log records. + * + * This needs to be run any time new trace/log records are added + * or changed. The logcat.properties file in the mqttv3.internal.nls + * is updated to match the trace records in the paho source files. + */ +public class LogMessageExtractor { + + public static void main(String[] args) { + if (args == null) { + args = new String[] {}; + } + if (args.length != 0 && args.length != 2 && args.length != 4) { + usageAndExit(); + } + // Set defaults by assuming this is run from an eclipse workspace with paho projects loaded + String dir = "../org.eclipse.paho.client.mqttv3/src"; + String file = dir+"/org/eclipse/paho/client/mqttv3/internal/nls/logcat.properties"; + + for (int i=0;i0) { + rc = rc1;; + } + } else { + if (f.isDirectory()) { + File[] files = f.listFiles(); + for (int i=0;i0) { + rc = rc1;; + } + } + } + } + return rc; + } + + public short parseFile(File f) throws Exception { + String filename = f.getAbsolutePath(); + String classname = filename.substring(this.basedir.length()+1).replaceAll("/","."); + classname = classname.substring(0,classname.length()-5); + BufferedReader in = new BufferedReader(new FileReader(f)); + String line; + int lineNo = 1; + short rc = 0; + + while ( (line = in.readLine()) != null) { + Matcher m = pattern.matcher(line); + if (m.matches()) { + String number = m.group(1); + if (this.points.containsKey(number)) { + System.out.println("Duplicate Trace Point: "+number); + System.out.println(" "+this.points.get(number)); + System.out.println(" "+classname+":"+lineNo); + rc=1; + } + // The original extractor put out 4 values for each trace point +// out.println(number+".class="+classname); +// out.println(number+".line="+lineNo); +// out.println(number+".value="+m.group(2)); + this.points.put(number, classname+":"+lineNo); + out.println(number+"="+m.group(2)); + } + lineNo++; + } + in.close(); + + return rc; + } +} diff --git a/org.eclipse.paho.client.mqttv3.internal.traceformat/src/org/eclipse/paho/client/mqttv3/internal/logBuilder/LogMessageExtractor.java b/org.eclipse.paho.client.mqttv3.internal.traceformat/src/org/eclipse/paho/client/mqttv3/internal/logBuilder/LogMessageExtractor.java deleted file mode 100644 index 27f5b3b..0000000 --- a/org.eclipse.paho.client.mqttv3.internal.traceformat/src/org/eclipse/paho/client/mqttv3/internal/logBuilder/LogMessageExtractor.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal.logBuilder; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.PrintStream; -import java.util.HashMap; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Scan all Paho source files and extract NLSable trace and log records. - * - * This needs to be run any time new trace/log records are added - * or changed. The logcat.properties file in the mqttv3.internal.nls - * is updated to match the trace records in the paho source files. - */ -public class LogMessageExtractor { - - public static void main(String[] args) { - if (args == null) { - args = new String[] {}; - } - if (args.length != 0 && args.length != 2 && args.length != 4) { - usageAndExit(); - } - // Set defaults by assuming this is run from an eclipse workspace with paho projects loaded - String dir = "../org.eclipse.paho.client.mqttv3/src"; - String file = dir+"/org/eclipse/paho/client/mqttv3/internal/nls/logcat.properties"; - - for (int i=0;i0) { - rc = rc1;; - } - } else { - if (f.isDirectory()) { - File[] files = f.listFiles(); - for (int i=0;i0) { - rc = rc1;; - } - } - } - } - return rc; - } - - public short parseFile(File f) throws Exception { - String filename = f.getAbsolutePath(); - String classname = filename.substring(this.basedir.length()+1).replaceAll("/","."); - classname = classname.substring(0,classname.length()-5); - BufferedReader in = new BufferedReader(new FileReader(f)); - String line; - int lineNo = 1; - short rc = 0; - - while ( (line = in.readLine()) != null) { - Matcher m = pattern.matcher(line); - if (m.matches()) { - String number = m.group(1); - if (this.points.containsKey(number)) { - System.out.println("Duplicate Trace Point: "+number); - System.out.println(" "+this.points.get(number)); - System.out.println(" "+classname+":"+lineNo); - rc=1; - } - // The original extractor put out 4 values for each trace point -// out.println(number+".class="+classname); -// out.println(number+".line="+lineNo); -// out.println(number+".value="+m.group(2)); - this.points.put(number, classname+":"+lineNo); - out.println(number+"="+m.group(2)); - } - lineNo++; - } - in.close(); - - return rc; - } -} diff --git a/org.eclipse.paho.client.mqttv3/pom.xml b/org.eclipse.paho.client.mqttv3/pom.xml new file mode 100644 index 0000000..905528c --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/pom.xml @@ -0,0 +1,80 @@ + + + + org.eclipse.paho + paho-parent + 0.9.0 + + + 4.0.0 + paho-mqtt-client + 0.9.0 + jar + Paho :: MQTT Client library + MQTT Client library + ${paho.url} + + + ${maven.build.timestamp} + yyMMdd + + + + + + src/main/java + true + + + + + org.apache.felix + maven-bundle-plugin + 2.3.7 + true + + + generate-manifest + + manifest + + + + L${build.level} + . + $(maven-symbolicname);singleton:=true + javax.net;resolution:=optional, javax.net.ssl;resolution:=optional + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.3.2 + + + artifact-jar + + jar + + + + test-jar + + test-jar + + + + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + + + \ No newline at end of file diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/IMqttActionListener.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/IMqttActionListener.java new file mode 100644 index 0000000..53d26b8 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/IMqttActionListener.java @@ -0,0 +1,29 @@ +package org.eclipse.paho.client.mqttv3; + +/** + * Implementors of this interface will be notified when an asynchronous action completes. + * + *

A listener is registered on an MqttToken and a token is associated + * with an action like connect or publish. When used with tokens on the MqttAsyncClient + * the listener will be called back on the MQTT clients thread. The listener will be informed + * if the action succeeds or fails. It is important that the listener returns control quickly + * otherwise the operation of the MQTT client will be stalled. + *

+ */ +public interface IMqttActionListener { + /** + * This method is invoked when an action has completed successfully. + * @param asyncActionToken associated with the action that has completed + */ + public void onSuccess(IMqttToken asyncActionToken ); + /** + * This method is invoked when an action fails. + * If a client is disconnected while an action is in progress + * onFailure will be called. For connections + * that use clean session set to false, any QOS 1 and 2 messages that + * are in the process of being delivered will be delivered to the requested + * quality of service next time the client connects. + * @param asyncActionToken associated with the action that has failed + */ + public void onFailure(IMqttToken asyncActionToken, Throwable exception); +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/IMqttAsyncClient.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/IMqttAsyncClient.java new file mode 100644 index 0000000..6201ed5 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/IMqttAsyncClient.java @@ -0,0 +1,713 @@ +package org.eclipse.paho.client.mqttv3; + +/** + * Enables an application to communicate with an MQTT server using using non-blocking methods. + *

+ * It provides applications a simple programming interface to all features of the MQTT version 3.1 + * specification including: + *

    + *
  • connect + *
  • publish + *
  • subscribe + *
  • unsubscribe + *
  • disconnect + *
+ *

+ *

+ * There are two styles of MQTT client, this one and {@link IMqttClient}. + *

    + *
  • IMqttAsyncClient provides a set of non blocking methods that return control to the + * invoking application after initial validation of parameters and state. The main processing is + * performed in the background so as not to block the application programs thread. This non + * blocking approach is handy when the application needs to carry on processing while the + * MQTT action takes place. For instance connecting to an MQTT server can take time, using + * the non blocking connect method allows an application to display a busy indicator while the + * connect action takes place in the background. Non blocking methods are particularly useful + * in event oriented programs and graphical programs where invoking methods that take time + * to complete on the the main or GUI thread can cause problems. The non-blocking interface + * can also be used in blocking form.
  • + *
  • IMqttClient provides a set of methods that block and return control to the application + * program once the MQTT action has completed. It is a thin layer that sits on top of + * IMqttAsyncClient implementation and is provided mainly for compatibility with earlier + * versions of the MQTT client. In most circumstances it is recommended to use IMqttAsyncClient + * based clients which allow an application to mix both non-blocking and blocking calls.
  • + *
+ *

+ *

+ * An application is not restricted to using one style, if an IMqttAsyncClient based client is used + * as both blocking and non-blocking methods can be used in the same application. If an IMqttClient + * based client is used then only blocking methods are available to the application. + * For more details on the blocking client see {@link IMqttClient}

+ * + *

There are two forms of non-blocking method: + *

    + *
  1. + *
    + *     IMqttToken token = asyncClient.method(parms)
    + *     
    + *

    In this form the method returns a token that can be used to track the + * progress of the action (method). The method provides a waitForCompletion() + * method that once invoked will block until the action completes. Once + * completed there are method on the token that can be used to check if the + * action completed successfully or not. For example + * to wait until a connect completes: + *

    + *      IMqttToken conToken;
    + *   	conToken = asyncClient.client.connect(conToken);
    + *     ... do some work...
    + *   	conToken.waitForCompletion();
    + *     
    + * /p> + *

    To turn a method into a blocking invocation the following form can be used: + *

    + *     IMqttToken token;
    + *     token = asyncClient.method(parms).waitForCompletion();
    + *     
    + + *
  2. + * + *
  3. + *
    + *     IMqttToken token method(parms, Object userContext, IMqttActionListener callback)
    + *     
    + *

    In this form a callback is registered with the method. The callback will be + * notified when the action succeeds or fails. The callback is invoked on the thread + * managed by the MQTT client so it is important that processing is minimised in the + * callback. If not the operation of the MQTT client will be inhibited. For example + * to be notified (called back) when a connect completes: + *

    + *     	IMqttToken conToken;	
    + *	    conToken = asyncClient.connect("some context",new new MqttAsyncActionListener() {			
    + *			public void onSuccess(IMqttToken asyncActionToken) {
    + *				log("Connected");
    + *			}
    + *				
    + *			public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
    + *				log ("connect failed" +exception);
    + *			}
    + *		  });
    + *	    An optional context object can be passed into the method which will then be made
    + *      available in the callback. The context is stored by the MQTT client) in the token
    + *      which is then returned to the invoker. The token is provided to the callback methods
    + *      where the context can then be accessed. 
    + *     
    + *

    + *
  4. + *
+ *

To understand when the delivery of a message is complete either of the two methods above + * can be used to either wait on or be notified when the publish completes. An alternative is to + * use the {@link MqttCallback#deliveryComplete(IMqttDeliveryToken)} method which will + * also be notified when a message has been delivered to the requested quality of service.

+ * + */ +public interface IMqttAsyncClient { + /** + * Connects to an MQTT server using the default options. + *

The default options are specified in {@link MqttConnectOptions} class. + *

+ * + * @throws MqttSecurityException for security related problems + * @throws MqttException for non security related problems + * @return token used to track and wait for the connect to complete. The token + * will be passed to the callback methtods if a callback is set. + * @see #connect(MqttConnectOptions, Object, IMqttActionListener) + */ + public IMqttToken connect() throws MqttException, MqttSecurityException; + + /** + * Connects to an MQTT server using the provided connect options. + *

The connection will be established using the options specified in the + * {@link MqttConnectOptions} parameter. + *

+ * + * @param options a set of connection parameters that override the defaults. + * @throws MqttSecurityException for security related problems + * @throws MqttException for non security related problems + * @return token used to track and wait for the connect to complete. The token + * will be passed to any callback that has been set. + * @see #connect(MqttConnectOptions, Object, IMqttActionListener) + */ + public IMqttToken connect(MqttConnectOptions options) throws MqttException, MqttSecurityException ; + /** + * Connects to an MQTT server using the default options. + *

The default options are specified in {@link MqttConnectOptions} class. + *

+ * + * @param userContext optional object used to pass context to the callback. Use + * null if not required. + * @param callback optional listener that will be notified when the connect completes. Use + * null if not required. + * @throws MqttSecurityException for security related problems + * @throws MqttException for non security related problems + * @return token used to track and wait for the connect to complete. The token + * will be passed to any callback that has been set. + * @see #connect(MqttConnectOptions, Object, IMqttActionListener) + */ + public IMqttToken connect(Object userContext, IMqttActionListener callback) throws MqttException, MqttSecurityException; + + + /** + * Connects to an MQTT server using the specified options. + *

The server to connect to is specified on the constructor. + * It is recommended to call {@link #setCallback(MqttCallback)} prior to + * connecting in order that messages destined for the client can be accepted + * as soon as the client is connected. + *

+ *

The method returns control before the connect completes. Completion can + * be tracked by: + *

    + *
  • Waiting on the returned token {@link IMqttToken#waitForCompletion()} or
  • + *
  • Passing in a callback {@link IMqttActionListener}
  • + *
+ *

+ * + * @param options a set of connection parameters that override the defaults. + * @param userContext optional object for used to pass context to the callback. Use + * null if not required. + * @param callback optional listener that will be notified when the connect completes. Use + * null if not required. + * @return token used to track and wait for the connect to complete. The token + * will be passed to any callback that has been set. + * @throws MqttSecurityException for security related problems + * @throws MqttException for non security related problems including communication errors + */ + public IMqttToken connect(MqttConnectOptions options, Object userContext, IMqttActionListener callback) throws MqttException, MqttSecurityException; + + /** + * Disconnects from the server. + *

An attempt is made to quiesce the client allowing outstanding + * work to complete before disconnecting. It will wait + * for a maximum of 30 seconds for work to quiesce before disconnecting. + * This method must not be called from inside {@link MqttCallback} methods. + *

+ * + * @return token used to track and wait for disconnect to complete. The token + * will be passed to any callback that has been set. + * @throws MqttException for problems encountered while disconnecting + * @see #disconnect(long, Object, IMqttActionListener) + */ + public IMqttToken disconnect( ) throws MqttException; + + /** + * Disconnects from the server. + *

An attempt is made to quiesce the client allowing outstanding + * work to complete before disconnecting. It will wait + * for a maximum of the specified quiesce time for work to complete before disconnecting. + * This method must not be called from inside {@link MqttCallback} methods. + *

+ * @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. + * @return token used to track and wait for disconnect to complete. The token + * will be passed to the callback methtods if a callback is set. + * @throws MqttException for problems encountered while disconnecting + * @see #disconnect(long, Object, IMqttActionListener) + */ + public IMqttToken disconnect(long quiesceTimeout) throws MqttException; + + /** + * Disconnects from the server. + *

An attempt is made to quiesce the client allowing outstanding + * work to complete before disconnecting. It will wait + * for a maximum of 30 seconds for work to quiesce before disconnecting. + * This method must not be called from inside {@link MqttCallback} methods. + *

+ * + * @param userContext optional object used to pass context to the callback. Use + * null if not required. + * @param callback optional listener that will be notified when the disconnect completes. Use + * null if not required. + * @return token used to track and wait for the disconnect to complete. The token + * will be passed to any callback that has been set. + * @throws MqttException for problems encountered while disconnecting + * @see #disconnect(long, Object, IMqttActionListener) + */ + public IMqttToken disconnect( Object userContext, IMqttActionListener callback) throws MqttException; + + /** + * Disconnects from the server. + *

+ * The client will wait for {@link MqttCallback} methods to + * complete. It will then wait for up to the quiesce timeout to allow for + * work which has already been initiated to complete. For instance when a QOS 2 + * message has started flowing to the server but the QOS 2 flow has not completed.It + * prevents new messages being accepted and does not send any messages that have + * been accepted but not yet started delivery across the network to the server. When + * work has completed or after the quiesce timeout, the client will disconnect from + * the server. If the cleansession flag was set to false and is set to false the + * next time a connection is made QoS 1 and 2 messages that + * were not previously delivered will be delivered.

+ *

This method must not be called from inside {@link MqttCallback} methods.

+ *

The method returns control before the disconnect completes. Completion can + * be tracked by: + *

    + *
  • Waiting on the returned token {@link IMqttToken#waitForCompletion()} or
  • + *
  • Passing in a callback {@link IMqttActionListener}
  • + *
+ *

+ * + * @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 userContext optional object used to pass context to the callback. Use + * null if not required. + * @param callback optional listener that will be notified when the disconnect completes. Use + * null if not required. + * @return token used to track and wait for the connect to complete. The token + * will be passed to any callback that has been set. + * @throws MqttException for problems encountered while disconnecting + */ + public IMqttToken disconnect(long quiesceTimeout, Object userContext, IMqttActionListener callback) throws MqttException; + + + /** + * Determines if this client is currently connected to the server. + * + * @return true if connected, false otherwise. + */ + public boolean isConnected(); + + /** + * Returns the client ID used by this client. + *

All clients connected to the + * same server or server farm must have a unique ID. + *

+ * + * @return the client ID used by this client. + */ + public String getClientId(); + + /** + * Returns the address of the server used by this client. + *

The format of the returned String is the same as that used on the constructor. + *

+ * + * @return the server's address, as a URI String. + * @see MqttAsyncClient#MqttAsyncClient(String, String) + */ + public String getServerURI(); + + /** + * Publishes a message to a topic on the server + *

A convenience method, which will + * create a new {@link MqttMessage} object with a byte array payload and the + * specified QoS, and then publish it. + *

+ * + * @param topic to deliver the message to, for example "finance/stock/ibm". + * @param payload the byte array to use as the payload + * @param qos the Quality of Service to deliver the message at. Valid values are 0, 1 or 2. + * @param retained whether or not this message should be retained by the server. + * @return token used to track and wait for the publish to complete. The token + * will be passed to any callback that has been set. + * @throws MqttPersistenceException when a problem occurs storing the message + * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2. + * @throws MqttException for other errors encountered while publishing the message. + * For instance if too many messages are being processed. + * @see #publish(String, MqttMessage, Object, IMqttActionListener) + * @see MqttMessage#setQos(int) + * @see MqttMessage#setRetained(boolean) + */ + public IMqttDeliveryToken publish(String topic, byte[] payload, int qos, + boolean retained ) throws MqttException, MqttPersistenceException; + + /** + * Publishes a message to a topic on the server + *

A convenience method, which will + * create a new {@link MqttMessage} object with a byte array payload and the + * specified QoS, and then publish it. + *

+ * + * @param topic to deliver the message to, for example "finance/stock/ibm". + * @param payload the byte array to use as the payload + * @param qos the Quality of Service to deliver the message at. Valid values are 0, 1 or 2. + * @param retained whether or not this message should be retained by the server. + * @param userContext optional object used to pass context to the callback. Use + * null if not required. + * @param callback optional listener that will be notified when message delivery + * hsa completed to the requested quality of service + * @return token used to track and wait for the publish to complete. The token + * will be passed to any callback that has been set. + * @throws MqttPersistenceException when a problem occurs storing the message + * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2. + * @throws MqttException for other errors encountered while publishing the message. + * For instance client not connected. + * @see #publish(String, MqttMessage, Object, IMqttActionListener) + * @see MqttMessage#setQos(int) + * @see MqttMessage#setRetained(boolean) + */ + public IMqttDeliveryToken publish(String topic, byte[] payload, int qos, + boolean retained, Object userContext, IMqttActionListener callback ) throws MqttException, MqttPersistenceException; + + /** + * Publishes a message to a topic on the server + * Takes an {@link MqttMessage} message and delivers it to the server at the + * requested quality of service. + * + * @param topic to deliver the message to, for example "finance/stock/ibm". + * @param message to deliver to the server + * @return token used to track and wait for the publish to complete. The token + * will be passed to any callback that has been set. + * @throws MqttPersistenceException when a problem occurs storing the message + * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2. + * @throws MqttException for other errors encountered while publishing the message. + * For instance client not connected. + * @see #publish(String, MqttMessage, Object, IMqttActionListener) + */ + public IMqttDeliveryToken publish(String topic, MqttMessage message ) throws MqttException, MqttPersistenceException; + + /** + * Publishes a message to a topic on the server. + *

+ * Once this method has returned cleanly, the message has been accepted for publication by the + * client and will be delivered on a background thread. + * In the event the connection fails or the client stops. Messages will be delivered to the + * requested quality of service once the connection is re-established to the server on condition that: + *

    + *
  • The connection is re-established with the same clientID + *
  • The original connection was made with (@link MqttConnectOptions#setCleanSession(boolean)} + * set to false + *
  • The connection is re-established with (@link MqttConnectOptions#setCleanSession(boolean)} + * set to false + * + *

    + * + *

    When building 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.
    • + *
    + *

    + *

    The method returns control before the publish completes. Completion can + * be tracked by: + *

      + *
    • Setting an {@link IMqttAsyncClient#setCallback(MqttCallback)} where the + * {@link MqttCallback#deliveryComplete(IMqttDeliveryToken)} + * method will be called.
    • + *
    • Waiting on the returned token {@link MqttToken#waitForCompletion()} or
    • + *
    • Passing in a callback {@link IMqttActionListener} to this method
    • + *
    + *

    + * + * @param topic to deliver the message to, for example "finance/stock/ibm". + * @param message to deliver to the server + * @param userContext optional object used to pass context to the callback. Use + * null if not required. + * @param callback optional listener that will be notified when message delivery + * has completed to the requested quality of service + * @return token used to track and wait for the publish to complete. The token + * will be passed to callback methtods if set. + * @throws MqttPersistenceException when a problem occurs storing the message + * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2. + * @throws MqttException for other errors encountered while publishing the message. + * For instance client not connected. + * @see MqttMessage + */ + public IMqttDeliveryToken publish(String topic, MqttMessage message, + Object userContext, IMqttActionListener callback) throws MqttException, MqttPersistenceException; + + /** + * Subscribe to a topic, which may include wildcards. + * + * @see #subscribe(String[], int[], Object, IMqttActionListener) + * + * @param topicFilter the topic to subscribe to, which can include wildcards. + * @param qos the maximum quality of service at which to subscribe. Messages + * published at a lower quality of service will be received at the published + * QOS. Messages published at a higher quality of service will be received using + * the QOS specified on the subscribe. + * @return token used to track and wait for the subscribe to complete. The token + * will be passed to callback methtods if set. + * @throws MqttException if there was an error registering the subscription. + */ + public IMqttToken subscribe(String topicFilter, int qos) throws MqttException; + + /** + * Subscribe to a topic, which may include wildcards. + * + * @see #subscribe(String[], int[], Object, IMqttActionListener) + * + * @param topicFilter the topic to subscribe to, which can include wildcards. + * @param qos the maximum quality of service at which to subscribe. Messages + * published at a lower quality of service will be received at the published + * QOS. Messages published at a higher quality of service will be received using + * the QOS specified on the subscribe. + * @param userContext optional object used to pass context to the callback. Use + * null if not required. + * @param callback optional listener that will be notified when subscribe + * has completed + * @return token used to track and wait for the subscribe to complete. The token + * will be passed to callback methtods if set. + * @throws MqttException if there was an error registering the subscription. + */ + public IMqttToken subscribe(String topicFilter, int qos, Object userContext, IMqttActionListener callback) + throws MqttException; + + /** + * Subscribe to multiple topics, each of which may include wildcards. + * + *

    Provides an optimised way to subscribe to multiple topics compared to + * subscribing to each one individually.

    + * + * @see #subscribe(String[], int[], Object, IMqttActionListener) + * + * @param topicFilters one or more topics to subscribe to, which can include wildcards + * @param qos the maximum quality of service at which to subscribe. Messages + * published at a lower quality of service will be received at the published + * QOS. Messages published at a higher quality of service will be received using + * the QOS specified on the subscribe. + * @return token used to track and wait for the subscribe to complete. The token + * will be passed to callback methtods if set. + * @throws MqttException if there was an error registering the subscription. + */ + public IMqttToken subscribe(String[] topicFilters, int[] qos) throws MqttException; + + /** + * Subscribes to multiple topics, each of which may include wildcards. + *

    Provides an optimised way to subscribe to multiple topics compared to + * subscribing to each one individually.

    + *

    The {@link #setCallback(MqttCallback)} method + * should be called before this method, otherwise any received messages + * will be discarded. + *

    + *

    + * If (@link MqttConnectOptions#setCleanSession(boolean)} was set to true + * when when connecting to the server then the subscription remains in place + * until either: + *

      + *
    • The client disconnects
    • + *
    • An unsubscribe method is called to un-subscribe the topic
    • + * + *

      + *

      + * If (@link MqttConnectOptions#setCleanSession(boolean)} was set to false + * when connecting to the server then the subscription remains in place + * until either: + *

        + *
      • An unsubscribe method is called to un-subscribe the topic
      • + *
      • The next time the client connects with CleanSession set to true
      + * + * With CleanSession set to false the MQTT server will store messages on + * behalf of the client when the client is not connected. The next time the + * client connects with the same client ID the server will + * deliver the stored messages to the client. + *

      + * + *

      The "topic filter" string used when subscribing + * may contain special characters, which allow you to subscribe to multiple topics + * at once.

      + *

      The topic level separator is used to introduce structure into the topic, and + * can therefore be specified within the topic for that purpose. The multi-level + * wildcard and single-level wildcard can be used for subscriptions, but they + * cannot be used within a topic by the publisher of a message. + *

      + *
      Topic level separator
      + *
      The forward slash (/) is used to separate each level within + * a topic tree and provide a hierarchical structure to the topic space. The + * use of the topic level separator is significant when the two wildcard characters + * are encountered in topics specified by subscribers.
      + * + *
      Multi-level wildcard
      + *

      The number sign (#) is a wildcard character that matches + * any number of levels within a topic. For example, if you subscribe to + * finance/stock/ibm/#, you receive + * messages on these topics: + *

         finance/stock/ibm
      finance/stock/ibm/closingprice
      finance/stock/ibm/currentprice
      + *

      + *

      The multi-level wildcard + * can represent zero or more levels. Therefore, finance/# can also match + * the singular finance, where # represents zero levels. The topic + * level separator is meaningless in this context, because there are no levels + * to separate.

      + * + *

      The multi-level wildcard can + * be specified only on its own or next to the topic level separator character. + * Therefore, # and finance/# are both valid, but finance# is + * not valid. The multi-level wildcard must be the last character + * used within the topic tree. For example, finance/# is valid but + * finance/#/closingprice is not valid.

      + * + *
      Single-level wildcard
      + *

      The plus sign (+) is a wildcard character that matches only one topic + * level. For example, finance/stock/+ matches + * finance/stock/ibm and finance/stock/xyz, + * but not finance/stock/ibm/closingprice. Also, because the single-level + * wildcard matches only a single level, finance/+ does not match finance.

      + * + *

      Use + * the single-level wildcard at any level in the topic tree, and in conjunction + * with the multilevel wildcard. Specify the single-level wildcard next to the + * topic level separator, except when it is specified on its own. Therefore, + * + and finance/+ are both valid, but finance+ is + * not valid. The single-level wildcard can be used at the end of the + * topic tree or within the topic tree. + * For example, finance/+ and finance/+/ibm are both valid.

      + *
      + *
      + *

      + *

      The method returns control before the subscribe completes. Completion can + * be tracked by: + *

        + *
      • Waiting on the supplied token {@link MqttToken#waitForCompletion()} or
      • + *
      • Passing in a callback {@link IMqttActionListener} to this method
      • + *
      + *

      + * + * @param topicFilters one or more topics to subscribe to, which can include wildcards + * @param qos the maximum quality of service to subscribe each topic at.Messages + * published at a lower quality of service will be received at the published + * QOS. Messages published at a higher quality of service will be received using + * the QOS specified on the subscribe. + * @param userContext optional object used to pass context to the callback. Use + * null if not required. + * @param callback optional listener that will be notified when subscribe + * has completed + * @return token used to track and wait for the subscribe to complete. The token + * will be passed to callback methtods if set. + * @throws MqttException if there was an error registering the subscription. + * @throws IllegalArgumentException if the two supplied arrays are not the same size. + */ + public IMqttToken subscribe(String[] topicFilters, int[] qos, Object userContext, IMqttActionListener callback) + throws MqttException; + + /** + * Requests the server unsubscribe the client from a topic. + * + * @see #unsubscribe(String[], Object, IMqttActionListener) + * @param topicFilter the topic to unsubscribe from. It must match a topicFilter + * specified on an earlier subscribe. + * @return token used to track and wait for the unsubscribe to complete. The token + * will be passed to callback methtods if set. + * @throws MqttException if there was an error unregistering the subscription. + */ + public IMqttToken unsubscribe(String topicFilter) throws MqttException; + + /** + * Requests the server unsubscribe the client from one or more topics. + * + * @see #unsubscribe(String[], Object, IMqttActionListener) + * + * @param topicFilters one or more topics to unsubscribe from. Each topicFilter + * must match one specified on an earlier subscribe. * + * @return token used to track and wait for the unsubscribe to complete. The token + * will be passed to callback methtods if set. + * @throws MqttException if there was an error unregistering the subscription. + */ + public IMqttToken unsubscribe(String[] topicFilters) throws MqttException; + + /** + * Requests the server unsubscribe the client from a topics. + * + * @see #unsubscribe(String[], Object, IMqttActionListener) + * + * @param topicFilter the topic to unsubscribe from. It must match a topicFilter + * specified on an earlier subscribe. + * @param userContext optional object used to pass context to the callback. Use + * null if not required. + * @param callback optional listener that will be notified when unsubscribe + * has completed + * @return token used to track and wait for the unsubscribe to complete. The token + * will be passed to callback methtods if set. + * @throws MqttException if there was an error unregistering the subscription. + */ + public IMqttToken unsubscribe(String topicFilter, Object userContext, IMqttActionListener callback) + throws MqttException; + + /** + * Requests the server unsubscribe the client from one or more topics + *

      + * Unsubcribing is the opposite of subscribing. When the server receives the + * unsubscribe request it looks to see if it can find a matching subscription for the + * client and then removes it. After this point the server will send no more + * messages to the client for this subscription. + *

      + *

      The topic(s) specified on the unsubscribe must match the topic(s) + * specified in the original subscribe request for the unsubscribe to succeed + *

      + *

      The method returns control before the unsubscribe completes. Completion can + * be tracked by: + *

        + *
      • Waiting on the returned token {@link MqttToken#waitForCompletion()} or
      • + *
      • Passing in a callback {@link IMqttActionListener} to this method
      • + *
      + *

      + * + * @param topicFilters one or more topics to unsubscribe from. Each topicFilter + * must match one specified on an earlier subscribe. + * @param userContext optional object used to pass context to the callback. Use + * null if not required. + * @param callback optional listener that will be notified when unsubscribe + * has completed + * @return token used to track and wait for the unsubscribe to complete. The token + * will be passed to callback methtods if set. + * @throws MqttException if there was an error unregistering the subscription. + */ + public IMqttToken unsubscribe(String[] topicFilters, Object userContext, IMqttActionListener callback) + throws MqttException; + + + /** + * Sets a callback listener to use for events that happen asynchronously. + *

      There are a number of events that the listener will be notified about. + * These include: + *

        + *
      • A new message has arrived and is ready to be processed
      • + *
      • The connection to the server has been lost
      • + *
      • Delivery of a message to the server has completed
      • + *
      + *

      + *

      Other events that track the progress of an individual operation such + * as connect and subscribe can be tracked using the {@link MqttToken} returned from + * each non-blocking method or using setting a {@link IMqttActionListener} on the + * non-blocking method.

      + * @see MqttCallback + * @param callback which will be invoked for certain asyncrhonous events + */ + public void setCallback(MqttCallback callback); + + /** + * Returns the delivery tokens for any outstanding publish operations. + *

      If a client has been restarted and there are messages that were in the + * process of being delivered when the client stopped this method + * returns a token for each in-flight message enabling the delivery to be tracked + * Alternately the {@link MqttCallback#deliveryComplete(IMqttDeliveryToken)} + * callback can be used to track the delivery of outstanding messages. + *

      + *

      If a client connects with cleansession true then there will be no + * delivery tokens as the cleansession option deletes all earlier state. + * For state to be remembered the client must connect with cleansession + * set to false

      + * @return zero or more delivery tokens + */ + public IMqttDeliveryToken[] getPendingDeliveryTokens(); + + /** + * Close the client + * Releases all resource associated with the client. After the client has + * been closed it cannot be reused. For instance attempts to connect will fail. + * @throws MqttException if the client is not disconnected. + */ + public void close() throws MqttException; +} \ No newline at end of file diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/IMqttClient.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/IMqttClient.java new file mode 100644 index 0000000..8f12ac3 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/IMqttClient.java @@ -0,0 +1,462 @@ +package org.eclipse.paho.client.mqttv3; + +/** + * Enables an application to communicate with an MQTT server using using blocking methods. + *

      + * This interface allows applications to utilise all features of the MQTT version 3.1 + * specification including: + *

        + *
      • connect + *
      • publish + *
      • subscribe + *
      • unsubscribe + *
      • disconnect + *
      + *

      + *

      + * There are two styles of MQTT client, this one and {@link IMqttAsyncClient}. + *

        + *
      • IMqttClient provides a set of methods that block and return control to the application + * program once the MQTT action has completed.
      • + *
      • IMqttAsyncClient provides a set of non blocking methods that return control to the + * invoking application after initial validation of parameters and state. The main processing is + * performed in the background so as not to block the application programs thread. This non + * blocking approach is handy when the application wants to carry on processing while the + * MQTT action takes place. For instance connecting to an MQTT server can take time, using + * the non blocking connect method allows an application to display a busy indicator while the + * connect action is occurring. Non blocking methods are particularly useful in event oriented + * programs and graphical programs where issuing methods that take time to complete on the the + * main or GUI thread can cause problems.
      • + *
      + *

      + *

      + * The non-blocking client can also be used in a blocking form by turning a non-blocking + * method into a blocking invocation using the following pettern: + *

      + *     IMqttToken token;
      + *     token = asyncClient.method(parms).waitForCompletion();
      + *     
      + * Using the non-blocking client allows an application to use a mixture of blocking and + * non-blocking styles. Using the blocking client only allows an application to use one + * style. The blocking client provides compatibility with earlier versions + * of the MQTT client.

      + */ +public interface IMqttClient { //extends IMqttAsyncClient { + /** + * Connects to an MQTT server using the default options. + *

      The default options are specified in {@link MqttConnectOptions} class. + *

      + * + * @throws MqttSecurityException when the server rejects the connect for security + * reasons + * @throws MqttException for non security related problems + * @see #connect(MqttConnectOptions) + */ + public void connect() throws MqttSecurityException, MqttException; + + /** + * Connects to an MQTT server using the specified options. + *

      The server to connect to is specified on the constructor. + * It is recommended to call {@link #setCallback(MqttCallback)} prior to + * connecting in order that messages destined for the client can be accepted + * as soon as the client is connected. + *

      + *

      This is a blocking method that returns once connect completes

      + * + * @param options a set of connection parameters that override the defaults. + * @throws MqttSecurityException when the server rejects the connect for security + * reasons + * @throws MqttException for non security related problems including communication errors + */ + public void connect(MqttConnectOptions options) throws MqttSecurityException, MqttException; + + /** + * Disconnects from the server. + *

      An attempt is made to quiesce the client allowing outstanding + * work to complete before disconnecting. It will wait + * for a maximum of 30 seconds for work to quiesce before disconnecting. + * This method must not be called from inside {@link MqttCallback} methods. + *

      + * + * @see #disconnect(long) + */ + public void disconnect() throws MqttException; + + /** + * Disconnects from the server. + *

      + * The client will wait for all {@link MqttCallback} methods to + * complete. It will then wait for up to the quiesce timeout to allow for + * work which has already been initiated to complete - for example, it will + * wait for the QoS 2 flows from earlier publications to complete. When work has + * completed or after the quiesce timeout, the client will disconnect from + * the server. If the cleansession flag was set to false and is set to false the + * next time a connection is made QoS 1 and 2 messages that + * were not previously delivered will be delivered.

      + * + *

      This is a blocking method that returns once disconnect completes

      + * + * @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. + * @throws MqttException if a problem is encountered while disconnecting + */ + public void disconnect(long quiesceTimeout) throws MqttException; + + /** + * Subscribe to a topic, which may include wildcards using a QOS of 1. + * + * @see #subscribe(String[], int[]) + * + * @param topicFilter the topic to subscribe to, which can include wildcards. + * @throws MqttException if there was an error registering the subscription. + */ + public void subscribe(String topicFilter) throws MqttException, MqttSecurityException; + + /** + * Subscribes to a one or more topics, which may include wildcards using a QOS of 1. + * + * @see #subscribe(String[], int[]) + * + * @param topicFilters the topic to subscribe to, which can include wildcards. + * @throws MqttException if there was an error registering the subscription. + */ + public void subscribe(String[] topicFilters) throws MqttException; + + /** + * Subscribe to a topic, which may include wildcards. + * + * @see #subscribe(String[], int[]) + * + * @param topicFilter the topic to subscribe to, which can include wildcards. + * @param qos the maximum quality of service at which to subscribe. Messages + * published at a lower quality of service will be received at the published + * QOS. Messages published at a higher quality of service will be received using + * the QOS specified on the subscribe. + * @throws MqttException if there was an error registering the subscription. + */ + public void subscribe(String topicFilter, int qos) throws MqttException; + + /** + * Subscribes to multiple topics, each of which may include wildcards. + *

      The {@link #setCallback(MqttCallback)} method + * should be called before this method, otherwise any received messages + * will be discarded. + *

      + *

      + * If (@link MqttConnectOptions#setCleanSession(boolean)} was set to true + * when when connecting to the server then the subscription remains in place + * until either: + *

        + *
      • The client disconnects
      • + *
      • An unsubscribe method is called to un-subscribe the topic
      • + * + *

        + *

        + * If (@link MqttConnectOptions#setCleanSession(boolean)} was set to false + * when when connecting to the server then the subscription remains in place + * until either: + *

          + *
        • An unsubscribe method is called to un-subscribe the topic
        • + *
        • The client connects with CleanSession set to true
        + * + * With CleanSession set to false the MQTT server will store messages on + * behalf of the client when the client is not connected. The next time the + * client connects with the same client ID the server will + * deliver the stored messages to the client. + *

        + * + *

        The "topic filter" string used when subscribing + * may contain special characters, which allow you to subscribe to multiple topics + * at once.

        + *

        The topic level separator is used to introduce structure into the topic, and + * can therefore be specified within the topic for that purpose. The multi-level + * wildcard and single-level wildcard can be used for subscriptions, but they + * cannot be used within a topic by the publisher of a message. + *

        + *
        Topic level separator
        + *
        The forward slash (/) is used to separate each level within + * a topic tree and provide a hierarchical structure to the topic space. The + * use of the topic level separator is significant when the two wildcard characters + * are encountered in topics specified by subscribers.
        + * + *
        Multi-level wildcard
        + *

        The number sign (#) is a wildcard character that matches + * any number of levels within a topic. For example, if you subscribe to + * finance/stock/ibm/#, you receive + * messages on these topics: + *

           finance/stock/ibm
        finance/stock/ibm/closingprice
        finance/stock/ibm/currentprice
        + *

        + *

        The multi-level wildcard + * can represent zero or more levels. Therefore, finance/# can also match + * the singular finance, where # represents zero levels. The topic + * level separator is meaningless in this context, because there are no levels + * to separate.

        + * + *

        The multi-level wildcard can + * be specified only on its own or next to the topic level separator character. + * Therefore, # and finance/# are both valid, but finance# is + * not valid. The multi-level wildcard must be the last character + * used within the topic tree. For example, finance/# is valid but + * finance/#/closingprice is not valid.

        + * + *
        Single-level wildcard
        + *

        The plus sign (+) is a wildcard character that matches only one topic + * level. For example, finance/stock/+ matches + * finance/stock/ibm and finance/stock/xyz, + * but not finance/stock/ibm/closingprice. Also, because the single-level + * wildcard matches only a single level, finance/+ does not match finance.

        + * + *

        Use + * the single-level wildcard at any level in the topic tree, and in conjunction + * with the multilevel wildcard. Specify the single-level wildcard next to the + * topic level separator, except when it is specified on its own. Therefore, + * + and finance/+ are both valid, but finance+ is + * not valid. The single-level wildcard can be used at the end of the + * topic tree or within the topic tree. + * For example, finance/+ and finance/+/ibm are both valid.

        + *
        + *
        + *

        + * + *

        This is a blocking method that returns once subscribe completes

        + * + * @param topicFilters one or more topics to subscribe to, which can include wildcards. + * @param qos the maximum quality of service to subscribe each topic at.Messages + * published at a lower quality of service will be received at the published + * QOS. Messages published at a higher quality of service will be received using + * the QOS specified on the subscribe. + * @throws MqttException if there was an error registering the subscription. + * @throws IllegalArgumentException if the two supplied arrays are not the same size. + */ + public void subscribe(String[] topicFilters, int[] qos) throws MqttException; + + /** + * Requests the server unsubscribe the client from a topic. + * + * @see #unsubscribe(String[]) + * @param topicFilter the topic to unsubscribe from. It must match a topicFilter + * specified on the subscribe. + * @throws MqttException if there was an error unregistering the subscription. + */ + public void unsubscribe(String topicFilter) throws MqttException; + + /** + * Requests the server unsubscribe the client from one or more topics + *

        + * Unsubcribing is the opposite of subscribing. When the server receives the + * unsubscribe request it looks to see if it can find a subscription for the + * client and then removes it. After this point the server will send no more + * messages to the client for this subscription. + *

        + *

        The topic(s) specified on the unsubscribe must match the topic(s) + * specified in the original subscribe request for the subscribe to succeed + *

        + * + *

        This is a blocking method that returns once unsubscribe completes

        + * + * @param topicFilters one or more topics to unsubscribe from. Each topicFilter + * must match one specified on a subscribe + * @throws MqttException if there was an error unregistering the subscription. + */ + public void unsubscribe(String[] topicFilters) throws MqttException; + + + /** + * Publishes a message to a topic on the server and return once it is delivered + *

        This is a convenience method, which will + * create a new {@link MqttMessage} object with a byte array payload and the + * specified QoS, and then publish it. All other values in the + * message will be set to the defaults. + *

        + * + * @param topic to deliver the message to, for example "finance/stock/ibm". + * @param payload the byte array to use as the payload + * @param qos the Quality of Service to deliver the message at. Valid values are 0, 1 or 2. + * @param retained whether or not this message should be retained by the server. + * @throws MqttPersistenceException when a problem with storing the message + * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2. + * @throws MqttException for other errors encountered while publishing the message. + * For instance client not connected. + * @see #publish(String, MqttMessage) + * @see MqttMessage#setQos(int) + * @see MqttMessage#setRetained(boolean) + */ + public void publish(String topic, byte[] payload, int qos, boolean retained) throws MqttException, MqttPersistenceException; + + /** + * Publishes a message to a topic on the server. + *

        + * Delivers a message to the server at the requested quality of service and returns control + * once the message has been delivered. In the event the connection fails or the client + * stops, any messages that are in the process of being delivered will be delivered once + * a connection is re-established to the server on condition that: + *

          + *
        • The connection is re-established with the same clientID + *
        • The original connection was made with (@link MqttConnectOptions#setCleanSession(boolean)} + * set to false + *
        • The connection is re-established with (@link MqttConnectOptions#setCleanSession(boolean)} + * set to false + *
        + *

        + *

        In the event that the connection breaks or the client stops it is still possible to determine + * when the delivery of the message completes. Prior to re-establishing the connection to the server: + *

          + *
        • Register a {@link #setCallback(MqttCallback)} callback on the client and the delivery complete + * callback will be notified once a delivery of a message completes + *
        • or call {@link #getPendingDeliveryTokens()} which will return a token for each message that + * is in-flight. The token can be used to wait for delivery to complete. + *
        + *

        + * + *

        When building 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.
        • + *
        + *

        + * + *

        This is a blocking method that returns once publish completes

        * + * + * @param topic to deliver the message to, for example "finance/stock/ibm". + * @param message to delivery to the server + * @throws MqttPersistenceException when a problem with storing the message + * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2. + * @throws MqttException for other errors encountered while publishing the message. + * For instance client not connected. + */ + public void publish(String topic, MqttMessage message) throws MqttException, MqttPersistenceException; + + /** + * Sets the callback listener to use for events that happen asynchronously. + *

        There are a number of events that listener will be notified about. These include + *

          + *
        • A new message has arrived and is ready to be processed
        • + *
        • The connection to the server has been lost
        • + *
        • Delivery of a message to the server has completed.
        • + *
        + *

        + *

        Other events that track the progress of an individual operation such + * as connect and subscribe can be tracked using the {@link MqttToken} passed to the + * operation

        + * @see MqttCallback + * @param callback the class to callback when for events related to the client + */ + public void setCallback(MqttCallback callback); + + /** + * Get a topic object which can be used to publish messages. + *

        An alternative method that should be used in preference to this one when publishing a message is: + *

          + *
        • {@link MqttClient#publish(String, MqttMessage)} to publish a message in a blocking manner + *
        • or use publish methods on the non blocking client like {@link IMqttAsyncClient#publish(String, MqttMessage, Object, IMqttActionListener)} + *
        + *

        + *

        When building 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. + */ + public MqttTopic getTopic(String topic); + + /** + * Determines if this client is currently connected to the server. + * + * @return true if connected, false otherwise. + */ + public boolean isConnected(); + + /** + * Returns the client ID used by this client. + *

        All clients connected to the + * same server or server farm must have a unique ID. + *

        + * + * @return the client ID used by this client. + */ + public String getClientId(); + + /** + * Returns the address of the server used by this client, as a URI. + *

        The format is the same as specified on the constructor. + *

        + * + * @return the server's address, as a URI String. + * @see MqttAsyncClient#MqttAsyncClient(String, String) + */ + public String getServerURI(); + + /** + * Returns the delivery tokens for any outstanding publish operations. + *

        If a client has been restarted and there are messages that were in the + * process of being delivered when the client stopped this method will + * return a token for each message enabling the delivery to be tracked + * Alternately the {@link MqttCallback#deliveryComplete(IMqttDeliveryToken)} + * callback can be used to track the delivery of outstanding messages. + *

        + *

        If a client connects with cleansession true then there will be no + * delivery tokens as the cleansession option deletes all earlier state. + * For state to be remembered the client must connect with cleansession + * set to false

        + * @return zero or more delivery tokens + */ + public IMqttDeliveryToken[] getPendingDeliveryTokens(); + + /** + * Close the client + * Releases all resource associated with the client. After the client has + * been closed it cannot be reused. For instance attempts to connect will fail. + * @throws MqttException if the client is not disconnected. + */ + public void close() throws MqttException; +} \ No newline at end of file diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/IMqttDeliveryToken.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/IMqttDeliveryToken.java new file mode 100644 index 0000000..48a42e4 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/IMqttDeliveryToken.java @@ -0,0 +1,41 @@ +package org.eclipse.paho.client.mqttv3; +/** + * Provides a mechanism for tracking the delivery of a message + * + *

        A subclass of IMqttToken that allows the delivery of a message to be tracked. + * Unlike instances of IMqttToken delivery tokens can be used across connection + * and client restarts. This enables the delivery of a messages to be tracked + * after failures. There are two approaches + *

          + *
        • A list of delivery tokens for in-flight messages can be obtained using + * {@link IMqttAsyncClient#getPendingDeliveryTokens()}. The waitForCompletion + * method can then be used to block until the delivery is complete. + *
        • A {@link MqttCallback} can be set on the client. Once a message has been + * delivered the {@link MqttCallback#deliveryComplete(IMqttDeliveryToken)} method will + * be called withe delivery token being passed as a parameter. + *
        + *

        + * An action is in progress until either: + *

          + *
        • isComplete() returns true or + *
        • getException() is not null. If a client shuts down before delivery is complete. + * an exception is returned. As long as the Java Runtime is not stopped a delivery token + * is valid across a connection disconnect and reconnect. In the event the client + * is shut down the getPendingDeliveryTokens method can be used once the client is + * restarted to obtain a list of delivery tokens for inflight messages. + *
        + *

        + * + */ + +public interface IMqttDeliveryToken extends IMqttToken { + /** + * Returns the message associated with this token. + *

        Until the message has been delivered, the message being delivered will + * be returned. Once the message has been delivered null will be + * returned. + * @return the message associated with this token or null if already delivered. + * @throws MqttException if there was a problem completing retrieving the message + */ + public MqttMessage getMessage() throws MqttException; +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/IMqttToken.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/IMqttToken.java new file mode 100644 index 0000000..8aaabc6 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/IMqttToken.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3; + + +/** + * Provides a mechanism for tracking the completion of an asynchronous task. + * + *

        When using the asynchronous/non-blocking MQTT programming interface all + * methods /operations that take any time and in particular those that involve + * any network operation return control to the caller immediately. The operation + * then proceeds to run in the background so as not to block the invoking thread. + * An IMqttToken is used to track the state of the operation. An application can use the + * token to wait for an operation to complete. A token is passed to callbacks + * once the operation completes and provides context linking it to the original + * request. A token is associated with a single operation.

        + *

        + * An action is in progress until either: + *

          + *
        • isComplete() returns true or + *
        • getException() is not null. + *
        + *

        + * + */ +public interface IMqttToken { + + /** + * Blocks the current thread until the action this token is associated with has + * completed. + * + * @throws MqttException if there was a problem with the action associated with the token. + * @see #waitForCompletion(long) + */ + public void waitForCompletion() throws MqttException; + + /** + * Blocks the current thread until the action this token is associated with has + * completed. + *

        The timeout specifies the maximum time it will block for. If the action + * completes before the timeout then control returns immediately, if not + * it will block until the timeout expires.

        + *

        If the action being tracked fails or the timeout expires an exception will + * be thrown. In the event of a timeout the action may complete after timeout. + *

        + * + * @param timeout the maximum amount of time to wait for, in milliseconds. + * @throws MqttException if there was a problem with the action associated with the token. + */ + public void waitForCompletion(long timeout) throws MqttException; + + /** + * Returns whether or not the action has finished. + *

        True will be returned both in the case where the action finished successfully + * and in the case where it failed. If the action failed {@link #getException()} will + * be non null. + *

        + */ + public boolean isComplete(); + + /** + * Returns an exception providing more detail if an operation failed + *

        While an action in in progress and when an action completes successfully + * null will be returned. Certain errors like timeout or shutting down will not + * set the exception as the action has not failed or completed at that time + *

        + * @return exception may return an exception if the operation failed. Null will be + * returned while action is in progress and if action completes successfully. + */ + public MqttException getException(); + + /** + * Register a listener to be notified when an action completes. + *

        Once a listener is registered it will be invoked when the action the token + * is associated with either succeeds or fails. + *

        + * @param listener to be invoked once the action completes + */ + public void setActionCallback(IMqttActionListener listener); + + /** + * Return the async listener for this token. + * @return listener that is set on the token or null if a listener is not registered. + */ + public IMqttActionListener getActionCallback(); + + /** + * Returns the MQTT client that is responsible for processing the asynchronous + * action + */ + public IMqttAsyncClient getClient(); + + /** + * Returns the topic string(s) for the action being tracked by this + * token. If the action has not been initiated or the action has not + * topic associated with it such as connect then null will be returned. + * + * @return the topic string(s) for the subscribe being tracked by this token or null + */ + public String[] getTopics(); + + /** + * Store some context associated with an action. + *

        Allows the caller of an action to store some context that can be + * accessed from within the ActionListener associated with the action. This + * can be useful when the same ActionListener is associated with multiple + * actions

        + * @param userContext to associate with an action + */ + public void setUserContext(Object userContext); + + /** + * Retrieve the context associated with an action. + *

        Allows the ActionListener associated with an action to retrieve any context + * that was associated with the action when the action was invoked. If not + * context was provided null is returned.

        + + * @return Object context associated with an action or null if there is none. + */ + public Object getUserContext(); + + /** + * Returns the message ID of the message that is associated with the token. + * A message id of zero will be returned for tokens associated with + * connect, disconnect and ping operations as there can only ever + * be one of these outstanding at a time. For other operations + * the MQTT message id flowed over the network. + */ + public int getMessageId(); + +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttAsyncClient.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttAsyncClient.java new file mode 100644 index 0000000..2a63346 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttAsyncClient.java @@ -0,0 +1,747 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3; + +import java.util.Hashtable; +import java.util.Properties; + +import javax.net.SocketFactory; +import javax.net.ssl.SSLSocketFactory; + +import org.eclipse.paho.client.mqttv3.internal.ClientComms; +import org.eclipse.paho.client.mqttv3.internal.ExceptionHelper; +import org.eclipse.paho.client.mqttv3.internal.LocalNetworkModule; +import org.eclipse.paho.client.mqttv3.internal.NetworkModule; +import org.eclipse.paho.client.mqttv3.internal.SSLNetworkModule; +import org.eclipse.paho.client.mqttv3.internal.TCPNetworkModule; +import org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory; +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; + +/** + * 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 working while the + * MQTT action completes on a background thread. + * This implementation is compatible with all Java SE runtimes from 1.4.2 and up. + *

        + *

        An application can connect to an MQTT server using: + *

          + *
        • A plain TCP socket + *
        • An 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 it cleared when a client disconnects. If + * connecting with cleansession set to false, to provide reliable message delivery + * then a persistent message store should be used such as the default one. + *

        + *

        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 { // DestinationProvider { + + private static final int URI_TYPE_TCP = 0; + private static final int URI_TYPE_SSL = 1; + private static final int URI_TYPE_LOCAL = 2; + + private String clientId; + private String serverURI; + private int serverURIType; + protected ClientComms comms; + private Hashtable topics; + private MqttClientPersistence persistence; + + final static String className = MqttAsyncClient.class.getName(); + public Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className); + + /** + * Create an MqttAsyncClient that can be used to communicate with an MQTT server. + *

        + * The address of the server should be a URI, using a scheme of either + * "tcp://" for a TCP connection or "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 identified to connect to an MQTT server, it + * must be unique across all clients connecting to the same + * server. 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 are used and 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 default instance of {@link MqttDefaultFilePersistence} is used by + * the client. To specify a different persistence implementation, 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 + * @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 23 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 can be used to communicate with an MQTT server. + *

        + * The address of the server should be a URI, using a scheme of either + * "tcp://" for a TCP connection or "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 identified to connect to an MQTT server, it + * must be unique across all clients connecting to the same + * server. 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 are used and 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.

        + *

        + * The persistence mechanism is used to enable reliable messaging. + * For qualities of server (QoS) 1 or 2 to work, messages must be persisted + * to disk by both the client and the server. If this is not done, then + * a failure in the client or server can result in lost messages. A pluggable + * persistence mechanism is supported via the {@link MqttClientPersistence} + * interface. A 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 + * @param clientId a client identifier that is unique on the server being connected to + * @param persistence the persistence mechanism to use. + * @throws IllegalArgumentException if the URI does not start with + * "tcp://", "ssl://" or "local://". + * @throws IllegalArgumentException if the clientId is null or is greater than 23 characters in length + * @throws MqttException if any other problem was encountered + */ + public MqttAsyncClient(String serverURI, String clientId, MqttClientPersistence persistence) throws MqttException { + + log.setResourceName(clientId); + + if (clientId == null || clientId.length() == 0 || clientId.length() > 23) { + throw new IllegalArgumentException(); + } + final String methodName = "MqttAsyncClient"; + + this.serverURI = serverURI; + this.serverURIType = validateURI(serverURI); + this.clientId = clientId; + + this.persistence = persistence; + if (this.persistence == null) { + this.persistence = new MemoryPersistence(); + } + + // @TRACE 101= ClientID={0} ServerURI={1} PersistenceType={2} + log.fine(className,methodName,"101",new Object[]{clientId,serverURI,persistence}); + + this.persistence.open(clientId, serverURI); + this.comms = new ClientComms(this, this.persistence); + this.persistence.close(); + this.topics = new Hashtable(); + + } + + private int validateURI(String srvURI) { + if (srvURI.startsWith("tcp://")) { + return URI_TYPE_TCP; + } + else if (srvURI.startsWith("ssl://")) { + return URI_TYPE_SSL; + } + else if (srvURI.startsWith("local://")) { + return URI_TYPE_LOCAL; + } + else { + throw new IllegalArgumentException(); + } + } + + /** + * Factory method to create the correct network module, based on the + * supplied address URI. + * + * @param address the URI for the server. + * @return a network module appropriate to the specified address. + */ + protected NetworkModule createNetworkModule(String address, MqttConnectOptions options) throws MqttException, MqttSecurityException { + final String methodName = "createNetworkModule"; + // @TRACE 115=URI={0} + log.fine(className,methodName, "115", new Object[] {address}); + + NetworkModule netModule; + String shortAddress; + String host; + int port; + SocketFactory factory = options.getSocketFactory(); + switch (serverURIType) { + case URI_TYPE_TCP: + shortAddress = address.substring(6); + host = getHostName(shortAddress); + port = getPort(shortAddress, 1883); + if (factory == null) { + factory = SocketFactory.getDefault(); + options.setSocketFactory(factory); + } + else if (factory instanceof SSLSocketFactory) { + throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_SOCKET_FACTORY_MISMATCH); + } + netModule = new TCPNetworkModule(factory, host, port, clientId); + ((TCPNetworkModule)netModule).setConnectTimeout(options.getConnectionTimeout()); + break; + case URI_TYPE_SSL: + shortAddress = address.substring(6); + host = getHostName(shortAddress); + port = getPort(shortAddress, 8883); + SSLSocketFactoryFactory factoryFactory = null; + if (factory == null) { +// try { + factoryFactory = new SSLSocketFactoryFactory(); + Properties sslClientProps = options.getSSLProperties(); + if (null != sslClientProps) + factoryFactory.initialize(sslClientProps, null); + factory = factoryFactory.createSocketFactory(null); +// } +// catch (MqttDirectException ex) { +// throw ExceptionHelper.createMqttException(ex.getCause()); +// } + } + else if ((factory instanceof SSLSocketFactory) == false) { + throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_SOCKET_FACTORY_MISMATCH); + } + + // Create the network module... + netModule = new SSLNetworkModule((SSLSocketFactory) factory, host, port, clientId); + ((SSLNetworkModule)netModule).setSSLhandshakeTimeout(options.getConnectionTimeout()); + // Ciphers suites need to be set, if they are available + if (factoryFactory != null) { + String[] enabledCiphers = factoryFactory.getEnabledCipherSuites(null); + if (enabledCiphers != null) { + ((SSLNetworkModule) netModule).setEnabledCiphers(enabledCiphers); + } + } + break; + case URI_TYPE_LOCAL: + netModule = new LocalNetworkModule(address.substring(8)); + break; + default: + // This shouldn't happen, as long as validateURI() has been called. + netModule = null; + } + return netModule; + } + + private int getPort(String uri, int defaultPort) { + int port; + int portIndex = uri.lastIndexOf(':'); + if (portIndex == -1) { + port = defaultPort; + } + else { + port = Integer.valueOf(uri.substring(portIndex + 1)).intValue(); + } + return port; + } + + private String getHostName(String uri) { + int schemeIndex = uri.lastIndexOf('/'); + int portIndex = uri.lastIndexOf(':'); + if (portIndex == -1) { + portIndex = uri.length(); + } + return uri.substring(schemeIndex + 1, 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); + } + + // @TRACE 103=cleanSession={0} connectionTimeout={1} TimekeepAlive={2} userName={3} password={4} will={5} userContext={6} callback={7} + log.fine(className,methodName, "103", + new Object[]{ + new Boolean(options.isCleanSession()), + new Integer(options.getConnectionTimeout()), + new Integer(options.getKeepAliveInterval()), + options.getUserName(), + ((null == options.getPassword())?"[null]":"[notnull]"), + ((null == options.getWillMessage())?"[null]":"[notnull]"), + userContext, + callback }); + comms.setNetworkModule(createNetworkModule(serverURI, options)); + + this.persistence.open(clientId, serverURI); + + if (options.isCleanSession()) { + persistence.clear(); + } + + MqttToken token = new MqttToken(getClientId()); + token.setActionCallback(callback); + token.setUserContext(userContext); + + comms.connect(options, token); + + return token; + } + + /* (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(30000, 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(className,methodName, "104",new Object[]{ new Long(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(className,methodName,"105",null,ex); + throw ex; + } + //@TRACE 108=< + log.fine(className,methodName,"108"); + + return token; + } + + /* (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; + } + + /** + * 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, MqttDeliveryToken)} to publish a message in a non-blocking manner or + *
        • {@link MqttClient#publishBlock(String, MqttMessage, MqttDeliveryToken)} 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) { + validateTopic(topic); + + MqttTopic result = (MqttTopic)topics.get(topic); + if (result == null) { + result = new MqttTopic(topic, comms); + topics.put(topic,result); + } + return result; + } + + /* (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 { + final String methodName = "subscribe"; + + if (topicFilters.length != qos.length) { + throw new IllegalArgumentException(); + } + String subs = ""; + for (int i=0;i0) { + subs+=", "; + } + subs+=topicFilters[i]+":"+qos[i]; + } + //@TRACE 106=Subscribe topic={0} userContext={1} callback={2} + log.fine(className,methodName,"106",new Object[]{subs, 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(className,methodName,"109"); + + 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"; + String subs = ""; + for (int i=0;i0) { + subs+=", "; + } + subs+=topicFilters[i]; + } + //@TRACE 107=Unsubscribe topic={0} userContext={1} callback={2} + log.fine(className, methodName,"107",new Object[]{subs, userContext, callback}); + + 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(className,methodName,"110"); + + return token; + } + + /* (non-Javadoc) + * @see IMqttAsyncClient#setCallback(MqttCallback) + */ + public void setCallback(MqttCallback callback) { + comms.setCallback(callback); + } + + /** + * Returns a randomly generated client identifier based on the current user's login + * name 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() { + return (System.getProperty("user.name") + "." + System.currentTimeMillis()); + } + + /* (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(className,methodName,"111", new Object[] {topic, userContext, callback}); + + validateTopic(topic); + + 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(className,methodName,"112"); + + return token; + } + + /* (non-Javadoc) + * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#close() + */ + public void close() throws MqttException { + final String methodName = "close"; + //@TRACE 113=< + log.fine(className,methodName,"113"); + comms.close(); + //@TRACE 114=> + log.fine(className,methodName,"114"); + + } + + /** + * Return a debug object that can be used to help solve problems. + */ + public Debug getDebug() { + return new Debug(clientId,comms); + } + + /** + * Checks a topic is valid when publishing a message + *

        Checks the topic does not contain a wild card character.

        + * @param topic to validate + * @throws IllegalArgumentException if the topic is not valid + */ + static public void validateTopic(String topic) { + if ((topic.indexOf('#') == -1) && (topic.indexOf('+') == -1)) { + return; + } + // The topic string does not comply with topic string rules. + throw new IllegalArgumentException(); + } +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttCallback.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttCallback.java new file mode 100644 index 0000000..5972803 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttCallback.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3; + + +/** + * Enables an application to be notified when asynchronous + * events related to the client occur. + * Classes implementing this interface + * can be registered on both types of client: {@link IMqttClient#setCallback(MqttCallback)} + * and {@link IMqttAsyncClient#setCallback(MqttCallback)} + */ +public interface MqttCallback { + /** + * This method is called when the connection to the server is lost. + * + * @param cause the reason behind the loss of connection. + */ + public void connectionLost(Throwable cause); + + /** + * This method is called when a message arrives from the server. + * + *

        + * This method is invoked synchronously by the MQTT client. An + * acknowledgement is not sent back to the server until this + * method returns cleanly.

        + *

        + * If an implementation of this method throws an Exception, then the + * client will be shut down. When the client is next re-connected, any QoS + * 1 or 2 messages will be redelivered by the server.

        + *

        + * Any additional messages which arrive while an + * implementation of this method is running, will build up in memory, and + * will then back up on the network.

        + *

        + * If an application needs to persist data, then it + * should ensure the data is persisted prior to returning from this method, as + * after returning from this method, the message is considered to have been + * delivered, and will not be reproducable.

        + *

        + * It is possible to send a new message within an implementation of this callback + * (for example, a response to this message), but the implementation must not + * disconnect the client, as it will be impossible to send an acknowledgement for + * the message being processed, and a deadlock will occur.

        + * + * @param topic name of the topic on the message was published to + * @param message the actual message. + * @throws Exception if a terminal error has occurred, and the client should be + * shut down. + */ + public void messageArrived(String topic, MqttMessage message) throws Exception; + + /** + * Called when delivery for a message has been completed, and all + * acknowledgements have been received. For QOS 0 messages it is + * called once the message has been handed to the network for + * delivery. For QOS 1 it is called when PUBACK is received and + * for QOS 2 when PUBCOMP is received. The token will be the same + * token as that returned when the message was published. + * + * @param token the delivery token associated with the message. + */ + public void deliveryComplete(IMqttDeliveryToken token); + +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttClient.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttClient.java new file mode 100644 index 0000000..f38a50e --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttClient.java @@ -0,0 +1,376 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3; + +import java.util.Properties; +import javax.net.SocketFactory; + +import org.eclipse.paho.client.mqttv3.logging.Logger; +import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; +import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence; +import org.eclipse.paho.client.mqttv3.util.Debug; + +/** + * Lightweight client for talking to an MQTT server using methods that block + * until an operation completes. + * + *

        This class implements the blocking {@link IMqttClient} client interface where all + * actions block until they have completed (or timed out). + * This implementation is compatible with all Java SE runtimes from 1.4.2 and up. + *

        + *

        An application can connect to an MQTT server using: + *

          + *
        • A plain TCP socket + *
        • An 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 it cleared when a client disconnects. If + * connecting with cleansession set to false, to provide reliable message delivery + * then a persistent message store should be used such as the default one.

        + *

        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 IMqttClient + */ +public class MqttClient implements IMqttClient { //), DestinationProvider { + + protected MqttAsyncClient aClient = null; // Delegate implementation to MqttAshyncClient + protected long timeToWait = -1; // How long each method should wait for action to complete + + final static String className = MqttClient.class.getName(); + public Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className); + + + /** + * Create an MqttClient that can be used to communicate with an MQTT server. + *

        + * The address of the server should be a URI, using a scheme of either + * "tcp://" for a TCP connection or "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 identified to connect to an MQTT server, it + * must be unique across all clients connecting to the same + * server. 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 are used and 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 default instance of {@link MqttDefaultFilePersistence} is used by + * the client. To specify a different persistence implementation, or to turn + * off persistence, use the {@link #MqttClient(String, String, MqttClientPersistence)} constructor. + * + * @param serverURI the address of the server to connect to, specified as a URI + * @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 23 characters in length + * @throws MqttException if any other problem was encountered + */ + public MqttClient(String serverURI, String clientId) throws MqttException { + this(serverURI,clientId, new MqttDefaultFilePersistence()); + } + + /** + * Create an MqttAsyncClient that can be used to communicate with an MQTT server. + *

        + * The address of the server should be a URI, using a scheme of either + * "tcp://" for a TCP connection or "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 identified to connect to an MQTT server, it + * must be unique across all clients connecting to the same + * server. 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 are used and 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.

        + *

        + * The persistence mechanism is used to enable reliable messaging. + * For qualities of server (QoS) 1 or 2 to work, messages must be persisted + * to disk by both the client and the server. If this is not done, then + * a failure in the client or server can result in lost messages. A pluggable + * persistence mechanism is supported via the {@link MqttClientPersistence} + * interface. A 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 + * @param clientId a client identifier that is unique on the server being connected to + * @param persistence the persistence mechanism to use. + * @throws IllegalArgumentException if the URI does not start with + * "tcp://", "ssl://" or "local://". + * @throws IllegalArgumentException if the clientId is null or is greater than 23 characters in length + * @throws MqttException if any other problem was encountered + */ + public MqttClient(String serverURI, String clientId, MqttClientPersistence persistence) throws MqttException { + aClient = new MqttAsyncClient(serverURI, clientId, persistence); + } + + /* + * @see IMqttClient#connect() + */ + public void connect() throws MqttSecurityException, MqttException { + this.connect(new MqttConnectOptions()); + } + + /* + * @see IMqttClient#connect(MqttConnectOptions) + */ + public void connect(MqttConnectOptions options) throws MqttSecurityException, MqttException { + aClient.connect(options, null, null).waitForCompletion(getTimeToWait()); + } + + /* + * @see IMqttClient#disconnect() + */ + public void disconnect() throws MqttException { + this.disconnect(30000); + } + + /* + * @see IMqttClient#disconnect(long) + */ + public void disconnect(long quiesceTimeout) throws MqttException { + aClient.disconnect(quiesceTimeout, null, null).waitForCompletion(); + } + + /* + * @see IMqttClient#subscribe(String) + */ + public void subscribe(String topicFilter) throws MqttException { + this.subscribe(new String[] {topicFilter}, new int[] {1}); + } + + /* + * @see IMqttClient#subscribe(String[]) + */ + public void subscribe(String[] topicFilters) throws MqttException { + int[] qos = new int[topicFilters.length]; + for (int i=0; iSet the maximum time to wait for an action to complete before + * returning control to the invoking application. Control is returned + * when: + *
          + *
        • the action completes + *
        • or when the timeout if exceeded + *
        • or when the client is disconnect/shutdown + *
            + * The default value is -1 which means the action will not timeout. + * In the event of a timeout the action carries on running in the + * background until it completes. The timeout is used on methods that + * block while the action is in progress. + *

            + * @param timeToWaitInMillis before the action times out. A value or 0 or -1 will wait until + * the action finishes and not timeout. + */ + public void setTimeToWait(long timeToWaitInMillis) throws IllegalArgumentException{ + if (timeToWait < -1) { + throw new IllegalArgumentException(); + } + this.timeToWait = timeToWaitInMillis; + } + + /** + * Return the maximum time to wait for an action to complete. + * @see MqttClient#setTimeToWait(long) + */ + public long getTimeToWait() { + return this.timeToWait; + } + + /* (non-Javadoc) + * @see org.eclipse.paho.client.mqttv3.IMqttClient#close() + */ + public void close() throws MqttException { + aClient.close(); + } + + /* (non-Javadoc) + * @see org.eclipse.paho.client.mqttv3.IMqttClient#getClientId() + */ + public String getClientId() { + return aClient.getClientId(); + } + + /* (non-Javadoc) + * @see org.eclipse.paho.client.mqttv3.IMqttClient#getPendingDeliveryTokens() + */ + public IMqttDeliveryToken[] getPendingDeliveryTokens() { + return aClient.getPendingDeliveryTokens(); + } + + /* (non-Javadoc) + * @see org.eclipse.paho.client.mqttv3.IMqttClient#getServerURI() + */ + public String getServerURI() { + return aClient.getServerURI(); + } + + /* (non-Javadoc) + * @see org.eclipse.paho.client.mqttv3.IMqttClient#getTopic(java.lang.String) + */ + public MqttTopic getTopic(String topic) { + return aClient.getTopic(topic); + } + + /* (non-Javadoc) + * @see org.eclipse.paho.client.mqttv3.IMqttClient#isConnected() + */ + public boolean isConnected() { + return aClient.isConnected(); + } + + /* (non-Javadoc) + * @see org.eclipse.paho.client.mqttv3.IMqttClient#setCallback(org.eclipse.paho.client.mqttv3.MqttCallback) + */ + public void setCallback(MqttCallback callback) { + aClient.setCallback(callback); + } + + /** + * Returns a randomly generated client identifier based on the current user's login + * name 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() { + return MqttAsyncClient.generateClientId(); + } + + /** + * Return a debug object that can be used to help solve problems. + */ + public Debug getDebug() { + return (aClient.getDebug()); + } +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttClientPersistence.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttClientPersistence.java new file mode 100644 index 0000000..6240b19 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttClientPersistence.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3; + +import java.util.Enumeration; + +/** + * Represents a persistent data store, used to store outbound and inbound messages while they + * are in flight, enabling delivery to the QOS specified. You can specify an implementation + * of this interface using {@link MqttClient#MqttClient(String, String, MqttClientPersistence)}, + * which the {@link MqttClient} will use to persist QoS 1 and 2 messages. + *

            + * If the methods defined throw the MqttPersistenceException then the state of the data persisted + * should remain as prior to the method being called. For example, if {@link #put(String, MqttPersistable)} + * throws an exception at any point then the data will be assumed to not be in the persistent store. + * Similarly if {@link #remove(String)} throws an exception then the data will be + * assumed to still be held in the persistent store.

            + *

            + * It is up to the persistence interface to log any exceptions or error information + * which may be required when diagnosing a persistence failure.

            + */ +public interface MqttClientPersistence { + /** + * Initialise the persistent store. + * If a persistent store exists for this client ID then open it, otherwise + * create a new one. If the persistent store is already open then just return. + * An application may use the same client ID to connect to many different + * servers, so the client ID in conjunction with the + * connection will uniquely identify the persistence store required. + * + * @param clientId The client for which the persistent store should be opened. + * @param serverURI The connection string as specified when the MQTT client instance was created. + * @throws MqttPersistenceException if there was a problem opening the persistent store. + */ + public void open(String clientId, String serverURI) throws MqttPersistenceException; + + /** + * Close the persistent store that was previously opened. + * This will be called when a client application disconnects from the broker. + * @throws MqttPersistenceException + */ + public void close() throws MqttPersistenceException; + + /** + * Puts the specified data into the persistent store. + * @param key the key for the data, which will be used later to retrieve it. + * @param persistable the data to persist + * @throws MqttPersistenceException if there was a problem putting the data + * into the persistent store. + */ + public void put(String key, MqttPersistable persistable) throws MqttPersistenceException; + + /** + * Gets the specified data out of the persistent store. + * @param key the key for the data, which was used when originally saving it. + * @return the un-persisted data + * @throws MqttPersistenceException if there was a problem getting the data + * from the persistent store. + */ + public MqttPersistable get(String key) throws MqttPersistenceException; + + /** + * Remove the data for the specified key. + */ + public void remove(String key) throws MqttPersistenceException; + + /** + * Returns an Enumeration over the keys in this persistent data store. + * @return an enumeration of {@link String} objects. + */ + public Enumeration keys() throws MqttPersistenceException; + + /** + * Clears persistence, so that it no longer contains any persisted data. + */ + public void clear() throws MqttPersistenceException; + + /** + * Returns whether or not data is persisted using the specified key. + * @param key the key for data, which was used when originally saving it. + */ + public boolean containsKey(String key) throws MqttPersistenceException; +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttConnectOptions.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttConnectOptions.java new file mode 100644 index 0000000..72e28b5 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttConnectOptions.java @@ -0,0 +1,366 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3; + +import java.util.Properties; + +import javax.net.SocketFactory; + +import org.eclipse.paho.client.mqttv3.util.Debug; + +/** + * Holds the set of options that control how the client connects to a server. + */ +public class MqttConnectOptions { + /** + * The default keep alive interval in seconds if one is not specified + */ + public static final int KEEP_ALIVE_INTERVAL_DEFAULT = 60; + /** + * The default connection timeout in seconds if one is not specified + */ + public static final int CONNECTION_TIMEOUT_DEFAULT = 30; + /** + * The default clean session setting if one is not specified + */ + public static final boolean CLEAN_SESSION_DEFAULT = true; + + private int keepAliveInterval = KEEP_ALIVE_INTERVAL_DEFAULT; + private String willDestination = null; + private MqttMessage willMessage = null; + private String userName; + private char[] password; + private SocketFactory socketFactory; + private Properties sslClientProps = null; + private boolean cleanSession = CLEAN_SESSION_DEFAULT; + private int connectionTimeout = CONNECTION_TIMEOUT_DEFAULT; + + /** + * Constructs a new MqttConnectOptions object using the + * default values. + * + * The defaults are: + *
              + *
            • The keepalive interval is 60 seconds
            • + *
            • Clean Session is true
            • + *
            • The message delivery retry interval is 15 seconds
            • + *
            • The connection timeout period is 30 seconds
            • + *
            • No Will message is set
            • + *
            • A standard SocketFactory is used
            • + *
            + * More information about these values can be found in the setter methods. + */ + public MqttConnectOptions() { + } + + /** + * Returns the password to use for the connection. + * @return the password to use for the connection. + */ + public char[] getPassword() { + return password; + } + + /** + * Sets the password to use for the connection. + */ + public void setPassword(char[] password) { + this.password = password; + } + + /** + * Returns the user name to use for the connection. + * @return the user name to use for the connection. + */ + public String getUserName() { + return userName; + } + + /** + * Sets the user name to use for the connection. + * @throws IllegalArgumentException if the user name is blank or only + * contains whitespace characters. + */ + public void setUserName(String userName) { + if ((userName != null) && (userName.trim().equals(""))) { + throw new IllegalArgumentException(); + } + this.userName = userName; + } + + /** + * Sets the "Last Will and Testament" (LWT) for the connection. + * In the event that this client unexpectedly loses its connection to the + * server, the server will publish a message to itself using the supplied + * details. + * + * @param topic the topic to publish to. + * @param payload the byte payload for the message. + * @param qos the quality of service to publish the message at (0, 1 or 2). + * @param retained whether or not the message should be retained. + */ + public void setWill(MqttTopic topic, byte[] payload, int qos, boolean retained) { + String topicS = topic.getName(); + validateWill(topicS, payload); + this.setWill(topicS, new MqttMessage(payload), qos, retained); + } + + /** + * Sets the "Last Will and Testament" (LWT) for the connection. + * In the event that this client unexpectedly loses its connection to the + * server, the server will publish a message to itself using the supplied + * details. + * + * @param topic the topic to publish to. + * @param payload the byte payload for the message. + * @param qos the quality of service to publish the message at (0, 1 or 2). + * @param retained whether or not the message should be retained. + */ + public void setWill(String topic, byte[] payload, int qos, boolean retained) { + validateWill(topic, payload); + this.setWill(topic, new MqttMessage(payload), qos, retained); + } + + + /** + * Validates the will fields. + */ + private void validateWill(String dest, Object payload) { + if ((dest == null) || (payload == null)) { + throw new IllegalArgumentException(); + } + MqttAsyncClient.validateTopic(dest); + } + + /** + * Sets up the will information, based on the supplied parameters. + */ + protected void setWill(String topic, MqttMessage msg, int qos, boolean retained) { + willDestination = topic; + willMessage = msg; + willMessage.setQos(qos); + willMessage.setRetained(retained); + // Prevent any more changes to the will message + willMessage.setMutable(false); + } + + /** + * Returns the "keep alive" interval. + * @see #setKeepAliveInterval(int) + * @return the keep alive interval. + */ + public int getKeepAliveInterval() { + return keepAliveInterval; + } + + /** + * Sets the "keep alive" interval. + * This value, measured in seconds, defines the maximum time interval + * between messages sent or received. It enables the client to + * detect if the server is no longer available, without + * having to wait for the TCP/IP timeout. The client will ensure + * that at least one message travels across the network within each + * keep alive period. In the absence of a data-related message during + * the time period, the client sends a very small "ping" message, which + * the server will acknowledge. + * A value of 0 disables keepalive processing in the client. + *

            The default value is 60 seconds

            + * + * @param keepAliveInterval the interval, measured in seconds, must be >= 0. + */ + public void setKeepAliveInterval(int keepAliveInterval)throws IllegalArgumentException { + if (keepAliveInterval <0 ) { + throw new IllegalArgumentException(); + } + this.keepAliveInterval = keepAliveInterval; + } + + /** + * Returns the connection timeout value. + * @see #setConnectionTimeout(int) + * @return the connection timeout value. + */ + public int getConnectionTimeout() { + return connectionTimeout; + } + + /** + * Sets the connection timeout value. + * This value, measured in seconds, defines the maximum time interval + * the client will wait for the network connection to the MQTT server to be established. + * The default timeout is 30 seconds. + * @param connectionTimeout the timeout value, measured in seconds. + */ + public void setConnectionTimeout(int connectionTimeout) { + this.connectionTimeout = connectionTimeout; + } + + /** + * Returns the socket factory that will be used when connecting, or + * null if one has not been set. + */ + public SocketFactory getSocketFactory() { + return socketFactory; + } + + /** + * Sets the SocketFactory to use. This allows an application + * to apply its own policies around the creation of network sockets. If + * using an SSL connection, an SSLSocketFactory can be used + * to supply application-specific security settings. + * @param socketFactory the factory to use. + */ + public void setSocketFactory(SocketFactory socketFactory) { + this.socketFactory = socketFactory; + } + + /** + * Returns the topic to be used for last will and testament (LWT). + * @return the MqttTopic to use, or null if LWT is not set. + * @see #setWill(MqttTopic, byte[], int, boolean) + */ + public String getWillDestination() { + return willDestination; + } + + /** + * Returns the message to be sent as last will and testament (LWT). + * The returned object is "read only". Calling any "setter" methods on + * the returned object will result in an + * IllegalStateException being thrown. + * @return the message to use, or null if LWT is not set. + */ + public MqttMessage getWillMessage() { + return willMessage; + } + + /** + * Returns the SSL properties for the connection. + * @return the properties for the SSL connection + */ + public Properties getSSLProperties() { + return sslClientProps; + } + + /** + * Sets the SSL properties for the connection. Note that these + * properties are only valid if an implementation of the Java + * Secure Socket Extensions (JSSE) is available. These properties are + * not used if a SocketFactory has been set using + * {@link #setSocketFactory(SocketFactory)}. + * The following properties can be used:

            + *
            + *
            com.ibm.ssl.protocol
            + *
            One of: SSL, SSLv3, TLS, TLSv1, SSL_TLS.
            + *
            com.ibm.ssl.contextProvider + *
            Underlying JSSE provider. For example "IBMJSSE2" or "SunJSSE"
            + * + *
            com.ibm.ssl.keyStore
            + *
            The name of the file that contains the KeyStore object that you + * want the KeyManager to use. For example /mydir/etc/key.p12
            + * + *
            com.ibm.ssl.keyStorePassword
            + *
            The password for the KeyStore object that you want the KeyManager to use. + * The password can either be in plain-text, + * or may be obfuscated using the static method: + * com.ibm.micro.security.Password.obfuscate(char[] password). + * This obfuscates the password using a simple and insecure XOR and Base64 + * encoding mechanism. Note that this is only a simple scrambler to + * obfuscate clear-text passwords.
            + * + *
            com.ibm.ssl.keyStoreType
            + *
            Type of key store, for example "PKCS12", "JKS", or "JCEKS".
            + * + *
            com.ibm.ssl.keyStoreProvider
            + *
            Key store provider, for example "IBMJCE" or "IBMJCEFIPS".
            + * + *
            com.ibm.ssl.trustStore
            + *
            The name of the file that contains the KeyStore object that you + * want the TrustManager to use.
            + * + *
            com.ibm.ssl.trustStorePassword
            + *
            The password for the TrustStore object that you want the + * TrustManager to use. The password can either be in plain-text, + * or may be obfuscated using the static method: + * com.ibm.micro.security.Password.obfuscate(char[] password). + * This obfuscates the password using a simple and insecure XOR and Base64 + * encoding mechanism. Note that this is only a simple scrambler to + * obfuscate clear-text passwords.
            + * + *
            com.ibm.ssl.trustStoreType
            + *
            The type of KeyStore object that you want the default TrustManager to use. + * Same possible values as "keyStoreType".
            + * + *
            com.ibm.ssl.trustStoreProvider
            + *
            Trust store provider, for example "IBMJCE" or "IBMJCEFIPS".
            + * + *
            com.ibm.ssl.enabledCipherSuites
            + *
            A list of which ciphers are enabled. Values are dependent on the provider, + * for example: SSL_RSA_WITH_AES_128_CBC_SHA;SSL_RSA_WITH_3DES_EDE_CBC_SHA.
            + * + *
            com.ibm.ssl.keyManager
            + *
            Sets the algorithm that will be used to instantiate a KeyManagerFactory object + * instead of using the default algorithm available in the platform. Example values: + * "IbmX509" or "IBMJ9X509". + *
            + * + *
            com.ibm.ssl.trustManager
            + *
            Sets the algorithm that will be used to instantiate a TrustManagerFactory object + * instead of using the default algorithm available in the platform. Example values: + * "PKIX" or "IBMJ9X509". + *
            + *
            + */ + public void setSSLProperties(Properties props) { + this.sslClientProps = props; + } + + /** + * Returns whether the server should remember state for the client across reconnects. + * @return the clean session flag + */ + public boolean isCleanSession() { + return this.cleanSession; + } + + /** + * Sets whether the server should remember state for the client across reconnects. + * This includes subscriptions and the state of any in-flight messages. + */ + public void setCleanSession(boolean cleanSession) { + this.cleanSession = cleanSession; + } + + public Properties getDebug() { + Properties p = new Properties(); + p.put("CleanSession", new Boolean(isCleanSession())); + p.put("ConTimeout", new Integer(getConnectionTimeout())); + p.put("KeepAliveInterval", new Integer(getKeepAliveInterval())); + p.put("UserName", (getUserName()==null)?"null":getUserName()); + p.put("WillDestination", (getWillDestination()==null)?"null":getWillDestination()); + if (getSocketFactory()==null) { + p.put("SocketFactory", "null"); + } else { + p.put("SocketFactory", getSocketFactory()); + } + if (getSSLProperties()==null) { + p.put("SSLProperties", "null"); + } else { + p.put("SSLProperties", getSSLProperties()); + } + return p; + } + + public String toString() { + return Debug.dumpProperties(getDebug(), "Connection options"); + } +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttDeliveryToken.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttDeliveryToken.java new file mode 100644 index 0000000..fe9b129 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttDeliveryToken.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3; + +/** + * Provides a mechanism to track the delivery progress of a message. + * + *

            + * Used to track the the delivery progress of a message when a publish is + * executed in a non-blocking manner (run in the background)

            + * + * @see MqttToken + */ +public class MqttDeliveryToken extends MqttToken implements IMqttDeliveryToken { + + + public MqttDeliveryToken() { + super(); + } + + public MqttDeliveryToken(String logContext) { + super(logContext); + } + + /** + * Returns the message associated with this token. + *

            Until the message has been delivered, the message being delivered will + * be returned. Once the message has been delivered null will be + * returned. + * @return the message associated with this token or null if already delivered. + * @throws MqttException if there was a problem completing retrieving the message + */ + public MqttMessage getMessage() throws MqttException { + return internalTok.getMessage(); + } + + protected void setMessage(MqttMessage msg) { + internalTok.setMessage(msg); + } +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttException.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttException.java new file mode 100644 index 0000000..677b2d9 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttException.java @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3; + +import org.eclipse.paho.client.mqttv3.internal.MessageCatalog; + +/** + * Thrown if an error occurs communicating with the server. + */ +public class MqttException extends Exception { + private static final long serialVersionUID = 300L; + + /** + * Client encountered an exception. Use the {@link #getCause()} + * method to get the underlying reason. + */ + public static final short REASON_CODE_CLIENT_EXCEPTION = 0x00; + + // CONNACK return codes + /** The protocol version requested is not supported by the server. */ + public static final short REASON_CODE_INVALID_PROTOCOL_VERSION = 0x01; + /** The server has rejected the supplied client ID */ + public static final short REASON_CODE_INVALID_CLIENT_ID = 0x02; + /** The broker was not available to handle the request. */ + public static final short REASON_CODE_BROKER_UNAVAILABLE = 0x03; + /** Authentication with the server has failed, due to a bad user name or password. */ + public static final short REASON_CODE_FAILED_AUTHENTICATION = 0x04; + /** Not authorized to perform the requested operation */ + public static final short REASON_CODE_NOT_AUTHORIZED = 0x05; + + /** An unexpected error has occurred. */ + public static final short REASON_CODE_UNEXPECTED_ERROR = 0x06; + + /** + * Client timed out while waiting for a response from the server. + * The server is no longer responding to keep-alive messages. + */ + public static final short REASON_CODE_CLIENT_TIMEOUT = 32000; + /** + * Internal error, caused by no new message IDs being available. + */ + public static final short REASON_CODE_NO_MESSAGE_IDS_AVAILABLE = 32001; + + /** + * The client is already connected. + */ + public static final short REASON_CODE_CLIENT_CONNECTED = 32100; + /** + * The client is already disconnected. + */ + public static final short REASON_CODE_CLIENT_ALREADY_DISCONNECTED = 32101; + /** + * The client is currently disconnecting and cannot accept any new work. + * This can occur when waiting on a token, and then disconnecting the client. + * If the message delivery does not complete within the quiesce timeout + * period, then the waiting token will be notified with an exception. + */ + public static final short REASON_CODE_CLIENT_DISCONNECTING = 32102; + + /** Unable to connect to server */ + public static final short REASON_CODE_SERVER_CONNECT_ERROR = 32103; + + /** + * The client is not connected to the server. The {@link MqttClient#connect()} + * or {@link MqttClient#connect(MqttConnectOptions)} method must be called + * first. It is also possible that the connection was lost - see + * {@link MqttClient#setCallback(MqttCallback)} for a way to track lost + * connections. + */ + public static final short REASON_CODE_CLIENT_NOT_CONNECTED = 32104; + + /** + * Server URI and supplied SocketFactory do not match. + * URIs beginning tcp:// must use a javax.net.SocketFactory, + * and URIs beginning ssl:// must use a javax.net.ssl.SSLSocketFactory. + */ + public static final short REASON_CODE_SOCKET_FACTORY_MISMATCH = 32105; + + /** + * SSL configuration error. + */ + public static final short REASON_CODE_SSL_CONFIG_ERROR = 32106; + + /** + * Thrown when an attempt to call {@link MqttClient#disconnect()} has been + * made from within a method on {@link MqttCallback}. These methods are invoked + * by the client's thread, and must not be used to control disconnection. + * + * @see MqttCallback#messageArrived(String, MqttMessage) + */ + public static final short REASON_CODE_CLIENT_DISCONNECT_PROHIBITED = 32107; + + /** + * Protocol error: the message was not recognized as a valid MQTT packet. + * Possible reasons for this include connecting to a non-MQTT server, or + * connecting to an SSL server port when the client isn't using SSL. + */ + public static final short REASON_CODE_INVALID_MESSAGE = 32108; + + /** + * The client has been unexpectedly disconnected from the server. The {@link #getCause() cause} + * will provide more details. + */ + public static final short REASON_CODE_CONNECTION_LOST = 32109; + + /** + * A connect operation in already in progress, only one connect can happen + * at a time. + */ + public static final short REASON_CODE_CONNECT_IN_PROGRESS = 32110; + + /** + * The client is closed - no operations are permitted on the client in this + * state. New up a new client to continue. + */ + public static final short REASON_CODE_CLIENT_CLOSED = 32111; + + /** + * A request has been made to use a token that is already associated with + * another action. If the action is complete the reset() can ve called on the + * token to allow it to be reused. + */ + public static final short REASON_CODE_TOKEN_INUSE = 32201; + + /** + * A request has been made to send a message but the maximum number of inflight + * messages has already been reached. Once one or more messages have been moved + * then new messages can be sent. + */ + public static final short REASON_CODE_MAX_INFLIGHT = 32202; + + private int reasonCode; + private Throwable cause; + + /** + * Constructs a new MqttException with the specified code + * as the underlying reason. + * @param reasonCode the reason code for the exception. + */ + public MqttException(int reasonCode) { + super(); + this.reasonCode = reasonCode; + } + + /** + * Constructs a new MqttException with the specified + * Throwable as the underlying reason. + * @param cause the underlying cause of the exception. + */ + public MqttException(Throwable cause) { + super(); + this.reasonCode = REASON_CODE_CLIENT_EXCEPTION; + this.cause = cause; + } + + /** + * Constructs a new MqttException with the specified + * Throwable as the underlying reason. + * @param reason the reason code for the exception. + * @param cause the underlying cause of the exception. + */ + public MqttException(int reason, Throwable cause) { + super(); + this.reasonCode = reason; + this.cause = cause; + } + + + /** + * Returns the reason code for this exception. + * @return the code representing the reason for this exception. + */ + public int getReasonCode() { + return reasonCode; + } + + /** + * Returns the underlying cause of this exception, if available. + * @return the Throwable that was the root cause of this exception, + * which may be null. + */ + public Throwable getCause() { + return cause; + } + + /** + * Returns the detail message for this exception. + * @return the detail message, which may be null. + */ + public String getMessage() { + return MessageCatalog.getMessage(reasonCode); + } + + /** + * Returns a String representation of this exception. + * @return a String representation of this exception. + */ + public String toString() { + String result = getMessage() + " (" + reasonCode + ")"; + if (cause != null) { + result = result + " - " + cause.toString(); + } + return result; + } +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttMessage.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttMessage.java new file mode 100644 index 0000000..4c5f88f --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttMessage.java @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3; + +/** + * An MQTT message holds the application payload and options + * specifying how the message is to be delivered + * The message includes a "payload" (the body of the message) + * represented as a byte[]. + */ +public class MqttMessage { + + private boolean mutable = true; + private byte[] payload; + private int qos = 1; + private boolean retained = false; + private boolean dup = false; + + /** + * Utility method to validate the supplied QoS value. + * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2. + */ + public static void validateQos(int qos) { + if ((qos < 0) || (qos > 2)) { + throw new IllegalArgumentException(); + } + } + + /** + * Constructs a message with an empty payload, and all other values + * set to defaults. + * + * The defaults are: + *

              + *
            • Message QoS set to 1
            • + *
            • Message will not be "retained" by the server
            • + *
            + */ + public MqttMessage() { + setPayload(new byte[]{}); + } + + /** + * Constructs a message with the specified byte array as a payload, + * and all other values set to defaults. + */ + public MqttMessage(byte[] payload) { + setPayload(payload); + } + + /** + * Returns the payload as a byte array. + * + * @return the payload as a byte array. + */ + public byte[] getPayload() { + return payload; + } + + /** + * Clears the payload, resetting it to be empty. + * @throws IllegalStateException if this message cannot be edited + */ + public void clearPayload() { + checkMutable(); + this.payload = new byte[] {}; + } + + /** + * Sets the payload of this message to be the specified byte array. + * + * @param payload the payload for this message. + * @throws IllegalStateException if this message cannot be edited + * @throws NullPointerException if no payload is provided + */ + public void setPayload(byte[] payload) { + checkMutable(); + if (payload == null) { + throw new NullPointerException(); + } + this.payload = payload; + } + + /** + * Returns whether or not this message should be/was retained by the server. + * For messages received from the server, this method returns whether or not + * the message was from a current publisher, or was "retained" by the server as + * the last message published on the topic. + * + * @return true if the message should be, or was, retained by + * the server. + * @see #setRetained(boolean) + */ + public boolean isRetained() { + return retained; + } + + /** + * Whether or not the publish message should be retained by the messaging engine. + * Sending a message with the retained set to false will clear the + * retained message from the server. The default value is false + * + * @param retained whether or not the messaging engine should retain the message. + * @throws IllegalStateException if this message cannot be edited + */ + public void setRetained(boolean retained) { + checkMutable(); + this.retained = retained; + } + + /** + * Returns the quality of service for this message. + * @return the quality of service to use, either 0, 1, or 2. + * @see #setQos(int) + */ + public int getQos() { + return qos; + } + + /** + * Sets the quality of service for this message. + *
              + *
            • Quality of service 0 - indicates that a message should + * be delivered at most once (zero or one times). The message will not be persisted to disk, + * and will not be acknowledged across the network. This QoS is the fastest, + * but should only be used for messages which are not valuable - note that + * if the server cannot process the message (for example, there + * is an authorization problem), then an + * {@link MqttCallback#deliveryComplete(IMqttDeliveryToken)}. + * Also known as "fire and forget".
            • + * + *
            • Quality of service 1 - indicates that a message should + * be delivered at least once (one or more times). The message can only be delivered safely if + * it can be persisted, so the application must supply a means of + * persistence using MqttConnectOptions. + * If a persistence mechanism is not specified, the message will not be + * delivered in the event of a client failure. + * The message will be acknowledged across the network. + * This is the default QoS.
            • + * + *
            • Quality of service 2 - indicates that a message should + * be delivered once. The message will be persisted to disk, and will + * be subject to a two-phase acknowledgement across the network. + * The message can only be delivered safely if + * it can be persisted, so the application must supply a means of + * persistence using MqttConnectOptions. + * If a persistence mechanism is not specified, the message will not be + * delivered in the event of a client failure.
            • + * + * If persistence is not configured, QOS 1 and 2 messages will still be delivered + * in the event of a network or server problem as the client will hold state in memory. + * If the MQTT client is shutdown or fails and persistence is not configured then + * delivery of QOS 1 and 2 messages can not be maintained as client side state will + * be lost. + * + * @param qos the "quality of service" to use. Set to 0, 1, 2. + * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2. + * @throws IllegalStateException if this message cannot be edited + */ + public void setQos(int qos) { + checkMutable(); + validateQos(qos); + this.qos = qos; + } + + /** + * Returns a string representation of this messages payload. + * Makes an attempt to return the payload as a string. As the + * MQTT client has no control over the content of the payload + * it may fail. + * @return a string representation of this message. + */ + public String toString() { + return new String(payload); + } + + /** + * Sets the mutability of this object (whether or not its values can be + * changed. + * @param mutable true if the values can be changed, + * false to prevent them from being changed. + */ + protected void setMutable(boolean mutable) { + this.mutable = mutable; + } + + protected void checkMutable() throws IllegalStateException { + if (!mutable) { + throw new IllegalStateException(); + } + } + + protected void setDuplicate(boolean dup) { + this.dup = dup; + } + + /** + * Returns whether or not this message might be a duplicate of one which has + * already been received. This will only be set on messages received from + * the server. + * @return true if the message might be a duplicate. + */ + public boolean isDuplicate() { + return this.dup; + } +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttPersistable.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttPersistable.java new file mode 100644 index 0000000..5508c52 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttPersistable.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3; + +/** + * Represents an object used to pass data to be persisted across the + * {@link org.eclipse.paho.client.mqttv3.MqttClientPersistence MqttClientPersistence} + * interface. + *

              + * When data is passed across the interface the header and payload are + * separated, so that unnecessary message copies may be avoided. + * For example, if a 10 MB payload was published it would be inefficient + * to create a byte array a few bytes larger than 10 MB and copy the + * MQTT message header and payload into a contiguous byte array.

              + *

              + * When the request to persist data is made a separate byte array and offset + * is passed for the header and payload. Only the data between + * offset and length need be persisted. + * So for example, a message to be persisted consists of a header byte + * array starting at offset 1 and length 4, plus a payload byte array + * starting at offset 30 and length 40000. There are three ways in which + * the persistence implementation may return data to the client on + * recovery: + *

                + *
              • It could return the data as it was passed in + * originally, with the same byte arrays and offsets.
              • + *
              • It could safely just persist and return the bytes from the offset + * for the specified length. For example, return a header byte array with + * offset 0 and length 4, plus a payload byte array with offset 0 and length + * 40000
              • + *
              • It could return the header and payload as a contiguous byte array + * with the header bytes preceeding the payload. The contiguous byte array + * should be set as the header byte array, with the payload byte array being + * null. For example, return a single byte array with offset 0 + * and length 40004. + * This is useful when recovering from a file where the header and payload + * could be written as a contiguous stream of bytes.
              • + *
              + *

              + */ +public interface MqttPersistable { + + /** + * Returns the header bytes in an array. + * The bytes start at {@link #getHeaderOffset()} + * and continue for {@link #getHeaderLength()}. + * @return the header bytes. + */ + public byte[] getHeaderBytes() throws MqttPersistenceException; + + /** + * Returns the length of the header. + * @return the header length + */ + public int getHeaderLength() throws MqttPersistenceException; + + /** + * Returns the offset of the header within the byte array returned by {@link #getHeaderBytes()}. + * @return the header offset. + * + */ + public int getHeaderOffset() throws MqttPersistenceException; + + /** + * Returns the payload bytes in an array. + * The bytes start at {@link #getPayloadOffset()} + * and continue for {@link #getPayloadLength()}. + * @return the payload bytes. + */ + public byte[] getPayloadBytes() throws MqttPersistenceException; + + /** + * Returns the length of the payload. + * @return the payload length. + */ + public int getPayloadLength() throws MqttPersistenceException; + + /** + * Returns the offset of the payload within the byte array returned by {@link #getPayloadBytes()}. + * @return the payload offset. + * + */ + public int getPayloadOffset() throws MqttPersistenceException; +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttPersistenceException.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttPersistenceException.java new file mode 100644 index 0000000..7525080 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttPersistenceException.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3; + +/** + * This exception is thrown by the implementor of the persistence + * interface if there is a problem reading or writing persistent data. + */ +public class MqttPersistenceException extends MqttException { + private static final long serialVersionUID = 300L; + + /** Persistence is already being used by another client. */ + public static final short REASON_CODE_PERSISTENCE_IN_USE = 32200; + + /** + * Constructs a new MqttPersistenceException + */ + public MqttPersistenceException() { + super(REASON_CODE_CLIENT_EXCEPTION); + } + + /** + * Constructs a new MqttPersistenceException with the specified code + * as the underlying reason. + * @param reasonCode the reason code for the exception. + */ + public MqttPersistenceException(int reasonCode) { + super(reasonCode); + } + /** + * Constructs a new MqttPersistenceException with the specified + * Throwable as the underlying reason. + * @param cause the underlying cause of the exception. + */ + public MqttPersistenceException(Throwable cause) { + super(cause); + } + /** + * Constructs a new MqttPersistenceException with the specified + * Throwable as the underlying reason. + * @param reason the reason code for the exception. + * @param cause the underlying cause of the exception. + */ + public MqttPersistenceException(int reason, Throwable cause) { + super(reason, cause); + } +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttSecurityException.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttSecurityException.java new file mode 100644 index 0000000..9069e41 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttSecurityException.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3; + +/** + * Thrown when a client is not authorized to perform an operation, or + * if there is a problem with the security configuration. + */ +public class MqttSecurityException extends MqttException { + private static final long serialVersionUID = 300L; + + /** + * Constructs a new MqttSecurityException with the specified code + * as the underlying reason. + * @param reasonCode the reason code for the exception. + */ + public MqttSecurityException(int reasonCode) { + super(reasonCode); + } + + /** + * Constructs a new MqttSecurityException with the specified + * Throwable as the underlying reason. + * @param cause the underlying cause of the exception. + */ + public MqttSecurityException(Throwable cause) { + super(cause); + } + /** + * Constructs a new MqttSecurityException with the specified + * code and Throwable as the underlying reason. + * @param reasonCode the reason code for the exception. + * @param cause the underlying cause of the exception. + */ + public MqttSecurityException(int reasonCode, Throwable cause) { + super(reasonCode, cause); + } +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttToken.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttToken.java new file mode 100644 index 0000000..1fc826b --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttToken.java @@ -0,0 +1,71 @@ +package org.eclipse.paho.client.mqttv3; + +import org.eclipse.paho.client.mqttv3.internal.Token; + +/** + * Provides a mechanism for tracking the completion of an asynchronous action. + *

              + * A token that implements the ImqttToken interface is returned from all non-blocking + * method with the exception of publish. + *

              + * + * @see IMqttToken + */ + +public class MqttToken implements IMqttToken { + /** + * A reference to the the class that provides most of the implementation of the + * MqttToken. MQTT application programs must not use the internal class. + */ + public Token internalTok = null; + + public MqttToken() { + } + + public MqttToken(String logContext) { + internalTok = new Token(logContext); + } + + public MqttException getException() { + return internalTok.getException(); + } + + public boolean isComplete() { + return internalTok.isComplete(); + } + + public void setActionCallback(IMqttActionListener listener) { + internalTok.setActionCallback(listener); + + } + public IMqttActionListener getActionCallback() { + return internalTok.getActionCallback(); + } + + public void waitForCompletion() throws MqttException { + internalTok.waitForCompletion(-1); + } + + public void waitForCompletion(long timeout) throws MqttException { + internalTok.waitForCompletion(timeout); + } + + public IMqttAsyncClient getClient() { + return internalTok.getClient(); + } + + public String[] getTopics() { + return internalTok.getTopics(); + } + + public Object getUserContext() { + return internalTok.getUserContext(); + } + + public void setUserContext(Object userContext) { + internalTok.setUserContext(userContext); } + + public int getMessageId() { + return internalTok.getMessageID(); + } +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttTopic.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttTopic.java new file mode 100644 index 0000000..a486f7b --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttTopic.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3; + +import org.eclipse.paho.client.mqttv3.internal.ClientComms; +import org.eclipse.paho.client.mqttv3.internal.wire.MqttPublish; + +/** + * Represents a topic destination, used for publish/subscribe messaging. + */ +public class MqttTopic { + + private ClientComms comms; + private String name; + + public MqttTopic(String name, ClientComms comms) { + this.comms = comms; + this.name = name; + } + + /** + * Publishes a message on the topic. This is a convenience method, which will + * create a new {@link MqttMessage} object with a byte array payload and the + * specified QoS, and then publish it. All other values in the + * message will be set to the defaults. + + * @param payload the byte array to use as the payload + * @param qos the Quality of Service. Valid values are 0, 1 or 2. + * @param retained whether or not this message should be retained by the server. + * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2. + * @see #publish(MqttMessage) + * @see MqttMessage#setQos(int) + * @see MqttMessage#setRetained(boolean) + */ + public MqttDeliveryToken publish(byte[] payload, int qos, boolean retained) throws MqttException, MqttPersistenceException { + MqttMessage message = new MqttMessage(payload); + message.setQos(qos); + message.setRetained(retained); + return this.publish(message); + } + + /** + * Publishes the specified message to this topic, but does not wait for delivery + * of the message to complete. The returned {@link MqttDeliveryToken token} can be used + * to track the delivery status of the message. Once this method has + * returned cleanly, the message has been accepted for publication by the + * client. Message delivery will be completed in the background when a connection + * is available. + * + * @param message the message to publish + * @return an MqttDeliveryToken for tracking the delivery of the message + */ + public MqttDeliveryToken publish(MqttMessage message) throws MqttException, MqttPersistenceException { + MqttDeliveryToken token = new MqttDeliveryToken(comms.getClient().getClientId()); + token.setMessage(message); + comms.sendNoWait(createPublish(message), token); + token.internalTok.waitUntilSent(); + return token; + } + + /** + * Returns the name of the queue or topic. + * + * @return the name of this destination. + */ + public String getName() { + return name; + } + + /** + * Create a PUBLISH packet from the specified message. + */ + private MqttPublish createPublish(MqttMessage message) { + return new MqttPublish(this.getName(), message); + } + + /** + * Returns a string representation of this topic. + * @return a string representation of this topic. + */ + public String toString() { + return getName(); + } + +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/ClientComms.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/ClientComms.java new file mode 100644 index 0000000..0f1aa2d --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/ClientComms.java @@ -0,0 +1,582 @@ +/* +* Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal; + +import java.util.Enumeration; +import java.util.Properties; +import java.util.Vector; + +import org.eclipse.paho.client.mqttv3.IMqttAsyncClient; +import org.eclipse.paho.client.mqttv3.MqttCallback; +import org.eclipse.paho.client.mqttv3.MqttClientPersistence; +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; +import org.eclipse.paho.client.mqttv3.MqttDeliveryToken; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.MqttPersistenceException; +import org.eclipse.paho.client.mqttv3.MqttToken; +import org.eclipse.paho.client.mqttv3.MqttTopic; +import org.eclipse.paho.client.mqttv3.internal.wire.MqttConnack; +import org.eclipse.paho.client.mqttv3.internal.wire.MqttConnect; +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.MqttWireMessage; +import org.eclipse.paho.client.mqttv3.logging.Logger; +import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; + +/** + * Handles client communications with the server. Sends and receives MQTT V3 + * messages. + */ +public class ClientComms { + public static String VERSION = "${project.version}"; + public static String BUILD_LEVEL = "L${build.level}"; + + private IMqttAsyncClient client; + NetworkModule networkModule; + CommsReceiver receiver; + CommsSender sender; + CommsCallback callback; + ClientState clientState; + MqttConnectOptions conOptions; + private MqttClientPersistence persistence; + CommsTokenStore tokenStore; + boolean stoppingComms = false; + + final static byte CONNECTED =0; + final static byte CONNECTING =1; + final static byte DISCONNECTING =2; + final static byte DISCONNECTED =3; + final static byte CLOSED =4; + + private byte conState = DISCONNECTED; + Object conLock = new Object(); // Used to syncrhonize connection state + private boolean closePending = false; + + final static String className = ClientComms.class.getName(); + Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className); + + + /** + * Creates a new ClientComms object, using the specified module to handle + * the network calls. + */ + public ClientComms(IMqttAsyncClient client, MqttClientPersistence persistence) throws MqttException { + this.conState = DISCONNECTED; + this.client = client; + this.persistence = persistence; + this.tokenStore = new CommsTokenStore(getClient().getClientId()); + this.callback = new CommsCallback(this); + this.clientState = new ClientState(persistence, tokenStore, this.callback, this); + + callback.setClientState(clientState); + log.setResourceName(getClient().getClientId()); + } + + /** + * Sends a message to the server. Does not check if connected this validation must be done + * by invoking routines. + * @param message + * @param token + * @throws MqttException + */ + void internalSend(MqttWireMessage message, MqttToken token) throws MqttException { + final String methodName = "internalSend"; + //@TRACE 200=internalSend key={0} message={1} token={2} + log.fine(className, methodName, "200", new Object[]{message.getKey(), message, token}); + + if (token.getClient() == null ) { + // Associate the client with the token - also marks it as in use. + token.internalTok.setClient(getClient()); + } else { + // Token is already in use - cannot reuse + //@TRACE 213=fail: token in use: key={0} message={1} token={2} + log.fine(className, methodName, "213", new Object[]{message.getKey(), message, token}); + + throw new MqttException(MqttException.REASON_CODE_TOKEN_INUSE); + } + + try { + // Persist if needed and send the message + this.clientState.send(message, token); + } catch(MqttException e) { + if (message instanceof MqttPublish) { + this.clientState.undo((MqttPublish)message); + } + throw e; + } + } + + /** + * Sends a message to the broker if in connected state, but only waits for the message to be + * stored, before returning. + */ + public void sendNoWait(MqttWireMessage message, MqttToken token) throws MqttException { + final String methodName = "sendNoWait"; + if (isConnected() || + (!isConnected() && message instanceof MqttConnect) || + (isDisconnecting() && message instanceof MqttDisconnect)) { + this.internalSend(message, token); + } else { + //@TRACE 208=failed: not connected + log.fine(className, methodName, "208"); + throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_NOT_CONNECTED); + } + } + + /** + * Tidy up + * - call each main class and let it tidy up e.g. releasing the token + * store which normally survives a disconnect. + * @throws MqttException if not disconnected + */ + public void close() throws MqttException { + final String methodName = "close"; + synchronized (conLock) { + if (!isClosed()) { + // Must be disconnected before close can take place + if (!isDisconnected()) { + //@TRACE 224=failed: not disconnected + log.fine(className, methodName, "224"); + + if (isConnecting()) { + throw new MqttException(MqttException.REASON_CODE_CONNECT_IN_PROGRESS); + } else if (isConnected()) { + throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_CONNECTED); + } else if (isDisconnecting()) { + closePending = true; + return; + } + } + + conState = CLOSED; + + // ShutdownConnection has already cleaned most things + clientState.close(); + clientState = null; + callback = null; + persistence = null; + sender = null; + receiver = null; + networkModule = null; + conOptions = null; + tokenStore = null; + } + } + } + + /** + * Sends a connect message and waits for an ACK or NACK. + * Connecting is a special case which will also start up the + * network connection, receive thread, and keep alive thread. + */ + public void connect(MqttConnectOptions options, MqttToken token) throws MqttException { + final String methodName = "connect"; + synchronized (conLock) { + if (isDisconnected() && !closePending) { + //@TRACE 214=state=CONNECTING + log.fine(className,methodName,"214"); + + conState = CONNECTING; + + this.conOptions = options; + + MqttConnect connect = new MqttConnect(client.getClientId(), + options.isCleanSession(), + options.getKeepAliveInterval(), + options.getUserName(), + options.getPassword(), + options.getWillMessage(), + options.getWillDestination()); + + this.clientState.setKeepAliveSecs(options.getKeepAliveInterval()); + this.clientState.setCleanSession(options.isCleanSession()); + + tokenStore.open(); + ConnectBG conbg = new ConnectBG(this, token, connect); + conbg.start(); + } + else { + // @TRACE 207=connect failed: not disconnected {0} + log.fine(className,methodName,"207", new Object[] {new Byte(conState)}); + if (isClosed() || closePending) { + throw new MqttException(MqttException.REASON_CODE_CLIENT_CLOSED); + } else if (isConnecting()) { + throw new MqttException(MqttException.REASON_CODE_CONNECT_IN_PROGRESS); + } else if (isDisconnecting()) { + throw new MqttException(MqttException.REASON_CODE_CLIENT_DISCONNECTING); + } else { + throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_CONNECTED); + } + } + } + } + + public void connectComplete( MqttConnack cack, MqttException mex) throws MqttException { + final String methodName = "connectComplete"; + int rc = cack.getReturnCode(); + synchronized (conLock) { + if (rc == 0) { + // We've successfully connected + // @TRACE 215=state=CONNECTED + log.fine(className,methodName,"215"); + + conState = CONNECTED; + return; + } + } + + // @TRACE 204=connect failed: rc={0} + log.fine(className,methodName,"204", new Object[]{new Integer(rc)}); + throw mex; + } + + /** + * Shuts down the connection to the server. + * This may have been invoked as a result of a user calling disconnect or + * an abnormal disconnection. The method may be invoked multiple times + * in parallel as each thread when it receives an error uses this method + * to ensure that shutdown completes successfully. + */ + public void shutdownConnection(MqttToken token, MqttException reason) { + final String methodName = "shutdownConnection"; + boolean wasConnected; + MqttToken endToken = null; //Token to notify after disconnect completes + + // This method could concurrently be invoked from many places only allow it + // to run once. + synchronized(conLock) { + if (stoppingComms || closePending) { + return; + } + stoppingComms = true; + + //@TRACE 216=state=DISCONNECTING + log.fine(className,methodName,"216"); + + wasConnected = (isConnected() || isDisconnecting()); + conState = DISCONNECTING; + } + + // Update the token with the reason for shutdown if it + // is not already complete. + if (token != null && !token.isComplete()) { + token.internalTok.setException(reason); + } + + // Stop the thread that is used to call the user back + // when actions complete + if (callback!= null) {callback.stop(); } + + // Stop the network module, send and receive now not possible + try { + if (networkModule != null) {networkModule.stop();} + }catch(Exception ioe) { + // Ignore as we are shutting down + } + + // Stop the thread that handles inbound work from the network + if (receiver != null) {receiver.stop();} + + // Stop any new tokens being saved by app and throwing an exception if they do + tokenStore.quiesce(new MqttException(MqttException.REASON_CODE_CLIENT_DISCONNECTING)); + + // Notify any outstanding tokens with the exception of + // con or discon which may be returned and will be notified at + // the end + endToken = handleOldTokens(token, reason); + + try { + // Clean session handling and tidy up + clientState.disconnected(reason); + }catch(Exception ex) { + // Ignore as we are shutting down + } + + if (sender != null) { sender.stop(); } + + try { + if (persistence != null) {persistence.close();} + }catch(Exception ex) { + // Ignore as we are shutting down + } + // All disconnect logic has been completed allowing the + // client to be marked as disconnected. + synchronized(conLock) { + //@TRACE 217=state=DISCONNECTED + log.fine(className,methodName,"217"); + + conState = DISCONNECTED; + stoppingComms = false; + } + + // Internal disconnect processing has completed. If there + // is a disconnect token or a connect in error notify + // it now. This is done at the end to allow a new connect + // to be processed and now throw a currently disconnecting error. + // any outstanding tokens and unblock any waiters + if (endToken != null & callback != null) { + callback.asyncOperationComplete(endToken); + } + + if (wasConnected && callback != null) { + // Let the user know client has disconnected either normally or abnormally + callback.connectionLost(reason); + } + + // While disconnecting, close may have been requested - try it now + synchronized(conLock) { + if (closePending) { + try { + close(); + } catch (Exception e) { // ignore any errors as closing + } + } + } + } + + // Tidy up. There may be tokens outstanding as the client was + // not disconnected/quiseced cleanly! Work out what tokens still + // need to be notified and waiters unblocked. Store the + // disconnect or connect token to notify after disconnect is + // complete. + private MqttToken handleOldTokens(MqttToken token, MqttException reason) { + final String methodName = "handleOldTokens"; + //@TRACE 222=> + log.fine(className,methodName,"222"); + + MqttToken tokToNotifyLater = null; + try { + // First the token that was related to the disconnect / shutdown may + // not be in the token table - temporarily add it if not + if (token != null) { + if (tokenStore.getToken(token.internalTok.getKey())==null) { + tokenStore.saveToken(token, token.internalTok.getKey()); + } + } + + Vector toksToNot = clientState.resolveOldTokens(reason); + Enumeration toksToNotE = toksToNot.elements(); + while(toksToNotE.hasMoreElements()) { + MqttToken tok = (MqttToken)toksToNotE.nextElement(); + + if (tok.internalTok.getKey().equals(MqttDisconnect.KEY) || + tok.internalTok.getKey().equals(MqttConnect.KEY)) { + // Its con or discon so remember and notify @ end of disc routine + tokToNotifyLater = tok; + } else { + // notify waiters and callbacks of outstanding tokens + // that a problem has occurred and disconnect is in + // progress + callback.asyncOperationComplete(tok); + } + } + }catch(Exception ex) { + // Ignore as we are shutting down + } + return tokToNotifyLater; + } + + public void disconnect(MqttDisconnect disconnect, long quiesceTimeout, MqttToken token) throws MqttException { + final String methodName = "disconnect"; + synchronized (conLock){ + if (isClosed()) { + //@TRACE 223=failed: in closed state + log.fine(className,methodName,"223"); + throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_CLOSED); + } else if (isDisconnected()) { + //@TRACE 211=failed: already disconnected + log.fine(className,methodName,"211"); + throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_ALREADY_DISCONNECTED); + } else if (isDisconnecting()) { + //@TRACE 219=failed: already disconnecting + log.fine(className,methodName,"219"); + throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_DISCONNECTING); + } else if (Thread.currentThread() == callback.getThread()) { + //@TRACE 210=failed: called on callback thread + log.fine(className,methodName,"210"); + // Not allowed to call disconnect() from the callback, as it will deadlock. + throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_DISCONNECT_PROHIBITED); + } + + //@TRACE 218=state=DISCONNECTING + log.fine(className,methodName,"218"); + conState = DISCONNECTING; + DisconnectBG discbg = new DisconnectBG(disconnect,quiesceTimeout,token); + discbg.start(); + } + } + + public boolean isConnected() { + return conState == CONNECTED; + } + + public boolean isConnecting() { + return conState == CONNECTING; + } + public boolean isDisconnected() { + return conState == DISCONNECTED; + } + + public boolean isDisconnecting() { + return conState == DISCONNECTING; + } + public boolean isClosed() { + return conState == CLOSED; + } + + + public void setCallback(MqttCallback mqttCallback) { + this.callback.setCallback(mqttCallback); + } + + protected MqttTopic getTopic(String topic) { + return new MqttTopic(topic, this); + } + public void setNetworkModule(NetworkModule networkModule) { + this.networkModule = networkModule; + } + public MqttDeliveryToken[] getPendingDeliveryTokens() { + return tokenStore.getOutstandingDelTokens(); + } + protected void deliveryComplete(MqttPublish msg) throws MqttPersistenceException { + this.clientState.deliveryComplete(msg); + } + + public IMqttAsyncClient getClient() { + return client; + } + + public long getKeepAlive() { + return this.clientState.getKeepAlive(); + } + + public ClientState getClientState() { + return clientState; + } + + public MqttConnectOptions getConOptions() { + return conOptions; + } + + public Properties getDebug() { + Properties props = new Properties(); + props.put("conState", new Integer(conState)); + props.put("serverURI", getClient().getServerURI()); + props.put("callback", callback); + props.put("stoppingComms", new Boolean(stoppingComms)); + return props; + } + + + + // Kick off the connect processing in the background so that it does not block. For instance + // the socket could take time to create. + private class ConnectBG implements Runnable { + ClientComms clientComms = null; + Thread cBg = null; + MqttToken conToken; + MqttConnect conPacket; + + ConnectBG(ClientComms cc, MqttToken cToken, MqttConnect cPacket) { + clientComms = cc; + conToken = cToken; + conPacket = cPacket; + cBg = new Thread(this, "MQTT Con: "+getClient().getClientId()); + } + + void start() { + cBg.start(); + } + + public void run() { + final String methodName = "connectBG:run"; + MqttException mqttEx = null; + //@TRACE 220=> + log.fine(className, methodName, "220"); + + try { + // Reset an exception on existing delivery tokens. + // This will have been set if disconnect occured before delivery was + // fully processed. + MqttDeliveryToken[] toks = tokenStore.getOutstandingDelTokens(); + for (int i=0; i + log.fine(className, methodName, "221"); + + // Allow current inbound and outbound work to complete + clientState.quiesce(quiesceTimeout); + try { + internalSend(disconnect, token); + token.internalTok.waitUntilSent(); + } + catch (MqttException ex) { + } + finally { + token.internalTok.markComplete(null, null); + shutdownConnection(token, null); + } + } + } +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/ClientDefaults.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/ClientDefaults.java new file mode 100644 index 0000000..996ce0b --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/ClientDefaults.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal; + +public class ClientDefaults { + public static final int MAX_MSG_SIZE = 1024 * 1024 * 256; // 256 MB +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/ClientState.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/ClientState.java new file mode 100644 index 0000000..b39c5ef --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/ClientState.java @@ -0,0 +1,1146 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal; + +import java.io.EOFException; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Properties; +import java.util.Vector; + +import org.eclipse.paho.client.mqttv3.MqttClientPersistence; +import org.eclipse.paho.client.mqttv3.MqttDeliveryToken; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.MqttMessage; +import org.eclipse.paho.client.mqttv3.MqttPersistable; +import org.eclipse.paho.client.mqttv3.MqttPersistenceException; +import org.eclipse.paho.client.mqttv3.MqttToken; +import org.eclipse.paho.client.mqttv3.internal.wire.MqttAck; +import org.eclipse.paho.client.mqttv3.internal.wire.MqttConnack; +import org.eclipse.paho.client.mqttv3.internal.wire.MqttConnect; +import org.eclipse.paho.client.mqttv3.internal.wire.MqttPingReq; +import org.eclipse.paho.client.mqttv3.internal.wire.MqttPingResp; +import org.eclipse.paho.client.mqttv3.internal.wire.MqttPubAck; +import org.eclipse.paho.client.mqttv3.internal.wire.MqttPubComp; +import org.eclipse.paho.client.mqttv3.internal.wire.MqttPubRec; +import org.eclipse.paho.client.mqttv3.internal.wire.MqttPubRel; +import org.eclipse.paho.client.mqttv3.internal.wire.MqttPublish; +import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage; +import org.eclipse.paho.client.mqttv3.logging.Logger; +import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; + +/** + * The core of the client, which holds the state information for pending and + * in-flight messages. + * + * Messages that have been accepted for delivery are moved between several objects + * while being delivered. + * + * 1) When the client is not running messages are stored in a persistent store that + * implements the MqttClientPersistent Interface. The default is MqttDefaultFilePersistencew + * which stores messages safely across failures and system restarts. If no persistence + * is specified there is a fall back to MemoryPersistence which will maintain the messages + * while the Mqtt client is instantiated. + * + * 2) When the client or specifically ClientState is instantiated the messages are + * read from the persistent store into: + * - outboundqos2 hashtable if a qos 2 publish or pubrel + * - outboundqos1 hashtable if a qos 1 publish + * (see restoreState) + * + * 3) On Connect, copy messages from the outbound hashtables to the pendingMessages or + * pendingFlows vector in messageid order. + * - Initial message publish goes onto the pendingmessages buffer. + * - Pubrel goes onto the pendingflows buffer + * (see restoreInflightMessages) + * + * 4) Sender thread reads messages from the pendingflows and pendingmessages buffer + * one at a time. The message is removed from the pendingbuffer but remains on the + * outbound* hashtable. The hashtable is the place where the full set of outstanding + * messages are stored in memory. (Persistence is only used at start up) + * + * 5) Receiver thread - receives wire messages: + * - if QOS 1 then remove from persistence and outboundqos1 + * - if QOS 2 pubrec send pubrel. Updating the outboundqos2 entry with the pubrel + * and update persistence. + * - if QOS 2 pubcomp remove from persistence and outboundqos2 + * + * Notes: + * because of the multi threaded nature of the client it is vital that any changes to this + * class take concurrency into account. For instance as soon as a flow / message is put on + * the wire it is possible for the receiving thread to receive the ack and to be processing + * the response before the sending side has finished processing. For instance a connect may + * be sent, the conack received before the connect notify send has been processed! + * + */ +public class ClientState { + private static final String PERSISTENCE_SENT_PREFIX = "s-"; + private static final String PERSISTENCE_CONFIRMED_PREFIX = "sc-"; + private static final String PERSISTENCE_RECEIVED_PREFIX = "r-"; + + private static final int MIN_MSG_ID = 1; // Lowest possible MQTT message ID to use + private static final int MAX_MSG_ID = 65535; // Highest possible MQTT message ID to use + private int nextMsgId = MIN_MSG_ID - 1; // The next available message ID to use + private Hashtable inUseMsgIds; // Used to store a set of in-use message IDs + + volatile private Vector pendingMessages; + volatile private Vector pendingFlows; + + private CommsTokenStore tokenStore; + private ClientComms clientComms = null; + private CommsCallback callback = null; + private long keepAlive; + private boolean cleanSession; + private MqttClientPersistence persistence; + + private int maxInflight = 10; + private int actualInFlight = 0; + private int inFlightPubRels = 0; + + private Object queueLock = new Object(); + private Object quiesceLock = new Object(); + private boolean quiescing = false; + + private long lastOutboundActivity = 0; + private long lastInboundActivity = 0; + private long lastPing = 0; + private MqttWireMessage pingCommand; + private boolean pingOutstanding = false; + + private boolean connected = false; + + private Hashtable outboundQoS2 = null; + private Hashtable outboundQoS1 = null; + private Hashtable inboundQoS2 = null; + + private final static String className = ClientState.class.getName(); + private Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className); + + protected ClientState(MqttClientPersistence persistence, CommsTokenStore tokenStore, + CommsCallback callback, ClientComms clientComms) throws MqttException { + + log.setResourceName(clientComms.getClient().getClientId()); + log.finer(className, "", "" ); + + inUseMsgIds = new Hashtable(); + pendingMessages = new Vector(this.maxInflight); + pendingFlows = new Vector(); + outboundQoS2 = new Hashtable(); + outboundQoS1 = new Hashtable(); + inboundQoS2 = new Hashtable(); + pingCommand = new MqttPingReq(); + inFlightPubRels = 0; + actualInFlight = 0; + + this.persistence = persistence; + this.callback = callback; + this.tokenStore = tokenStore; + this.clientComms = clientComms; + + restoreState(); + } + + protected void setKeepAliveSecs(long keepAliveSecs) { + this.keepAlive = keepAliveSecs*1000; + } + protected long getKeepAlive() { + return this.keepAlive; + } + protected void setCleanSession(boolean cleanSession) { + this.cleanSession = cleanSession; + } + + private String getSendPersistenceKey(MqttWireMessage message) { + return PERSISTENCE_SENT_PREFIX + message.getMessageId(); + } + + private String getSendConfirmPersistenceKey(MqttWireMessage message) { + return PERSISTENCE_CONFIRMED_PREFIX + message.getMessageId(); + } + + private String getReceivedPersistenceKey(MqttWireMessage message) { + return PERSISTENCE_RECEIVED_PREFIX + message.getMessageId(); + } + + protected void clearState() throws MqttException { + final String methodName = "clearState"; + //@TRACE 603=clearState + log.fine(className, methodName,">"); + + persistence.clear(); + inUseMsgIds.clear(); + pendingMessages.clear(); + pendingFlows.clear(); + outboundQoS2.clear(); + outboundQoS1.clear(); + inboundQoS2.clear(); + tokenStore.clear(); + } + + private MqttWireMessage restoreMessage(String key, MqttPersistable persistable) throws MqttException { + final String methodName = "restoreMessage"; + MqttWireMessage message = null; + + try { + message = MqttWireMessage.createWireMessage(persistable); + } + catch (MqttException ex) { + //@TRACE 602=key={0} exception + log.fine(className, methodName, "602", new Object[] {key}, ex); + if (ex.getCause() instanceof EOFException) { + // Premature end-of-file means that the message is corrupted + if (key != null) { + persistence.remove(key); + } + } + else { + throw ex; + } + } + //@TRACE 601=key={0} message={1} + log.fine(className, methodName, "601", new Object[]{key,message}); + return message; + } + + /** + * Inserts a new message to the list, ensuring that list is ordered from lowest to highest in terms of the message id's. + * @param list the list to insert the message into + * @param newMsg the message to insert into the list + */ + private void insertInOrder(Vector list, MqttWireMessage newMsg) { + int newMsgId = newMsg.getMessageId(); + for (int i = 0; i < list.size(); i++) { + MqttWireMessage otherMsg = (MqttWireMessage) list.elementAt(i); + int otherMsgId = otherMsg.getMessageId(); + if (otherMsgId > newMsgId) { + list.insertElementAt(newMsg, i); + return; + } + } + list.addElement(newMsg); + } + + /** + * Produces a new list with the messages properly ordered according to their message id's. + * @param list the list containing the messages to produce a new reordered list for + * - this will not be modified or replaced, i.e., be read-only to this method + * @return a new reordered list + */ + private Vector reOrder(Vector list) { + + // here up the new list + Vector newList = new Vector(); + + if (list.size() == 0) { + return newList; // nothing to reorder + } + + int previousMsgId = 0; + int largestGap = 0; + int largestGapMsgIdPosInList = 0; + for (int i = 0; i < list.size(); i++) { + int currentMsgId = ((MqttWireMessage) list.elementAt(i)).getMessageId(); + if (currentMsgId - previousMsgId > largestGap) { + largestGap = currentMsgId - previousMsgId; + largestGapMsgIdPosInList = i; + } + previousMsgId = currentMsgId; + } + int lowestMsgId = ((MqttWireMessage) list.elementAt(0)).getMessageId(); + int highestMsgId = previousMsgId; // last in the sorted list + + // we need to check that the gap after highest msg id to the lowest msg id is not beaten + if (MAX_MSG_ID - highestMsgId + lowestMsgId > largestGap) { + largestGapMsgIdPosInList = 0; + } + + // starting message has been located, let's start from this point on + for (int i = largestGapMsgIdPosInList; i < list.size(); i++) { + newList.addElement(list.elementAt(i)); + } + + // and any wrapping back to the beginning + for (int i = 0; i < largestGapMsgIdPosInList; i++) { + newList.addElement(list.elementAt(i)); + } + + return newList; + } + + /** + * Restores the state information from persistence. + */ + protected void restoreState() throws MqttException { + final String methodName = "restoreState"; + Enumeration messageKeys = persistence.keys(); + MqttPersistable persistable; + String key; + int highestMsgId = nextMsgId; + Vector orphanedPubRels = new Vector(); + //@TRACE 600=> + log.fine(className, methodName, "600"); + + while (messageKeys.hasMoreElements()) { + key = (String) messageKeys.nextElement(); + persistable = persistence.get(key); + MqttWireMessage message = restoreMessage(key, persistable); + if (message != null) { + if (key.startsWith(PERSISTENCE_RECEIVED_PREFIX)) { + //@TRACE 604=inbound QoS 2 publish key={0} message={1} + log.fine(className,methodName,"604", new Object[]{key,message}); + + // The inbound messages that we have persisted will be QoS 2 + inboundQoS2.put(new Integer(message.getMessageId()),message); + } else if (key.startsWith(PERSISTENCE_SENT_PREFIX)) { + MqttPublish sendMessage = (MqttPublish) message; + highestMsgId = Math.max(sendMessage.getMessageId(), highestMsgId); + if (persistence.containsKey(getSendConfirmPersistenceKey(sendMessage))) { + MqttPersistable persistedConfirm = persistence.get(getSendConfirmPersistenceKey(sendMessage)); + // QoS 2, and CONFIRM has already been sent... + MqttPubRel confirmMessage = (MqttPubRel) restoreMessage(key, persistedConfirm); + if (confirmMessage != null) { + confirmMessage.setDuplicate(true); + //@TRACE 605=outbound QoS 2 pubrel key={0} message={1} + log.fine(className,methodName, "605", new Object[]{key,message}); + + outboundQoS2.put(new Integer(confirmMessage.getMessageId()), confirmMessage); + } else { + //@TRACE 606=outbound QoS 2 completed key={0} message={1} + log.fine(className,methodName, "606", new Object[]{key,message}); + } + } else { + // QoS 1 or 2, with no CONFIRM sent... + // Put the SEND to the list of pending messages, ensuring message ID ordering... + sendMessage.setDuplicate(true); + if (sendMessage.getMessage().getQos() == 2) { + //@TRACE 607=outbound QoS 2 publish key={0} message={1} + log.fine(className,methodName, "607", new Object[]{key,message}); + + outboundQoS2.put(new Integer(sendMessage.getMessageId()),sendMessage); + } else { + //@TRACE 608=outbound QoS 1 publish key={0} message={1} + log.fine(className,methodName, "608", new Object[]{key,message}); + + outboundQoS1.put(new Integer(sendMessage.getMessageId()),sendMessage); + } + } + MqttDeliveryToken tok = tokenStore.restoreToken(sendMessage); + tok.internalTok.setClient(clientComms.getClient()); + inUseMsgIds.put(new Integer(sendMessage.getMessageId()),new Integer(sendMessage.getMessageId())); + } + else if (key.startsWith(PERSISTENCE_CONFIRMED_PREFIX)) { + MqttPubRel pubRelMessage = (MqttPubRel) message; + if (!persistence.containsKey(getSendPersistenceKey(pubRelMessage))) { + orphanedPubRels.addElement(key); + } + } + } + } + + messageKeys = orphanedPubRels.elements(); + while(messageKeys.hasMoreElements()) { + key = (String) messageKeys.nextElement(); + //@TRACE 609=removing orphaned pubrel key={0} + log.fine(className,methodName, "609", new Object[]{key}); + + persistence.remove(key); + } + + nextMsgId = highestMsgId; + } + + private void restoreInflightMessages() { + final String methodName = "restoreInflightMessages"; + pendingMessages = new Vector(this.maxInflight); + pendingFlows = new Vector(); + + Enumeration keys = outboundQoS2.keys(); + while (keys.hasMoreElements()) { + Object key = keys.nextElement(); + Object msg = outboundQoS2.get(key); + if (msg instanceof MqttPublish) { + //@TRACE 610=QoS 2 publish key={0} + log.fine(className,methodName, "610", new Object[]{key}); + + insertInOrder(pendingMessages, (MqttPublish)msg); + } else if (msg instanceof MqttPubRel) { + //@TRACE 611=QoS 2 pubrel key={0} + log.fine(className,methodName, "611", new Object[]{key}); + + insertInOrder(pendingFlows, (MqttPubRel)msg); + } + } + keys = outboundQoS1.keys(); + while (keys.hasMoreElements()) { + Object key = keys.nextElement(); + MqttPublish msg = (MqttPublish)outboundQoS1.get(key); + //@TRACE 612=QoS 1 publish key={0} + log.fine(className,methodName, "612", new Object[]{key}); + + insertInOrder(pendingMessages, msg); + } + + this.pendingFlows = reOrder(pendingFlows); + this.pendingMessages = reOrder(pendingMessages); + } + + /** + * Submits a message for delivery. This method will block until there is + * room in the inFlightWindow for the message. The message is put into + * persistence before returning. + * + * @param message the message to send + * @param token the token that can be used to track delivery of the message + * @throws MqttException + */ + public void send(MqttWireMessage message, MqttToken token) throws MqttException { + final String methodName = "send"; + if (message.isMessageIdRequired() && (message.getMessageId() == 0)) { + message.setMessageId(getNextMessageId()); + } + if (token != null ) { + try { + token.internalTok.setMessageID(message.getMessageId()); + } catch (Exception e) { + } + } + + if (message instanceof MqttPublish) { + synchronized (queueLock) { + if (actualInFlight >= this.maxInflight) { + //@TRACE 613= sending {0} msgs at max inflight window + log.fine(className, methodName, "613", new Object[]{new Integer(actualInFlight)}); + + throw new MqttException(MqttException.REASON_CODE_MAX_INFLIGHT); + } + + MqttMessage innerMessage = ((MqttPublish) message).getMessage(); + //@TRACE 628=pending publish key={0} qos={1} message={2} + log.fine(className,methodName,"628", new Object[]{new Integer(message.getMessageId()), new Integer(innerMessage.getQos()), message}); + + switch(innerMessage.getQos()) { + case 2: + outboundQoS2.put(new Integer(message.getMessageId()), message); + persistence.put(getSendPersistenceKey(message), (MqttPublish) message); + break; + case 1: + outboundQoS1.put(new Integer(message.getMessageId()), message); + persistence.put(getSendPersistenceKey(message), (MqttPublish) message); + break; + } + tokenStore.saveToken(token, message); + pendingMessages.addElement(message); + queueLock.notifyAll(); + } + } else { + //@TRACE 615=pending send key={0} message {1} + log.fine(className,methodName,"615", new Object[]{new Integer(message.getMessageId()), message}); + + if (message instanceof MqttConnect) { + synchronized (queueLock) { + // Add the connect action at the head of the pending queue ensuring it jumps + // ahead of any of other pending actions. + tokenStore.saveToken(token, message); + pendingFlows.insertElementAt(message,0); + queueLock.notifyAll(); + } + } else { + if (message instanceof MqttPingReq) { + this.pingCommand = message; + } + else if (message instanceof MqttPubRel) { + outboundQoS2.put(new Integer(message.getMessageId()), message); + persistence.put(getSendConfirmPersistenceKey(message), (MqttPubRel) message); + } + else if (message instanceof MqttPubComp) { + persistence.remove(getReceivedPersistenceKey(message)); + } + + synchronized (queueLock) { + if ( !(message instanceof MqttAck )) { + tokenStore.saveToken(token, message); + } + pendingFlows.addElement(message); + queueLock.notifyAll(); + } + } + } + } + + /** + * This removes the MqttSend message from the outbound queue and persistence. + * @param message + * @throws MqttPersistenceException + */ + protected void undo(MqttPublish message) throws MqttPersistenceException { + final String methodName = "undo"; + synchronized (queueLock) { + //@TRACE 618=key={0} QoS={1} + log.fine(className,methodName,"618", new Object[]{new Integer(message.getMessageId()), new Integer(message.getMessage().getQos())}); + + if (message.getMessage().getQos() == 1) { + outboundQoS1.remove(new Integer(message.getMessageId())); + } else { + outboundQoS2.remove(new Integer(message.getMessageId())); + } + pendingMessages.removeElement(message); + persistence.remove(getSendPersistenceKey(message)); + tokenStore.removeToken(message); + checkQuiesceLock(); + } + } + + /** + * Check and send a ping if needed and check for ping timeout. + * Need to send a ping if nothing has been sent or received + * in the last keepalive interval. It is important to check for + * both sent and received packets in order to catch the case where an + * app is solely sending QOS 0 messages or receiving QOS 0 messages. + * QOS 0 message are not good enough for checking a connection is + * alive as they are one way messages. + * + * If a ping has been sent but no data has been received in the + * last keepalive interval then the connection is deamed to be broken. + */ + private void checkForActivity() throws MqttException { + final String methodName = "checkForActivity"; + + if (connected && this.keepAlive > 0) { + long time = System.currentTimeMillis(); + + if (!pingOutstanding) { + // Is a ping required? + if (time - lastOutboundActivity >= this.keepAlive || + time - lastInboundActivity >= this.keepAlive) { + + //@TRACE 620=ping needed. keepAlive={0} lastOutboundActivity={1} lastInboundActivity={2} + log.fine(className,methodName,"620", new Object[]{new Long(this.keepAlive),new Long(lastOutboundActivity),new Long(lastInboundActivity)}); + + pingOutstanding = true; + lastPing = time; + MqttToken token = new MqttToken(clientComms.getClient().getClientId()); + tokenStore.saveToken(token, pingCommand); + pendingFlows.insertElementAt(pingCommand, 0); + } + } else if (time - lastPing >= this.keepAlive) { + // A ping is outstanding but no packet has been received in KA so connection is deemed broken + //@TRACE 619=Timed out as no activity, keepAlive={0} lastOutboundActivity={1} lastInboundActivity={2} + log.severe(className,methodName,"619", new Object[]{new Long(this.keepAlive),new Long(lastOutboundActivity),new Long(lastInboundActivity)}); + + // A ping has already been sent. At this point, assume that the + // broker has hung and the TCP layer hasn't noticed. + throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_TIMEOUT); + } + } + } + + /** + * This returns the next piece of work, ie message, for the CommsSender + * to send over the network. + * Calls to this method block until either: + * - there is a message to be sent + * - the keepAlive interval is exceeded, which triggers a ping message + * to be returned + * - {@link #disconnected(MqttException, boolean)} is called + * @return the next message to send, or null if the client is disconnected + */ + protected MqttWireMessage get() throws MqttException { + final String methodName = "get"; + MqttWireMessage result = null; + + synchronized (queueLock) { + while (result == null) { + if (pendingMessages.isEmpty() && pendingFlows.isEmpty()) { + try { + long ttw = getTimeUntilPing(); + //@TRACE 644=nothing to send, wait for {0} ms + log.fine(className,methodName, "644", new Object[] {new Long(ttw)}); + + queueLock.wait(getTimeUntilPing()); + } catch (InterruptedException e) { + } + } + + // Handle the case where not connected. This should only be the case if: + // - in the process of disconnecting / shutting down + // - in the process of connecting + if (!connected && + (pendingFlows.isEmpty() || !((MqttWireMessage)pendingFlows.elementAt(0) instanceof MqttConnect))) { + //@TRACE 621=no outstanding flows and not connected + log.fine(className,methodName,"621"); + + return null; + } + + // Check if there is a need to send a ping to keep the session alive. + // Note this check is done before processing messages. If not done first + // an app that only publishes QOS 0 messages will prevent keepalive processing + // from functioning. + checkForActivity(); + + // Now process any queued flows or messages + if (!pendingFlows.isEmpty()) { + // Process the first "flow" in the queue + result = (MqttWireMessage)pendingFlows.elementAt(0); + pendingFlows.removeElementAt(0); + if (result instanceof MqttPubRel) { + inFlightPubRels++; + + //@TRACE 617=+1 inflightpubrels={0} + log.fine(className,methodName,"617", new Object[]{new Integer(inFlightPubRels)}); + } + + checkQuiesceLock(); + } else if (!pendingMessages.isEmpty()) { + // If the inflight window is full then messages are not + // processed until the inflight window has space. + if (actualInFlight < this.maxInflight) { + // The in flight window is not full so process the + // first message in the queue + result = (MqttWireMessage)pendingMessages.elementAt(0); + pendingMessages.removeElementAt(0); + actualInFlight++; + + //@TRACE 623=+1 actualInFlight={0} + log.fine(className,methodName,"623",new Object[]{new Integer(actualInFlight)}); + } else { + //@TRACE 622=inflight window full + log.fine(className,methodName,"622"); + } + } + } + } + return result; + } + + public void setKeepAliveInterval(long interval) { + this.keepAlive = interval; + } + + /** + * Deduce how long to to wait until a ping is required. + * + * In order to keep the connection alive the server must see activity within + * the keepalive interval. If the application is not sending / receiving + * any messages then the client will send a ping. This method works out + * the next time that a ping must be sent in order for the server to + * know the client is alive. + * @return time before a ping needs to be sent to keep alive the connection + */ + long getTimeUntilPing() { + long pingin = getKeepAlive(); + // If KA is zero which means just wait for work or + // if a ping is outstanding return the KA value + if (connected && (getKeepAlive() > 0) && !pingOutstanding) { + + long time = System.currentTimeMillis(); + long timeSinceOut = (time-lastOutboundActivity); + long timeSinceIn = (time-lastInboundActivity); + + if (timeSinceOut > timeSinceIn) { + pingin = (getKeepAlive()-timeSinceOut); + } else { + pingin = (getKeepAlive()-timeSinceIn); + } + + // Unlikely to be negative or zero but in the case it is return a + // small value > 0 to cause a ping to occur + if (pingin <= 0) { + pingin = 10; + } + } + return (pingin); + } + + /** + * Called by the CommsSender when a message has been sent + * @param message + */ + protected void notifySent(MqttWireMessage message) { + final String methodName = "notifySent"; + + this.lastOutboundActivity = System.currentTimeMillis(); + //@TRACE 625=key={0} + log.fine(className,methodName,"625",new Object[]{message.getKey()}); + + MqttToken token = tokenStore.getToken(message); + token.internalTok.notifySent(); + if (message instanceof MqttPublish) { + if (((MqttPublish)message).getMessage().getQos() == 0) { + // once a QOS 0 message is sent we can clean up its records straight away as + // we won't be hearing about it again + token.internalTok.markComplete(null, null); + callback.asyncOperationComplete(token); + decrementInFlight(); + releaseMessageId(message.getMessageId()); + tokenStore.removeToken(message); + checkQuiesceLock(); + } + } + } + + private void decrementInFlight() { + final String methodName = "decrementInFlight"; + synchronized (queueLock) { + actualInFlight--; + //@TRACE 646=-1 actualInFlight={0} + log.fine(className,methodName,"646",new Object[]{new Integer(actualInFlight)}); + + if (!checkQuiesceLock()) { + queueLock.notifyAll(); + } + } + } + + protected boolean checkQuiesceLock() { + final String methodName = "checkQuiesceLock"; +// if (quiescing && actualInFlight == 0 && pendingFlows.size() == 0 && inFlightPubRels == 0 && callback.isQuiesced()) { + int tokC = tokenStore.count(); + if (quiescing && tokC == 0 && pendingFlows.size() == 0 && callback.isQuiesced()) { + //@TRACE 626=quiescing={0} actualInFlight={1} pendingFlows={2} inFlightPubRels={3} callbackQuiesce={4} tokens={5} + log.fine(className,methodName,"626",new Object[]{new Boolean(quiescing), new Integer(actualInFlight), new Integer(pendingFlows.size()), new Integer(inFlightPubRels), new Boolean(callback.isQuiesced()), new Integer(tokC)}); + synchronized (quiesceLock) { + quiesceLock.notifyAll(); + } + return true; + } + return false; + } + + /** + * Called by the CommsReceiver when an ack has arrived. + * + * @param message + * @throws MqttException + */ + protected void notifyReceivedAck(MqttAck ack) throws MqttException { + final String methodName = "notifyReceivedAck"; + this.lastInboundActivity = System.currentTimeMillis(); + + // @TRACE 627=received key={0} message={1} + log.fine(className, methodName, "627", new Object[] { + new Integer(ack.getMessageId()), ack }); + + MqttToken token = tokenStore.getToken(ack); + MqttException mex = null; + + if (ack instanceof MqttPubRec) { + // Complete the QOS 2 flow. Unlike all other + // flows, QOS is a 2 phase flow. The second phase sends a + // pubrel - the operation is not complete until a pubcomp + // is received + MqttPubRel rel = new MqttPubRel((MqttPubRec) ack); + this.send(rel, token); + } else if (ack instanceof MqttPubAck || ack instanceof MqttPubComp) { + // QoS 1 & 2 notify users of result before removing from + // persistence + notifyResult(ack, token, mex); + // Do not remove publish / delivery token at this stage + // do this when the persistence is removed later + } else if (ack instanceof MqttPingResp) { + pingOutstanding = false; + notifyResult(ack, token, mex); + tokenStore.removeToken(ack); + } else if (ack instanceof MqttConnack) { + int rc = ((MqttConnack) ack).getReturnCode(); + if (rc == 0) { + synchronized (queueLock) { + if (cleanSession) { + clearState(); + // Add the connect token back in so that users can be + // notified when connect completes. + tokenStore.saveToken(token,ack); + } + inFlightPubRels = 0; + actualInFlight = 0; + restoreInflightMessages(); + connected(); + } + } else { + mex = ExceptionHelper.createMqttException(rc); + throw mex; + } + + clientComms.connectComplete((MqttConnack) ack, mex); + notifyResult(ack, token, mex); + tokenStore.removeToken(ack); + + // Notify the sender thread that there maybe work for it to do now + synchronized (queueLock) { + queueLock.notifyAll(); + } + } else { + // Sub ack or unsuback + notifyResult(ack, token, mex); + releaseMessageId(ack.getMessageId()); + tokenStore.removeToken(ack); + } + + checkQuiesceLock(); + } + + /** + * Called by the CommsReceiver when a message has been received. + * Handles inbound messages and other flows such as pubrel. + * + * @param message + * @throws MqttException + */ + protected void notifyReceivedMsg(MqttWireMessage message) throws MqttException { + final String methodName = "notifyReceivedMsg"; + this.lastInboundActivity = System.currentTimeMillis(); + + // @TRACE 651=received key={0} message={1} + log.fine(className, methodName, "651", new Object[] { + new Integer(message.getMessageId()), message }); + + if (!quiescing) { + if (message instanceof MqttPublish) { + MqttPublish send = (MqttPublish) message; + switch (send.getMessage().getQos()) { + case 0: + case 1: + if (callback != null) { + callback.messageArrived(send); + } + break; + case 2: + persistence.put(getReceivedPersistenceKey(message), + (MqttPublish) message); + inboundQoS2.put(new Integer(send.getMessageId()), send); + this.send(new MqttPubRec(send), null); + } + } else if (message instanceof MqttPubRel) { + MqttPublish sendMsg = (MqttPublish) inboundQoS2 + .get(new Integer(message.getMessageId())); + if (sendMsg != null) { + if (callback != null) { + callback.messageArrived(sendMsg); + } + } else { + // Original publish has already been delivered. + MqttPubComp pubComp = new MqttPubComp(message + .getMessageId()); + this.send(pubComp, null); + } + } + } + } + + + /** + * Called when waiters and callbacks have processed the message. For + * messages where delivery is complete the message can be removed from + * persistence and counters adjusted accordingly. Also tidy up by removing + * token from store... + * + * @param message + * @throws MqttException + */ + protected void notifyComplete(MqttToken token) throws MqttException { + final String methodName = "notifyComplete"; + + MqttWireMessage message = token.internalTok.getWireMessage(); + + if (message != null && message instanceof MqttAck) { + // @TRACE 629=received key={0} token={1} message={2} + log.fine(className, methodName, "629", new Object[] { + new Integer(message.getMessageId()), token, message }); + + MqttAck ack = (MqttAck) message; + + if (ack instanceof MqttPubAck) { + // QoS 1 - user notified now remove from persistence... + persistence.remove(getSendPersistenceKey(message)); + outboundQoS1.remove(new Integer(ack.getMessageId())); + decrementInFlight(); + releaseMessageId(message.getMessageId()); + tokenStore.removeToken(message); + // @TRACE 650=removed Qos 1 publish. key={0} + log.fine(className, methodName, "650", + new Object[] { new Integer(ack.getMessageId()) }); + } else if (ack instanceof MqttPubComp) { + // QoS 2 - user notified now remove from persistence... + persistence.remove(getSendPersistenceKey(message)); + persistence.remove(getSendConfirmPersistenceKey(message)); + outboundQoS2.remove(new Integer(ack.getMessageId())); + + inFlightPubRels--; + decrementInFlight(); + releaseMessageId(message.getMessageId()); + tokenStore.removeToken(message); + + // @TRACE 645=removed QoS 2 publish/pubrel. key={0}, -1 inFlightPubRels={1} + log.fine(className, methodName, "645", new Object[] { + new Integer(ack.getMessageId()), + new Integer(inFlightPubRels) }); + } + + checkQuiesceLock(); + } + } + + protected void notifyResult(MqttWireMessage ack, MqttToken token, MqttException ex) { + final String methodName = "notifyResult"; + // unblock any threads waiting on the token + token.internalTok.markComplete(ack, ex); + + // Let the user know an async operation has completed and then remove the token + if (ack != null && ack instanceof MqttAck && !(ack instanceof MqttPubRec)) { + //@TRACE 648=key{0}, msg={1}, excep={2} + log.fine(className,methodName, "648", new Object [] {token.internalTok.getKey(), ack, ex}); + callback.asyncOperationComplete(token); + } + // There are cases where there is no ack as the operation failed before + // an ack was received + if (ack == null ) { + //@TRACE 649=key={0},excep={1} + log.fine(className,methodName, "649", new Object [] { token.internalTok.getKey(), ex}); + callback.asyncOperationComplete(token); + } + } + + /** + * Called when the client has successfully connected to the broker + */ + public void connected() { + final String methodName = "connected"; + //@TRACE 631=connected + log.fine(className, methodName, "631"); + this.connected = true; + } + + /** + * + * Called during shutdown to work out if there are any tokens still + * to be notified and waiters to be unblocked. Notifying and unblocking + * takes place after most shutdown processing has completed. The tokenstore + * is tidied up so it only contains outstanding delivery tokens which are + * valid after reconnect (if clean session is false) + * @param reason The root cause of the disconnection, or null if it is a clean disconnect + */ + public Vector resolveOldTokens(MqttException reason) { + final String methodName = "resolveOldTokens"; + //@TRACE 632=reason {0} + log.fine(className,methodName,"632", new Object[] {reason}); + + // If any outstanding let the user know the reason why it is still + // outstanding by putting the reason shutdown is occurring into the + // token. + MqttException shutReason = reason; + if (reason == null) { + shutReason = new MqttException(MqttException.REASON_CODE_CLIENT_DISCONNECTING); + } + + // Set the token up so it is ready to be notified after disconnect + // processing has completed. Do not + // remove the token from the store if it is a delivery token, it is + // valid after a reconnect. + Vector outT = tokenStore.getOutstandingTokens(); + Enumeration outTE = outT.elements(); + while (outTE.hasMoreElements()) { + MqttToken tok = (MqttToken)outTE.nextElement(); + synchronized (tok) { + if (!tok.isComplete() && !tok.internalTok.isCompletePending() && tok.getException() == null) { + tok.internalTok.setException(shutReason); + } + } + if (!(tok instanceof MqttDeliveryToken)) { + // If not a delivery token it is not valid on + // restart so remove + tokenStore.removeToken(tok.internalTok.getKey()); + } + } + return outT; + } + + /** + * Called when the client has been disconnected from the broker. + * @param reason The root cause of the disconnection, or null if it is a clean disconnect + */ + public void disconnected(MqttException reason) { + final String methodName = "disconnected"; + //@TRACE 633=disconnected + log.fine(className,methodName,"633", new Object[] {reason}); + + this.connected = false; + + try { + if (cleanSession) { + clearState(); + } + + pendingMessages.clear(); + pendingFlows.clear(); + // Reset pingOutstanding to allow reconnects to assume no previous ping. + pingOutstanding = false; + + } catch (MqttException e) { + // Ignore as we have disconnected at this point + } + } + + /** + * Releases a message ID back into the pool of available message IDs. + * If the supplied message ID is not in use, then nothing will happen. + * + * @param msgId A message ID that can be freed up for re-use. + */ + private synchronized void releaseMessageId(int msgId) { + inUseMsgIds.remove(new Integer(msgId)); + } + + /** + * Get the next MQTT message ID that is not already in use, and marks + * it as now being in use. + * + * @return the next MQTT message ID to use + */ + private synchronized int getNextMessageId() throws MqttException { + int startingMessageId = nextMsgId; + // Allow two complete passes of the message ID range. This gives + // any asynchronous releases a chance to occur + int loopCount = 0; + do { + nextMsgId++; + if ( nextMsgId > MAX_MSG_ID ) { + nextMsgId = MIN_MSG_ID; + } + if (nextMsgId == startingMessageId) { + loopCount++; + if (loopCount == 2) { + throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_NO_MESSAGE_IDS_AVAILABLE); + } + } + } while( inUseMsgIds.containsKey( new Integer(nextMsgId) ) ); + Integer id = new Integer(nextMsgId); + inUseMsgIds.put(id, id); + return nextMsgId; + } + + /** + * Quiesce the client state, preventing any new messages getting sent, + * and preventing the callback on any newly received messages. + * After the timeout expires, delete any pending messages except for + * outbound ACKs, and wait for those ACKs to complete. + */ + public void quiesce(long timeout) { + final String methodName = "quiesce"; + // If the timeout is greater than zero t + if (timeout > 0 ) { + //@TRACE 637=timeout={0} + log.fine(className,methodName, "637",new Object[]{new Long(timeout)}); + synchronized (queueLock) { + this.quiescing = true; + } + // We don't want to handle any new inbound messages + callback.quiesce(); + notifyQueueLock(); + + synchronized (quiesceLock) { + try { + // If token count is not zero there is outbound work to process and + // if pending flows is not zero there is outstanding work to complete and + // if call back is not quiseced there it needs to complete. + int tokc = tokenStore.count(); + if (tokc > 0 || pendingFlows.size() >0 || !callback.isQuiesced()) { + //@TRACE 639=wait for outstanding: actualInFlight={0} pendingFlows={1} inFlightPubRels={2} tokens={3} + log.fine(className, methodName,"639", new Object[]{new Integer(actualInFlight), new Integer(pendingFlows.size()), new Integer(inFlightPubRels), new Integer(tokc)}); + + // wait for outstanding in flight messages to complete and + // any pending flows to complete + quiesceLock.wait(timeout); + } + } + catch (InterruptedException ex) { + // Don't care, as we're shutting down anyway + } + } + + // Quiesce time up or inflight messsages delivered. Ensure pending delivery + // vectors are cleared ready for disconnect to be sent as the final flow. + synchronized (queueLock) { + pendingMessages.clear(); + pendingFlows.clear(); + quiescing = false; + actualInFlight = 0; + } + //@TRACE 640=finished + log.fine(className, methodName, "640"); + } + } + + protected void notifyQueueLock() { + final String methodName = "notifyQueueLock"; + synchronized (queueLock) { + //@TRACE 638=notifying queueLock holders + log.fine(className,methodName,"638"); + queueLock.notifyAll(); + } + } + + protected void deliveryComplete(MqttPublish message) throws MqttPersistenceException { + final String methodName = "deliveryComplete"; + + //@TRACE 641=remove publish from persistence. key={0} + log.fine(className,methodName,"641", new Object[]{new Integer(message.getMessageId())}); + + persistence.remove(getReceivedPersistenceKey(message)); + inboundQoS2.remove(new Integer(message.getMessageId())); + } + + /** + * Tidy up + * - ensure that tokens are released as they are maintained over a + * disconnect / connect cycle. + */ + protected void close() { + inUseMsgIds.clear(); + pendingMessages.clear(); + pendingFlows.clear(); + outboundQoS2.clear(); + outboundQoS1.clear(); + inboundQoS2.clear(); + tokenStore.clear(); + inUseMsgIds = null; + pendingMessages = null; + pendingFlows = null; + outboundQoS2 = null; + outboundQoS1 = null; + inboundQoS2 = null; + tokenStore = null; + callback = null; + clientComms = null; + persistence = null; + pingCommand = null; + } + + public Properties getDebug() { + Properties props = new Properties(); + props.put("In use msgids", inUseMsgIds); + props.put("pendingMessages", pendingMessages); + props.put("pendingFlows", pendingFlows); + props.put("maxInflight", new Integer(maxInflight)); + props.put("nextMsgID", new Integer(nextMsgId)); + props.put("actualInFlight", new Integer(actualInFlight)); + props.put("inFlightPubRels", new Integer(inFlightPubRels)); + props.put("quiescing", new Boolean(quiescing)); + props.put("pingoutstanding", new Boolean(pingOutstanding)); + props.put("lastOutboundActivity", new Long(lastOutboundActivity)); + props.put("lastInboundActivity", new Long(lastInboundActivity)); + props.put("outboundQoS2", outboundQoS2); + props.put("outboundQoS1", outboundQoS1); + props.put("inboundQoS2", inboundQoS2); + props.put("tokens", tokenStore); + return props; + } +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/CommsCallback.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/CommsCallback.java new file mode 100644 index 0000000..26369f9 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/CommsCallback.java @@ -0,0 +1,384 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal; + +import java.util.Vector; + +import org.eclipse.paho.client.mqttv3.IMqttActionListener; +import org.eclipse.paho.client.mqttv3.MqttCallback; +import org.eclipse.paho.client.mqttv3.MqttDeliveryToken; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.MqttToken; +import org.eclipse.paho.client.mqttv3.internal.wire.MqttPubAck; +import org.eclipse.paho.client.mqttv3.internal.wire.MqttPubComp; +import org.eclipse.paho.client.mqttv3.internal.wire.MqttPublish; +import org.eclipse.paho.client.mqttv3.logging.Logger; +import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; + +/** + * Bridge between Receiver and the external API. This class gets called by + * Receiver, and then converts the comms-centric MQTT message objects into ones + * understood by the external API. + */ +public class CommsCallback implements Runnable { + private static int INBOUND_QUEUE_SIZE = 10; + private MqttCallback mqttCallback; + private ClientComms clientComms; + private Vector messageQueue; + private Vector completeQueue; + public boolean running = false; + private boolean quiescing = false; + private Object lifecycle = new Object(); + private Thread callbackThread; + private Object workAvailable = new Object(); + private Object spaceAvailable = new Object(); + private ClientState clientState; + + final static String className = CommsCallback.class.getName(); + Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT, className); + + CommsCallback(ClientComms clientComms) { + this.clientComms = clientComms; + this.messageQueue = new Vector(INBOUND_QUEUE_SIZE); + this.completeQueue = new Vector(INBOUND_QUEUE_SIZE); + log.setResourceName(clientComms.getClient().getClientId()); + } + + public void setClientState(ClientState clientState) { + this.clientState = clientState; + } + + /** + * Starts up the Callback thread. + */ + public void start(String threadName) { + synchronized (lifecycle) { + if (running == false) { + // Praparatory work before starting the background thread. + // For safety ensure any old events are cleared. + messageQueue.clear(); + completeQueue.clear(); + + running = true; + quiescing = false; + callbackThread = new Thread(this, threadName); + callbackThread.start(); + } + } + } + + /** + * Stops the callback thread. + * This call will block until stop has completed. + */ + public void stop() { + final String methodName = "stop"; + synchronized (lifecycle) { + if (running) { + // @TRACE 700=stopping + log.fine(className, methodName, "700"); + running = false; + if (!Thread.currentThread().equals(callbackThread)) { + try { + synchronized (workAvailable) { + // @TRACE 701=notify workAvailable and wait for run + // to finish + log.fine(className, methodName, "701"); + workAvailable.notifyAll(); + } + // Wait for the thread to finish. + callbackThread.join(); + } catch (InterruptedException ex) { + } + } + } + callbackThread = null; + // @TRACE 703=stopped + log.fine(className, methodName, "703"); + } + } + + public void setCallback(MqttCallback mqttCallback) { + this.mqttCallback = mqttCallback; + } + + public void run() { + final String methodName = "run"; + while (running) { + try { + // If no work is currently available, then wait until there is some... + try { + synchronized (workAvailable) { + if (running & messageQueue.isEmpty() + && completeQueue.isEmpty()) { + // @TRACE 704=wait for workAvailable + log.fine(className, methodName, "704"); + workAvailable.wait(); + } + } + } catch (InterruptedException e) { + } + + if (running) { + // Check for deliveryComplete callbacks... + if (!completeQueue.isEmpty()) { + // First call the delivery arrived callback if needed + MqttToken token = (MqttToken) completeQueue.elementAt(0); + handleActionComplete(token); + completeQueue.removeElementAt(0); + } + + // Check for messageArrived callbacks... + if (!messageQueue.isEmpty()) { + // Note, there is a window on connect where a publish + // could arrive before we've + // finished the connect logic. + MqttPublish message = (MqttPublish) messageQueue + .elementAt(0); + + handleMessage(message); + messageQueue.removeElementAt(0); + } + } + + if (quiescing) { + clientState.checkQuiesceLock(); + } + + synchronized (spaceAvailable) { + // Notify the spaceAvailable lock, to say that there's now + // some space on the queue... + + // @TRACE 706=notify spaceAvailable + log.fine(className, methodName, "706"); + spaceAvailable.notifyAll(); + } + } catch (Throwable ex) { + // Users code could throw an Error or Exception e.g. in the case + // of class NoClassDefFoundError + // @TRACE 714=callback threw exception + log.fine(className, methodName, "714", null, ex); + running = false; + clientComms.shutdownConnection(null, new MqttException(ex)); + } + } + } + + private void handleActionComplete(MqttToken token) + throws MqttException { + final String methodName = "handleActionComplete"; + synchronized (token) { + // @TRACE 705=callback and notify for key={0} + log.fine(className, methodName, "705", new Object[] { token.internalTok.getKey() }); + + // Unblock any waiters and if pending complete now set completed + token.internalTok.notifyComplete(); + + if (!token.internalTok.isNotified()) { + // If a callback is registered and delivery has finished + // call delivery complete callback. + if ( mqttCallback != null + && token instanceof MqttDeliveryToken + && token.isComplete()) { + mqttCallback.deliveryComplete((MqttDeliveryToken) token); + } + // Now call async action completion callbacks + fireActionEvent(token); + } + + // Set notified so we don't tell the user again about this action. + if ( token instanceof MqttDeliveryToken && token.isComplete()) { + token.internalTok.setNotified(true); + } + + if (token.isComplete()) { + // Finish by doing any post processing such as delete + // from persistent store but only do so if the action + // is complete + clientState.notifyComplete(token); + } + } + } + + /** + * This method is called when the connection to the server is lost. If there + * is no cause then it was a clean disconnect. The connectionLost callback + * will be invoked if registered and run on the thread that requested + * shutdown e.g. receiver or sender thread. If the request was a user + * initiated disconnect then the disconnect token will be notified. + * + * @param cause the reason behind the loss of connection. + */ + public void connectionLost(MqttException cause) { + final String methodName = "connectionLost"; + // If there was a problem and a client callback has been set inform + // the connection lost listener of the problem. + try { + if (mqttCallback != null && cause != null) { + // @TRACE 708=call connectionLost + log.fine(className, methodName, "708", new Object[] { cause }); + mqttCallback.connectionLost(cause); + } + } catch (java.lang.Throwable t) { + // Just log the fact that a throwable has caught connection lost + // is called during shutdown processing so no need to do anything else + // @TRACE 720=exception from connectionLost {0} + log.fine(className, methodName, "720", new Object[] { t }); + } + } + + /** + * An action has completed - if a completion listener has been set on the + * token then invoke it with the outcome of the action. + * + * @param token + */ + public void fireActionEvent(MqttToken token) { + final String methodName = "fireActionEvent"; + + if (token != null) { + IMqttActionListener asyncCB = token.getActionCallback(); + if (asyncCB != null) { + if (token.getException() == null) { + // @TRACE 716=call onSuccess key={0} + log.fine(className, methodName, "716", + new Object[] { token.internalTok.getKey() }); + asyncCB.onSuccess(token); + } else { + // @TRACE 717=call onFailure key {0} + log.fine(className, methodName, "716", + new Object[] { token.internalTok.getKey() }); + asyncCB.onFailure(token, token.getException()); + } + } + } + } + + /** + * This method is called when a message arrives on a topic. Messages are + * only added to the queue for inbound messages if the client is not + * quiescing. + * + * @param sendMessage + * the MQTT SEND message. + */ + public void messageArrived(MqttPublish sendMessage) { + final String methodName = "messageArrived"; + if (mqttCallback != null) { + // If we already have enough messages queued up in memory, wait + // until some more queue space becomes available. This helps + // the client protect itself from getting flooded by messages + // from the server. + synchronized (spaceAvailable) { + if (!quiescing && messageQueue.size() >= INBOUND_QUEUE_SIZE) { + try { + // @TRACE 709=wait for spaceAvailable + log.fine(className, methodName, "709"); + spaceAvailable.wait(); + } catch (InterruptedException ex) { + } + } + } + if (!quiescing) { + messageQueue.addElement(sendMessage); + // Notify the CommsCallback thread that there's work to do... + synchronized (workAvailable) { + // @TRACE 710=new msg avail, notify workAvailable + log.fine(className, methodName, "710"); + workAvailable.notifyAll(); + } + } + } + } + + /** + * Let the call back thread quiesce. Prevent new inbound messages being + * added to the process queue and let existing work quiesce. (until the + * thread is told to shutdown). + */ + public void quiesce() { + final String methodName = "quiesce"; + this.quiescing = true; + synchronized (spaceAvailable) { + // @TRACE 711=quiesce notify spaceAvailable + log.fine(className, methodName, "711"); + // Unblock anything waiting for space... + spaceAvailable.notifyAll(); + } + } + + public boolean isQuiesced() { + if (quiescing && completeQueue.size() == 0 && messageQueue.size() == 0) { + return true; + } + return false; + } + + private void handleMessage(MqttPublish publishMessage) + throws MqttException, Exception { + final String methodName = "handleMessage"; + // If quisecing process any pending messages. + if (mqttCallback != null) { + String destName = publishMessage.getTopicName(); + + // @TRACE 713=call messageArrived key={0} topic={1} + log.fine(className, methodName, "713", new Object[] { + new Integer(publishMessage.getMessageId()), destName }); + mqttCallback.messageArrived(destName, publishMessage.getMessage()); + if (publishMessage.getMessage().getQos() == 1) { + this.clientComms.internalSend(new MqttPubAck(publishMessage), + new MqttToken(clientComms.getClient().getClientId())); + } else if (publishMessage.getMessage().getQos() == 2) { + this.clientComms.deliveryComplete(publishMessage); + MqttPubComp pubComp = new MqttPubComp(publishMessage); + this.clientComms.internalSend(pubComp, new MqttToken(clientComms.getClient().getClientId())); + } + } + } + + public void asyncOperationComplete(MqttToken token) { + final String methodName = "asyncOperationComplete"; + + if (running) { + // invoke callbacks on callback thread + completeQueue.addElement(token); + synchronized (workAvailable) { + // @TRACE 715=new workAvailable. key={0} + log.fine(className, methodName, "715", new Object[] { token.internalTok.getKey() }); + workAvailable.notifyAll(); + } + } else { + // invoke async callback on invokers thread + try { + handleActionComplete(token); + } catch (Throwable ex) { + // Users code could throw an Error or Exception e.g. in the case + // of class NoClassDefFoundError + // @TRACE 719=callback threw ex: + log.fine(className, methodName, "719", null, ex); + + // Shutdown likely already in progress but no harm to confirm + System.err.println("problem in asyncopcomplete "+ex); + ex.printStackTrace(); + clientComms.shutdownConnection(null, new MqttException(ex)); + } + + } + } + + /** + * Returns the thread used by this callback. + */ + protected Thread getThread() { + return callbackThread; + } +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/CommsReceiver.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/CommsReceiver.java new file mode 100644 index 0000000..84c49cd --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/CommsReceiver.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal; + +import java.io.IOException; +import java.io.InputStream; + +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.MqttToken; +import org.eclipse.paho.client.mqttv3.internal.wire.MqttAck; +import org.eclipse.paho.client.mqttv3.internal.wire.MqttInputStream; +import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage; +import org.eclipse.paho.client.mqttv3.logging.Logger; +import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; + +/** + * Receives MQTT packets from the server. + */ +public class CommsReceiver implements Runnable { + private boolean running = false; + private Object lifecycle = new Object(); + private ClientState clientState = null; + private ClientComms clientComms = null; + private MqttInputStream in; + private CommsTokenStore tokenStore = null; + private Thread recThread = null; + + private final static String className = CommsReceiver.class.getName(); + private Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className); + + public CommsReceiver(ClientComms clientComms, ClientState clientState,CommsTokenStore tokenStore, InputStream in) { + this.in = new MqttInputStream(in); + this.clientComms = clientComms; + this.clientState = clientState; + this.tokenStore = tokenStore; + log.setResourceName(clientComms.getClient().getClientId()); + } + + /** + * Starts up the Receiver's thread. + */ + public void start(String threadName) { + final String methodName = "start"; + //@TRACE 855=starting + log.fine(className,methodName, "855"); + synchronized (lifecycle) { + if (running == false) { + running = true; + recThread = new Thread(this, threadName); + recThread.start(); + } + } + } + + /** + * Stops the Receiver's thread. This call will block. + */ + public void stop() { + final String methodName = "stop"; + synchronized (lifecycle) { + //@TRACE 850=stopping + log.fine(className,methodName, "850"); + if (running) { + running = false; + if (!Thread.currentThread().equals(recThread)) { + try { + // Wait for the thread to finish. + recThread.join(); + } + catch (InterruptedException ex) { + } + } + } + } + recThread = null; + //@TRACE 851=stopped + log.fine(className,methodName,"851"); + } + + /** + * Run loop to receive messages from the server. + */ + public void run() { + final String methodName = "run"; + MqttToken token = null; + + while (running && (in != null)) { + try { + //@TRACE 852=network read message + log.fine(className,methodName,"852"); + MqttWireMessage message = in.readMqttWireMessage(); + + if (message instanceof MqttAck) { + token = tokenStore.getToken(message); + if (token!=null) { + synchronized (token) { + // Ensure the notify processing is done under a lock on the token + // This ensures that the send processing can complete before the + // receive processing starts! ( request and ack and ack processing + // can occur before request processing is complete if not! + clientState.notifyReceivedAck((MqttAck)message); + } + } else { + // It its an ack and there is no token then something is not right. + // An ack should always have a token assoicated with it. + throw new MqttException(MqttException.REASON_CODE_UNEXPECTED_ERROR); + } + } else { + // A new message has arrived + clientState.notifyReceivedMsg(message); + } + } + catch (MqttException ex) { + //@TRACE 856=Stopping, MQttException + log.fine(className,methodName,"856",null,ex); + running = false; + // Token maybe null but that is handled in shutdown + clientComms.shutdownConnection(token, ex); + } + catch (IOException ioe) { + //@TRACE 853=Stopping due to IOException + log.fine(className,methodName,"853"); + + running = false; + // An EOFException could be raised if the broker processes the + // DISCONNECT and ends the socket before we complete. As such, + // only shutdown the connection if we're not already shutting down. + if (!clientComms.isDisconnecting()) { + clientComms.shutdownConnection(token, new MqttException(MqttException.REASON_CODE_CONNECTION_LOST, ioe)); + } // else { + } + } + + //@TRACE 854=< + log.fine(className,methodName,"854"); + } + + public boolean isRunning() { + return running; + } +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/CommsSender.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/CommsSender.java new file mode 100644 index 0000000..44781fc --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/CommsSender.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal; + +import java.io.OutputStream; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.MqttToken; +import org.eclipse.paho.client.mqttv3.internal.wire.MqttAck; +import org.eclipse.paho.client.mqttv3.internal.wire.MqttOutputStream; +import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage; +import org.eclipse.paho.client.mqttv3.logging.Logger; +import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; + + +public class CommsSender implements Runnable { + /** + * Sends MQTT packets to the server on its own thread + */ + private boolean running = false; + private Object lifecycle = new Object(); + private ClientState clientState = null; + private MqttOutputStream out; + private ClientComms clientComms = null; + private CommsTokenStore tokenStore = null; + private Thread sendThread = null; + + private final static String className = CommsSender.class.getName(); + private Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT, className); + + public CommsSender(ClientComms clientComms, ClientState clientState, CommsTokenStore tokenStore, OutputStream out) { + this.out = new MqttOutputStream(out); + this.clientComms = clientComms; + this.clientState = clientState; + this.tokenStore = tokenStore; + log.setResourceName(clientComms.getClient().getClientId()); + } + + /** + * Starts up the Sender thread. + */ + public void start(String threadName) { + synchronized (lifecycle) { + if (running == false) { + running = true; + sendThread = new Thread(this, threadName); + sendThread.start(); + } + } + } + + /** + * Stops the Sender's thread. This call will block. + */ + public void stop() { + final String methodName = "stop"; + + synchronized (lifecycle) { + //@TRACE 800=stopping sender + log.fine(className,methodName,"800"); + if (running) { + running = false; + if (!Thread.currentThread().equals(sendThread)) { + try { + // first notify get routine to finish + clientState.notifyQueueLock(); + // Wait for the thread to finish. + sendThread.join(); + } + catch (InterruptedException ex) { + } + } + } + sendThread=null; + //@TRACE 801=stopped + log.fine(className,methodName,"801"); + } + } + + public void run() { + final String methodName = "run"; + MqttWireMessage message = null; + while (running && (out != null)) { + try { + message = clientState.get(); + if (message != null) { + //@TRACE 802=network send key={0} msg={1} + log.fine(className,methodName,"802", new Object[] {message.getKey(),message}); + + if (message instanceof MqttAck) { + out.write(message); + out.flush(); + } else { + MqttToken token = tokenStore.getToken(message); + // While quiescing the tokenstore can be cleared so need + // to check for null for the case where clear occurs + // while trying to send a message. + if (token != null) { + synchronized (token) { + out.write(message); + out.flush(); + clientState.notifySent(message); + } + } + } + } else { // null message + //@TRACE 803=get message returned null, stopping} + log.fine(className,methodName,"803"); + + running = false; + } + } catch (MqttException me) { + handleRunException(message, me); + } catch (Exception ex) { + handleRunException(message, ex); + } + } // end while + + //@TRACE 805=< + log.fine(className, methodName,"805"); + + } + + private void handleRunException(MqttWireMessage message, Exception ex) { + final String methodName = "handleRunException"; + //@TRACE 804=exception + log.fine(className,methodName,"804",null, ex); + MqttException mex; + if ( !(ex instanceof MqttException)) { + mex = new MqttException(MqttException.REASON_CODE_CONNECTION_LOST, ex); + } else { + mex = (MqttException)ex; + } + + running = false; + clientComms.shutdownConnection(null, mex); + } +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/CommsTokenStore.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/CommsTokenStore.java new file mode 100644 index 0000000..e7a6f99 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/CommsTokenStore.java @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal; + +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; + +import org.eclipse.paho.client.mqttv3.MqttDeliveryToken; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.MqttToken; +import org.eclipse.paho.client.mqttv3.internal.wire.MqttPublish; +import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage; +import org.eclipse.paho.client.mqttv3.logging.Logger; +import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; + + +/** + * Provides a "token" based system for storing and tracking actions across + * multiple threads. + * When a message is sent, a token is associated with the message + * and saved using the {@link #saveToken(MqttToken, MqttWireMessage)} method. Anyone interested + * in tacking the state can call one of the wait methods on the token or using + * the asynchronous listener callback method on the operation. + * The {@link CommsReceiver} class, on another thread, reads responses back from + * the network. It uses the response to find the relevant token, which it can then + * notify. + * + * Note: + * Ping, connect and disconnect do not have a unique message id as + * only one outstanding request of each type is allowed to be outstanding + */ +public class CommsTokenStore { + /** Maps message-specific data (usually message IDs) to tokens */ + private Hashtable tokens; + private String logContext; + private MqttException closedResponse = null; + + final static String className = CommsTokenStore.class.getName(); + Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT, className); + + public CommsTokenStore(String logContext) { + final String methodName = ""; + + log.setResourceName(logContext); + this.tokens = new Hashtable(); + this.logContext = logContext; + //@TRACE 308=<> + log.fine(className,methodName,"308");//,new Object[]{message}); + + } + + /** + * Based on the message type that has just been received return the associated + * token from the token store or null if one does not exist. + * @param message whose token is to be returned + * @return token for the requested message + */ + public MqttToken getToken(MqttWireMessage message) { + String key = message.getKey(); + return (MqttToken)tokens.get(key); + } + + public MqttToken getToken(String key) { + return (MqttToken)tokens.get(key); + } + + + public MqttToken removeToken(MqttWireMessage message) { + if (message != null) { + return removeToken(message.getKey()); + } + return null; + } + + public MqttToken removeToken(String key) { + final String methodName = "removeToken"; + //@TRACE 306=key={0} + log.fine(className,methodName,"306",new Object[]{key}); + + if (key != null) { + synchronized(tokens) { + MqttToken tok = (MqttToken)tokens.get(key); + if (tok != null) { + synchronized(tok) { + + return (MqttToken) tokens.remove(key); + } + } + } + } + return null; + } + + /** + * Restores a token after a client restart. This method could be called + * for a SEND of CONFIRM, but either way, the original SEND is what's + * needed to re-build the token. + */ + protected MqttDeliveryToken restoreToken(MqttPublish message) { + final String methodName = "restoreToken"; + MqttDeliveryToken token; + synchronized(tokens) { + String key = new Integer(message.getMessageId()).toString(); + if (this.tokens.containsKey(key)) { + token = (MqttDeliveryToken)this.tokens.get(key); + //@TRACE 302=existing key={0} message={1} token={2} + log.fine(className,methodName, "302",new Object[]{key, message,token}); + } else { + token = new MqttDeliveryToken(logContext); + token.internalTok.setKey(key); + this.tokens.put(key, token); + //@TRACE 303=creating new token key={0} message={1} token={2} + log.fine(className,methodName,"303",new Object[]{key, message, token}); + } + } + return token; + } + + // For outbound messages store the token in the token store + // For pubrel use the existing publish token + protected void saveToken(MqttToken token, MqttWireMessage message) throws MqttException { + final String methodName = "saveToken"; + + synchronized(tokens) { + if (closedResponse == null) { + String key = message.getKey(); + //@TRACE 300=key={0} message={1} + log.fine(className,methodName,"300",new Object[]{key, message}); + + saveToken(token,key); + } else { + throw closedResponse; + } + } + } + + protected void saveToken(MqttToken token, String key) { + final String methodName = "saveToken"; + + synchronized(tokens) { + //@TRACE 307=key={0} token={1} + log.fine(className,methodName,"307",new Object[]{key,token.toString()}); + token.internalTok.setKey(key); + this.tokens.put(key, token); + } + } + + protected void quiesce(MqttException quiesceResponse) { + final String methodName = "quiesce"; + + synchronized(tokens) { + //@TRACE 309=resp={0} + log.fine(className,methodName,"309",new Object[]{quiesceResponse}); + + closedResponse = quiesceResponse; + } + } + + public void open() { + final String methodName = "open"; + + synchronized(tokens) { + //@TRACE 310=> + log.fine(className,methodName,"310"); + + closedResponse = null; + } + } + + public MqttDeliveryToken[] getOutstandingDelTokens() { + final String methodName = "getOutstandingDelTokens"; + + synchronized(tokens) { + //@TRACE 311=> + log.fine(className,methodName,"311"); + + Vector list = new Vector(); + Enumeration enumeration = tokens.elements(); + MqttToken token; + while(enumeration.hasMoreElements()) { + token = (MqttToken)enumeration.nextElement(); + if (token != null + && token instanceof MqttDeliveryToken + && !token.internalTok.isNotified()) { + + list.addElement(token); + } + } + + MqttDeliveryToken[] result = new MqttDeliveryToken[list.size()]; + return (MqttDeliveryToken[]) list.toArray(result); + } + } + + public Vector getOutstandingTokens() { + final String methodName = "getOutstandingTokens"; + + synchronized(tokens) { + //@TRACE 312=> + log.fine(className,methodName,"312"); + + Vector list = new Vector(); + Enumeration enumeration = tokens.elements(); + MqttToken token; + while(enumeration.hasMoreElements()) { + token = (MqttToken)enumeration.nextElement(); + if (token != null) { + list.addElement(token); + } + } + return list; + } + } + + /** + * Empties the token store without notifying any of the tokens. + */ + public void clear() { + final String methodName = "clear"; + //@TRACE 305=> {0} tokens + log.fine(className, methodName, "305", new Object[] {new Integer(tokens.size())}); + synchronized(tokens) { + tokens.clear(); + } + } + + public int count() { + synchronized(tokens) { + return tokens.size(); + } + } + public String toString() { + String lineSep = System.getProperty("line.separator","\n"); + StringBuffer toks = new StringBuffer(); + synchronized(tokens) { + Enumeration enumeration = tokens.elements(); + MqttToken token; + while(enumeration.hasMoreElements()) { + token = (MqttToken)enumeration.nextElement(); + toks.append("{"+token.internalTok+"}"+lineSep); + } + return toks.toString(); + } + } +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/DestinationProvider.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/DestinationProvider.java new file mode 100644 index 0000000..4001b5e --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/DestinationProvider.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal; + +import org.eclipse.paho.client.mqttv3.MqttTopic; + +/** + * This interface exists to act as a common type for + * MqttClient and MqttMIDPClient so they can be passed to + * ClientComms without either client class need to know + * about the other. + * Specifically, this allows the MIDP client to work + * without the non-MIDP MqttClient/MqttConnectOptions + * classes being present. + */ +public interface DestinationProvider { + public MqttTopic getTopic(String topic); +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/ExceptionHelper.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/ExceptionHelper.java new file mode 100644 index 0000000..fe5b037 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/ExceptionHelper.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal; + +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.MqttSecurityException; + +/** + * Utility class to help create exceptions of the correct type. + */ +public class ExceptionHelper { + public static MqttException createMqttException(int reasonCode) { + if ((reasonCode == MqttException.REASON_CODE_FAILED_AUTHENTICATION) || + (reasonCode == MqttException.REASON_CODE_NOT_AUTHORIZED)) { + return new MqttSecurityException(reasonCode); + } + + return new MqttException(reasonCode); + } + + public static MqttException createMqttException(Throwable cause) { + if (cause.getClass().getName().equals("java.security.GeneralSecurityException")) { + return new MqttSecurityException(cause); + } + return new MqttException(cause); + } + + /** + * Returns whether or not the specified class is available to the current + * class loader. This is used to protect the code against using Java SE + * APIs on Java ME. + */ + public static boolean isClassAvailable(String className) { + boolean result = false; + try { + Class.forName(className); + result = true; + } + catch (ClassNotFoundException ex) { + } + return result; + } +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/FileLock.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/FileLock.java new file mode 100644 index 0000000..576d860 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/FileLock.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal; +/** + * FileLock - used to obtain a lock that can be used to prevent other MQTT clients + * using the same persistent store. If the lock is already held then an exception + * is thrown. + * + * Some Java runtimes such as JME MIDP do not support file locking or even + * the Java classes that support locking. The class is coded to both compile + * and work on all Java runtimes. In Java runtimes that do not support + * locking it will look as though a lock has been obtained but in reality + * no lock has been obtained. + */ +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.lang.reflect.Method; + +public class FileLock { + private File lockFile; + private RandomAccessFile file; + private Object fileLock; + + /** + * Creates an NIO FileLock on the specified file if on a suitable Java runtime. + * @param clientDir the a File of the directory to contain the lock file. + * @param lockFilename name of the the file to lock + * @throws Exception if the lock could not be obtained for any reason + */ + public FileLock(File clientDir, String lockFilename) throws Exception { + // Create a file to obtain a lock on. + lockFile = new File(clientDir,lockFilename); + if (ExceptionHelper.isClassAvailable("java.nio.channels.FileLock")) { + try { + this.file = new RandomAccessFile(lockFile,"rw"); + Method m = file.getClass().getMethod("getChannel",new Class[]{}); + Object channel = m.invoke(file,new Object[]{}); + m = channel.getClass().getMethod("tryLock",new Class[]{}); + this.fileLock = m.invoke(channel, new Object[]{}); + } catch(NoSuchMethodException nsme) { + this.fileLock = null; + } catch(IllegalArgumentException iae) { + this.fileLock = null; + } catch(IllegalAccessException iae) { + this.fileLock = null; + } + if (fileLock == null) { + // Lock not obtained + release(); + throw new Exception("Problem obtaining file lock"); + } + } + } + + /** + * Releases the lock. + */ + public void release() { + try { + if (fileLock != null) { + Method m = fileLock.getClass().getMethod("release",new Class[]{}); + m.invoke(fileLock, new Object[]{}); + fileLock = null; + } + } catch (Exception e) { + // Ignore exceptions + } + if (file != null) { + try { + file.close(); + } catch (IOException e) { + } + file = null; + } + + if (lockFile != null && lockFile.exists()) { + lockFile.delete(); + } + lockFile = null; + } + +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/LocalNetworkModule.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/LocalNetworkModule.java new file mode 100644 index 0000000..cb4c7cb --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/LocalNetworkModule.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Method; + +import org.eclipse.paho.client.mqttv3.MqttException; + + +/** + * Special comms class that allows an MQTT client to use a non TCP / optimised + * mechanism to talk to an MQTT server when running in the same JRE instance as the + * MQTT server. + * + * This class checks for the existence of the optimised comms adatper class i.e. the one + * that provides the optimised communication mechanism. If not available the request + * to connect using the optimised mechanism is rejected. + * + * The only known server that implements this is the microbroker:- an MQTT server that + * ships with a number of IBM products. + */ +public class LocalNetworkModule implements NetworkModule { + private Class LocalListener; + private String brokerName; + private Object localAdapter; + + public LocalNetworkModule(String brokerName) { + this.brokerName = brokerName; + } + + public void start() throws IOException, MqttException{ + if (!ExceptionHelper.isClassAvailable("com.ibm.mqttdirect.modules.local.bindings.LocalListener")) { + throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_SERVER_CONNECT_ERROR); + } + try { + LocalListener = Class.forName("com.ibm.mqttdirect.modules.local.bindings.LocalListener"); + Method connect_m = LocalListener.getMethod("connect", new Class[]{ java.lang.String.class }); + localAdapter = connect_m.invoke(null,new Object[]{ brokerName }); + } catch(Exception e) { + } + if(localAdapter == null) { + throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_SERVER_CONNECT_ERROR); + } + } + + public InputStream getInputStream() throws IOException { + InputStream stream = null; + try { + Method m = LocalListener.getMethod("getClientInputStream",new Class[]{}); + stream = (InputStream)m.invoke(this.localAdapter,new Object[]{}); + } catch(Exception e) { + } + return stream; + } + + public OutputStream getOutputStream() throws IOException { + OutputStream stream = null; + try { + Method m = LocalListener.getMethod("getClientOutputStream",new Class[]{}); + stream = (OutputStream)m.invoke(this.localAdapter,new Object[]{}); + } catch(Exception e) { + } + return stream; + } + + public void stop() throws IOException { + if (localAdapter != null) { + try { + Method m = LocalListener.getMethod("close",new Class[]{}); + m.invoke(this.localAdapter,new Object[]{}); + } catch(Exception e) { + } + } + } +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/MessageCatalog.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/MessageCatalog.java new file mode 100644 index 0000000..295b15c --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/MessageCatalog.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal; + +/** + * Catalog of human readable error messages. + */ +public abstract class MessageCatalog { + private static MessageCatalog INSTANCE = null; + + public static final String getMessage(int id) { + if (INSTANCE == null) { + if (ExceptionHelper.isClassAvailable("java.util.ResourceBundle")) { + try { + // Hide this class reference behind reflection so that the class does not need to + // be present when compiled on midp + INSTANCE = (MessageCatalog)Class.forName("org.eclipse.paho.client.mqttv3.internal.ResourceBundleCatalog").newInstance(); + } catch (Exception e) { + return ""; + } + } else if (ExceptionHelper.isClassAvailable("org.eclipse.paho.client.mqttv3.internal.MIDPCatalog")){ + try { + INSTANCE = (MessageCatalog)Class.forName("org.eclipse.paho.client.mqttv3.internal.MIDPCatalog").newInstance(); + } catch (Exception e) { + return ""; + } + } + } + return INSTANCE.getLocalizedMessage(id); + } + + protected abstract String getLocalizedMessage(int id); +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/MqttPersistentData.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/MqttPersistentData.java new file mode 100644 index 0000000..c56cd5b --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/MqttPersistentData.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal; + +import org.eclipse.paho.client.mqttv3.MqttPersistable; + +public class MqttPersistentData implements MqttPersistable { + // Message key + private String key = null; + + // Message header + private byte[] header = null; + private int hOffset = 0; + private int hLength = 0; + + // Message payload + private byte[] payload = null; + private int pOffset = 0; + private int pLength = 0; + + /** + * Construct a data object to pass across the MQTT client persistence + * interface.
              + * When this Object is passed to the persistence implementation the key is + * used by the client to identify the persisted data to which further + * update or deletion requests are targeted.
              + * When this Object is created for returning to the client when it is + * recovering its state from persistence the key is not required to be set. + * The client can determine the key from the data. + * @param key The key which identifies this data + * @param header The message header + * @param hOffset The start offset of the header bytes in header. + * @param hLength The length of the header in the header bytes array. + * @param payload The message payload + * @param pOffset The start offset of the payload bytes in payload. + * @param pLength The length of the payload in the payload bytes array + * when persisting the message. + */ + public MqttPersistentData( String key, + byte[] header, + int hOffset, + int hLength, + byte[] payload, + int pOffset, + int pLength) { + this.key = key; + this.header = header; + this.hOffset = hOffset; + this.hLength = hLength; + this.payload = payload; + this.pOffset = pOffset; + this.pLength = pLength; + } + + public String getKey() { + return key; + } + + public byte[] getHeaderBytes() { + return header; + } + + public int getHeaderLength() { + return hLength; + } + + public int getHeaderOffset() { + return hOffset; + } + + public byte[] getPayloadBytes() { + return payload; + } + + public int getPayloadLength() { + if ( payload == null ) { + return 0; + } + return pLength; + } + + public int getPayloadOffset() { + return pOffset; + } +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/NetworkModule.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/NetworkModule.java new file mode 100644 index 0000000..b6db39a --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/NetworkModule.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.eclipse.paho.client.mqttv3.MqttException; + + +public interface NetworkModule { + public void start() throws IOException, MqttException; + + public InputStream getInputStream() throws IOException; + + public OutputStream getOutputStream() throws IOException; + + public void stop() throws IOException; +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/ResourceBundleCatalog.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/ResourceBundleCatalog.java new file mode 100644 index 0000000..c93e22b --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/ResourceBundleCatalog.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal; + +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +public class ResourceBundleCatalog extends MessageCatalog { + + private ResourceBundle bundle; + + public ResourceBundleCatalog() throws MissingResourceException { + bundle = ResourceBundle.getBundle("org.eclipse.paho.client.mqttv3.internal.nls.messages"); + } + + protected String getLocalizedMessage(int id) { + try { + return bundle.getString(Integer.toString(id)); + } catch(MissingResourceException mre) { + return "MqttException"; + } + } +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/SSLNetworkModule.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/SSLNetworkModule.java new file mode 100644 index 0000000..4a653a4 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/SSLNetworkModule.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal; + +import java.io.IOException; + +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; + +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.logging.Logger; +import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; + +/** + * A network module for connecting over SSL. + */ +public class SSLNetworkModule extends TCPNetworkModule { + private String[] enabledCiphers; + private int handshakeTimeoutSecs; + + final static String className = SSLNetworkModule.class.getName(); + Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className); + + /** + * Constructs a new SSLNetworkModule using the specified host and + * port. The supplied SSLSocketFactory is used to supply the network + * socket. + */ + public SSLNetworkModule(SSLSocketFactory factory, String host, int port, String resourceContext) { + super(factory, host, port, resourceContext); + log.setResourceName(resourceContext); + } + + /** + * Returns the enabled cipher suites. + */ + public String[] getEnabledCiphers() { + return enabledCiphers; + } + + /** + * Sets the enabled cipher suites on the underlying network socket. + */ + public void setEnabledCiphers(String[] enabledCiphers) { + final String methodName = "setEnabledCiphers"; + this.enabledCiphers = enabledCiphers; + if ((socket != null) && (enabledCiphers != null)) { + if (log.isLoggable(Logger.FINE)) { + String ciphers = ""; + for (int i=0;i0) { + ciphers+=","; + } + ciphers+=enabledCiphers[i]; + } + //@TRACE 260=setEnabledCiphers ciphers={0} + log.fine(className,methodName,"260",new Object[]{ciphers}); + } + ((SSLSocket) socket).setEnabledCipherSuites(enabledCiphers); + } + } + + public void setSSLhandshakeTimeout(int timeout) { + this.handshakeTimeoutSecs = timeout; + } + + public void start() throws IOException, MqttException { + super.start(); + setEnabledCiphers(enabledCiphers); + int soTimeout = socket.getSoTimeout(); + if ( soTimeout == 0 ) { + // RTC 765: Set a timeout to avoid the SSL handshake being blocked indefinitely + socket.setSoTimeout(this.handshakeTimeoutSecs*1000); + } + ((SSLSocket)socket).startHandshake(); + // reset timeout to default value + socket.setSoTimeout(soTimeout); + } +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/TCPNetworkModule.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/TCPNetworkModule.java new file mode 100644 index 0000000..49547c9 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/TCPNetworkModule.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.ConnectException; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; + +import javax.net.SocketFactory; + +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.logging.Logger; +import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; + +/** + * A network module for connecting over TCP. + */ +public class TCPNetworkModule implements NetworkModule { + protected Socket socket; + private SocketFactory factory; + private String host; + private int port; + private int conTimeout; + + final static String className = TCPNetworkModule.class.getName(); + Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className); + + /** + * Constructs a new TCPNetworkModule using the specified host and + * port. The supplied SocketFactory is used to supply the network + * socket. + */ + public TCPNetworkModule(SocketFactory factory, String host, int port, String resourceContext) { + log.setResourceName(resourceContext); + this.factory = factory; + this.host = host; + this.port = port; + + } + + /** + * Starts the module, by creating a TCP socket to the server. + */ + public void start() throws IOException, MqttException { + final String methodName = "start"; + try { +// InetAddress localAddr = InetAddress.getLocalHost(); +// socket = factory.createSocket(host, port, localAddr, 0); + // @TRACE 252=connect to host {0} port {1} timeout {2} + log.fine(className,methodName, "252", new Object[] {host, new Integer(port), new Long(conTimeout*1000)}); + SocketAddress sockaddr = new InetSocketAddress(host, port); + socket = factory.createSocket(); + socket.connect(sockaddr, conTimeout*1000); + + // SetTcpNoDelay was originally set ot true disabling Nagle's algorithm. + // This should not be required. +// socket.setTcpNoDelay(true); // TCP_NODELAY on, which means we do not use Nagle's algorithm + } + catch (ConnectException ex) { + //@TRACE 250=Failed to create TCP socket + log.fine(className,methodName,"250",null,ex); + throw new MqttException(MqttException.REASON_CODE_SERVER_CONNECT_ERROR, ex); + } + } + + public InputStream getInputStream() throws IOException { + return socket.getInputStream(); + } + + public OutputStream getOutputStream() throws IOException { + return socket.getOutputStream(); + } + + /** + * Stops the module, by closing the TCP socket. + */ + public void stop() throws IOException { + if (socket != null) { + socket.close(); + } + } + + /** + * Set the maximum time to wait for a socket to be established + * @param timeout + */ + public void setConnectTimeout(int timeout) { + this.conTimeout = timeout; + } +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/Token.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/Token.java new file mode 100644 index 0000000..b966510 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/Token.java @@ -0,0 +1,348 @@ +package org.eclipse.paho.client.mqttv3.internal; + +import org.eclipse.paho.client.mqttv3.IMqttActionListener; +import org.eclipse.paho.client.mqttv3.IMqttAsyncClient; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.MqttMessage; +import org.eclipse.paho.client.mqttv3.internal.wire.MqttAck; +import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage; +import org.eclipse.paho.client.mqttv3.logging.Logger; +import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; + +public class Token { + volatile private boolean completed = false; + private boolean pendingComplete = false; + private boolean sent = false; + + private Object responseLock = new Object(); + private Object sentLock = new Object(); + + protected MqttMessage message = null; + private MqttWireMessage response = null; + private MqttException exception = null; + private String[] topics = null; + + private String key; + + private IMqttAsyncClient client = null; + private IMqttActionListener callback = null; + + private Object userContext = null; + + public int messageID = 0; + public boolean notified = false; + + public Token(String logContext) { + log.setResourceName(logContext); + } + + public int getMessageID() { + return messageID; + } + + public void setMessageID(int messageID) { + this.messageID = messageID; + } + + final static String className = Token.class.getName(); + Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className); + + public boolean checkResult() throws MqttException { + if ( getException() != null) { + throw getException(); + } + return true; + } + + public MqttException getException() { + return exception; + } + + public boolean isComplete() { + return completed; + } + + protected boolean isCompletePending() { + return pendingComplete; + } + + protected boolean isInUse() { + return (getClient() != null && !isComplete()); + } + + public void setActionCallback(IMqttActionListener listener) { + this.callback = listener; + + } + public IMqttActionListener getActionCallback() { + return callback; + } + + public void waitForCompletion() throws MqttException { + waitForCompletion(-1); + } + + public void waitForCompletion(long timeout) throws MqttException { + final String methodName = "waitForCompletion"; + //@TRACE 407=key={0} wait max={1} token={2} + log.fine(className,methodName, "407",new Object[]{getKey(), new Long(timeout), this}); + + MqttWireMessage resp = waitForResponse(timeout); + if (resp == null && !completed) { + //@TRACE 406=key={0} timed out token={1} + log.fine(className,methodName, "406",new Object[]{getKey(), this}); + throw new MqttException(MqttException.REASON_CODE_CLIENT_TIMEOUT); + } + checkResult(); + } + + /** + * Waits for the message delivery to complete, but doesn't throw an exception + * in the case of a NACK. It does still throw an exception if something else + * goes wrong (e.g. an IOException). This is used for packets like CONNECT, + * which have useful information in the ACK that needs to be accessed. + */ + protected MqttWireMessage waitForResponse() throws MqttException { + return waitForResponse(-1); + } + + protected MqttWireMessage waitForResponse(long timeout) throws MqttException { + final String methodName = "waitForResponse"; + synchronized (responseLock) { + //@TRACE 400=>key={0} timeout={1} sent={2} completed={3} hasException={4} response={5} token={6} + log.fine(className, methodName, "400",new Object[]{getKey(), new Long(timeout),new Boolean(sent),new Boolean(completed),(exception==null)?"false":"true",response,this},exception); + + if (!this.completed) { + if (this.exception == null) { + try { + //@TRACE 408=key={0} wait max={1} + log.fine(className,methodName,"408",new Object[] {getKey(),new Long(timeout)}); + + if (timeout == -1) { + responseLock.wait(); + } else { + responseLock.wait(timeout); + } + } catch (InterruptedException e) { + exception = new MqttException(e); + } + } + if (!this.completed) { + if (this.exception != null) { + //@TRACE 401=failed with exception + log.fine(className,methodName,"401",null,exception); + throw exception; + } + } + } + } + //@TRACE 402=key={0} response={1} + log.fine(className,methodName, "402",new Object[]{getKey(), this.response}); + return this.response; + } + + /** + * Mark the token as complete and ready for users to be notified. + * @param msg response message. Optional - there are no response messages for some flows + * @param ex if there was a problem store the exception in the token. + */ + protected void markComplete(MqttWireMessage msg, MqttException ex) { + final String methodName = "markComplete"; + //@TRACE 404=>key={0} response={1} excep={2} + log.fine(className,methodName,"404",new Object[]{getKey(),msg,ex}); + + synchronized(responseLock) { + // ACK means that everything was OK, so mark the message for garbage collection. + if (msg instanceof MqttAck) { + this.message = null; + } + this.pendingComplete = true; + this.response = msg; + this.exception = ex; + } + } + /** + * Notifies this token that a response message (an ACK or NACK) has been + * received. + */ + protected void notifyComplete() { + final String methodName = "notifyComplete"; + //@TRACE 411=>key={0} response={1} excep={2} + log.fine(className,methodName,"404",new Object[]{getKey(),this.response, this.exception}); + + synchronized (responseLock) { + // If pending complete is set then normally the token can be marked + // as complete and users notified. An abnormal error may have + // caused the client to shutdown beween pending complete being set + // and notifying the user. In this case - the action must be failed. + if (exception == null && pendingComplete) { + completed = true; + pendingComplete = false; + } else { + pendingComplete = false; + } + + responseLock.notifyAll(); + } + synchronized (sentLock) { + sent=true; + sentLock.notifyAll(); + } + } + +// /** +// * Notifies this token that an exception has occurred. This is only +// * used for things like IOException, and not for MQTT NACKs. +// */ +// protected void notifyException() { +// final String methodName = "notifyException"; +// //@TRACE 405=token={0} excep={1} +// log.fine(className,methodName, "405",new Object[]{this,this.exception}); +// synchronized (responseLock) { +// responseLock.notifyAll(); +// } +// synchronized (sentLock) { +// sentLock.notifyAll(); +// } +// } + + public void waitUntilSent() throws MqttException { + final String methodName = "waitUntilSent"; + synchronized (sentLock) { + synchronized (responseLock) { + if (this.exception != null) { + throw this.exception; + } + } + if (!sent) { + try { + //@TRACE 409=wait key={0} + log.fine(className,methodName, "409",new Object[]{getKey()}); + + sentLock.wait(); + } catch (InterruptedException e) { + } + } + + if (!sent) { + if (this.exception == null) { + throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_UNEXPECTED_ERROR); + } + throw this.exception; + } + } + } + + /** + * Notifies this token that the associated message has been sent + * (i.e. written to the TCP/IP socket). + */ + protected void notifySent() { + final String methodName = "notifySent"; + //@TRACE 403=> key={0} + log.fine(className, methodName, "403",new Object[]{getKey()}); + synchronized (responseLock) { + this.response = null; + this.completed = false; + } + synchronized (sentLock) { + sent = true; + sentLock.notifyAll(); + } + } + + public IMqttAsyncClient getClient() { + return client; + } + + protected void setClient(IMqttAsyncClient client) { + this.client = client; + } + + public void reset() throws MqttException { + final String methodName = "reset"; + if (isInUse() ) { + // Token is already in use - cannot reset + throw new MqttException(MqttException.REASON_CODE_TOKEN_INUSE); + } + //@TRACE 410=> key={0} + log.fine(className, methodName, "410",new Object[]{getKey()}); + + client = null; + completed = false; + response = null; + sent = false; + exception = null; + userContext = null; + } + + public MqttMessage getMessage() { + return message; + } + + public MqttWireMessage getWireMessage() { + return response; + } + + + public void setMessage(MqttMessage msg) { + this.message = msg; + } + + public String[] getTopics() { + return topics; + } + + public void setTopics(String[] topics) { + this.topics = topics; + } + + public Object getUserContext() { + return userContext; + } + + public void setUserContext(Object userContext) { + this.userContext = userContext; + } + + public void setKey(String key) { + this.key = key; + } + + public String getKey() { + return key; + } + + public void setException(MqttException exception) { + synchronized(responseLock) { + this.exception = exception; + } + } + + public boolean isNotified() { + return notified; + } + + public void setNotified(boolean notified) { + this.notified = notified; + } + + public String toString() { + StringBuffer tok = new StringBuffer(); + tok.append("key="+getKey()); + tok.append(" ,topics="); + if (getTopics() != null) { + for (int i=0; i + * The SSLSocketFacotryFactory can be reconfigured at any time. A + * reconfiguration does not affect existing socket factories. + *

              + * All properties share the same key space; i.e. the configuration ID is not + * part of the property keys. + *

              + * The methods should be called in the following order: + *

                + *
              1. isSupportedOnJVM(): to check whether this class is supported on + * the runtime platform. Not all runtimes support SSL/TLS.
              2. + *
              3. SSLSocketFactoryFactory(): the constructor. Clients + * (in the same JVM) may share an SSLSocketFactoryFactory, or have one each.
              4. + *
              5. initialize(properties, configID): to initialize this object with + * the required SSL properties for a configuration. This may be called multiple + * times, once for each required configuration.It may be called again to change the required SSL + * properties for a particular configuration
              6. + *
              7. getEnabledCipherSuites(configID): to later set the enabled + * cipher suites on the socket [see below].
              8. + *
              + *
                + *
              • For an MQTT server:
              • + *
                  + *
                1. getKeyStore(configID): Optionally, to check that if there is no + * keystore, then that all the enabled cipher suits are anonymous.
                2. + *
                3. createServerSocketFactory(configID): to create an + * SSLServerSocketFactory.
                4. + *
                5. getClientAuthentication(configID): to later set on the + * SSLServerSocket (itself created from the SSLServerSocketFactory) whether + * client authentication is needed.
                6. + *
                + *
              • For an MQTT client:
              • + *
                  + *
                1. createSocketFactory(configID): to create an SSLSocketFactory.
                2. + *
                + *
              + */ +public class SSLSocketFactoryFactory { + private final static String CLASS_NAME = "org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory"; + /** + * Property keys specific to the client). + */ + public final static String SSLPROTOCOL="com.ibm.ssl.protocol"; + public final static String JSSEPROVIDER="com.ibm.ssl.contextProvider"; + public final static String KEYSTORE="com.ibm.ssl.keyStore"; + public final static String KEYSTOREPWD="com.ibm.ssl.keyStorePassword"; + public final static String KEYSTORETYPE="com.ibm.ssl.keyStoreType"; + public final static String KEYSTOREPROVIDER="com.ibm.ssl.keyStoreProvider"; + public final static String KEYSTOREMGR="com.ibm.ssl.keyManager"; + public final static String TRUSTSTORE="com.ibm.ssl.trustStore"; + public final static String TRUSTSTOREPWD="com.ibm.ssl.trustStorePassword"; + public final static String TRUSTSTORETYPE="com.ibm.ssl.trustStoreType"; + public final static String TRUSTSTOREPROVIDER="com.ibm.ssl.trustStoreProvider"; + public final static String TRUSTSTOREMGR="com.ibm.ssl.trustManager"; + public final static String CIPHERSUITES="com.ibm.ssl.enabledCipherSuites"; + public final static String CLIENTAUTH="com.ibm.ssl.clientAuthentication"; + + /** + * Property keys used for java system properties + */ + public final static String SYSKEYSTORE="javax.net.ssl.keyStore"; + public final static String SYSKEYSTORETYPE="javax.net.ssl.keyStoreType"; + public final static String SYSKEYSTOREPWD="javax.net.ssl.keyStorePassword"; + public final static String SYSTRUSTSTORE="javax.net.ssl.trustStore"; + public final static String SYSTRUSTSTORETYPE="javax.net.ssl.trustStoreType"; + public final static String SYSTRUSTSTOREPWD="javax.net.ssl.trustStorePassword"; + public final static String SYSKEYMGRALGO="ssl.KeyManagerFactory.algorithm"; + public final static String SYSTRUSTMGRALGO="ssl.TrustManagerFactory.algorithm"; + + + public final static String DEFAULT_PROTOCOL = "TLS"; // "SSL_TLS" is not supported by DesktopEE + + private final static String propertyKeys[] = { SSLPROTOCOL, JSSEPROVIDER, + KEYSTORE, KEYSTOREPWD, KEYSTORETYPE, KEYSTOREPROVIDER, KEYSTOREMGR, + TRUSTSTORE, TRUSTSTOREPWD, TRUSTSTORETYPE, TRUSTSTOREPROVIDER, + TRUSTSTOREMGR, CIPHERSUITES, CLIENTAUTH}; + + private Hashtable configs; // a hashtable that maps configIDs to properties. + + private Properties defaultProperties; + + private static final byte[] key = { (byte) 0x9d, (byte) 0xa7, (byte) 0xd9, + (byte) 0x80, (byte) 0x05, (byte) 0xb8, (byte) 0x89, (byte) 0x9c }; + + private static final String xorTag = "{xor}"; + + private Logger logger = null; + + + /** + * Not all of the JVM/Platforms support all of its + * security features. This method determines if is supported. + * + * @return whether dependent classes can be instantiated on the current + * JVM/platform. + * + * @throws Error + * if any unexpected error encountered whilst checking. Note + * this should not be a ClassNotFoundException, which should + * cause the method to return false. + */ + public static boolean isSupportedOnJVM() throws LinkageError, ExceptionInInitializerError { + String requiredClassname = "javax.net.ssl.SSLServerSocketFactory"; + try { + Class.forName(requiredClassname); + } catch (ClassNotFoundException e) { + return false; + } + return true; + } + + + /** + * Create new instance of class. + * Constructor used by clients. + */ + public SSLSocketFactoryFactory() { + configs = new Hashtable(); + } + + /** + * Create new instance of class. + * Constructor used by the broker. + */ + public SSLSocketFactoryFactory(Logger logger) { + this(); + this.logger = logger; + } + + /** + * Checks whether a key belongs to the supported IBM SSL property keys. + * + * @param key + * @return whether a key belongs to the supported IBM SSL property keys. + */ + private boolean keyValid(String key) { + int i = 0; + while (i < propertyKeys.length) { + if (propertyKeys[i].equals(key)) { + break; + } + ++i; + } + return i < propertyKeys.length; + } + + /** + * Checks whether the property keys belong to the supported IBM SSL property + * key set. + * + * @param properties + * @throws IllegalArgumentException + * if any of the properties is not a valid IBM SSL property key. + */ + private void checkPropertyKeys(Properties properties) + throws IllegalArgumentException { + Set keys = properties.keySet(); + Iterator i = keys.iterator(); + while (i.hasNext()) { + String k = (String) i.next(); + if (!keyValid(k)) { + throw new IllegalArgumentException(k + " is not a valid IBM SSL property key."); + } + } + } + + /** + * Convert byte array to char array, where each char is constructed from two + * bytes. + * + * @param b + * byte array + * @return char array + */ + public static char[] toChar(byte[] b) { + if(b==null) return null; + char[] c= new char[b.length/2]; + int i=0; int j=0; + while(i> 8)& 0xFF); + } + return b; + } + + /** + * Obfuscates the password using a simple and not very secure XOR mechanism. + * This should not be used for cryptographical purpose, it's a simple + * scrambler to obfuscate clear-text passwords. + * + * @see org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory#deObfuscate + * + * @param password + * The password to be encrypted, as a char[] array. + * @return An obfuscated password as a String. + */ + public static String obfuscate(char[] password) { + if (password == null) + return null; + byte[] bytes = toByte(password); + for (int i = 0; i < bytes.length; i++) { + bytes[i] = (byte) ((bytes[i] ^ key[i % key.length]) & 0x00ff); + } + String encryptedValue = xorTag + + new String(SimpleBase64Encoder.encode(bytes)); + return encryptedValue; + } + + /** + * The inverse operation of obfuscate: returns a cleartext password that was + * previously obfuscated using the XOR scrambler. + * + * @see org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory#obfuscate + * + * @param ePassword + * An obfuscated password. + * @return An array of char, containing the clear text password. + */ + public static char[] deObfuscate(String ePassword) { + if (ePassword == null) + return null; + byte[] bytes = null; + try { + bytes = SimpleBase64Encoder.decode(ePassword.substring(xorTag + .length())); + } catch (Exception e) { + return null; + } + + for (int i = 0; i < bytes.length; i++) { + bytes[i] = (byte) ((bytes[i] ^ key[i % key.length]) & 0x00ff); + } + return toChar(bytes); + } + + /** + * Converts an array of ciphers into a single String. + * + * @param ciphers + * The array of cipher names. + * @return A string containing the name of the ciphers, separated by comma. + */ + public static String packCipherSuites(String[] ciphers) { + String cipherSet=null; + if (ciphers != null) { + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < ciphers.length; i++) { + buf.append(ciphers[i]); + if (i < ciphers.length - 1) { + buf.append(','); + } + } + cipherSet = buf.toString(); + } + return cipherSet; + } + + /** + * Inverse operation of packCipherSuites: converts a string of cipher names + * into an array of cipher names + * + * @param ciphers + * A list of ciphers, separated by comma. + * @return An array of string, each string containing a single cipher name. + */ + public static String[] unpackCipherSuites(String ciphers) { + // can't use split as split is not available on all java platforms. + if(ciphers==null) return null; + Vector c=new Vector(); + int i=ciphers.indexOf(','); + int j=0; + // handle all commas. + while(i>-1) { + // add stuff before and up to (but not including) the comma. + c.add(ciphers.substring(j, i)); + j=i+1; // skip the comma. + i=ciphers.indexOf(',',j); + } + // add last element after the comma or only element if no comma is present. + c.add(ciphers.substring(j)); + String[] s = new String[c.size()]; + c.toArray(s); + return s; + } + + /** + * Obfuscate any key & trust store passwords within the given properties. + * + * @see org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory#obfuscate + * + * @param p + * properties + */ + private void convertPassword(Properties p) { + String pw = p.getProperty(KEYSTOREPWD); + if (pw != null && !pw.startsWith(xorTag)) { + String epw = obfuscate(pw.toCharArray()); + p.put(KEYSTOREPWD, epw); + } + pw = p.getProperty(TRUSTSTOREPWD); + if (pw != null && !pw.startsWith(xorTag)) { + String epw = obfuscate(pw.toCharArray()); + p.put(TRUSTSTOREPWD, epw); + } + } + + /** + * Returns the properties object for configuration configID or creates a new + * one if required. + * + * @param configID + * The configuration identifier for selecting a configuration or + * null for the default configuration. + * @return the properties object for configuration configID + */ +// private Properties getOrCreate(String configID) { +// Properties res = null; +// if (configID == null) { +// if (this.defaultProperties == null) { +// this.defaultProperties = new Properties(); +// } +// res = this.defaultProperties; +// } else { +// res = (Properties) this.configs.get(configID); +// if (res == null) { +// res = new Properties(); +// this.configs.put(configID, res); +// } +// } +// return res; +// } + + /** + * Initializes the SSLSocketFactoryFactory with the provided properties for + * the provided configuration. + * + * @param props + * A properties object containing IBM SSL properties that are + * qualified by one or more configuration identifiers. + * @param configID + * The configuration identifier for selecting a configuration or + * null for the default configuration. + * @throws IllegalArgumentException + * if any of the properties is not a valid IBM SSL property key. + */ + public void initialize(Properties props, String configID) + throws IllegalArgumentException { + checkPropertyKeys(props); + // copy the properties. + Properties p = new Properties(); + p.putAll(props); + convertPassword(p); + if (configID != null) { + this.configs.put(configID, p); + } else { + this.defaultProperties = p; + } + } + + /** + * Merges the given IBM SSL properties into the existing configuration, + * overwriting existing properties. This method is used to selectively + * change properties for a given configuration. The method throws an + * IllegalArgumentException if any of the properties is not a valid IBM SSL + * property key. + * + * @param props + * A properties object containing IBM SSL properties + * @param configID + * The configuration identifier for selecting a configuration or + * null for the default configuration. + * @throws IllegalArgumentException + * if any of the properties is not a valid IBM SSL property key. + */ + public void merge(Properties props, String configID) + throws IllegalArgumentException { + checkPropertyKeys(props); + Properties p = this.defaultProperties; + if (configID == null) { + p = (Properties) this.configs.get(configID); + } + if (p == null) { + p = new Properties(); + } + convertPassword(props); + p.putAll(props); + if (configID != null) { + this.configs.put(configID, p); + } else { + this.defaultProperties = p; + } + + } + + /** + * Remove the configuration of a given configuration identifier. + * + * @param configID + * The configuration identifier for selecting a configuration or + * null for the default configuration. + * @return true, if the configuation could be removed. + */ + public boolean remove(String configID) { + boolean res = false; + if (configID != null) { + res = this.configs.remove(configID) != null; + } else { + if(null != this.defaultProperties) { + res = true; + this.defaultProperties = null; + } + } + return res; + } + + /** + * Returns the configuration of the SSLSocketFactoryFactory for a given + * configuration. Note that changes in the property are reflected in the + * SSLSocketFactoryFactory. + * + * @param configID + * The configuration identifier for selecting a configuration or + * null for the default configuration. + * @return A property object containing the current configuration of the + * SSLSocketFactoryFactory. Note that it could be null. + */ + public Properties getConfiguration(String configID) { + return (Properties) (configID == null ? this.defaultProperties + : this.configs.get(configID)); + } + + /** + * @return Returns the set of configuration IDs that exist in the SSLSocketFactoryFactory. + */ +// public String[] getConfigurationIDs() { +// Set s = this.configs.keySet(); +// String[] configs = new String[s.size()]; +// configs = (String[]) s.toArray(configs); +// return configs; +// } + + /** + * If the value is not null, then put it in the properties object using the + * key. If the value is null, then remove the entry in the properties object + * with the key. + * + * @param p + * @param key + * @param value + */ +// private final void putOrRemove(Properties p, String key, String value) { +// if (value == null) { +// p.remove(key); +// } else { +// p.put(key, value); +// } +// } + + /** + * Sets the SSL protocol variant. If protocol is NULL then an existing value + * will be removed. + * + * @param configID + * The configuration identifier for selecting a configuration or + * null for the default configuration. + * @param protocol + * One of SSL, SSLv3, TLS, TLSv1, SSL_TLS + */ +// public void setSSLProtocol(String configID, String protocol) { +// Properties p = getOrCreate(configID); +// putOrRemove(p, SSLPROTOCOL, protocol); +// } + + /** + * Sets the JSSE context provider. If provider is null, then an existing + * value will be removed. + * + * @param configID + * The configuration identifier for selecting a configuration or + * null for the default configuration. + * @param provider + * The JSSE provider. For example "IBMJSSE2" or "SunJSSE". + */ +// public void setJSSEProvider(String configID, String provider) { +// Properties p = getOrCreate(configID); +// putOrRemove(p, JSSEPROVIDER, provider); +// } + + /** + * Sets the filename of the keyStore object. A null value is ignored. + * + * @param configID + * The configuration identifier for selecting a configuration or + * null for the default configuration. + * @param keyStore + * A filename that points to a valid keystore. + */ +// public void setKeyStore(String configID, String keyStore) { +// if (keyStore == null) +// return; +// Properties p = getOrCreate(configID); +// putOrRemove(p, KEYSTORE, keyStore); +// } + + /** + * Sets the password that is used for the keystore. The password must be + * provided in plain text, but it will be stored internally in a scrambled + * XOR format. + * + * @see org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory#obfuscate + * + * @param configID + * The configuration identifier for selecting a configuration or + * null for the default configuration. + * @param password + * The keystore password + */ +// public void setKeyStorePassword(String configID, char[] password) { +// if (password == null) +// return; +// Properties p = getOrCreate(configID); +// // convert password, using XOR-based scrambling. +// String ePasswd = obfuscate(password); +// for(int i=0;i=3){ + encoded.append(to64((((bytes[i] & 0xff) << 16) + | (int) ((bytes[i+1] & 0xff) << 8) | (int) (bytes[i+2] & 0xff)),4)); + i+=3; + j-=3; + } + // j==2 | j==1 | j==0 + if(j==2) { + // there is a rest of 2 bytes. This encodes into 3 chars. + encoded.append(to64(((bytes[i] &0xff)<<8) | ((bytes[i+1] & 0xff)),3)); + } + if(j==1) { + // there is a rest of 1 byte. This encodes into 1 char. + encoded.append(to64(((bytes[i] & 0xff)),2)); + } + return encoded.toString(); + } + + public static byte[] decode(String string) { + byte[] encoded=string.getBytes(); + int len=encoded.length; + byte[] decoded=new byte[len*3/4]; + int i=0; + int j=len; + int k=0; + while(j>=4) { + long d=from64(encoded, i, 4); + j-=4; + i+=4; + for(int l=2;l>=0;l--) { + decoded[k+l]=(byte) (d & 0xff); + d=d >>8; + } + k+=3; + } + // j==3 | j==2 + if(j==3) { + long d=from64(encoded, i, 3); + for(int l=1;l>=0;l--) { + decoded[k+l]=(byte) (d & 0xff); + d=d >>8; + } + } + if(j==2) { + long d=from64(encoded, i, 2); + decoded[k]=(byte) (d & 0xff); + } + return decoded; + } + + /* the core conding routine. Translates an input integer into + * a string of the given length.*/ + private final static String to64(long input, int size) { + final StringBuffer result = new StringBuffer(size); + while (size > 0) { + size--; + result.append(PWDCHARS_ARRAY[((int) (input & 0x3f))]); + input = input >> 6; + } + return result.toString(); + } + + /* + * The reverse operation of to64 + */ + private final static long from64(byte[] encoded, int idx, int size) { + long res=0; + int f=0; + while(size>0) { + size--; + long r=0; + // convert encoded[idx] back into a 6-bit value. + byte d=encoded[idx++]; + if(d=='/') { + r=1; + } + if(d>='0' && d<='9') { + r=2+d-'0'; + } + if(d>='A' && d<='Z') { + r=12+d-'A'; + } + if(d>='a' && d<='z') { + r=38+d-'a'; + } + res=res+((long)r << f); + f+=6; + } + return res; + } + +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/CountingInputStream.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/CountingInputStream.java new file mode 100644 index 0000000..524c62f --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/CountingInputStream.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal.wire; + +import java.io.IOException; +import java.io.InputStream; + +/** + * An input stream that counts the bytes read from it. + */ +public class CountingInputStream extends InputStream { + private InputStream in; + private int counter; + + /** + * Constructs a new CountingInputStream wrapping the supplied + * input stream. + */ + public CountingInputStream(InputStream in) { + this.in = in; + this.counter = 0; + } + + public int read() throws IOException { + int i = in.read(); + if (i != -1) { + counter++; + } + return i; + } + + /** + * Returns the number of bytes read since the last reset. + */ + public int getCounter() { + return counter; + } + + /** + * Resets the counter to zero. + */ + public void resetCounter() { + counter = 0; + } +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttAck.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttAck.java new file mode 100644 index 0000000..13ce622 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttAck.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal.wire; + + +/** + * Abstract super-class of all acknowledgement messages. + */ +public abstract class MqttAck extends MqttWireMessage { + public MqttAck(byte type) { + super(type); + } + + protected byte getMessageInfo() { + return 0; + } + +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttConnack.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttConnack.java new file mode 100644 index 0000000..612c321 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttConnack.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal.wire; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; + +import org.eclipse.paho.client.mqttv3.MqttException; + + +/** + * An on-the-wire representation of an MQTT CONNACK. + */ +public class MqttConnack extends MqttAck { + private int returnCode; + + public MqttConnack(byte info, byte[] variableHeader) throws IOException { + super(MqttWireMessage.MESSAGE_TYPE_CONNACK); + ByteArrayInputStream bais = new ByteArrayInputStream(variableHeader); + DataInputStream dis = new DataInputStream(bais); + dis.readByte(); + returnCode = dis.readUnsignedByte(); + dis.close(); + } + + public int getReturnCode() { + return returnCode; + } + + protected byte[] getVariableHeader() throws MqttException { + // Not needed, as the client never encodes a CONNACK + return new byte[0]; + } + + /** + * Returns whether or not this message needs to include a message ID. + */ + public boolean isMessageIdRequired() { + return false; + } + + public String getKey() { + return new String("Con"); + } +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttConnect.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttConnect.java new file mode 100644 index 0000000..7c42e1f --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttConnect.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal.wire; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.MqttMessage; + +/** + * An on-the-wire representation of an MQTT CONNECT message. + */ +public class MqttConnect extends MqttWireMessage { + private String clientId; + private boolean cleanSession; + private MqttMessage willMessage; + private String userName; + private char[] password; + private int keepAliveInterval; + private String willDestination; + public static String KEY="Con"; + + + public MqttConnect(String clientId, + boolean cleanSession, + int keepAliveInterval, + String userName, + char[] password, + MqttMessage willMessage, + String willDestination) { + super(MqttWireMessage.MESSAGE_TYPE_CONNECT); + this.clientId = clientId; + this.cleanSession = cleanSession; + this.keepAliveInterval = keepAliveInterval; + this.userName = userName; + this.password = password; + this.willMessage = willMessage; + this.willDestination = willDestination; + } + + protected byte getMessageInfo() { + return (byte) 0; + } + + public boolean isCleanSession() { + return cleanSession; + } + + protected byte[] getVariableHeader() throws MqttException { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(baos); + this.encodeUTF8(dos,"MQIsdp"); + dos.write(3); + byte connectFlags = 0; + + if (cleanSession) { + connectFlags |= 0x02; + } + + if (willMessage != null ) { + connectFlags |= 0x04; + connectFlags |= (willMessage.getQos()<<3); + if (willMessage.isRetained()) { + connectFlags |= 0x20; + } + } + + if (userName != null) { + connectFlags |= 0x80; + if (password != null) { + connectFlags |= 0x40; + } + } + dos.write(connectFlags); + dos.writeShort(keepAliveInterval); + dos.flush(); + return baos.toByteArray(); + } catch(IOException ioe) { + throw new MqttException(ioe); + } + } + + public byte[] getPayload() throws MqttException { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(baos); + this.encodeUTF8(dos,clientId); + + if (willMessage != null) { + this.encodeUTF8(dos,willDestination); + dos.writeShort(willMessage.getPayload().length); + dos.write(willMessage.getPayload()); + } + + if (userName != null) { + this.encodeUTF8(dos,userName); + if (password != null) { + this.encodeUTF8(dos,new String(password)); + } + } + dos.flush(); + return baos.toByteArray(); + } + catch (IOException ex) { + throw new MqttException(ex); + } + } + + /** + * Returns whether or not this message needs to include a message ID. + */ + public boolean isMessageIdRequired() { + return false; + } + + public String getKey() { + return new String(KEY); + } +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttDisconnect.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttDisconnect.java new file mode 100644 index 0000000..1c48d47 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttDisconnect.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal.wire; + +import org.eclipse.paho.client.mqttv3.MqttException; + +/** + * An on-the-wire representation of an MQTT DISCONNECT message. + */ +public class MqttDisconnect extends MqttWireMessage { + public static String KEY="Disc"; + + public MqttDisconnect() { + super(MqttWireMessage.MESSAGE_TYPE_DISCONNECT); + } + + protected byte getMessageInfo() { + return (byte) 0; + } + + protected byte[] getVariableHeader() throws MqttException { + return new byte[0]; + } + + /** + * Returns whether or not this message needs to include a message ID. + */ + public boolean isMessageIdRequired() { + return false; + } + + public String getKey() { + return new String(KEY); + } +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttInputStream.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttInputStream.java new file mode 100644 index 0000000..9eaec05 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttInputStream.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal.wire; + +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.internal.ExceptionHelper; + + +/** + * An MqttInputStream lets applications read instances of + * MqttWireMessage. + */ +public class MqttInputStream extends InputStream { + private DataInputStream in; + + public MqttInputStream(InputStream in) { + this.in = new DataInputStream(in); + } + + public int read() throws IOException { + return in.read(); + } + + public int available() throws IOException { + return in.available(); + } + + public void close() throws IOException { + in.close(); + } + + /** + * Reads an MqttWireMessage from the stream. + */ + public MqttWireMessage readMqttWireMessage() throws IOException, MqttException { + ByteArrayOutputStream bais = new ByteArrayOutputStream(); + byte first = in.readByte(); + byte type = (byte) ((first >>> 4) & 0x0F); + if ((type < MqttWireMessage.MESSAGE_TYPE_CONNECT) || + (type > MqttWireMessage.MESSAGE_TYPE_DISCONNECT)) { + // Invalid MQTT message type... + throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_INVALID_MESSAGE); + } + long remLen = MqttWireMessage.readMBI(in).getValue(); + bais.write(first); + // bit silly, we decode it then encode it + bais.write(MqttWireMessage.encodeMBI(remLen)); + byte[] packet = new byte[(int)(bais.size()+remLen)]; + in.readFully(packet,bais.size(),packet.length - bais.size()); + byte[] header = bais.toByteArray(); + System.arraycopy(header,0,packet,0, header.length); + MqttWireMessage message = MqttWireMessage.createWireMessage(packet); + return message; + } +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttOutputStream.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttOutputStream.java new file mode 100644 index 0000000..5df98c6 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttOutputStream.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal.wire; + +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import org.eclipse.paho.client.mqttv3.MqttException; + + +/** + * An MqttOutputStream lets applications write instances of + * MqttWireMessage. + */ +public class MqttOutputStream extends OutputStream { + private BufferedOutputStream out; + + public MqttOutputStream(OutputStream out) { + this.out = new BufferedOutputStream(out); + } + + public void close() throws IOException { + out.close(); + } + + public void flush() throws IOException { + out.flush(); + } + + public void write(byte[] b) throws IOException { + out.write(b); + } + + public void write(byte[] b, int off, int len) throws IOException { + out.write(b, off, len); + } + + public void write(int b) throws IOException { + out.write(b); + } + + /** + * Writes an MqttWireMessage to the stream. + */ + public void write(MqttWireMessage message) throws IOException, MqttException { + byte[] bytes = message.getHeader(); + byte[] pl = message.getPayload(); +// out.write(message.getHeader()); +// out.write(message.getPayload()); + out.write(bytes,0,bytes.length); + out.write(pl,0,pl.length); + } +} + diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPersistableWireMessage.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPersistableWireMessage.java new file mode 100644 index 0000000..bc2d39f --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPersistableWireMessage.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal.wire; + +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.MqttPersistable; +import org.eclipse.paho.client.mqttv3.MqttPersistenceException; + +public abstract class MqttPersistableWireMessage extends MqttWireMessage + implements MqttPersistable { + + public MqttPersistableWireMessage(byte type) { + super(type); + } + + public byte[] getHeaderBytes() throws MqttPersistenceException { + try { + return getHeader(); + } + catch (MqttException ex) { + throw new MqttPersistenceException(ex.getCause()); + } + } + + public int getHeaderLength() throws MqttPersistenceException { + return getHeaderBytes().length; + } + + public int getHeaderOffset() throws MqttPersistenceException{ + return 0; + } + +// public String getKey() throws MqttPersistenceException { +// return new Integer(getMessageId()).toString(); +// } + + public byte[] getPayloadBytes() throws MqttPersistenceException { + try { + return getPayload(); + } + catch (MqttException ex) { + throw new MqttPersistenceException(ex.getCause()); + } + } + + public int getPayloadLength() throws MqttPersistenceException { + return 0; + } + + public int getPayloadOffset() throws MqttPersistenceException { + return 0; + } + +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPingReq.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPingReq.java new file mode 100644 index 0000000..4f397fd --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPingReq.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal.wire; + +import org.eclipse.paho.client.mqttv3.MqttException; + +/** + * An on-the-wire representation of an MQTT PINGREQ message. + */ +public class MqttPingReq extends MqttWireMessage { + public MqttPingReq() { + super(MqttWireMessage.MESSAGE_TYPE_PINGREQ); + } + + /** + * Returns false as message IDs are not required for MQTT + * PINGREQ messages. + */ + public boolean isMessageIdRequired() { + return false; + } + + protected byte[] getVariableHeader() throws MqttException { + return new byte[0]; + } + + protected byte getMessageInfo() { + return 0; + } + + public String getKey() { + return new String("Ping"); + } +} + diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPingResp.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPingResp.java new file mode 100644 index 0000000..abb28d3 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPingResp.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal.wire; + +import org.eclipse.paho.client.mqttv3.MqttException; + + +/** + * An on-the-wire representation of an MQTT PINGRESP. + */ +public class MqttPingResp extends MqttAck { + public MqttPingResp(byte info, byte[] variableHeader) { + super(MqttWireMessage.MESSAGE_TYPE_PINGRESP); + } + + protected byte[] getVariableHeader() throws MqttException { + // Not needed, as the client never encodes a PINGRESP + return new byte[0]; + } + + /** + * Returns whether or not this message needs to include a message ID. + */ + public boolean isMessageIdRequired() { + return false; + } + + public String getKey() { + return new String("Ping"); + } +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubAck.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubAck.java new file mode 100644 index 0000000..4b92a43 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubAck.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal.wire; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; + +import org.eclipse.paho.client.mqttv3.MqttException; + + + +/** + * An on-the-wire representation of an MQTT PUBACK message. + */ +public class MqttPubAck extends MqttAck { + public MqttPubAck(byte info, byte[] data) throws IOException { + super(MqttWireMessage.MESSAGE_TYPE_PUBACK); + ByteArrayInputStream bais = new ByteArrayInputStream(data); + DataInputStream dis = new DataInputStream(bais); + msgId = dis.readUnsignedShort(); + dis.close(); + } + + public MqttPubAck(MqttPublish publish) { + super(MqttWireMessage.MESSAGE_TYPE_PUBACK); + msgId = publish.getMessageId(); + } + + protected byte[] getVariableHeader() throws MqttException { + return encodeMessageId(); + } +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubComp.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubComp.java new file mode 100644 index 0000000..3176e0d --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubComp.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal.wire; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; + +import org.eclipse.paho.client.mqttv3.MqttException; + + + +/** + * An on-the-wire representation of an MQTT PUBCOMP message. + */ +public class MqttPubComp extends MqttAck { + public MqttPubComp(byte info, byte[] data) throws IOException { + super(MqttWireMessage.MESSAGE_TYPE_PUBCOMP); + ByteArrayInputStream bais = new ByteArrayInputStream(data); + DataInputStream dis = new DataInputStream(bais); + msgId = dis.readUnsignedShort(); + dis.close(); + } + + public MqttPubComp(MqttPublish publish) { + super(MqttWireMessage.MESSAGE_TYPE_PUBCOMP); + this.msgId = publish.getMessageId(); + } + + public MqttPubComp(int msgId) { + super(MqttWireMessage.MESSAGE_TYPE_PUBCOMP); + this.msgId = msgId; + } + + protected byte[] getVariableHeader() throws MqttException { + return encodeMessageId(); + } +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubRec.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubRec.java new file mode 100644 index 0000000..864da02 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubRec.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal.wire; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; + +import org.eclipse.paho.client.mqttv3.MqttException; + + + +/** + * An on-the-wire representation of an MQTT PUBREC message. + */ +public class MqttPubRec extends MqttAck { + public MqttPubRec(byte info, byte[] data) throws IOException { + super(MqttWireMessage.MESSAGE_TYPE_PUBREC); + ByteArrayInputStream bais = new ByteArrayInputStream(data); + DataInputStream dis = new DataInputStream(bais); + msgId = dis.readUnsignedShort(); + dis.close(); + } + + public MqttPubRec(MqttPublish publish) { + super(MqttWireMessage.MESSAGE_TYPE_PUBREC); + msgId = publish.getMessageId(); + } + + protected byte[] getVariableHeader() throws MqttException { + return encodeMessageId(); + } +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubRel.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubRel.java new file mode 100644 index 0000000..8c12d4c --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubRel.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal.wire; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; + +import org.eclipse.paho.client.mqttv3.MqttException; + +/** + * An on-the-wire representation of an MQTT PUBREL message. + */ +public class MqttPubRel extends MqttPersistableWireMessage { + + /** + * Createa a pubrel message based on a pubrec + * @param pubRec + */ + public MqttPubRel(MqttPubRec pubRec) { + super(MqttWireMessage.MESSAGE_TYPE_PUBREL); + this.setMessageId(pubRec.getMessageId()); + } + + /** + * Creates a pubrel based on a pubrel set of bytes read fro the network + * @param info + * @param data + * @throws IOException + */ + public MqttPubRel(byte info, byte[] data) throws IOException { + super(MqttWireMessage.MESSAGE_TYPE_PUBREL); + ByteArrayInputStream bais = new ByteArrayInputStream(data); + DataInputStream dis = new DataInputStream(bais); + msgId = dis.readUnsignedShort(); + dis.close(); + } + + protected byte[] getVariableHeader() throws MqttException { + return encodeMessageId(); + } + + protected byte getMessageInfo() { + return (byte)( 2 | (this.duplicate?8:0)); + } + +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPublish.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPublish.java new file mode 100644 index 0000000..7c9d620 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPublish.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal.wire; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.MqttMessage; + + +/** + * An on-the-wire representation of an MQTT SEND message. + */ +public class MqttPublish extends MqttPersistableWireMessage { + + private MqttMessage message; + private String topicName; + + private byte[] encodedPayload = null; + + public MqttPublish(String name, MqttMessage message) { + super(MqttWireMessage.MESSAGE_TYPE_PUBLISH); + this.topicName = name; + this.message = message; + } + + /** + * Constructs a new MqttPublish object. + * @param info the message info byte + * @param data the variable header and payload bytes + */ + public MqttPublish(byte info, byte[] data) throws MqttException, IOException { + super(MqttWireMessage.MESSAGE_TYPE_PUBLISH); + this.message = new MqttReceivedMessage(); + message.setQos((info >> 1) & 0x03); + if ((info & 0x01) == 0x01) { + message.setRetained(true); + } + if ((info & 0x08) == 0x08) { + ((MqttReceivedMessage) message).setDuplicate(true); + } + + ByteArrayInputStream bais = new ByteArrayInputStream(data); + CountingInputStream counter = new CountingInputStream(bais); + DataInputStream dis = new DataInputStream(counter); + topicName = this.decodeUTF8(dis); + if (message.getQos() > 0) { + msgId = dis.readUnsignedShort(); + } + byte[] payload = new byte[data.length-counter.getCounter()]; + dis.readFully(payload); + dis.close(); + message.setPayload(payload); + } + + protected byte getMessageInfo() { + byte info = (byte) (message.getQos() << 1); + if (message.isRetained()) { + info |= 0x01; + } + if (message.isDuplicate() || duplicate ) { + info |= 0x08; + } + + return info; + } + + public String getTopicName() { + return topicName; + } + + public MqttMessage getMessage() { + return message; + } + + protected static byte[] encodePayload(MqttMessage message) { + return message.getPayload(); + } + + public byte[] getPayload() throws MqttException { + if (encodedPayload == null) { + encodedPayload = encodePayload(message); + } + return encodedPayload; + } + + public int getPayloadLength() { + int length = 0; + try { + length = getPayload().length; + } catch(MqttException me) { + } + return length; + } + + public void setMessageId(int msgId) { + super.setMessageId(msgId); + if (message instanceof MqttReceivedMessage) { + ((MqttReceivedMessage)message).setMessageId(msgId); + } + } + + protected byte[] getVariableHeader() throws MqttException { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(baos); + this.encodeUTF8(dos, topicName); + if (message.getQos() > 0) { + dos.writeShort(msgId); + } + dos.flush(); + return baos.toByteArray(); + } + catch (IOException ex) { + throw new MqttException(ex); + } + } + + public boolean isMessageIdRequired() { + // all publishes require a message ID as it's used as the key to the token store + return true; + } +} \ No newline at end of file diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttReceivedMessage.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttReceivedMessage.java new file mode 100644 index 0000000..68773d0 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttReceivedMessage.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal.wire; + +import org.eclipse.paho.client.mqttv3.MqttMessage; + +public class MqttReceivedMessage extends MqttMessage { + + private int messageId; + + public void setMessageId(int msgId) { + this.messageId = msgId; + } + + public int getMessageId() { + return messageId; + } + + // This method exists here to get around the protected visibility of the + // super class method. + public void setDuplicate(boolean value) { + super.setDuplicate(value); + } +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttSuback.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttSuback.java new file mode 100644 index 0000000..39ef1f0 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttSuback.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal.wire; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; + +import org.eclipse.paho.client.mqttv3.MqttException; + + +/** + * An on-the-wire representation of an MQTT SUBACK. + */ +public class MqttSuback extends MqttAck { + private int[] grantedQos; // Not currently made available to anyone. + + public MqttSuback(byte info, byte[] data) throws IOException { + super(MqttWireMessage.MESSAGE_TYPE_SUBACK); + ByteArrayInputStream bais = new ByteArrayInputStream(data); + DataInputStream dis = new DataInputStream(bais); + msgId = dis.readUnsignedShort(); + int index = 0; + grantedQos = new int[data.length-2]; + int qos = dis.read(); + while (qos != -1) { + grantedQos[index] = qos; + index++; + qos = dis.read(); + } + dis.close(); + } + + protected byte[] getVariableHeader() throws MqttException { + // Not needed, as the client never encodes a SUBACK + return new byte[0]; + } +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttSubscribe.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttSubscribe.java new file mode 100644 index 0000000..0eb68fe --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttSubscribe.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal.wire; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.MqttMessage; + + +/** + * An on-the-wire representation of an MQTT SUBSCRIBE message. + */ +public class MqttSubscribe extends MqttWireMessage { + private String[] names; + private int[] qos; + + /** + * Constructor for on an on hte wire MQTT subscribe message + * @param names - one or more topics to subscribe to + * @param qos - the max QOS that each each topic will be subscribed at + */ + public MqttSubscribe(String[] names, int[] qos) { + super(MqttWireMessage.MESSAGE_TYPE_SUBSCRIBE); + this.names = names; + this.qos = qos; + + if (names.length != qos.length) { + throw new IllegalArgumentException(); + } + + for (int i=0;i> 4); + byte info = (byte) (first &= 0x0f); + long remLen = readMBI(in).getValue(); + long totalToRead = counter.getCounter() + remLen; + + MqttWireMessage result; + long remainder = totalToRead - counter.getCounter(); + byte[] data = new byte[0]; + // The remaining bytes must be the payload... + if (remainder > 0) { + data = new byte[(int) remainder]; + in.readFully(data, 0, data.length); + } + + if (type == MqttWireMessage.MESSAGE_TYPE_PUBLISH) { + result = new MqttPublish(info, data); + } + else if (type == MqttWireMessage.MESSAGE_TYPE_PUBACK) { + result = new MqttPubAck(info, data); + } + else if (type == MqttWireMessage.MESSAGE_TYPE_PUBCOMP) { + result = new MqttPubComp(info, data); + } + else if (type == MqttWireMessage.MESSAGE_TYPE_CONNACK) { + result = new MqttConnack(info, data); + } + else if (type == MqttWireMessage.MESSAGE_TYPE_PINGRESP) { + result = new MqttPingResp(info, data); + } + else if (type == MqttWireMessage.MESSAGE_TYPE_SUBACK) { + result = new MqttSuback(info, data); + } + else if (type == MqttWireMessage.MESSAGE_TYPE_UNSUBACK) { + result = new MqttUnsubAck(info, data); + } + else if (type == MqttWireMessage.MESSAGE_TYPE_PUBREL) { + result = new MqttPubRel(info, data); + } + else if (type == MqttWireMessage.MESSAGE_TYPE_PUBREC) { + result = new MqttPubRec(info, data); + } + else { + throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_UNEXPECTED_ERROR); + } + return result; + } catch(IOException io) { + throw new MqttException(io); + } + } + + protected static byte[] encodeMBI( long number) { + int numBytes = 0; + long no = number; + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + // Encode the remaining length fields in the four bytes + do { + byte digit = (byte)(no % 128); + no = no / 128; + if (no > 0) { + digit |= 0x80; + } + bos.write(digit); + numBytes++; + } while ( (no > 0) && (numBytes<4) ); + + return bos.toByteArray(); + } + + /** + * Decodes an MQTT Multi-Byte Integer from the given stream. + */ + protected static MultiByteInteger readMBI(DataInputStream in) throws IOException { + byte digit; + long msgLength = 0; + int multiplier = 1; + int count = 0; + + do { + digit = in.readByte(); + count++; + msgLength += ((digit & 0x7F) * multiplier); + multiplier *= 128; + } while ((digit & 0x80) != 0); + + return new MultiByteInteger(msgLength, count); + } + + protected byte[] encodeMessageId() throws MqttException { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(baos); + dos.writeShort(msgId); + dos.flush(); + return baos.toByteArray(); + } + catch (IOException ex) { + throw new MqttException(ex); + } + } + + public boolean isRetryable() { + return false; + } + + public void setDuplicate(boolean duplicate) { + this.duplicate = duplicate; + } + + /** + * Encodes a String given into UTF-8, before writing this to the DataOutputStream the length of the + * encoded string is encoded into two bytes and then written to the DataOutputStream. @link{DataOutputStream#writeUFT(String)} + * should be no longer used. @link{DataOutputStream#writeUFT(String)} does not correctly encode UTF-16 surrogate characters. + * + * @param dos The stream to write the encoded UTF-8 String to. + * @param stringToEncode The String to be encoded + * @throws MqttException Thrown when an error occurs with either the encoding or writing the data to the stream + */ + protected void encodeUTF8(DataOutputStream dos, String stringToEncode) throws MqttException + { + try { + + byte[] encodedString = stringToEncode.getBytes("UTF-8"); + byte byte1 = (byte) ((encodedString.length >>> 8) & 0xFF); + byte byte2 = (byte) ((encodedString.length >>> 0) & 0xFF); + + + dos.write(byte1); + dos.write(byte2); + dos.write(encodedString); + } + catch(UnsupportedEncodingException ex) + { + throw new MqttException(ex); + } catch (IOException ex) { + throw new MqttException(ex); + } + } + + /** + * Decodes a UTF-8 string from the DataInputStream provided. @link(DataInoutStream#readUTF()) should be no longer used, because @link(DataInoutStream#readUTF()) + * does not decode UTF-16 surrogate characters correctly. + * + * @param input The input stream from which to read the encoded string + * @return a decoded String from the DataInputStream + * @throws MqttException thrown when an error occurs with either reading from the stream or + * decoding the encoded string. + */ + protected String decodeUTF8(DataInputStream input) throws MqttException + { + int encodedLength; + try { + encodedLength = input.readUnsignedShort(); + + byte[] encodedString = new byte[encodedLength]; + input.readFully(encodedString); + + return new String(encodedString, "UTF-8"); + } catch (IOException ex) { + throw new MqttException(ex); + } + } + +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MultiByteArrayInputStream.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MultiByteArrayInputStream.java new file mode 100644 index 0000000..0519c82 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MultiByteArrayInputStream.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal.wire; + +import java.io.IOException; +import java.io.InputStream; + +public class MultiByteArrayInputStream extends InputStream { + + private byte[] bytesA; + private int offsetA; + private int lengthA; + private byte[] bytesB; + private int offsetB; + private int lengthB; + + private int pos = 0; + + public MultiByteArrayInputStream(byte[] bytesA, int offsetA, int lengthA, byte[] bytesB, int offsetB, int lengthB) { + this.bytesA = bytesA; + this.bytesB = bytesB; + this.offsetA = offsetA; + this.offsetB = offsetB; + this.lengthA = lengthA; + this.lengthB = lengthB; + } + public int read() throws IOException { + int result = -1; + if (posA sample java.util.logging properties file - jsr47min.properties is provided that demonstrates + * how to run with a memory based trace facility that runs with minimal performance + * overhead. The memory buffer can be dumped when a log/trace record is written matching + * the MemoryHandlers trigger level or when the push method is invoked on the MemoryHandler. + * {@link org.eclipse.paho.client.mqttv3.util.Debug Debug} provides method to make it easy + * to dump the memory buffer as well as other useful debug info. + */ +public class JSR47Logger implements Logger { + private java.util.logging.Logger julLogger = null; + private ResourceBundle logMessageCatalog = null; + private ResourceBundle traceMessageCatalog = null; + private String catalogID = null; + private String resourceName = null; + private String loggerName = null; + + /** + * + * @param logMsgCatalog The resource bundle associated with this logger + * @param loggerID The suffix for the loggerName (will be appeneded to org.eclipse.paho.client.mqttv3 + * @param resourceContext A context for the logger e.g. clientID or appName... + */ + public void initialise(ResourceBundle logMsgCatalog, String loggerID, String resourceContext ) { + this.traceMessageCatalog = logMessageCatalog; + this.resourceName = resourceContext; +// loggerName = "org.eclipse.paho.client.mqttv3." + ((null == loggerID || 0 == loggerID.length()) ? "internal" : loggerID); + loggerName = loggerID; + this.julLogger = java.util.logging.Logger.getLogger(loggerName); + this.logMessageCatalog = logMsgCatalog; + this.traceMessageCatalog = logMsgCatalog; + this.catalogID = logMessageCatalog.getString("0"); + + } + + public void setResourceName(String logContext) { + this.resourceName = logContext; + } + + public boolean isLoggable(int level) { + return julLogger.isLoggable(mapJULLevel(level)); // || InternalTracer.isLoggable(level); + } + + public void severe(String sourceClass, String sourceMethod, String msg) { + log(SEVERE, sourceClass, sourceMethod, msg, null, null); + } + + public void severe(String sourceClass, String sourceMethod, String msg, Object[] inserts) { + log(SEVERE, sourceClass, sourceMethod, msg, inserts, null); + } + + public void severe(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown) { + log(SEVERE, sourceClass, sourceMethod, msg, inserts, thrown); + } + + public void warning(String sourceClass, String sourceMethod, String msg) { + log(WARNING, sourceClass, sourceMethod, msg, null, null); + } + + public void warning(String sourceClass, String sourceMethod, String msg, Object[] inserts) { + log(WARNING, sourceClass, sourceMethod, msg, inserts, null); + } + + public void warning(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown) { + log(WARNING, sourceClass, sourceMethod, msg, inserts, thrown); + } + + public void info(String sourceClass, String sourceMethod, String msg) { + log(INFO, sourceClass, sourceMethod, msg, null, null); + } + + public void info(String sourceClass, String sourceMethod, String msg, Object[] inserts) { + log(INFO, sourceClass, sourceMethod, msg, inserts, null); + } + + public void info(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown) { + log(INFO, sourceClass, sourceMethod, msg, inserts, thrown); + } + + public void config(String sourceClass, String sourceMethod, String msg) { + log(CONFIG, sourceClass, sourceMethod, msg, null, null); + } + + public void config(String sourceClass, String sourceMethod, String msg, Object[] inserts) { + log(CONFIG, sourceClass, sourceMethod, msg, inserts, null); + } + + public void config(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown) { + log(CONFIG, sourceClass, sourceMethod, msg, inserts, thrown); + } + + public void log(int level, String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown) { +// InternalTracer.log(this.catalogID, level, sourceClass, sourceMethod, msg, inserts, thrown); + java.util.logging.Level julLevel = mapJULLevel(level); + if (julLogger.isLoggable(julLevel)) { + logToJsr47(julLevel, sourceClass, sourceMethod, this.catalogID, this.logMessageCatalog, msg, inserts, thrown); + } + } + +// public void setTrace(Trace trace) { +// InternalTracer.setTrace(trace); +// } + + public void fine(String sourceClass, String sourceMethod, String msg) { + trace(FINE, sourceClass, sourceMethod, msg, null, null); + } + + public void fine(String sourceClass, String sourceMethod, String msg, Object[] inserts) { + trace(FINE, sourceClass, sourceMethod, msg, inserts, null); + } + + public void fine(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex) { + trace(FINE, sourceClass, sourceMethod, msg, inserts, ex); + } + + public void finer(String sourceClass, String sourceMethod, String msg) { + trace(FINER, sourceClass, sourceMethod, msg, null, null); + } + + public void finer(String sourceClass, String sourceMethod, String msg, Object[] inserts) { + trace(FINER, sourceClass, sourceMethod, msg, inserts, null); + } + + public void finer(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex) { + trace(FINER, sourceClass, sourceMethod, msg, inserts, ex); + } + + public void finest(String sourceClass, String sourceMethod, String msg) { + trace(FINEST, sourceClass, sourceMethod, msg, null, null); + } + + public void finest(String sourceClass, String sourceMethod, String msg, Object[] inserts) { + trace(FINEST, sourceClass, sourceMethod, msg, inserts, null); + } + + public void finest(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex) { + trace(FINEST, sourceClass, sourceMethod, msg, inserts, ex); + } + + + public void trace(int level, String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex) { + java.util.logging.Level julLevel = mapJULLevel(level); + boolean isJULLoggable = julLogger.isLoggable(julLevel); +// if (FINE == level || isJULLoggable || InternalTracer.isLoggable(level)) { +// InternalTracer.traceForced(level, sourceClass, sourceMethod, msg, inserts); +// } + if (isJULLoggable) { + logToJsr47(julLevel, sourceClass, sourceMethod, this.catalogID, this.traceMessageCatalog, msg, inserts, ex); + } + } + + + private String getResourceMessage(ResourceBundle messageCatalog, String msg) { + String message; + try { + message = messageCatalog.getString(msg); + } catch (MissingResourceException e) { + // This is acceptable, simply return the given msg string. + message = msg; + } + return message; + } + + private void logToJsr47(java.util.logging.Level julLevel, String sourceClass, String sourceMethod, String catalogName, + ResourceBundle messageCatalog, String msg, Object[] inserts, Throwable thrown) { +// LogRecord logRecord = new LogRecord(julLevel, msg); + String formattedWithArgs = msg; + if (msg.indexOf("=====")== -1) { + formattedWithArgs = MessageFormat.format(getResourceMessage(messageCatalog, msg), inserts); + } + LogRecord logRecord = new LogRecord(julLevel, resourceName + ": " +formattedWithArgs); + + logRecord.setSourceClassName(sourceClass); + logRecord.setSourceMethodName(sourceMethod); + logRecord.setLoggerName(loggerName); +// logRecord.setResourceBundleName(catalogName); +// logRecord.setResourceBundle(messageCatalog); +// if (null != inserts) { +// logRecord.setParameters(inserts); +// } + if (null != thrown) { + logRecord.setThrown(thrown); + } + + julLogger.log(logRecord); + } + + private java.util.logging.Level mapJULLevel(int level) { + java.util.logging.Level julLevel = null; + + switch (level) { + case SEVERE: + julLevel = java.util.logging.Level.SEVERE; + break; + case WARNING: + julLevel = java.util.logging.Level.WARNING; + break; + case INFO: + julLevel = java.util.logging.Level.INFO; + break; + case CONFIG: + julLevel = java.util.logging.Level.CONFIG; + break; + case FINE: + julLevel = java.util.logging.Level.FINE; + break; + case FINER: + julLevel = java.util.logging.Level.FINER; + break; + case FINEST: + julLevel = java.util.logging.Level.FINEST; + break; + } + + return julLevel; + } + + public String formatMessage(String msg, Object[] inserts) { + String formatString; + try { + formatString = logMessageCatalog.getString(msg); + } catch (MissingResourceException e) { + formatString = msg; + } + return formatString; + } + + public void dumpTrace() { + dumpMemoryTrace47(julLogger); + } + + protected static void dumpMemoryTrace47(java.util.logging.Logger logger) { + MemoryHandler mHand = null; + + if (logger!= null) { + Handler[] handlers = logger.getHandlers(); + + for (int i=0; i + * The int levels define a set of standard logging levels that can be used to + * control logging output. The logging levels are ordered and are specified by + * ordered integers. Enabling logging at a given level also enables logging at + * all higher levels. + *

              + * Clients should use the the convenience methods such as severe() and fine() or + * one of the predefined level constants such as Logger.SEVERE and Logger.FINE + * with the appropriate log(int level...) or trace(int level...) methods. + *

              + * The levels in descending order are: + *

                + *
              • SEVERE (log - highest value)
              • + *
              • WARNING (log)
              • + *
              • INFO (log)
              • + *
              • CONFIG (log)
              • + *
              • FINE (trace)
              • + *
              • FINER (trace)
              • + *
              • FINEST (trace - lowest value)
              • + *
              + *

              + */ +public interface Logger { + /** + * SEVERE is a message level indicating a serious failure. + *

              + * In general SEVERE messages should describe events that are of + * considerable importance and which will prevent normal program execution. + * They should be reasonably intelligible to end users and to system + * administrators. + */ + public static final int SEVERE = 1; + /** + * WARNING is a message level indicating a potential problem. + *

              + * In general WARNING messages should describe events that will be of + * interest to end users or system managers, or which indicate potential + * problems. + */ + public static final int WARNING = 2; + /** + * INFO is a message level for informational messages. + *

              + * Typically INFO messages will be written to the console or its equivalent. + * So the INFO level should only be used for reasonably significant messages + * that will make sense to end users and system admins. + */ + public static final int INFO = 3; + /** + * CONFIG is a message level for static configuration messages. + *

              + * CONFIG messages are intended to provide a variety of static configuration + * information, to assist in debugging problems that may be associated with + * particular configurations. For example, CONFIG message might include the + * CPU type, the graphics depth, the GUI look-and-feel, etc. + */ + public static final int CONFIG = 4; + /** + * FINE is a message level providing tracing information. + *

              + * All of FINE, FINER, and FINEST are intended for relatively detailed + * tracing. The exact meaning of the three levels will vary between + * subsystems, but in general, FINEST should be used for the most voluminous + * detailed output, FINER for somewhat less detailed output, and FINE for + * the lowest volume (and most important) messages. + *

              + * In general the FINE level should be used for information that will be + * broadly interesting to developers who do not have a specialized interest + * in the specific subsystem. + *

              + * FINE messages might include things like minor (recoverable) failures. + * Issues indicating potential performance problems are also worth logging + * as FINE. + */ + public static final int FINE = 5; + /** + * FINER indicates a fairly detailed tracing message. By default logging + * calls for entering, returning, or throwing an exception are traced at + * this level. + */ + public static final int FINER = 6; + /** + * FINEST indicates a highly detailed tracing message. + */ + public static final int FINEST = 7; + + public void initialise(ResourceBundle messageCatalog, String loggerID, String resourceName); + + /** + * Set a name that can be used to provide context with each log record. + * This overrides the value passed in on initialise + */ + public void setResourceName(String logContext); + + /** + * Check if a message of the given level would actually be logged by this + * logger. This check is based on the Loggers effective level, which may be + * inherited from its parent. + * + * @param level + * a message logging level. + * @return true if the given message level is currently being logged. + */ + public boolean isLoggable(int level); + + /** + * Log a message, specifying source class and method, if the logger is + * currently enabled for the given message level. + * + * @param sourceClass + * Name of class that issued the logging request. + * @param sourceMethod + * Name of method that issued the logging request. + * @param msg + * The key in the message localization catalog for the message or + * the actual message itself. During formatting, if the logger + * has a mapping for the msg string, then the msg string is + * replaced by the localized value. Otherwise the original msg + * string is used. + */ + public void severe(String sourceClass, String sourceMethod, String msg); + + /** + * Log a message, specifying source class and method, with an array of + * object arguments, if the logger is currently enabled for the given + * message level. + * + * @param sourceClass + * Name of class that issued the logging request. + * @param sourceMethod + * Name of method that issued the logging request. + * @param msg + * The key in the message localization catalog for the message or + * the actual message itself. During formatting, if the logger + * has a mapping for the msg string, then the msg string is + * replaced by the localized value. Otherwise the original msg + * string is used. The formatter uses java.text.MessageFormat + * style formatting to format parameters, so for example a format + * string "{0} {1}" would format two inserts into the message. + * @param inserts + * Array of parameters to the message. + */ + public void severe(String sourceClass, String sourceMethod, String msg, Object[] inserts); + + /** + * Log a message, specifying source class and method, with an array of + * object arguments and a throwable, if the logger is currently enabled for + * the given message level. + * + * @param sourceClass + * Name of class that issued the logging request. + * @param sourceMethod + * Name of method that issued the logging request. + * @param msg + * The key in the message localization catalog for the message or + * the actual message itself. During formatting, if the logger + * has a mapping for the msg string, then the msg string is + * replaced by the localized value. Otherwise the original msg + * string is used. The formatter uses java.text.MessageFormat + * style formatting to format parameters, so for example a format + * string "{0} {1}" would format two inserts into the message. + * @param inserts + * Array of parameters to the message. + * @param thrown + * Throwable associated with log message. + */ + public void severe(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown); + + /** + * Log a message, specifying source class and method, if the logger is + * currently enabled for the given message level. + * + * @param sourceClass + * Name of class that issued the logging request. + * @param sourceMethod + * Name of method that issued the logging request. + * @param msg + * The key in the message localization catalog for the message or + * the actual message itself. During formatting, if the logger + * has a mapping for the msg string, then the msg string is + * replaced by the localized value. Otherwise the original msg + * string is used. + */ + public void warning(String sourceClass, String sourceMethod, String msg); + + /** + * Log a message, specifying source class and method, with an array of + * object arguments, if the logger is currently enabled for the given + * message level. + * + * @param sourceClass + * Name of class that issued the logging request. + * @param sourceMethod + * Name of method that issued the logging request. + * @param msg + * The key in the message localization catalog for the message or + * the actual message itself. During formatting, if the logger + * has a mapping for the msg string, then the msg string is + * replaced by the localized value. Otherwise the original msg + * string is used. The formatter uses java.text.MessageFormat + * style formatting to format parameters, so for example a format + * string "{0} {1}" would format two inserts into the message. + * @param inserts + * Array of parameters to the message. + */ + public void warning(String sourceClass, String sourceMethod, String msg, Object[] inserts); + + /** + * Log a message, specifying source class and method, with an array of + * object arguments and a throwable, if the logger is currently enabled for + * the given message level. + * + * @param sourceClass + * Name of class that issued the logging request. + * @param sourceMethod + * Name of method that issued the logging request. + * @param msg + * The key in the message localization catalog for the message or + * the actual message itself. During formatting, if the logger + * has a mapping for the msg string, then the msg string is + * replaced by the localized value. Otherwise the original msg + * string is used. The formatter uses java.text.MessageFormat + * style formatting to format parameters, so for example a format + * string "{0} {1}" would format two inserts into the message. + * @param inserts + * Array of parameters to the message. + * @param thrown + * Throwable associated with log message. + */ + public void warning(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown); + + /** + * Log a message, specifying source class and method, if the logger is + * currently enabled for the given message level. + * + * @param sourceClass + * Name of class that issued the logging request. + * @param sourceMethod + * Name of method that issued the logging request. + * @param msg + * The key in the message localization catalog for the message or + * the actual message itself. During formatting, if the logger + * has a mapping for the msg string, then the msg string is + * replaced by the localized value. Otherwise the original msg + * string is used. + */ + public void info(String sourceClass, String sourceMethod, String msg); + + /** + * Log a message, specifying source class and method, with an array of + * object arguments, if the logger is currently enabled for the given + * message level. + * + * @param sourceClass + * Name of class that issued the logging request. + * @param sourceMethod + * Name of method that issued the logging request. + * @param msg + * The key in the message localization catalog for the message or + * the actual message itself. During formatting, if the logger + * has a mapping for the msg string, then the msg string is + * replaced by the localized value. Otherwise the original msg + * string is used. The formatter uses java.text.MessageFormat + * style formatting to format parameters, so for example a format + * string "{0} {1}" would format two inserts into the message. + * @param inserts + * Array of parameters to the message. + */ + public void info(String sourceClass, String sourceMethod, String msg, Object[] inserts); + + /** + * Log a message, specifying source class and method, with an array of + * object arguments and a throwable, if the logger is currently enabled for + * the given message level. + * + * @param sourceClass + * Name of class that issued the logging request. + * @param sourceMethod + * Name of method that issued the logging request. + * @param msg + * The key in the message localization catalog for the message or + * the actual message itself. During formatting, if the logger + * has a mapping for the msg string, then the msg string is + * replaced by the localized value. Otherwise the original msg + * string is used. The formatter uses java.text.MessageFormat + * style formatting to format parameters, so for example a format + * string "{0} {1}" would format two inserts into the message. + * @param inserts + * Array of parameters to the message. + * @param thrown + * Throwable associated with log message. + */ + public void info(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown); + + /** + * Log a message, specifying source class and method, if the logger is + * currently enabled for the given message level. + * + * @param sourceClass + * Name of class that issued the logging request. + * @param sourceMethod + * Name of method that issued the logging request. + * @param msg + * The key in the message localization catalog for the message or + * the actual message itself. During formatting, if the logger + * has a mapping for the msg string, then the msg string is + * replaced by the localized value. Otherwise the original msg + * string is used. + */ + public void config(String sourceClass, String sourceMethod, String msg); + + /** + * Log a message, specifying source class and method, with an array of + * object arguments, if the logger is currently enabled for the given + * message level. + * + * @param sourceClass + * Name of class that issued the logging request. + * @param sourceMethod + * Name of method that issued the logging request. + * @param msg + * The key in the message localization catalog for the message or + * the actual message itself. During formatting, if the logger + * has a mapping for the msg string, then the msg string is + * replaced by the localized value. Otherwise the original msg + * string is used. The formatter uses java.text.MessageFormat + * style formatting to format parameters, so for example a format + * string "{0} {1}" would format two inserts into the message. + * @param inserts + * Array of parameters to the message. + */ + public void config(String sourceClass, String sourceMethod, String msg, Object[] inserts); + + /** + * Log a message, specifying source class and method, with an array of + * object arguments and a throwable, if the logger is currently enabled for + * the given message level. + * + * @param sourceClass + * Name of class that issued the logging request. + * @param sourceMethod + * Name of method that issued the logging request. + * @param msg + * The key in the message localization catalog for the message or + * the actual message itself. During formatting, if the logger + * has a mapping for the msg string, then the msg string is + * replaced by the localized value. Otherwise the original msg + * string is used. The formatter uses java.text.MessageFormat + * style formatting to format parameters, so for example a format + * string "{0} {1}" would format two inserts into the message. + * @param inserts + * Array of parameters to the message. + * @param thrown + * Throwable associated with log message. + */ + public void config(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown); + + /** + * Trace a message, specifying source class and method, if the logger is + * currently enabled for the given message level. + * + * @param sourceClass + * Name of class that issued the logging request. + * @param sourceMethod + * Name of method that issued the logging request. + * @param msg + * The key in the message catalog for the message or the actual + * message itself. During formatting, if the logger has a mapping + * for the msg string, then the msg string is replaced by the + * value. Otherwise the original msg string is used. + */ + public void fine(String sourceClass, String sourceMethod, String msg); + + /** + * Trace a message, specifying source class and method, with an array of + * object arguments, if the logger is currently enabled for the given + * message level. + * + * @param sourceClass + * Name of class that issued the logging request. + * @param sourceMethod + * Name of method that issued the logging request. + * @param msg + * The key in the message catalog for the message or the actual + * message itself. During formatting, if the logger has a mapping + * for the msg string, then the msg string is replaced by the + * value. Otherwise the original msg string is used. The + * formatter uses java.text.MessageFormat style formatting to + * format parameters, so for example a format string "{0} {1}" + * would format two inserts into the message. + * @param inserts + * Array of parameters to the message. + */ + public void fine(String sourceClass, String sourceMethod, String msg, Object[] inserts); + + public void fine(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex); + + /** + * Trace a message, specifying source class and method, if the logger is + * currently enabled for the given message level. + * + * @param sourceClass + * Name of class that issued the logging request. + * @param sourceMethod + * Name of method that issued the logging request. + * @param msg + * The key in the message catalog for the message or the actual + * message itself. During formatting, if the logger has a mapping + * for the msg string, then the msg string is replaced by the + * value. Otherwise the original msg string is used. + */ + public void finer(String sourceClass, String sourceMethod, String msg); + + /** + * Trace a message, specifying source class and method, with an array of + * object arguments, if the logger is currently enabled for the given + * message level. + * + * @param sourceClass + * Name of class that issued the logging request. + * @param sourceMethod + * Name of method that issued the logging request. + * @param msg + * The key in the message catalog for the message or the actual + * message itself. During formatting, if the logger has a mapping + * for the msg string, then the msg string is replaced by the + * value. Otherwise the original msg string is used. The + * formatter uses java.text.MessageFormat style formatting to + * format parameters, so for example a format string "{0} {1}" + * would format two inserts into the message. + * @param inserts + * Array of parameters to the message. + */ + public void finer(String sourceClass, String sourceMethod, String msg, Object[] inserts); + + public void finer(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex); + + /** + * Trace a message, specifying source class and method, if the logger is + * currently enabled for the given message level. + * + * @param sourceClass + * Name of class that issued the logging request. + * @param sourceMethod + * Name of method that issued the logging request. + * @param msg + * The key in the message catalog for the message or the actual + * message itself. During formatting, if the logger has a mapping + * for the msg string, then the msg string is replaced by the + * value. Otherwise the original msg string is used. + */ + public void finest(String sourceClass, String sourceMethod, String msg); + + /** + * Trace a message, specifying source class and method, with an array of + * object arguments, if the logger is currently enabled for the given + * message level. + * + * @param sourceClass + * Name of class that issued the logging request. + * @param sourceMethod + * Name of method that issued the logging request. + * @param msg + * The key in the message catalog for the message or the actual + * message itself. During formatting, if the logger has a mapping + * for the msg string, then the msg string is replaced by the + * value. Otherwise the original msg string is used. The + * formatter uses java.text.MessageFormat style formatting to + * format parameters, so for example a format string "{0} {1}" + * would format two inserts into the message. + * @param inserts + * Array of parameters to the message. + */ + public void finest(String sourceClass, String sourceMethod, String msg, Object[] inserts); + + public void finest(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex); + + /** + * Log a message, specifying source class and method, with an array of + * object arguments and a throwable, if the logger is currently enabled for + * the given message level. + * + * @param level + * One of the message level identifiers, e.g. SEVERE. + * @param sourceClass + * Name of class that issued the logging request. + * @param sourceMethod + * Name of method that issued the logging request. + * @param msg + * The key in the message localization catalog for the message or + * the actual message itself. During formatting, if the logger + * has a mapping for the msg string, then the msg string is + * replaced by the localized value. Otherwise the original msg + * string is used. The formatter uses java.text.MessageFormat + * style formatting to format parameters, so for example a format + * string "{0} {1}" would format two inserts into the message. + * @param inserts + * Array of parameters to the message, may be null. + * @param thrown + * Throwable associated with log message. + */ + public void log(int level, String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown); + + /** + * Log a trace message, specifying source class and method, with an array of + * object arguments and a throwable, if the logger is currently enabled for + * the given message level. + * + * @param level + * One of the message level identifiers, e.g. SEVERE. + * @param sourceClass + * Name of class that issued the logging request. + * @param sourceMethod + * Name of method that issued the logging request. + * @param msg + * The key in the message catalog for the message or the actual + * message itself. During formatting, if the logger has a mapping + * for the msg string, then the msg string is replaced by the + * value. Otherwise the original msg string is used. The + * formatter uses java.text.MessageFormat style formatting to + * format parameters, so for example a format string "{0} {1}" + * would format two inserts into the message. + * @param inserts + * Array of parameters to the message, may be null. + */ + public void trace(int level, String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex); + + /** + * Format a log message without causing it to be written to the log. + * + * @param msg + * The key in the message localization catalog for the message or + * the actual message itself. During formatting, if the logger + * has a mapping for the msg string, then the msg string is + * replaced by the localized value. Otherwise the original msg + * string is used. The formatter uses java.text.MessageFormat + * style formatting to format parameters, so for example a format + * string "{0} {1}" would format two inserts into the message. + * @param inserts + * Array of parameters to the message. + * @return The formatted message for the current locale. + */ + public String formatMessage(String msg, Object[] inserts); + + public void dumpTrace(); +} \ No newline at end of file diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/logging/LoggerFactory.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/logging/LoggerFactory.java new file mode 100644 index 0000000..348b185 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/logging/LoggerFactory.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.logging; + +import java.lang.reflect.Method; + +/** + * LoggerFactory will create a logger instance ready for use by the caller. + * + * The default is to create a logger that utilises the Java's built in + * logging facility java.util.logging (JSR47). It is possible to override + * this for systems where JSR47 is not available or an alternative logging + * facility is needed by using setLogger and passing the the class name of + * a logger that implements {@link Logger} + */ +import java.util.MissingResourceException; +import java.util.ResourceBundle; +/** + * A factory that returns a logger for use by the MQTT client. + * + * The default log and trace facility uses Java's build in log facility:- + * java.util.logging. For systems where this is not available or where + * an alternative logging framework is required the logging facility can be + * replaced using {@link org.eclipse.paho.client.mqttv3.logging.LoggerFactory#setLogger(String)} + * which takes an implementation of the {@link org.eclipse.paho.client.mqttv3.logging.Logger} + * interface. + */ +public class LoggerFactory { + /** + * Default message catalog. + */ + public final static String MQTT_CLIENT_MSG_CAT = "org.eclipse.paho.client.mqttv3.internal.nls.logcat"; + private final static String className = LoggerFactory.class.getName(); + + private static String overrideloggerClassName = null; + /** + * Default logger that uses java.util.logging. + */ + private static String jsr47LoggerClassName = "org.eclipse.paho.client.mqttv3.logging.JSR47Logger"; + + /** + * Find or create a logger for a named package/class. + * If a logger has already been created with the given name + * it is returned. Otherwise a new logger is created. By default a logger + * that uses java.util.logging will be returned. + * + * @param messageCatalogName the resource bundle containing the logging messages. + * @param loggerID unique name to identify this logger. + * @return a suitable Logger. + * @throws Exception + */ + public static Logger getLogger(String messageCatalogName, String loggerID) { + String loggerClassName = overrideloggerClassName; + Logger logger = null; + + if (loggerClassName == null) { + loggerClassName = jsr47LoggerClassName; + } +// logger = getJSR47Logger(ResourceBundle.getBundle(messageCatalogName), loggerID, null) ; + logger = getLogger(loggerClassName, ResourceBundle.getBundle(messageCatalogName), loggerID, null) ; +// } + + if (null == logger) { + throw new MissingResourceException("Error locating the logging class", className, loggerID); + } + + return logger; + } + + + /** + * Return an instance of a logger + * + * @param the class name of the load to load + * @param messageCatalog the resource bundle containing messages + * @param loggerID an identifier for the logger + * @param resourceName a name or context to associate with this logger instance. + * @return a ready for use logger + */ + private static Logger getLogger(String loggerClassName, ResourceBundle messageCatalog, String loggerID, String resourceName) { //, FFDC ffdc) { + Logger logger = null; + Class logClass = null; + + try { + logClass = Class.forName(loggerClassName); + } catch (NoClassDefFoundError ncdfe) { + return null; + } catch (ClassNotFoundException cnfe) { + return null; + } + if (null != logClass) { + // Now instantiate the log + try { + logger = (Logger)logClass.newInstance(); + } catch (IllegalAccessException e) { + return null; + } catch (InstantiationException e) { + return null; + } catch (ExceptionInInitializerError e) { + return null; + } catch (SecurityException e) { + return null; + } + logger.initialise(messageCatalog, loggerID, resourceName); + } + + return logger; + } + + /** + * When run in JSR47, this allows access to the properties in the logging.properties + * file. + * If not run in JSR47, or the property isn't set, returns null. + * @param name the property to return + * @return the property value, or null if it isn't set or JSR47 isn't being used + */ + public static String getLoggingProperty(String name) { + String result = null; + try { + // Hide behind reflection as java.util.logging is guaranteed to be + // available. + Class logManagerClass = Class.forName("java.util.logging.LogManager"); + Method m1 = logManagerClass.getMethod("getLogManager", new Class[]{}); + Object logManagerInstance = m1.invoke(null, null); + Method m2 = logManagerClass.getMethod("getProperty", new Class[]{String.class}); + result = (String)m2.invoke(logManagerInstance,new Object[]{name}); + } catch(Exception e) { + // Any error, assume JSR47 isn't available and return null + result = null; + } + return result; + } + + /** + * Set the class name of the logger that the LoggerFactory will load + * If not set getLogger will attempt to create a logger + * appropriate for the platform. + * @param loggerClassName - Logger implementation class name to use. + */ + public static void setLogger(String loggerClassName) { + LoggerFactory.overrideloggerClassName = loggerClassName; + } +} \ No newline at end of file diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/logging/SimpleLogFormatter.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/logging/SimpleLogFormatter.java new file mode 100644 index 0000000..893db7f --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/logging/SimpleLogFormatter.java @@ -0,0 +1,91 @@ +package org.eclipse.paho.client.mqttv3.logging; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.text.MessageFormat; +import java.util.Date; +import java.util.logging.Formatter; +import java.util.logging.LogRecord; + +/** + * SimpleLogFormatter prints a single line + * log record in human readable form. + */ +public class SimpleLogFormatter extends Formatter { + + final String ls = System.getProperty("line.separator"); + /** + * Constructs a SimpleFormatter object. + */ + public SimpleLogFormatter() { + super(); + } + + /** + * Format the logrecord as a single line with well defined columns. + */ + public String format(LogRecord r) { + StringBuffer sb = new StringBuffer(); + sb.append(r.getLevel().getName()+"\t"); + sb.append(MessageFormat.format("{0, date, yy-MM-dd} {0, time, kk:mm:ss.SSSS} ", + new Object[] { new Date(r.getMillis()) })+"\t"); + String cnm = r.getSourceClassName(); + String cn=""; + if (cnm != null) { + int cnl = cnm.length(); + if (cnl>20) { + cn = r.getSourceClassName().substring(cnl-19); + } else { + char sp[] = {' '}; + StringBuffer sb1= new StringBuffer().append(cnm); + cn = sb1.append(sp,0, 1).toString(); + } + } + sb.append(cn+"\t").append(" "); + sb.append(left(r.getSourceMethodName(),23,' ')+"\t"); + sb.append(r.getThreadID()+"\t"); + sb.append(formatMessage(r)).append(ls); + if (null != r.getThrown()) { + sb.append("Throwable occurred: "); + Throwable t = r.getThrown(); + PrintWriter pw = null; + try { + StringWriter sw = new StringWriter(); + pw = new PrintWriter(sw); + t.printStackTrace(pw); + sb.append(sw.toString()); + } finally { + if (pw != null) { + try { + pw.close(); + } catch (Exception e) { + // ignore + } + } + } + } + return sb.toString(); + } + + /** + * Left justify a string. + * + * @param s the string to justify + * @param width the field width to justify within + * @param fillChar the character to fill with + * + * @return the justified string. + */ + public static String left(String s, int width, char fillChar) { + if (s.length() >= width) { + return s; + } + StringBuffer sb = new StringBuffer(width); + sb.append(s); + for (int i = width - s.length(); --i >= 0;) { + sb.append(fillChar); + } + return sb.toString(); + } + +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/logging/jsr47min.properties b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/logging/jsr47min.properties new file mode 100644 index 0000000..0626551 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/logging/jsr47min.properties @@ -0,0 +1,83 @@ +# Properties file which configures the operation of the JDK logging facility. +# +# The configuration in this file is the suggesgted configuration +# for collecting trace for helping debug problems related to the +# Paho MQTT client. It configures trace to be continuosly collected +# in memory with minimal impact on performance. +# +# When the push trigger (by default a Severe level message) or a +# specific request is made to "push" the in memory trace then it +# is "pushed" to the configured target handler. By default +# this is the standard java.util.logging.FileHandler. The Paho Debug +# class can be used to push the memory trace to its target +# +# To enable trace either: +# - use this properties file as is and set the logging facility up +# to use it by configuring the util logging system property e.g. +# +# >java -Djava.util.logging.config.file=\jsr47min.properties +# +# - This contents of this file can also be merged with another +# java.util.logging config file to ensure provide wider logging +# and trace including Paho trace + +# Global logging properties. +# ------------------------------------------ +# The set of handlers to be loaded upon startup. +# Comma-separated list of class names. +# - Root handlers are not enabled by default - just handlers on the Paho packages. +#handlers=java.util.logging.MemoryHandler,java.util.logging.FileHandler, java.util.logging.ConsoleHandler + +# Default global logging level. +# Loggers and Handlers may override this level +#.level=INFO + +# Loggers +# ------------------------------------------ +# A memoryhandler is attached to the paho packages +# and the level specified to collected all trace related +# to paho packages. This will override any root/global +# level handlers if set. +org.eclipse.paho.client.mqttv3.handlers=java.util.logging.MemoryHandler +org.eclipse.paho.client.mqttv3.level=ALL +# It is possible to set more granular trace on a per class basis e.g. +#org.eclipse.paho.client.mqttv3.internal.ClientComms.level=ALL + +# Handlers +# ----------------------------------------- +# Note: the target handler that is associated with the MemoryHandler is not a root handler +# and hence not returned when getting the handlers from root. It appears accessing +# target handler programatically is not possible as target is a private variable in +# class MemoryHandler +java.util.logging.MemoryHandler.level=FINEST +java.util.logging.MemoryHandler.size=10000 +java.util.logging.MemoryHandler.push=SEVERE +java.util.logging.MemoryHandler.target=java.util.logging.FileHandler +#java.util.logging.MemoryHandler.target=java.util.logging.ConsoleHandler + + +# --- FileHandler --- +# Override of global logging level +java.util.logging.FileHandler.level=ALL + +# Naming style for the output file: +# (The output file is placed in the directory +# defined by the "user.home" System property.) +# See java.util.logging for more options +java.util.logging.FileHandler.pattern=%h/paho%u.log + +# Limiting size of output file in bytes: +java.util.logging.FileHandler.limit=200000 + +# Number of output files to cycle through, by appending an +# integer to the base file name: +java.util.logging.FileHandler.count=3 + +# Style of output (Simple or XML): +java.util.logging.FileHandler.formatter=org.eclipse.paho.client.mqttv3.logging.SimpleLogFormatter + +# --- ConsoleHandler --- +# Override of global logging level +#java.util.logging.ConsoleHandler.level=INFO +#java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter +#java.util.logging.ConsoleHandler.formatter=org.eclipse.paho.client.mqttv3.logging.SimpleLogFormatter diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/logging/package.html b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/logging/package.html new file mode 100644 index 0000000..a679cf1 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/logging/package.html @@ -0,0 +1,18 @@ + +Provides facilities to write and format log and trace to help debug problems. + +

              The default log and trace facility uses Java's build in log facility:- +java.util.logging. For systems where this is not available or where +an alternative logging framework is required the logging facility can be +replaced using {@link org.eclipse.paho.client.mqttv3.logging.LoggerFactory#setLogger(String)} +which takes an implementation of the {@link org.eclipse.paho.client.mqttv3.logging.Logger} +interface. + +

              A sample java.util.logging properties file - jsr47min.properties is provided that demonstrates +how to run with a memory based trace facility that runs with minimal performance +overhead. The memory buffer can be dumped when a log/trace record is written matching +the MemoryHandlers trigger level or when the push method is invoked on the MemoryHandler. +{@link org.eclipse.paho.client.mqttv3.util.Debug Debug} provides method to make it easy +to dump the memory buffer as well as other useful debug info. + + \ No newline at end of file diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/package.html b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/package.html new file mode 100644 index 0000000..1adc965 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/package.html @@ -0,0 +1,127 @@ + +Contains a programming interface enabling applications to communicate with an MQTT server + +

              +The MQ Telemetry Transport (MQTT) is a lightweight broker-based publish/subscribe +messaging protocol designed to be open, simple, lightweight and easy to implement. +These characteristics make it ideal for use in constrained environments, for example, +but not limited to: +

                +
              • Where the network is expensive, has low bandwidth or is unreliable such as mobile and vsat networks +
              • When run on an embedded or mobile device with limited processor, memory or battery +
              +

              Features of the protocol include: +

                +
              • The publish/subscribe message pattern to provide one-to-many message + distribution and decoupling of applications +
              • A messaging transport that is agnostic to the content of the payload +
              • The use of TCP/IP to provide network connectivity +
              • The use of SSL/TLS to provide network security and trust +
              • Three qualities of service for message delivery which are maintained across + network, client and server breaks. +
                  +
                • "At most once", where messages are delivered according to the best efforts + of the underlying TCP/IP network. Message loss or duplication can occur. + This level could be used, for example, with ambient sensor data where it + does not matter if an individual reading is lost as the next one will be published soon after. +
                • "At least once", where messages are assured to arrive but duplicates may occur. +
                • "Exactly once", where message are assured to arrive exactly once. This + level could be used, for example, with billing systems where duplicate or + lost messages could lead to incorrect charges being applied. +
                + The quality of service for message delivery is met even if the network connection + breaks, or the client or the server stop while a message is being delivered +
              • A small transport overhead (the fixed-length header is just 2 bytes), and + protocol exchanges minimised to reduce network traffic +
              • A mechanism to notify interested parties to an abnormal disconnection of + a client using the Last Will and Testament feature +
              + +

              The basic means of operating the client is:

              +
                +
              1. Create an instance of {@link org.eclipse.paho.client.mqttv3.MqttClient} or + {@link org.eclipse.paho.client.mqttv3.MqttAsyncClient}, providing + the address of an MQTT server and a unique client identifier.
              2. +
              3. connect to the server
              4. +
              5. Exchange messages with the server: +
                  +
                • publish messages to the server, + via a topic.
                • +
                • subscribe to one more topics. The server will send any messages + it receives on those topics to the client. The client will be informed when a message + arrives via a callback +
                +
              6. disconnect from the server.
              7. +
              + +

              The programming model and concepts like the protocol are small and easy to use. Key concepts +to use when creating MQTT application include: +

                +
              • Every client instance that connects to an MQTT server must have a unique client identifier. + If a second instance of a client with the same ID connects to a server the first instance will be + disconnected. +
              • For message delivery to be reliable and withstand both abnormal network breaks and clients/server + outages the client must use a persistent store to hold messages while they are being delivered. This is + the default case where a file based persistent store + {@link org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence MqttDefaultFilePersistence} is used. +
              • When connecting the {@link org.eclipse.paho.client.mqttv3.MqttConnectOptions#setCleanSession(boolean) cleansession} + option has a big impact on the operation of the client. If set to false: +
                  +
                • Message delivery will match the quality of service specified when the message was published even across + failures of the network, client or server +
                • The server will store messages for active subscriptions on behalf of the client when the client is not connected. + The server will deliver these messages to the client the next time it connects. +
                + If set to true: +
                  +
                • Any state stored on the client and server related to the client will be cleansed + before the connection is fully started. Subscriptions from earlier sessions will be unsubscribed + and any messages still in-flight from previous sessions will be deleted. +
                • When the client disconnects either as the result of the application requesting a disconnect + or a network failure, state related to the client will be cleansed just as at connect time. +
                • Messages will only be delivered to the quality of service requested at publish time if + the connection is maintained while the message is being delivered +
                +
              • When subscribing for messages the subscription can be for an absolute topic or a wildcarded topic. +
              • When unsubscribing the topic to be unsubscribed must match one specified on an earlier subscribe. +
              • There are two MQTT client libraries to choose from: +
                  +
                1. {@link org.eclipse.paho.client.mqttv3.IMqttAsyncClient MqttAsyncClient} which provides a non-blocking interface where + methods return before the requested operation has completed. The completion of the operation + can be monitored by in several ways: +
                    +
                  • Use the {@link org.eclipse.paho.client.mqttv3.IMqttToken#waitForCompletion waitForCompletion} + call on the token returned from the operation. This will block + until the operation completes. +
                  • Pass a {@link org.eclipse.paho.client.mqttv3.IMqttActionListener IMqttActionListener} + to the operation. The listener will then be called back when the operation completes. +
                  • Set a {@link org.eclipse.paho.client.mqttv3.MqttCallback MqttCallback} on the client. It + will be notified when a message arrive, a message have been delivered to the server and when the + connection to the server is lost. +
                  +
                2. {@link org.eclipse.paho.client.mqttv3.IMqttClient MqttClient} where methods block until + the operation has completed. +
                +
              • For both the blocking and non-blocking clients some operations are asynchronous. This includes: +
                  +
                • Notification that a new message has arrived: + {@link org.eclipse.paho.client.mqttv3.MqttCallback#messageArrived messageArrived}. +
                • Notification that the connection to the server has broken: + {@link org.eclipse.paho.client.mqttv3.MqttCallback#connectionLost connectionLost}. +
                • Notification that a message has been delivered to the server: + {@link org.eclipse.paho.client.mqttv3.MqttCallback#deliveryComplete deliveryComplete}. +
                + A client registers interest in these notifications by registering a + {@link org.eclipse.paho.client.mqttv3.MqttCallback MqttCallback} on the client +
              • There are a number of programs that demonstrate the different modes of + writing MQTT applications +
                  +
                • {@link org.eclipse.paho.sample.mqttv3app.Sample} uses the blocking client interface +
                • {@link org.eclipse.paho.sample.mqttv3app.SampleAsyncCallBack} uses the asynchronous client with + callbacks which are notified when an operation completes +
                • {@link org.eclipse.paho.sample.mqttv3app.SampleAsyncWait} uses the asynchronous client and + shows how to use the token returned from each operation to block until the operation completes. +
                +
              + + \ No newline at end of file diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/persist/MemoryPersistence.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/persist/MemoryPersistence.java new file mode 100644 index 0000000..de1a209 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/persist/MemoryPersistence.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.persist; + +import java.util.Enumeration; +import java.util.Hashtable; + +import org.eclipse.paho.client.mqttv3.MqttClientPersistence; +import org.eclipse.paho.client.mqttv3.MqttPersistable; +import org.eclipse.paho.client.mqttv3.MqttPersistenceException; + +/** + * Persistence that uses memory + * + * In cases where reliability is not required across client or device + * restarts memory this memory peristence can be used. In cases where + * reliability is required like when clean session is set to false + * then a non-volatile form of persistence should be used. + * + */ +public class MemoryPersistence implements MqttClientPersistence { + + private Hashtable data; + + /* (non-Javadoc) + * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#close() + */ + public void close() throws MqttPersistenceException { + data.clear(); + } + + /* (non-Javadoc) + * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#keys() + */ + public Enumeration keys() throws MqttPersistenceException { + return data.keys(); + } + + /* (non-Javadoc) + * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#get(java.lang.String) + */ + public MqttPersistable get(String key) throws MqttPersistenceException { + return (MqttPersistable)data.get(key); + } + + /* (non-Javadoc) + * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#open(java.lang.String, java.lang.String) + */ + public void open(String clientId, String serverURI) throws MqttPersistenceException { + this.data = new Hashtable(); + } + + /* (non-Javadoc) + * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#put(java.lang.String, org.eclipse.paho.client.mqttv3.MqttPersistable) + */ + public void put(String key, MqttPersistable persistable) throws MqttPersistenceException { + data.put(key, persistable); + } + + /* (non-Javadoc) + * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#remove(java.lang.String) + */ + public void remove(String key) throws MqttPersistenceException { + data.remove(key); + } + + /* (non-Javadoc) + * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#clear() + */ + public void clear() throws MqttPersistenceException { + data.clear(); + } + + /* (non-Javadoc) + * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#containsKey(java.lang.String) + */ + public boolean containsKey(String key) throws MqttPersistenceException { + return data.containsKey(key); + } +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/persist/MqttDefaultFilePersistence.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/persist/MqttDefaultFilePersistence.java new file mode 100644 index 0000000..2e4d44a --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/persist/MqttDefaultFilePersistence.java @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.persist; + +import java.io.File; +import java.io.FileFilter; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FilenameFilter; +import java.io.IOException; +import java.util.Enumeration; +import java.util.Vector; + +import org.eclipse.paho.client.mqttv3.MqttClientPersistence; +import org.eclipse.paho.client.mqttv3.MqttPersistable; +import org.eclipse.paho.client.mqttv3.MqttPersistenceException; +import org.eclipse.paho.client.mqttv3.internal.FileLock; +import org.eclipse.paho.client.mqttv3.internal.MqttPersistentData; + +/** + * An implementation of the {@link MqttClientPersistence} interface that provides + * file based persistence. + * + * A directory is specified when the Persistence object is created. When the persistence + * is then opened (see {@link #open(String, String)}), a sub-directory is made beneath the base + * for this client ID and connection key. This allows one persistence base directory + * to be shared by multiple clients. + * + * The sub-directory's name is created from a concatenation of the client ID and connection key + * with any instance of '/', '\\', ':' or ' ' removed. + */ +public class MqttDefaultFilePersistence implements MqttClientPersistence { + + private File dataDir; + private File clientDir = null; + private FileLock fileLock = null; + private static final String MESSAGE_FILE_EXTENSION = ".msg"; + private static final String MESSAGE_BACKUP_FILE_EXTENSION = ".bup"; + private static final String LOCK_FILENAME = ".lck"; + + private static final FilenameFilter FILE_FILTER = new FilenameFilter() { + public boolean accept(File dir, String name) { return name.endsWith(MESSAGE_FILE_EXTENSION); } + }; + + public MqttDefaultFilePersistence() { //throws MqttPersistenceException { + this(System.getProperty("user.dir")); + } + + /** + * Create an file-based persistent data store within the specified directory. + * @param directory the directory to use. + */ + public MqttDefaultFilePersistence(String directory) { //throws MqttPersistenceException { + dataDir = new File(directory); + } + + public void open(String clientId, String theConnection) throws MqttPersistenceException { + + if (dataDir.exists() && !dataDir.isDirectory()) { + throw new MqttPersistenceException(); + } else if (!dataDir.exists() ) { + if (!dataDir.mkdirs()) { + throw new MqttPersistenceException(); + } + } + if (!dataDir.canWrite()) { + throw new MqttPersistenceException(); + } + + + StringBuffer keyBuffer = new StringBuffer(); + for (int i=0;i +Contains implementations of the MqttClientPersistence interface. + +

              +An MQTT client needs a persistence mechanism to store messages while they +are in the process of being delivered. This package contains several +implementations of the interface. If a persistence class is not +specified on the constructor to an MQTT client, +{@link org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence MqttDefaultFilePersistence} +is used by default. + + \ No newline at end of file diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/util/Debug.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/util/Debug.java new file mode 100644 index 0000000..49e93c5 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/util/Debug.java @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.util; + +import java.util.Enumeration; +import java.util.Properties; + +import org.eclipse.paho.client.mqttv3.internal.ClientComms; +import org.eclipse.paho.client.mqttv3.logging.Logger; +import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; + +/** + * Utility to help debug problems with the Paho MQTT client + * Once initialised a call to dumpClientDebug will force any memory trace + * together with pertinent client and system state to the main log facility. + * + * No client wide lock is taken when the dump is progress. This means the + * set of client state may not be consistent as the client can still be + * processing work while the dump is in progress. + */ +public class Debug { + + final static String className = ClientComms.class.getName(); + Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className); + + static String separator = "=============="; + static String lineSep = System.getProperty("line.separator","\n"); + + String clientID; + ClientComms comms; + + /** + * Set the debug facility up for a specific client + * @param clientID the ID of the client being debugged + * @param comms the ClientComms object of the client being debugged + */ + public Debug(String clientID, ClientComms comms) { + this.clientID = clientID; + this.comms = comms; + log.setResourceName(clientID); + } + + /** + * Dump maximum debug info. + * This includes state specific to a client as well + * as debug that is JVM wide like trace and system properties. + * All state is written as debug log entries. + */ + public void dumpClientDebug() { + dumpClientComms(); + dumpConOptions(); + dumpClientState(); + dumpBaseDebug(); + } + + /** + * Dump of JVM wide debug info. + * This includes trace and system properties. + * Includes trace and system properties + */ + public void dumpBaseDebug() { + dumpVersion(); + dumpSystemProperties(); + dumpMemoryTrace(); + } + + /** + * If memory trace is being used a request is made to push it + * to the target handler. + */ + protected void dumpMemoryTrace() { + log.dumpTrace(); + } + + /** + * Dump information that show the version of the MQTT client being used. + */ + protected void dumpVersion() { + StringBuffer vInfo = new StringBuffer(); + vInfo.append(lineSep+separator+" Version Info "+ separator+lineSep); + vInfo.append(left("Version",20,' ') + ": "+ ClientComms.VERSION + lineSep); + vInfo.append(left("Build Level",20,' ') + ": "+ ClientComms.BUILD_LEVEL + lineSep); + vInfo.append(separator+separator+separator+lineSep); + log.fine(className,"dumpVersion", vInfo.toString()); + } + + /** + * Dump the current set of system.properties to a log record + */ + public void dumpSystemProperties() { + + Properties sysProps = System.getProperties(); + log.fine(className,"dumpSystemProperties", dumpProperties(sysProps, "SystemProperties").toString()); + } + + /** + * Dump interesting variables from ClientState + */ + public void dumpClientState() { + Properties props = null; + if (comms != null && comms.getClientState() != null ) { + props = comms.getClientState().getDebug(); + log.fine(className,"dumpClientState", dumpProperties(props, clientID + " : ClientState").toString()); + } + } + + /** + * Dump interesting variables from ClientComms + */ + public void dumpClientComms() { + Properties props = null; + if (comms != null) { + props = comms.getDebug(); + log.fine(className,"dumpClientComms", dumpProperties(props, clientID + " : ClientComms").toString()); + } + } + + /** + * Dump Connection options + */ + public void dumpConOptions() { + Properties props = null; + if (comms != null) { + props = comms.getConOptions().getDebug(); + log.fine(className,"dumpConOptions", dumpProperties(props, clientID + " : Connect Options").toString()); + } + } + + + /** + * Return a set of properties as a formatted string + */ + public static String dumpProperties(Properties props, String name) { + + StringBuffer propStr = new StringBuffer(); + Enumeration propsE = props.propertyNames(); + propStr.append(lineSep+separator+" "+name+" "+ separator+lineSep); + while (propsE.hasMoreElements()) { + String key = (String)propsE.nextElement(); + propStr.append(left(key,28,' ') + ": "+ props.get(key)+lineSep); + } + propStr.append(separator+separator+separator+lineSep); + + return propStr.toString(); + } + + /** + * Left justify a string. + * + * @param s the string to justify + * @param width the field width to justify within + * @param fillChar the character to fill with + * + * @return the justified string. + */ + public static String left(String s, int width, char fillChar) { + if (s.length() >= width) { + return s; + } + StringBuffer sb = new StringBuffer(width); + sb.append(s); + for (int i = width - s.length(); --i >= 0;) { + sb.append(fillChar); + } + return sb.toString(); + } + +} diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/util/package.html b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/util/package.html new file mode 100644 index 0000000..19c41d8 --- /dev/null +++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/util/package.html @@ -0,0 +1,5 @@ + +Provides helpers and utilities. + + + \ No newline at end of file diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/IMqttActionListener.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/IMqttActionListener.java deleted file mode 100644 index 53d26b8..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/IMqttActionListener.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.eclipse.paho.client.mqttv3; - -/** - * Implementors of this interface will be notified when an asynchronous action completes. - * - *

              A listener is registered on an MqttToken and a token is associated - * with an action like connect or publish. When used with tokens on the MqttAsyncClient - * the listener will be called back on the MQTT clients thread. The listener will be informed - * if the action succeeds or fails. It is important that the listener returns control quickly - * otherwise the operation of the MQTT client will be stalled. - *

              - */ -public interface IMqttActionListener { - /** - * This method is invoked when an action has completed successfully. - * @param asyncActionToken associated with the action that has completed - */ - public void onSuccess(IMqttToken asyncActionToken ); - /** - * This method is invoked when an action fails. - * If a client is disconnected while an action is in progress - * onFailure will be called. For connections - * that use clean session set to false, any QOS 1 and 2 messages that - * are in the process of being delivered will be delivered to the requested - * quality of service next time the client connects. - * @param asyncActionToken associated with the action that has failed - */ - public void onFailure(IMqttToken asyncActionToken, Throwable exception); -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/IMqttAsyncClient.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/IMqttAsyncClient.java deleted file mode 100644 index 6201ed5..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/IMqttAsyncClient.java +++ /dev/null @@ -1,713 +0,0 @@ -package org.eclipse.paho.client.mqttv3; - -/** - * Enables an application to communicate with an MQTT server using using non-blocking methods. - *

              - * It provides applications a simple programming interface to all features of the MQTT version 3.1 - * specification including: - *

                - *
              • connect - *
              • publish - *
              • subscribe - *
              • unsubscribe - *
              • disconnect - *
              - *

              - *

              - * There are two styles of MQTT client, this one and {@link IMqttClient}. - *

                - *
              • IMqttAsyncClient provides a set of non blocking methods that return control to the - * invoking application after initial validation of parameters and state. The main processing is - * performed in the background so as not to block the application programs thread. This non - * blocking approach is handy when the application needs to carry on processing while the - * MQTT action takes place. For instance connecting to an MQTT server can take time, using - * the non blocking connect method allows an application to display a busy indicator while the - * connect action takes place in the background. Non blocking methods are particularly useful - * in event oriented programs and graphical programs where invoking methods that take time - * to complete on the the main or GUI thread can cause problems. The non-blocking interface - * can also be used in blocking form.
              • - *
              • IMqttClient provides a set of methods that block and return control to the application - * program once the MQTT action has completed. It is a thin layer that sits on top of - * IMqttAsyncClient implementation and is provided mainly for compatibility with earlier - * versions of the MQTT client. In most circumstances it is recommended to use IMqttAsyncClient - * based clients which allow an application to mix both non-blocking and blocking calls.
              • - *
              - *

              - *

              - * An application is not restricted to using one style, if an IMqttAsyncClient based client is used - * as both blocking and non-blocking methods can be used in the same application. If an IMqttClient - * based client is used then only blocking methods are available to the application. - * For more details on the blocking client see {@link IMqttClient}

              - * - *

              There are two forms of non-blocking method: - *

                - *
              1. - *
                - *     IMqttToken token = asyncClient.method(parms)
                - *     
                - *

                In this form the method returns a token that can be used to track the - * progress of the action (method). The method provides a waitForCompletion() - * method that once invoked will block until the action completes. Once - * completed there are method on the token that can be used to check if the - * action completed successfully or not. For example - * to wait until a connect completes: - *

                - *      IMqttToken conToken;
                - *   	conToken = asyncClient.client.connect(conToken);
                - *     ... do some work...
                - *   	conToken.waitForCompletion();
                - *     
                - * /p> - *

                To turn a method into a blocking invocation the following form can be used: - *

                - *     IMqttToken token;
                - *     token = asyncClient.method(parms).waitForCompletion();
                - *     
                - - *
              2. - * - *
              3. - *
                - *     IMqttToken token method(parms, Object userContext, IMqttActionListener callback)
                - *     
                - *

                In this form a callback is registered with the method. The callback will be - * notified when the action succeeds or fails. The callback is invoked on the thread - * managed by the MQTT client so it is important that processing is minimised in the - * callback. If not the operation of the MQTT client will be inhibited. For example - * to be notified (called back) when a connect completes: - *

                - *     	IMqttToken conToken;	
                - *	    conToken = asyncClient.connect("some context",new new MqttAsyncActionListener() {			
                - *			public void onSuccess(IMqttToken asyncActionToken) {
                - *				log("Connected");
                - *			}
                - *				
                - *			public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
                - *				log ("connect failed" +exception);
                - *			}
                - *		  });
                - *	    An optional context object can be passed into the method which will then be made
                - *      available in the callback. The context is stored by the MQTT client) in the token
                - *      which is then returned to the invoker. The token is provided to the callback methods
                - *      where the context can then be accessed. 
                - *     
                - *

                - *
              4. - *
              - *

              To understand when the delivery of a message is complete either of the two methods above - * can be used to either wait on or be notified when the publish completes. An alternative is to - * use the {@link MqttCallback#deliveryComplete(IMqttDeliveryToken)} method which will - * also be notified when a message has been delivered to the requested quality of service.

              - * - */ -public interface IMqttAsyncClient { - /** - * Connects to an MQTT server using the default options. - *

              The default options are specified in {@link MqttConnectOptions} class. - *

              - * - * @throws MqttSecurityException for security related problems - * @throws MqttException for non security related problems - * @return token used to track and wait for the connect to complete. The token - * will be passed to the callback methtods if a callback is set. - * @see #connect(MqttConnectOptions, Object, IMqttActionListener) - */ - public IMqttToken connect() throws MqttException, MqttSecurityException; - - /** - * Connects to an MQTT server using the provided connect options. - *

              The connection will be established using the options specified in the - * {@link MqttConnectOptions} parameter. - *

              - * - * @param options a set of connection parameters that override the defaults. - * @throws MqttSecurityException for security related problems - * @throws MqttException for non security related problems - * @return token used to track and wait for the connect to complete. The token - * will be passed to any callback that has been set. - * @see #connect(MqttConnectOptions, Object, IMqttActionListener) - */ - public IMqttToken connect(MqttConnectOptions options) throws MqttException, MqttSecurityException ; - /** - * Connects to an MQTT server using the default options. - *

              The default options are specified in {@link MqttConnectOptions} class. - *

              - * - * @param userContext optional object used to pass context to the callback. Use - * null if not required. - * @param callback optional listener that will be notified when the connect completes. Use - * null if not required. - * @throws MqttSecurityException for security related problems - * @throws MqttException for non security related problems - * @return token used to track and wait for the connect to complete. The token - * will be passed to any callback that has been set. - * @see #connect(MqttConnectOptions, Object, IMqttActionListener) - */ - public IMqttToken connect(Object userContext, IMqttActionListener callback) throws MqttException, MqttSecurityException; - - - /** - * Connects to an MQTT server using the specified options. - *

              The server to connect to is specified on the constructor. - * It is recommended to call {@link #setCallback(MqttCallback)} prior to - * connecting in order that messages destined for the client can be accepted - * as soon as the client is connected. - *

              - *

              The method returns control before the connect completes. Completion can - * be tracked by: - *

                - *
              • Waiting on the returned token {@link IMqttToken#waitForCompletion()} or
              • - *
              • Passing in a callback {@link IMqttActionListener}
              • - *
              - *

              - * - * @param options a set of connection parameters that override the defaults. - * @param userContext optional object for used to pass context to the callback. Use - * null if not required. - * @param callback optional listener that will be notified when the connect completes. Use - * null if not required. - * @return token used to track and wait for the connect to complete. The token - * will be passed to any callback that has been set. - * @throws MqttSecurityException for security related problems - * @throws MqttException for non security related problems including communication errors - */ - public IMqttToken connect(MqttConnectOptions options, Object userContext, IMqttActionListener callback) throws MqttException, MqttSecurityException; - - /** - * Disconnects from the server. - *

              An attempt is made to quiesce the client allowing outstanding - * work to complete before disconnecting. It will wait - * for a maximum of 30 seconds for work to quiesce before disconnecting. - * This method must not be called from inside {@link MqttCallback} methods. - *

              - * - * @return token used to track and wait for disconnect to complete. The token - * will be passed to any callback that has been set. - * @throws MqttException for problems encountered while disconnecting - * @see #disconnect(long, Object, IMqttActionListener) - */ - public IMqttToken disconnect( ) throws MqttException; - - /** - * Disconnects from the server. - *

              An attempt is made to quiesce the client allowing outstanding - * work to complete before disconnecting. It will wait - * for a maximum of the specified quiesce time for work to complete before disconnecting. - * This method must not be called from inside {@link MqttCallback} methods. - *

              - * @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. - * @return token used to track and wait for disconnect to complete. The token - * will be passed to the callback methtods if a callback is set. - * @throws MqttException for problems encountered while disconnecting - * @see #disconnect(long, Object, IMqttActionListener) - */ - public IMqttToken disconnect(long quiesceTimeout) throws MqttException; - - /** - * Disconnects from the server. - *

              An attempt is made to quiesce the client allowing outstanding - * work to complete before disconnecting. It will wait - * for a maximum of 30 seconds for work to quiesce before disconnecting. - * This method must not be called from inside {@link MqttCallback} methods. - *

              - * - * @param userContext optional object used to pass context to the callback. Use - * null if not required. - * @param callback optional listener that will be notified when the disconnect completes. Use - * null if not required. - * @return token used to track and wait for the disconnect to complete. The token - * will be passed to any callback that has been set. - * @throws MqttException for problems encountered while disconnecting - * @see #disconnect(long, Object, IMqttActionListener) - */ - public IMqttToken disconnect( Object userContext, IMqttActionListener callback) throws MqttException; - - /** - * Disconnects from the server. - *

              - * The client will wait for {@link MqttCallback} methods to - * complete. It will then wait for up to the quiesce timeout to allow for - * work which has already been initiated to complete. For instance when a QOS 2 - * message has started flowing to the server but the QOS 2 flow has not completed.It - * prevents new messages being accepted and does not send any messages that have - * been accepted but not yet started delivery across the network to the server. When - * work has completed or after the quiesce timeout, the client will disconnect from - * the server. If the cleansession flag was set to false and is set to false the - * next time a connection is made QoS 1 and 2 messages that - * were not previously delivered will be delivered.

              - *

              This method must not be called from inside {@link MqttCallback} methods.

              - *

              The method returns control before the disconnect completes. Completion can - * be tracked by: - *

                - *
              • Waiting on the returned token {@link IMqttToken#waitForCompletion()} or
              • - *
              • Passing in a callback {@link IMqttActionListener}
              • - *
              - *

              - * - * @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 userContext optional object used to pass context to the callback. Use - * null if not required. - * @param callback optional listener that will be notified when the disconnect completes. Use - * null if not required. - * @return token used to track and wait for the connect to complete. The token - * will be passed to any callback that has been set. - * @throws MqttException for problems encountered while disconnecting - */ - public IMqttToken disconnect(long quiesceTimeout, Object userContext, IMqttActionListener callback) throws MqttException; - - - /** - * Determines if this client is currently connected to the server. - * - * @return true if connected, false otherwise. - */ - public boolean isConnected(); - - /** - * Returns the client ID used by this client. - *

              All clients connected to the - * same server or server farm must have a unique ID. - *

              - * - * @return the client ID used by this client. - */ - public String getClientId(); - - /** - * Returns the address of the server used by this client. - *

              The format of the returned String is the same as that used on the constructor. - *

              - * - * @return the server's address, as a URI String. - * @see MqttAsyncClient#MqttAsyncClient(String, String) - */ - public String getServerURI(); - - /** - * Publishes a message to a topic on the server - *

              A convenience method, which will - * create a new {@link MqttMessage} object with a byte array payload and the - * specified QoS, and then publish it. - *

              - * - * @param topic to deliver the message to, for example "finance/stock/ibm". - * @param payload the byte array to use as the payload - * @param qos the Quality of Service to deliver the message at. Valid values are 0, 1 or 2. - * @param retained whether or not this message should be retained by the server. - * @return token used to track and wait for the publish to complete. The token - * will be passed to any callback that has been set. - * @throws MqttPersistenceException when a problem occurs storing the message - * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2. - * @throws MqttException for other errors encountered while publishing the message. - * For instance if too many messages are being processed. - * @see #publish(String, MqttMessage, Object, IMqttActionListener) - * @see MqttMessage#setQos(int) - * @see MqttMessage#setRetained(boolean) - */ - public IMqttDeliveryToken publish(String topic, byte[] payload, int qos, - boolean retained ) throws MqttException, MqttPersistenceException; - - /** - * Publishes a message to a topic on the server - *

              A convenience method, which will - * create a new {@link MqttMessage} object with a byte array payload and the - * specified QoS, and then publish it. - *

              - * - * @param topic to deliver the message to, for example "finance/stock/ibm". - * @param payload the byte array to use as the payload - * @param qos the Quality of Service to deliver the message at. Valid values are 0, 1 or 2. - * @param retained whether or not this message should be retained by the server. - * @param userContext optional object used to pass context to the callback. Use - * null if not required. - * @param callback optional listener that will be notified when message delivery - * hsa completed to the requested quality of service - * @return token used to track and wait for the publish to complete. The token - * will be passed to any callback that has been set. - * @throws MqttPersistenceException when a problem occurs storing the message - * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2. - * @throws MqttException for other errors encountered while publishing the message. - * For instance client not connected. - * @see #publish(String, MqttMessage, Object, IMqttActionListener) - * @see MqttMessage#setQos(int) - * @see MqttMessage#setRetained(boolean) - */ - public IMqttDeliveryToken publish(String topic, byte[] payload, int qos, - boolean retained, Object userContext, IMqttActionListener callback ) throws MqttException, MqttPersistenceException; - - /** - * Publishes a message to a topic on the server - * Takes an {@link MqttMessage} message and delivers it to the server at the - * requested quality of service. - * - * @param topic to deliver the message to, for example "finance/stock/ibm". - * @param message to deliver to the server - * @return token used to track and wait for the publish to complete. The token - * will be passed to any callback that has been set. - * @throws MqttPersistenceException when a problem occurs storing the message - * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2. - * @throws MqttException for other errors encountered while publishing the message. - * For instance client not connected. - * @see #publish(String, MqttMessage, Object, IMqttActionListener) - */ - public IMqttDeliveryToken publish(String topic, MqttMessage message ) throws MqttException, MqttPersistenceException; - - /** - * Publishes a message to a topic on the server. - *

              - * Once this method has returned cleanly, the message has been accepted for publication by the - * client and will be delivered on a background thread. - * In the event the connection fails or the client stops. Messages will be delivered to the - * requested quality of service once the connection is re-established to the server on condition that: - *

                - *
              • The connection is re-established with the same clientID - *
              • The original connection was made with (@link MqttConnectOptions#setCleanSession(boolean)} - * set to false - *
              • The connection is re-established with (@link MqttConnectOptions#setCleanSession(boolean)} - * set to false - * - *

                - * - *

                When building 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.
                • - *
                - *

                - *

                The method returns control before the publish completes. Completion can - * be tracked by: - *

                  - *
                • Setting an {@link IMqttAsyncClient#setCallback(MqttCallback)} where the - * {@link MqttCallback#deliveryComplete(IMqttDeliveryToken)} - * method will be called.
                • - *
                • Waiting on the returned token {@link MqttToken#waitForCompletion()} or
                • - *
                • Passing in a callback {@link IMqttActionListener} to this method
                • - *
                - *

                - * - * @param topic to deliver the message to, for example "finance/stock/ibm". - * @param message to deliver to the server - * @param userContext optional object used to pass context to the callback. Use - * null if not required. - * @param callback optional listener that will be notified when message delivery - * has completed to the requested quality of service - * @return token used to track and wait for the publish to complete. The token - * will be passed to callback methtods if set. - * @throws MqttPersistenceException when a problem occurs storing the message - * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2. - * @throws MqttException for other errors encountered while publishing the message. - * For instance client not connected. - * @see MqttMessage - */ - public IMqttDeliveryToken publish(String topic, MqttMessage message, - Object userContext, IMqttActionListener callback) throws MqttException, MqttPersistenceException; - - /** - * Subscribe to a topic, which may include wildcards. - * - * @see #subscribe(String[], int[], Object, IMqttActionListener) - * - * @param topicFilter the topic to subscribe to, which can include wildcards. - * @param qos the maximum quality of service at which to subscribe. Messages - * published at a lower quality of service will be received at the published - * QOS. Messages published at a higher quality of service will be received using - * the QOS specified on the subscribe. - * @return token used to track and wait for the subscribe to complete. The token - * will be passed to callback methtods if set. - * @throws MqttException if there was an error registering the subscription. - */ - public IMqttToken subscribe(String topicFilter, int qos) throws MqttException; - - /** - * Subscribe to a topic, which may include wildcards. - * - * @see #subscribe(String[], int[], Object, IMqttActionListener) - * - * @param topicFilter the topic to subscribe to, which can include wildcards. - * @param qos the maximum quality of service at which to subscribe. Messages - * published at a lower quality of service will be received at the published - * QOS. Messages published at a higher quality of service will be received using - * the QOS specified on the subscribe. - * @param userContext optional object used to pass context to the callback. Use - * null if not required. - * @param callback optional listener that will be notified when subscribe - * has completed - * @return token used to track and wait for the subscribe to complete. The token - * will be passed to callback methtods if set. - * @throws MqttException if there was an error registering the subscription. - */ - public IMqttToken subscribe(String topicFilter, int qos, Object userContext, IMqttActionListener callback) - throws MqttException; - - /** - * Subscribe to multiple topics, each of which may include wildcards. - * - *

                Provides an optimised way to subscribe to multiple topics compared to - * subscribing to each one individually.

                - * - * @see #subscribe(String[], int[], Object, IMqttActionListener) - * - * @param topicFilters one or more topics to subscribe to, which can include wildcards - * @param qos the maximum quality of service at which to subscribe. Messages - * published at a lower quality of service will be received at the published - * QOS. Messages published at a higher quality of service will be received using - * the QOS specified on the subscribe. - * @return token used to track and wait for the subscribe to complete. The token - * will be passed to callback methtods if set. - * @throws MqttException if there was an error registering the subscription. - */ - public IMqttToken subscribe(String[] topicFilters, int[] qos) throws MqttException; - - /** - * Subscribes to multiple topics, each of which may include wildcards. - *

                Provides an optimised way to subscribe to multiple topics compared to - * subscribing to each one individually.

                - *

                The {@link #setCallback(MqttCallback)} method - * should be called before this method, otherwise any received messages - * will be discarded. - *

                - *

                - * If (@link MqttConnectOptions#setCleanSession(boolean)} was set to true - * when when connecting to the server then the subscription remains in place - * until either: - *

                  - *
                • The client disconnects
                • - *
                • An unsubscribe method is called to un-subscribe the topic
                • - * - *

                  - *

                  - * If (@link MqttConnectOptions#setCleanSession(boolean)} was set to false - * when connecting to the server then the subscription remains in place - * until either: - *

                    - *
                  • An unsubscribe method is called to un-subscribe the topic
                  • - *
                  • The next time the client connects with CleanSession set to true
                  - * - * With CleanSession set to false the MQTT server will store messages on - * behalf of the client when the client is not connected. The next time the - * client connects with the same client ID the server will - * deliver the stored messages to the client. - *

                  - * - *

                  The "topic filter" string used when subscribing - * may contain special characters, which allow you to subscribe to multiple topics - * at once.

                  - *

                  The topic level separator is used to introduce structure into the topic, and - * can therefore be specified within the topic for that purpose. The multi-level - * wildcard and single-level wildcard can be used for subscriptions, but they - * cannot be used within a topic by the publisher of a message. - *

                  - *
                  Topic level separator
                  - *
                  The forward slash (/) is used to separate each level within - * a topic tree and provide a hierarchical structure to the topic space. The - * use of the topic level separator is significant when the two wildcard characters - * are encountered in topics specified by subscribers.
                  - * - *
                  Multi-level wildcard
                  - *

                  The number sign (#) is a wildcard character that matches - * any number of levels within a topic. For example, if you subscribe to - * finance/stock/ibm/#, you receive - * messages on these topics: - *

                     finance/stock/ibm
                  finance/stock/ibm/closingprice
                  finance/stock/ibm/currentprice
                  - *

                  - *

                  The multi-level wildcard - * can represent zero or more levels. Therefore, finance/# can also match - * the singular finance, where # represents zero levels. The topic - * level separator is meaningless in this context, because there are no levels - * to separate.

                  - * - *

                  The multi-level wildcard can - * be specified only on its own or next to the topic level separator character. - * Therefore, # and finance/# are both valid, but finance# is - * not valid. The multi-level wildcard must be the last character - * used within the topic tree. For example, finance/# is valid but - * finance/#/closingprice is not valid.

                  - * - *
                  Single-level wildcard
                  - *

                  The plus sign (+) is a wildcard character that matches only one topic - * level. For example, finance/stock/+ matches - * finance/stock/ibm and finance/stock/xyz, - * but not finance/stock/ibm/closingprice. Also, because the single-level - * wildcard matches only a single level, finance/+ does not match finance.

                  - * - *

                  Use - * the single-level wildcard at any level in the topic tree, and in conjunction - * with the multilevel wildcard. Specify the single-level wildcard next to the - * topic level separator, except when it is specified on its own. Therefore, - * + and finance/+ are both valid, but finance+ is - * not valid. The single-level wildcard can be used at the end of the - * topic tree or within the topic tree. - * For example, finance/+ and finance/+/ibm are both valid.

                  - *
                  - *
                  - *

                  - *

                  The method returns control before the subscribe completes. Completion can - * be tracked by: - *

                    - *
                  • Waiting on the supplied token {@link MqttToken#waitForCompletion()} or
                  • - *
                  • Passing in a callback {@link IMqttActionListener} to this method
                  • - *
                  - *

                  - * - * @param topicFilters one or more topics to subscribe to, which can include wildcards - * @param qos the maximum quality of service to subscribe each topic at.Messages - * published at a lower quality of service will be received at the published - * QOS. Messages published at a higher quality of service will be received using - * the QOS specified on the subscribe. - * @param userContext optional object used to pass context to the callback. Use - * null if not required. - * @param callback optional listener that will be notified when subscribe - * has completed - * @return token used to track and wait for the subscribe to complete. The token - * will be passed to callback methtods if set. - * @throws MqttException if there was an error registering the subscription. - * @throws IllegalArgumentException if the two supplied arrays are not the same size. - */ - public IMqttToken subscribe(String[] topicFilters, int[] qos, Object userContext, IMqttActionListener callback) - throws MqttException; - - /** - * Requests the server unsubscribe the client from a topic. - * - * @see #unsubscribe(String[], Object, IMqttActionListener) - * @param topicFilter the topic to unsubscribe from. It must match a topicFilter - * specified on an earlier subscribe. - * @return token used to track and wait for the unsubscribe to complete. The token - * will be passed to callback methtods if set. - * @throws MqttException if there was an error unregistering the subscription. - */ - public IMqttToken unsubscribe(String topicFilter) throws MqttException; - - /** - * Requests the server unsubscribe the client from one or more topics. - * - * @see #unsubscribe(String[], Object, IMqttActionListener) - * - * @param topicFilters one or more topics to unsubscribe from. Each topicFilter - * must match one specified on an earlier subscribe. * - * @return token used to track and wait for the unsubscribe to complete. The token - * will be passed to callback methtods if set. - * @throws MqttException if there was an error unregistering the subscription. - */ - public IMqttToken unsubscribe(String[] topicFilters) throws MqttException; - - /** - * Requests the server unsubscribe the client from a topics. - * - * @see #unsubscribe(String[], Object, IMqttActionListener) - * - * @param topicFilter the topic to unsubscribe from. It must match a topicFilter - * specified on an earlier subscribe. - * @param userContext optional object used to pass context to the callback. Use - * null if not required. - * @param callback optional listener that will be notified when unsubscribe - * has completed - * @return token used to track and wait for the unsubscribe to complete. The token - * will be passed to callback methtods if set. - * @throws MqttException if there was an error unregistering the subscription. - */ - public IMqttToken unsubscribe(String topicFilter, Object userContext, IMqttActionListener callback) - throws MqttException; - - /** - * Requests the server unsubscribe the client from one or more topics - *

                  - * Unsubcribing is the opposite of subscribing. When the server receives the - * unsubscribe request it looks to see if it can find a matching subscription for the - * client and then removes it. After this point the server will send no more - * messages to the client for this subscription. - *

                  - *

                  The topic(s) specified on the unsubscribe must match the topic(s) - * specified in the original subscribe request for the unsubscribe to succeed - *

                  - *

                  The method returns control before the unsubscribe completes. Completion can - * be tracked by: - *

                    - *
                  • Waiting on the returned token {@link MqttToken#waitForCompletion()} or
                  • - *
                  • Passing in a callback {@link IMqttActionListener} to this method
                  • - *
                  - *

                  - * - * @param topicFilters one or more topics to unsubscribe from. Each topicFilter - * must match one specified on an earlier subscribe. - * @param userContext optional object used to pass context to the callback. Use - * null if not required. - * @param callback optional listener that will be notified when unsubscribe - * has completed - * @return token used to track and wait for the unsubscribe to complete. The token - * will be passed to callback methtods if set. - * @throws MqttException if there was an error unregistering the subscription. - */ - public IMqttToken unsubscribe(String[] topicFilters, Object userContext, IMqttActionListener callback) - throws MqttException; - - - /** - * Sets a callback listener to use for events that happen asynchronously. - *

                  There are a number of events that the listener will be notified about. - * These include: - *

                    - *
                  • A new message has arrived and is ready to be processed
                  • - *
                  • The connection to the server has been lost
                  • - *
                  • Delivery of a message to the server has completed
                  • - *
                  - *

                  - *

                  Other events that track the progress of an individual operation such - * as connect and subscribe can be tracked using the {@link MqttToken} returned from - * each non-blocking method or using setting a {@link IMqttActionListener} on the - * non-blocking method.

                  - * @see MqttCallback - * @param callback which will be invoked for certain asyncrhonous events - */ - public void setCallback(MqttCallback callback); - - /** - * Returns the delivery tokens for any outstanding publish operations. - *

                  If a client has been restarted and there are messages that were in the - * process of being delivered when the client stopped this method - * returns a token for each in-flight message enabling the delivery to be tracked - * Alternately the {@link MqttCallback#deliveryComplete(IMqttDeliveryToken)} - * callback can be used to track the delivery of outstanding messages. - *

                  - *

                  If a client connects with cleansession true then there will be no - * delivery tokens as the cleansession option deletes all earlier state. - * For state to be remembered the client must connect with cleansession - * set to false

                  - * @return zero or more delivery tokens - */ - public IMqttDeliveryToken[] getPendingDeliveryTokens(); - - /** - * Close the client - * Releases all resource associated with the client. After the client has - * been closed it cannot be reused. For instance attempts to connect will fail. - * @throws MqttException if the client is not disconnected. - */ - public void close() throws MqttException; -} \ No newline at end of file diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/IMqttClient.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/IMqttClient.java deleted file mode 100644 index 8f12ac3..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/IMqttClient.java +++ /dev/null @@ -1,462 +0,0 @@ -package org.eclipse.paho.client.mqttv3; - -/** - * Enables an application to communicate with an MQTT server using using blocking methods. - *

                  - * This interface allows applications to utilise all features of the MQTT version 3.1 - * specification including: - *

                    - *
                  • connect - *
                  • publish - *
                  • subscribe - *
                  • unsubscribe - *
                  • disconnect - *
                  - *

                  - *

                  - * There are two styles of MQTT client, this one and {@link IMqttAsyncClient}. - *

                    - *
                  • IMqttClient provides a set of methods that block and return control to the application - * program once the MQTT action has completed.
                  • - *
                  • IMqttAsyncClient provides a set of non blocking methods that return control to the - * invoking application after initial validation of parameters and state. The main processing is - * performed in the background so as not to block the application programs thread. This non - * blocking approach is handy when the application wants to carry on processing while the - * MQTT action takes place. For instance connecting to an MQTT server can take time, using - * the non blocking connect method allows an application to display a busy indicator while the - * connect action is occurring. Non blocking methods are particularly useful in event oriented - * programs and graphical programs where issuing methods that take time to complete on the the - * main or GUI thread can cause problems.
                  • - *
                  - *

                  - *

                  - * The non-blocking client can also be used in a blocking form by turning a non-blocking - * method into a blocking invocation using the following pettern: - *

                  - *     IMqttToken token;
                  - *     token = asyncClient.method(parms).waitForCompletion();
                  - *     
                  - * Using the non-blocking client allows an application to use a mixture of blocking and - * non-blocking styles. Using the blocking client only allows an application to use one - * style. The blocking client provides compatibility with earlier versions - * of the MQTT client.

                  - */ -public interface IMqttClient { //extends IMqttAsyncClient { - /** - * Connects to an MQTT server using the default options. - *

                  The default options are specified in {@link MqttConnectOptions} class. - *

                  - * - * @throws MqttSecurityException when the server rejects the connect for security - * reasons - * @throws MqttException for non security related problems - * @see #connect(MqttConnectOptions) - */ - public void connect() throws MqttSecurityException, MqttException; - - /** - * Connects to an MQTT server using the specified options. - *

                  The server to connect to is specified on the constructor. - * It is recommended to call {@link #setCallback(MqttCallback)} prior to - * connecting in order that messages destined for the client can be accepted - * as soon as the client is connected. - *

                  - *

                  This is a blocking method that returns once connect completes

                  - * - * @param options a set of connection parameters that override the defaults. - * @throws MqttSecurityException when the server rejects the connect for security - * reasons - * @throws MqttException for non security related problems including communication errors - */ - public void connect(MqttConnectOptions options) throws MqttSecurityException, MqttException; - - /** - * Disconnects from the server. - *

                  An attempt is made to quiesce the client allowing outstanding - * work to complete before disconnecting. It will wait - * for a maximum of 30 seconds for work to quiesce before disconnecting. - * This method must not be called from inside {@link MqttCallback} methods. - *

                  - * - * @see #disconnect(long) - */ - public void disconnect() throws MqttException; - - /** - * Disconnects from the server. - *

                  - * The client will wait for all {@link MqttCallback} methods to - * complete. It will then wait for up to the quiesce timeout to allow for - * work which has already been initiated to complete - for example, it will - * wait for the QoS 2 flows from earlier publications to complete. When work has - * completed or after the quiesce timeout, the client will disconnect from - * the server. If the cleansession flag was set to false and is set to false the - * next time a connection is made QoS 1 and 2 messages that - * were not previously delivered will be delivered.

                  - * - *

                  This is a blocking method that returns once disconnect completes

                  - * - * @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. - * @throws MqttException if a problem is encountered while disconnecting - */ - public void disconnect(long quiesceTimeout) throws MqttException; - - /** - * Subscribe to a topic, which may include wildcards using a QOS of 1. - * - * @see #subscribe(String[], int[]) - * - * @param topicFilter the topic to subscribe to, which can include wildcards. - * @throws MqttException if there was an error registering the subscription. - */ - public void subscribe(String topicFilter) throws MqttException, MqttSecurityException; - - /** - * Subscribes to a one or more topics, which may include wildcards using a QOS of 1. - * - * @see #subscribe(String[], int[]) - * - * @param topicFilters the topic to subscribe to, which can include wildcards. - * @throws MqttException if there was an error registering the subscription. - */ - public void subscribe(String[] topicFilters) throws MqttException; - - /** - * Subscribe to a topic, which may include wildcards. - * - * @see #subscribe(String[], int[]) - * - * @param topicFilter the topic to subscribe to, which can include wildcards. - * @param qos the maximum quality of service at which to subscribe. Messages - * published at a lower quality of service will be received at the published - * QOS. Messages published at a higher quality of service will be received using - * the QOS specified on the subscribe. - * @throws MqttException if there was an error registering the subscription. - */ - public void subscribe(String topicFilter, int qos) throws MqttException; - - /** - * Subscribes to multiple topics, each of which may include wildcards. - *

                  The {@link #setCallback(MqttCallback)} method - * should be called before this method, otherwise any received messages - * will be discarded. - *

                  - *

                  - * If (@link MqttConnectOptions#setCleanSession(boolean)} was set to true - * when when connecting to the server then the subscription remains in place - * until either: - *

                    - *
                  • The client disconnects
                  • - *
                  • An unsubscribe method is called to un-subscribe the topic
                  • - * - *

                    - *

                    - * If (@link MqttConnectOptions#setCleanSession(boolean)} was set to false - * when when connecting to the server then the subscription remains in place - * until either: - *

                      - *
                    • An unsubscribe method is called to un-subscribe the topic
                    • - *
                    • The client connects with CleanSession set to true
                    - * - * With CleanSession set to false the MQTT server will store messages on - * behalf of the client when the client is not connected. The next time the - * client connects with the same client ID the server will - * deliver the stored messages to the client. - *

                    - * - *

                    The "topic filter" string used when subscribing - * may contain special characters, which allow you to subscribe to multiple topics - * at once.

                    - *

                    The topic level separator is used to introduce structure into the topic, and - * can therefore be specified within the topic for that purpose. The multi-level - * wildcard and single-level wildcard can be used for subscriptions, but they - * cannot be used within a topic by the publisher of a message. - *

                    - *
                    Topic level separator
                    - *
                    The forward slash (/) is used to separate each level within - * a topic tree and provide a hierarchical structure to the topic space. The - * use of the topic level separator is significant when the two wildcard characters - * are encountered in topics specified by subscribers.
                    - * - *
                    Multi-level wildcard
                    - *

                    The number sign (#) is a wildcard character that matches - * any number of levels within a topic. For example, if you subscribe to - * finance/stock/ibm/#, you receive - * messages on these topics: - *

                       finance/stock/ibm
                    finance/stock/ibm/closingprice
                    finance/stock/ibm/currentprice
                    - *

                    - *

                    The multi-level wildcard - * can represent zero or more levels. Therefore, finance/# can also match - * the singular finance, where # represents zero levels. The topic - * level separator is meaningless in this context, because there are no levels - * to separate.

                    - * - *

                    The multi-level wildcard can - * be specified only on its own or next to the topic level separator character. - * Therefore, # and finance/# are both valid, but finance# is - * not valid. The multi-level wildcard must be the last character - * used within the topic tree. For example, finance/# is valid but - * finance/#/closingprice is not valid.

                    - * - *
                    Single-level wildcard
                    - *

                    The plus sign (+) is a wildcard character that matches only one topic - * level. For example, finance/stock/+ matches - * finance/stock/ibm and finance/stock/xyz, - * but not finance/stock/ibm/closingprice. Also, because the single-level - * wildcard matches only a single level, finance/+ does not match finance.

                    - * - *

                    Use - * the single-level wildcard at any level in the topic tree, and in conjunction - * with the multilevel wildcard. Specify the single-level wildcard next to the - * topic level separator, except when it is specified on its own. Therefore, - * + and finance/+ are both valid, but finance+ is - * not valid. The single-level wildcard can be used at the end of the - * topic tree or within the topic tree. - * For example, finance/+ and finance/+/ibm are both valid.

                    - *
                    - *
                    - *

                    - * - *

                    This is a blocking method that returns once subscribe completes

                    - * - * @param topicFilters one or more topics to subscribe to, which can include wildcards. - * @param qos the maximum quality of service to subscribe each topic at.Messages - * published at a lower quality of service will be received at the published - * QOS. Messages published at a higher quality of service will be received using - * the QOS specified on the subscribe. - * @throws MqttException if there was an error registering the subscription. - * @throws IllegalArgumentException if the two supplied arrays are not the same size. - */ - public void subscribe(String[] topicFilters, int[] qos) throws MqttException; - - /** - * Requests the server unsubscribe the client from a topic. - * - * @see #unsubscribe(String[]) - * @param topicFilter the topic to unsubscribe from. It must match a topicFilter - * specified on the subscribe. - * @throws MqttException if there was an error unregistering the subscription. - */ - public void unsubscribe(String topicFilter) throws MqttException; - - /** - * Requests the server unsubscribe the client from one or more topics - *

                    - * Unsubcribing is the opposite of subscribing. When the server receives the - * unsubscribe request it looks to see if it can find a subscription for the - * client and then removes it. After this point the server will send no more - * messages to the client for this subscription. - *

                    - *

                    The topic(s) specified on the unsubscribe must match the topic(s) - * specified in the original subscribe request for the subscribe to succeed - *

                    - * - *

                    This is a blocking method that returns once unsubscribe completes

                    - * - * @param topicFilters one or more topics to unsubscribe from. Each topicFilter - * must match one specified on a subscribe - * @throws MqttException if there was an error unregistering the subscription. - */ - public void unsubscribe(String[] topicFilters) throws MqttException; - - - /** - * Publishes a message to a topic on the server and return once it is delivered - *

                    This is a convenience method, which will - * create a new {@link MqttMessage} object with a byte array payload and the - * specified QoS, and then publish it. All other values in the - * message will be set to the defaults. - *

                    - * - * @param topic to deliver the message to, for example "finance/stock/ibm". - * @param payload the byte array to use as the payload - * @param qos the Quality of Service to deliver the message at. Valid values are 0, 1 or 2. - * @param retained whether or not this message should be retained by the server. - * @throws MqttPersistenceException when a problem with storing the message - * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2. - * @throws MqttException for other errors encountered while publishing the message. - * For instance client not connected. - * @see #publish(String, MqttMessage) - * @see MqttMessage#setQos(int) - * @see MqttMessage#setRetained(boolean) - */ - public void publish(String topic, byte[] payload, int qos, boolean retained) throws MqttException, MqttPersistenceException; - - /** - * Publishes a message to a topic on the server. - *

                    - * Delivers a message to the server at the requested quality of service and returns control - * once the message has been delivered. In the event the connection fails or the client - * stops, any messages that are in the process of being delivered will be delivered once - * a connection is re-established to the server on condition that: - *

                      - *
                    • The connection is re-established with the same clientID - *
                    • The original connection was made with (@link MqttConnectOptions#setCleanSession(boolean)} - * set to false - *
                    • The connection is re-established with (@link MqttConnectOptions#setCleanSession(boolean)} - * set to false - *
                    - *

                    - *

                    In the event that the connection breaks or the client stops it is still possible to determine - * when the delivery of the message completes. Prior to re-establishing the connection to the server: - *

                      - *
                    • Register a {@link #setCallback(MqttCallback)} callback on the client and the delivery complete - * callback will be notified once a delivery of a message completes - *
                    • or call {@link #getPendingDeliveryTokens()} which will return a token for each message that - * is in-flight. The token can be used to wait for delivery to complete. - *
                    - *

                    - * - *

                    When building 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.
                    • - *
                    - *

                    - * - *

                    This is a blocking method that returns once publish completes

                    * - * - * @param topic to deliver the message to, for example "finance/stock/ibm". - * @param message to delivery to the server - * @throws MqttPersistenceException when a problem with storing the message - * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2. - * @throws MqttException for other errors encountered while publishing the message. - * For instance client not connected. - */ - public void publish(String topic, MqttMessage message) throws MqttException, MqttPersistenceException; - - /** - * Sets the callback listener to use for events that happen asynchronously. - *

                    There are a number of events that listener will be notified about. These include - *

                      - *
                    • A new message has arrived and is ready to be processed
                    • - *
                    • The connection to the server has been lost
                    • - *
                    • Delivery of a message to the server has completed.
                    • - *
                    - *

                    - *

                    Other events that track the progress of an individual operation such - * as connect and subscribe can be tracked using the {@link MqttToken} passed to the - * operation

                    - * @see MqttCallback - * @param callback the class to callback when for events related to the client - */ - public void setCallback(MqttCallback callback); - - /** - * Get a topic object which can be used to publish messages. - *

                    An alternative method that should be used in preference to this one when publishing a message is: - *

                      - *
                    • {@link MqttClient#publish(String, MqttMessage)} to publish a message in a blocking manner - *
                    • or use publish methods on the non blocking client like {@link IMqttAsyncClient#publish(String, MqttMessage, Object, IMqttActionListener)} - *
                    - *

                    - *

                    When building 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. - */ - public MqttTopic getTopic(String topic); - - /** - * Determines if this client is currently connected to the server. - * - * @return true if connected, false otherwise. - */ - public boolean isConnected(); - - /** - * Returns the client ID used by this client. - *

                    All clients connected to the - * same server or server farm must have a unique ID. - *

                    - * - * @return the client ID used by this client. - */ - public String getClientId(); - - /** - * Returns the address of the server used by this client, as a URI. - *

                    The format is the same as specified on the constructor. - *

                    - * - * @return the server's address, as a URI String. - * @see MqttAsyncClient#MqttAsyncClient(String, String) - */ - public String getServerURI(); - - /** - * Returns the delivery tokens for any outstanding publish operations. - *

                    If a client has been restarted and there are messages that were in the - * process of being delivered when the client stopped this method will - * return a token for each message enabling the delivery to be tracked - * Alternately the {@link MqttCallback#deliveryComplete(IMqttDeliveryToken)} - * callback can be used to track the delivery of outstanding messages. - *

                    - *

                    If a client connects with cleansession true then there will be no - * delivery tokens as the cleansession option deletes all earlier state. - * For state to be remembered the client must connect with cleansession - * set to false

                    - * @return zero or more delivery tokens - */ - public IMqttDeliveryToken[] getPendingDeliveryTokens(); - - /** - * Close the client - * Releases all resource associated with the client. After the client has - * been closed it cannot be reused. For instance attempts to connect will fail. - * @throws MqttException if the client is not disconnected. - */ - public void close() throws MqttException; -} \ No newline at end of file diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/IMqttDeliveryToken.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/IMqttDeliveryToken.java deleted file mode 100644 index 48a42e4..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/IMqttDeliveryToken.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.eclipse.paho.client.mqttv3; -/** - * Provides a mechanism for tracking the delivery of a message - * - *

                    A subclass of IMqttToken that allows the delivery of a message to be tracked. - * Unlike instances of IMqttToken delivery tokens can be used across connection - * and client restarts. This enables the delivery of a messages to be tracked - * after failures. There are two approaches - *

                      - *
                    • A list of delivery tokens for in-flight messages can be obtained using - * {@link IMqttAsyncClient#getPendingDeliveryTokens()}. The waitForCompletion - * method can then be used to block until the delivery is complete. - *
                    • A {@link MqttCallback} can be set on the client. Once a message has been - * delivered the {@link MqttCallback#deliveryComplete(IMqttDeliveryToken)} method will - * be called withe delivery token being passed as a parameter. - *
                    - *

                    - * An action is in progress until either: - *

                      - *
                    • isComplete() returns true or - *
                    • getException() is not null. If a client shuts down before delivery is complete. - * an exception is returned. As long as the Java Runtime is not stopped a delivery token - * is valid across a connection disconnect and reconnect. In the event the client - * is shut down the getPendingDeliveryTokens method can be used once the client is - * restarted to obtain a list of delivery tokens for inflight messages. - *
                    - *

                    - * - */ - -public interface IMqttDeliveryToken extends IMqttToken { - /** - * Returns the message associated with this token. - *

                    Until the message has been delivered, the message being delivered will - * be returned. Once the message has been delivered null will be - * returned. - * @return the message associated with this token or null if already delivered. - * @throws MqttException if there was a problem completing retrieving the message - */ - public MqttMessage getMessage() throws MqttException; -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/IMqttToken.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/IMqttToken.java deleted file mode 100644 index 8aaabc6..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/IMqttToken.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3; - - -/** - * Provides a mechanism for tracking the completion of an asynchronous task. - * - *

                    When using the asynchronous/non-blocking MQTT programming interface all - * methods /operations that take any time and in particular those that involve - * any network operation return control to the caller immediately. The operation - * then proceeds to run in the background so as not to block the invoking thread. - * An IMqttToken is used to track the state of the operation. An application can use the - * token to wait for an operation to complete. A token is passed to callbacks - * once the operation completes and provides context linking it to the original - * request. A token is associated with a single operation.

                    - *

                    - * An action is in progress until either: - *

                      - *
                    • isComplete() returns true or - *
                    • getException() is not null. - *
                    - *

                    - * - */ -public interface IMqttToken { - - /** - * Blocks the current thread until the action this token is associated with has - * completed. - * - * @throws MqttException if there was a problem with the action associated with the token. - * @see #waitForCompletion(long) - */ - public void waitForCompletion() throws MqttException; - - /** - * Blocks the current thread until the action this token is associated with has - * completed. - *

                    The timeout specifies the maximum time it will block for. If the action - * completes before the timeout then control returns immediately, if not - * it will block until the timeout expires.

                    - *

                    If the action being tracked fails or the timeout expires an exception will - * be thrown. In the event of a timeout the action may complete after timeout. - *

                    - * - * @param timeout the maximum amount of time to wait for, in milliseconds. - * @throws MqttException if there was a problem with the action associated with the token. - */ - public void waitForCompletion(long timeout) throws MqttException; - - /** - * Returns whether or not the action has finished. - *

                    True will be returned both in the case where the action finished successfully - * and in the case where it failed. If the action failed {@link #getException()} will - * be non null. - *

                    - */ - public boolean isComplete(); - - /** - * Returns an exception providing more detail if an operation failed - *

                    While an action in in progress and when an action completes successfully - * null will be returned. Certain errors like timeout or shutting down will not - * set the exception as the action has not failed or completed at that time - *

                    - * @return exception may return an exception if the operation failed. Null will be - * returned while action is in progress and if action completes successfully. - */ - public MqttException getException(); - - /** - * Register a listener to be notified when an action completes. - *

                    Once a listener is registered it will be invoked when the action the token - * is associated with either succeeds or fails. - *

                    - * @param listener to be invoked once the action completes - */ - public void setActionCallback(IMqttActionListener listener); - - /** - * Return the async listener for this token. - * @return listener that is set on the token or null if a listener is not registered. - */ - public IMqttActionListener getActionCallback(); - - /** - * Returns the MQTT client that is responsible for processing the asynchronous - * action - */ - public IMqttAsyncClient getClient(); - - /** - * Returns the topic string(s) for the action being tracked by this - * token. If the action has not been initiated or the action has not - * topic associated with it such as connect then null will be returned. - * - * @return the topic string(s) for the subscribe being tracked by this token or null - */ - public String[] getTopics(); - - /** - * Store some context associated with an action. - *

                    Allows the caller of an action to store some context that can be - * accessed from within the ActionListener associated with the action. This - * can be useful when the same ActionListener is associated with multiple - * actions

                    - * @param userContext to associate with an action - */ - public void setUserContext(Object userContext); - - /** - * Retrieve the context associated with an action. - *

                    Allows the ActionListener associated with an action to retrieve any context - * that was associated with the action when the action was invoked. If not - * context was provided null is returned.

                    - - * @return Object context associated with an action or null if there is none. - */ - public Object getUserContext(); - - /** - * Returns the message ID of the message that is associated with the token. - * A message id of zero will be returned for tokens associated with - * connect, disconnect and ping operations as there can only ever - * be one of these outstanding at a time. For other operations - * the MQTT message id flowed over the network. - */ - public int getMessageId(); - -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttAsyncClient.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttAsyncClient.java deleted file mode 100644 index 2a63346..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttAsyncClient.java +++ /dev/null @@ -1,747 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3; - -import java.util.Hashtable; -import java.util.Properties; - -import javax.net.SocketFactory; -import javax.net.ssl.SSLSocketFactory; - -import org.eclipse.paho.client.mqttv3.internal.ClientComms; -import org.eclipse.paho.client.mqttv3.internal.ExceptionHelper; -import org.eclipse.paho.client.mqttv3.internal.LocalNetworkModule; -import org.eclipse.paho.client.mqttv3.internal.NetworkModule; -import org.eclipse.paho.client.mqttv3.internal.SSLNetworkModule; -import org.eclipse.paho.client.mqttv3.internal.TCPNetworkModule; -import org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory; -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; - -/** - * 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 working while the - * MQTT action completes on a background thread. - * This implementation is compatible with all Java SE runtimes from 1.4.2 and up. - *

                    - *

                    An application can connect to an MQTT server using: - *

                      - *
                    • A plain TCP socket - *
                    • An 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 it cleared when a client disconnects. If - * connecting with cleansession set to false, to provide reliable message delivery - * then a persistent message store should be used such as the default one. - *

                    - *

                    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 { // DestinationProvider { - - private static final int URI_TYPE_TCP = 0; - private static final int URI_TYPE_SSL = 1; - private static final int URI_TYPE_LOCAL = 2; - - private String clientId; - private String serverURI; - private int serverURIType; - protected ClientComms comms; - private Hashtable topics; - private MqttClientPersistence persistence; - - final static String className = MqttAsyncClient.class.getName(); - public Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className); - - /** - * Create an MqttAsyncClient that can be used to communicate with an MQTT server. - *

                    - * The address of the server should be a URI, using a scheme of either - * "tcp://" for a TCP connection or "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 identified to connect to an MQTT server, it - * must be unique across all clients connecting to the same - * server. 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 are used and 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 default instance of {@link MqttDefaultFilePersistence} is used by - * the client. To specify a different persistence implementation, 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 - * @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 23 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 can be used to communicate with an MQTT server. - *

                    - * The address of the server should be a URI, using a scheme of either - * "tcp://" for a TCP connection or "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 identified to connect to an MQTT server, it - * must be unique across all clients connecting to the same - * server. 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 are used and 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.

                    - *

                    - * The persistence mechanism is used to enable reliable messaging. - * For qualities of server (QoS) 1 or 2 to work, messages must be persisted - * to disk by both the client and the server. If this is not done, then - * a failure in the client or server can result in lost messages. A pluggable - * persistence mechanism is supported via the {@link MqttClientPersistence} - * interface. A 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 - * @param clientId a client identifier that is unique on the server being connected to - * @param persistence the persistence mechanism to use. - * @throws IllegalArgumentException if the URI does not start with - * "tcp://", "ssl://" or "local://". - * @throws IllegalArgumentException if the clientId is null or is greater than 23 characters in length - * @throws MqttException if any other problem was encountered - */ - public MqttAsyncClient(String serverURI, String clientId, MqttClientPersistence persistence) throws MqttException { - - log.setResourceName(clientId); - - if (clientId == null || clientId.length() == 0 || clientId.length() > 23) { - throw new IllegalArgumentException(); - } - final String methodName = "MqttAsyncClient"; - - this.serverURI = serverURI; - this.serverURIType = validateURI(serverURI); - this.clientId = clientId; - - this.persistence = persistence; - if (this.persistence == null) { - this.persistence = new MemoryPersistence(); - } - - // @TRACE 101= ClientID={0} ServerURI={1} PersistenceType={2} - log.fine(className,methodName,"101",new Object[]{clientId,serverURI,persistence}); - - this.persistence.open(clientId, serverURI); - this.comms = new ClientComms(this, this.persistence); - this.persistence.close(); - this.topics = new Hashtable(); - - } - - private int validateURI(String srvURI) { - if (srvURI.startsWith("tcp://")) { - return URI_TYPE_TCP; - } - else if (srvURI.startsWith("ssl://")) { - return URI_TYPE_SSL; - } - else if (srvURI.startsWith("local://")) { - return URI_TYPE_LOCAL; - } - else { - throw new IllegalArgumentException(); - } - } - - /** - * Factory method to create the correct network module, based on the - * supplied address URI. - * - * @param address the URI for the server. - * @return a network module appropriate to the specified address. - */ - protected NetworkModule createNetworkModule(String address, MqttConnectOptions options) throws MqttException, MqttSecurityException { - final String methodName = "createNetworkModule"; - // @TRACE 115=URI={0} - log.fine(className,methodName, "115", new Object[] {address}); - - NetworkModule netModule; - String shortAddress; - String host; - int port; - SocketFactory factory = options.getSocketFactory(); - switch (serverURIType) { - case URI_TYPE_TCP: - shortAddress = address.substring(6); - host = getHostName(shortAddress); - port = getPort(shortAddress, 1883); - if (factory == null) { - factory = SocketFactory.getDefault(); - options.setSocketFactory(factory); - } - else if (factory instanceof SSLSocketFactory) { - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_SOCKET_FACTORY_MISMATCH); - } - netModule = new TCPNetworkModule(factory, host, port, clientId); - ((TCPNetworkModule)netModule).setConnectTimeout(options.getConnectionTimeout()); - break; - case URI_TYPE_SSL: - shortAddress = address.substring(6); - host = getHostName(shortAddress); - port = getPort(shortAddress, 8883); - SSLSocketFactoryFactory factoryFactory = null; - if (factory == null) { -// try { - factoryFactory = new SSLSocketFactoryFactory(); - Properties sslClientProps = options.getSSLProperties(); - if (null != sslClientProps) - factoryFactory.initialize(sslClientProps, null); - factory = factoryFactory.createSocketFactory(null); -// } -// catch (MqttDirectException ex) { -// throw ExceptionHelper.createMqttException(ex.getCause()); -// } - } - else if ((factory instanceof SSLSocketFactory) == false) { - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_SOCKET_FACTORY_MISMATCH); - } - - // Create the network module... - netModule = new SSLNetworkModule((SSLSocketFactory) factory, host, port, clientId); - ((SSLNetworkModule)netModule).setSSLhandshakeTimeout(options.getConnectionTimeout()); - // Ciphers suites need to be set, if they are available - if (factoryFactory != null) { - String[] enabledCiphers = factoryFactory.getEnabledCipherSuites(null); - if (enabledCiphers != null) { - ((SSLNetworkModule) netModule).setEnabledCiphers(enabledCiphers); - } - } - break; - case URI_TYPE_LOCAL: - netModule = new LocalNetworkModule(address.substring(8)); - break; - default: - // This shouldn't happen, as long as validateURI() has been called. - netModule = null; - } - return netModule; - } - - private int getPort(String uri, int defaultPort) { - int port; - int portIndex = uri.lastIndexOf(':'); - if (portIndex == -1) { - port = defaultPort; - } - else { - port = Integer.valueOf(uri.substring(portIndex + 1)).intValue(); - } - return port; - } - - private String getHostName(String uri) { - int schemeIndex = uri.lastIndexOf('/'); - int portIndex = uri.lastIndexOf(':'); - if (portIndex == -1) { - portIndex = uri.length(); - } - return uri.substring(schemeIndex + 1, 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); - } - - // @TRACE 103=cleanSession={0} connectionTimeout={1} TimekeepAlive={2} userName={3} password={4} will={5} userContext={6} callback={7} - log.fine(className,methodName, "103", - new Object[]{ - new Boolean(options.isCleanSession()), - new Integer(options.getConnectionTimeout()), - new Integer(options.getKeepAliveInterval()), - options.getUserName(), - ((null == options.getPassword())?"[null]":"[notnull]"), - ((null == options.getWillMessage())?"[null]":"[notnull]"), - userContext, - callback }); - comms.setNetworkModule(createNetworkModule(serverURI, options)); - - this.persistence.open(clientId, serverURI); - - if (options.isCleanSession()) { - persistence.clear(); - } - - MqttToken token = new MqttToken(getClientId()); - token.setActionCallback(callback); - token.setUserContext(userContext); - - comms.connect(options, token); - - return token; - } - - /* (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(30000, 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(className,methodName, "104",new Object[]{ new Long(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(className,methodName,"105",null,ex); - throw ex; - } - //@TRACE 108=< - log.fine(className,methodName,"108"); - - return token; - } - - /* (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; - } - - /** - * 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, MqttDeliveryToken)} to publish a message in a non-blocking manner or - *
                    • {@link MqttClient#publishBlock(String, MqttMessage, MqttDeliveryToken)} 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) { - validateTopic(topic); - - MqttTopic result = (MqttTopic)topics.get(topic); - if (result == null) { - result = new MqttTopic(topic, comms); - topics.put(topic,result); - } - return result; - } - - /* (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 { - final String methodName = "subscribe"; - - if (topicFilters.length != qos.length) { - throw new IllegalArgumentException(); - } - String subs = ""; - for (int i=0;i0) { - subs+=", "; - } - subs+=topicFilters[i]+":"+qos[i]; - } - //@TRACE 106=Subscribe topic={0} userContext={1} callback={2} - log.fine(className,methodName,"106",new Object[]{subs, 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(className,methodName,"109"); - - 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"; - String subs = ""; - for (int i=0;i0) { - subs+=", "; - } - subs+=topicFilters[i]; - } - //@TRACE 107=Unsubscribe topic={0} userContext={1} callback={2} - log.fine(className, methodName,"107",new Object[]{subs, userContext, callback}); - - 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(className,methodName,"110"); - - return token; - } - - /* (non-Javadoc) - * @see IMqttAsyncClient#setCallback(MqttCallback) - */ - public void setCallback(MqttCallback callback) { - comms.setCallback(callback); - } - - /** - * Returns a randomly generated client identifier based on the current user's login - * name 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() { - return (System.getProperty("user.name") + "." + System.currentTimeMillis()); - } - - /* (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(className,methodName,"111", new Object[] {topic, userContext, callback}); - - validateTopic(topic); - - 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(className,methodName,"112"); - - return token; - } - - /* (non-Javadoc) - * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#close() - */ - public void close() throws MqttException { - final String methodName = "close"; - //@TRACE 113=< - log.fine(className,methodName,"113"); - comms.close(); - //@TRACE 114=> - log.fine(className,methodName,"114"); - - } - - /** - * Return a debug object that can be used to help solve problems. - */ - public Debug getDebug() { - return new Debug(clientId,comms); - } - - /** - * Checks a topic is valid when publishing a message - *

                    Checks the topic does not contain a wild card character.

                    - * @param topic to validate - * @throws IllegalArgumentException if the topic is not valid - */ - static public void validateTopic(String topic) { - if ((topic.indexOf('#') == -1) && (topic.indexOf('+') == -1)) { - return; - } - // The topic string does not comply with topic string rules. - throw new IllegalArgumentException(); - } -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttCallback.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttCallback.java deleted file mode 100644 index 5972803..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttCallback.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3; - - -/** - * Enables an application to be notified when asynchronous - * events related to the client occur. - * Classes implementing this interface - * can be registered on both types of client: {@link IMqttClient#setCallback(MqttCallback)} - * and {@link IMqttAsyncClient#setCallback(MqttCallback)} - */ -public interface MqttCallback { - /** - * This method is called when the connection to the server is lost. - * - * @param cause the reason behind the loss of connection. - */ - public void connectionLost(Throwable cause); - - /** - * This method is called when a message arrives from the server. - * - *

                    - * This method is invoked synchronously by the MQTT client. An - * acknowledgement is not sent back to the server until this - * method returns cleanly.

                    - *

                    - * If an implementation of this method throws an Exception, then the - * client will be shut down. When the client is next re-connected, any QoS - * 1 or 2 messages will be redelivered by the server.

                    - *

                    - * Any additional messages which arrive while an - * implementation of this method is running, will build up in memory, and - * will then back up on the network.

                    - *

                    - * If an application needs to persist data, then it - * should ensure the data is persisted prior to returning from this method, as - * after returning from this method, the message is considered to have been - * delivered, and will not be reproducable.

                    - *

                    - * It is possible to send a new message within an implementation of this callback - * (for example, a response to this message), but the implementation must not - * disconnect the client, as it will be impossible to send an acknowledgement for - * the message being processed, and a deadlock will occur.

                    - * - * @param topic name of the topic on the message was published to - * @param message the actual message. - * @throws Exception if a terminal error has occurred, and the client should be - * shut down. - */ - public void messageArrived(String topic, MqttMessage message) throws Exception; - - /** - * Called when delivery for a message has been completed, and all - * acknowledgements have been received. For QOS 0 messages it is - * called once the message has been handed to the network for - * delivery. For QOS 1 it is called when PUBACK is received and - * for QOS 2 when PUBCOMP is received. The token will be the same - * token as that returned when the message was published. - * - * @param token the delivery token associated with the message. - */ - public void deliveryComplete(IMqttDeliveryToken token); - -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttClient.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttClient.java deleted file mode 100644 index f38a50e..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttClient.java +++ /dev/null @@ -1,376 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3; - -import java.util.Properties; -import javax.net.SocketFactory; - -import org.eclipse.paho.client.mqttv3.logging.Logger; -import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; -import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence; -import org.eclipse.paho.client.mqttv3.util.Debug; - -/** - * Lightweight client for talking to an MQTT server using methods that block - * until an operation completes. - * - *

                    This class implements the blocking {@link IMqttClient} client interface where all - * actions block until they have completed (or timed out). - * This implementation is compatible with all Java SE runtimes from 1.4.2 and up. - *

                    - *

                    An application can connect to an MQTT server using: - *

                      - *
                    • A plain TCP socket - *
                    • An 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 it cleared when a client disconnects. If - * connecting with cleansession set to false, to provide reliable message delivery - * then a persistent message store should be used such as the default one.

                    - *

                    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 IMqttClient - */ -public class MqttClient implements IMqttClient { //), DestinationProvider { - - protected MqttAsyncClient aClient = null; // Delegate implementation to MqttAshyncClient - protected long timeToWait = -1; // How long each method should wait for action to complete - - final static String className = MqttClient.class.getName(); - public Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className); - - - /** - * Create an MqttClient that can be used to communicate with an MQTT server. - *

                    - * The address of the server should be a URI, using a scheme of either - * "tcp://" for a TCP connection or "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 identified to connect to an MQTT server, it - * must be unique across all clients connecting to the same - * server. 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 are used and 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 default instance of {@link MqttDefaultFilePersistence} is used by - * the client. To specify a different persistence implementation, or to turn - * off persistence, use the {@link #MqttClient(String, String, MqttClientPersistence)} constructor. - * - * @param serverURI the address of the server to connect to, specified as a URI - * @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 23 characters in length - * @throws MqttException if any other problem was encountered - */ - public MqttClient(String serverURI, String clientId) throws MqttException { - this(serverURI,clientId, new MqttDefaultFilePersistence()); - } - - /** - * Create an MqttAsyncClient that can be used to communicate with an MQTT server. - *

                    - * The address of the server should be a URI, using a scheme of either - * "tcp://" for a TCP connection or "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 identified to connect to an MQTT server, it - * must be unique across all clients connecting to the same - * server. 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 are used and 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.

                    - *

                    - * The persistence mechanism is used to enable reliable messaging. - * For qualities of server (QoS) 1 or 2 to work, messages must be persisted - * to disk by both the client and the server. If this is not done, then - * a failure in the client or server can result in lost messages. A pluggable - * persistence mechanism is supported via the {@link MqttClientPersistence} - * interface. A 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 - * @param clientId a client identifier that is unique on the server being connected to - * @param persistence the persistence mechanism to use. - * @throws IllegalArgumentException if the URI does not start with - * "tcp://", "ssl://" or "local://". - * @throws IllegalArgumentException if the clientId is null or is greater than 23 characters in length - * @throws MqttException if any other problem was encountered - */ - public MqttClient(String serverURI, String clientId, MqttClientPersistence persistence) throws MqttException { - aClient = new MqttAsyncClient(serverURI, clientId, persistence); - } - - /* - * @see IMqttClient#connect() - */ - public void connect() throws MqttSecurityException, MqttException { - this.connect(new MqttConnectOptions()); - } - - /* - * @see IMqttClient#connect(MqttConnectOptions) - */ - public void connect(MqttConnectOptions options) throws MqttSecurityException, MqttException { - aClient.connect(options, null, null).waitForCompletion(getTimeToWait()); - } - - /* - * @see IMqttClient#disconnect() - */ - public void disconnect() throws MqttException { - this.disconnect(30000); - } - - /* - * @see IMqttClient#disconnect(long) - */ - public void disconnect(long quiesceTimeout) throws MqttException { - aClient.disconnect(quiesceTimeout, null, null).waitForCompletion(); - } - - /* - * @see IMqttClient#subscribe(String) - */ - public void subscribe(String topicFilter) throws MqttException { - this.subscribe(new String[] {topicFilter}, new int[] {1}); - } - - /* - * @see IMqttClient#subscribe(String[]) - */ - public void subscribe(String[] topicFilters) throws MqttException { - int[] qos = new int[topicFilters.length]; - for (int i=0; iSet the maximum time to wait for an action to complete before - * returning control to the invoking application. Control is returned - * when: - *
                      - *
                    • the action completes - *
                    • or when the timeout if exceeded - *
                    • or when the client is disconnect/shutdown - *
                        - * The default value is -1 which means the action will not timeout. - * In the event of a timeout the action carries on running in the - * background until it completes. The timeout is used on methods that - * block while the action is in progress. - *

                        - * @param timeToWaitInMillis before the action times out. A value or 0 or -1 will wait until - * the action finishes and not timeout. - */ - public void setTimeToWait(long timeToWaitInMillis) throws IllegalArgumentException{ - if (timeToWait < -1) { - throw new IllegalArgumentException(); - } - this.timeToWait = timeToWaitInMillis; - } - - /** - * Return the maximum time to wait for an action to complete. - * @see MqttClient#setTimeToWait(long) - */ - public long getTimeToWait() { - return this.timeToWait; - } - - /* (non-Javadoc) - * @see org.eclipse.paho.client.mqttv3.IMqttClient#close() - */ - public void close() throws MqttException { - aClient.close(); - } - - /* (non-Javadoc) - * @see org.eclipse.paho.client.mqttv3.IMqttClient#getClientId() - */ - public String getClientId() { - return aClient.getClientId(); - } - - /* (non-Javadoc) - * @see org.eclipse.paho.client.mqttv3.IMqttClient#getPendingDeliveryTokens() - */ - public IMqttDeliveryToken[] getPendingDeliveryTokens() { - return aClient.getPendingDeliveryTokens(); - } - - /* (non-Javadoc) - * @see org.eclipse.paho.client.mqttv3.IMqttClient#getServerURI() - */ - public String getServerURI() { - return aClient.getServerURI(); - } - - /* (non-Javadoc) - * @see org.eclipse.paho.client.mqttv3.IMqttClient#getTopic(java.lang.String) - */ - public MqttTopic getTopic(String topic) { - return aClient.getTopic(topic); - } - - /* (non-Javadoc) - * @see org.eclipse.paho.client.mqttv3.IMqttClient#isConnected() - */ - public boolean isConnected() { - return aClient.isConnected(); - } - - /* (non-Javadoc) - * @see org.eclipse.paho.client.mqttv3.IMqttClient#setCallback(org.eclipse.paho.client.mqttv3.MqttCallback) - */ - public void setCallback(MqttCallback callback) { - aClient.setCallback(callback); - } - - /** - * Returns a randomly generated client identifier based on the current user's login - * name 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() { - return MqttAsyncClient.generateClientId(); - } - - /** - * Return a debug object that can be used to help solve problems. - */ - public Debug getDebug() { - return (aClient.getDebug()); - } -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttClientPersistence.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttClientPersistence.java deleted file mode 100644 index 6240b19..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttClientPersistence.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3; - -import java.util.Enumeration; - -/** - * Represents a persistent data store, used to store outbound and inbound messages while they - * are in flight, enabling delivery to the QOS specified. You can specify an implementation - * of this interface using {@link MqttClient#MqttClient(String, String, MqttClientPersistence)}, - * which the {@link MqttClient} will use to persist QoS 1 and 2 messages. - *

                        - * If the methods defined throw the MqttPersistenceException then the state of the data persisted - * should remain as prior to the method being called. For example, if {@link #put(String, MqttPersistable)} - * throws an exception at any point then the data will be assumed to not be in the persistent store. - * Similarly if {@link #remove(String)} throws an exception then the data will be - * assumed to still be held in the persistent store.

                        - *

                        - * It is up to the persistence interface to log any exceptions or error information - * which may be required when diagnosing a persistence failure.

                        - */ -public interface MqttClientPersistence { - /** - * Initialise the persistent store. - * If a persistent store exists for this client ID then open it, otherwise - * create a new one. If the persistent store is already open then just return. - * An application may use the same client ID to connect to many different - * servers, so the client ID in conjunction with the - * connection will uniquely identify the persistence store required. - * - * @param clientId The client for which the persistent store should be opened. - * @param serverURI The connection string as specified when the MQTT client instance was created. - * @throws MqttPersistenceException if there was a problem opening the persistent store. - */ - public void open(String clientId, String serverURI) throws MqttPersistenceException; - - /** - * Close the persistent store that was previously opened. - * This will be called when a client application disconnects from the broker. - * @throws MqttPersistenceException - */ - public void close() throws MqttPersistenceException; - - /** - * Puts the specified data into the persistent store. - * @param key the key for the data, which will be used later to retrieve it. - * @param persistable the data to persist - * @throws MqttPersistenceException if there was a problem putting the data - * into the persistent store. - */ - public void put(String key, MqttPersistable persistable) throws MqttPersistenceException; - - /** - * Gets the specified data out of the persistent store. - * @param key the key for the data, which was used when originally saving it. - * @return the un-persisted data - * @throws MqttPersistenceException if there was a problem getting the data - * from the persistent store. - */ - public MqttPersistable get(String key) throws MqttPersistenceException; - - /** - * Remove the data for the specified key. - */ - public void remove(String key) throws MqttPersistenceException; - - /** - * Returns an Enumeration over the keys in this persistent data store. - * @return an enumeration of {@link String} objects. - */ - public Enumeration keys() throws MqttPersistenceException; - - /** - * Clears persistence, so that it no longer contains any persisted data. - */ - public void clear() throws MqttPersistenceException; - - /** - * Returns whether or not data is persisted using the specified key. - * @param key the key for data, which was used when originally saving it. - */ - public boolean containsKey(String key) throws MqttPersistenceException; -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttConnectOptions.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttConnectOptions.java deleted file mode 100644 index 72e28b5..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttConnectOptions.java +++ /dev/null @@ -1,366 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3; - -import java.util.Properties; - -import javax.net.SocketFactory; - -import org.eclipse.paho.client.mqttv3.util.Debug; - -/** - * Holds the set of options that control how the client connects to a server. - */ -public class MqttConnectOptions { - /** - * The default keep alive interval in seconds if one is not specified - */ - public static final int KEEP_ALIVE_INTERVAL_DEFAULT = 60; - /** - * The default connection timeout in seconds if one is not specified - */ - public static final int CONNECTION_TIMEOUT_DEFAULT = 30; - /** - * The default clean session setting if one is not specified - */ - public static final boolean CLEAN_SESSION_DEFAULT = true; - - private int keepAliveInterval = KEEP_ALIVE_INTERVAL_DEFAULT; - private String willDestination = null; - private MqttMessage willMessage = null; - private String userName; - private char[] password; - private SocketFactory socketFactory; - private Properties sslClientProps = null; - private boolean cleanSession = CLEAN_SESSION_DEFAULT; - private int connectionTimeout = CONNECTION_TIMEOUT_DEFAULT; - - /** - * Constructs a new MqttConnectOptions object using the - * default values. - * - * The defaults are: - *
                          - *
                        • The keepalive interval is 60 seconds
                        • - *
                        • Clean Session is true
                        • - *
                        • The message delivery retry interval is 15 seconds
                        • - *
                        • The connection timeout period is 30 seconds
                        • - *
                        • No Will message is set
                        • - *
                        • A standard SocketFactory is used
                        • - *
                        - * More information about these values can be found in the setter methods. - */ - public MqttConnectOptions() { - } - - /** - * Returns the password to use for the connection. - * @return the password to use for the connection. - */ - public char[] getPassword() { - return password; - } - - /** - * Sets the password to use for the connection. - */ - public void setPassword(char[] password) { - this.password = password; - } - - /** - * Returns the user name to use for the connection. - * @return the user name to use for the connection. - */ - public String getUserName() { - return userName; - } - - /** - * Sets the user name to use for the connection. - * @throws IllegalArgumentException if the user name is blank or only - * contains whitespace characters. - */ - public void setUserName(String userName) { - if ((userName != null) && (userName.trim().equals(""))) { - throw new IllegalArgumentException(); - } - this.userName = userName; - } - - /** - * Sets the "Last Will and Testament" (LWT) for the connection. - * In the event that this client unexpectedly loses its connection to the - * server, the server will publish a message to itself using the supplied - * details. - * - * @param topic the topic to publish to. - * @param payload the byte payload for the message. - * @param qos the quality of service to publish the message at (0, 1 or 2). - * @param retained whether or not the message should be retained. - */ - public void setWill(MqttTopic topic, byte[] payload, int qos, boolean retained) { - String topicS = topic.getName(); - validateWill(topicS, payload); - this.setWill(topicS, new MqttMessage(payload), qos, retained); - } - - /** - * Sets the "Last Will and Testament" (LWT) for the connection. - * In the event that this client unexpectedly loses its connection to the - * server, the server will publish a message to itself using the supplied - * details. - * - * @param topic the topic to publish to. - * @param payload the byte payload for the message. - * @param qos the quality of service to publish the message at (0, 1 or 2). - * @param retained whether or not the message should be retained. - */ - public void setWill(String topic, byte[] payload, int qos, boolean retained) { - validateWill(topic, payload); - this.setWill(topic, new MqttMessage(payload), qos, retained); - } - - - /** - * Validates the will fields. - */ - private void validateWill(String dest, Object payload) { - if ((dest == null) || (payload == null)) { - throw new IllegalArgumentException(); - } - MqttAsyncClient.validateTopic(dest); - } - - /** - * Sets up the will information, based on the supplied parameters. - */ - protected void setWill(String topic, MqttMessage msg, int qos, boolean retained) { - willDestination = topic; - willMessage = msg; - willMessage.setQos(qos); - willMessage.setRetained(retained); - // Prevent any more changes to the will message - willMessage.setMutable(false); - } - - /** - * Returns the "keep alive" interval. - * @see #setKeepAliveInterval(int) - * @return the keep alive interval. - */ - public int getKeepAliveInterval() { - return keepAliveInterval; - } - - /** - * Sets the "keep alive" interval. - * This value, measured in seconds, defines the maximum time interval - * between messages sent or received. It enables the client to - * detect if the server is no longer available, without - * having to wait for the TCP/IP timeout. The client will ensure - * that at least one message travels across the network within each - * keep alive period. In the absence of a data-related message during - * the time period, the client sends a very small "ping" message, which - * the server will acknowledge. - * A value of 0 disables keepalive processing in the client. - *

                        The default value is 60 seconds

                        - * - * @param keepAliveInterval the interval, measured in seconds, must be >= 0. - */ - public void setKeepAliveInterval(int keepAliveInterval)throws IllegalArgumentException { - if (keepAliveInterval <0 ) { - throw new IllegalArgumentException(); - } - this.keepAliveInterval = keepAliveInterval; - } - - /** - * Returns the connection timeout value. - * @see #setConnectionTimeout(int) - * @return the connection timeout value. - */ - public int getConnectionTimeout() { - return connectionTimeout; - } - - /** - * Sets the connection timeout value. - * This value, measured in seconds, defines the maximum time interval - * the client will wait for the network connection to the MQTT server to be established. - * The default timeout is 30 seconds. - * @param connectionTimeout the timeout value, measured in seconds. - */ - public void setConnectionTimeout(int connectionTimeout) { - this.connectionTimeout = connectionTimeout; - } - - /** - * Returns the socket factory that will be used when connecting, or - * null if one has not been set. - */ - public SocketFactory getSocketFactory() { - return socketFactory; - } - - /** - * Sets the SocketFactory to use. This allows an application - * to apply its own policies around the creation of network sockets. If - * using an SSL connection, an SSLSocketFactory can be used - * to supply application-specific security settings. - * @param socketFactory the factory to use. - */ - public void setSocketFactory(SocketFactory socketFactory) { - this.socketFactory = socketFactory; - } - - /** - * Returns the topic to be used for last will and testament (LWT). - * @return the MqttTopic to use, or null if LWT is not set. - * @see #setWill(MqttTopic, byte[], int, boolean) - */ - public String getWillDestination() { - return willDestination; - } - - /** - * Returns the message to be sent as last will and testament (LWT). - * The returned object is "read only". Calling any "setter" methods on - * the returned object will result in an - * IllegalStateException being thrown. - * @return the message to use, or null if LWT is not set. - */ - public MqttMessage getWillMessage() { - return willMessage; - } - - /** - * Returns the SSL properties for the connection. - * @return the properties for the SSL connection - */ - public Properties getSSLProperties() { - return sslClientProps; - } - - /** - * Sets the SSL properties for the connection. Note that these - * properties are only valid if an implementation of the Java - * Secure Socket Extensions (JSSE) is available. These properties are - * not used if a SocketFactory has been set using - * {@link #setSocketFactory(SocketFactory)}. - * The following properties can be used:

                        - *
                        - *
                        com.ibm.ssl.protocol
                        - *
                        One of: SSL, SSLv3, TLS, TLSv1, SSL_TLS.
                        - *
                        com.ibm.ssl.contextProvider - *
                        Underlying JSSE provider. For example "IBMJSSE2" or "SunJSSE"
                        - * - *
                        com.ibm.ssl.keyStore
                        - *
                        The name of the file that contains the KeyStore object that you - * want the KeyManager to use. For example /mydir/etc/key.p12
                        - * - *
                        com.ibm.ssl.keyStorePassword
                        - *
                        The password for the KeyStore object that you want the KeyManager to use. - * The password can either be in plain-text, - * or may be obfuscated using the static method: - * com.ibm.micro.security.Password.obfuscate(char[] password). - * This obfuscates the password using a simple and insecure XOR and Base64 - * encoding mechanism. Note that this is only a simple scrambler to - * obfuscate clear-text passwords.
                        - * - *
                        com.ibm.ssl.keyStoreType
                        - *
                        Type of key store, for example "PKCS12", "JKS", or "JCEKS".
                        - * - *
                        com.ibm.ssl.keyStoreProvider
                        - *
                        Key store provider, for example "IBMJCE" or "IBMJCEFIPS".
                        - * - *
                        com.ibm.ssl.trustStore
                        - *
                        The name of the file that contains the KeyStore object that you - * want the TrustManager to use.
                        - * - *
                        com.ibm.ssl.trustStorePassword
                        - *
                        The password for the TrustStore object that you want the - * TrustManager to use. The password can either be in plain-text, - * or may be obfuscated using the static method: - * com.ibm.micro.security.Password.obfuscate(char[] password). - * This obfuscates the password using a simple and insecure XOR and Base64 - * encoding mechanism. Note that this is only a simple scrambler to - * obfuscate clear-text passwords.
                        - * - *
                        com.ibm.ssl.trustStoreType
                        - *
                        The type of KeyStore object that you want the default TrustManager to use. - * Same possible values as "keyStoreType".
                        - * - *
                        com.ibm.ssl.trustStoreProvider
                        - *
                        Trust store provider, for example "IBMJCE" or "IBMJCEFIPS".
                        - * - *
                        com.ibm.ssl.enabledCipherSuites
                        - *
                        A list of which ciphers are enabled. Values are dependent on the provider, - * for example: SSL_RSA_WITH_AES_128_CBC_SHA;SSL_RSA_WITH_3DES_EDE_CBC_SHA.
                        - * - *
                        com.ibm.ssl.keyManager
                        - *
                        Sets the algorithm that will be used to instantiate a KeyManagerFactory object - * instead of using the default algorithm available in the platform. Example values: - * "IbmX509" or "IBMJ9X509". - *
                        - * - *
                        com.ibm.ssl.trustManager
                        - *
                        Sets the algorithm that will be used to instantiate a TrustManagerFactory object - * instead of using the default algorithm available in the platform. Example values: - * "PKIX" or "IBMJ9X509". - *
                        - *
                        - */ - public void setSSLProperties(Properties props) { - this.sslClientProps = props; - } - - /** - * Returns whether the server should remember state for the client across reconnects. - * @return the clean session flag - */ - public boolean isCleanSession() { - return this.cleanSession; - } - - /** - * Sets whether the server should remember state for the client across reconnects. - * This includes subscriptions and the state of any in-flight messages. - */ - public void setCleanSession(boolean cleanSession) { - this.cleanSession = cleanSession; - } - - public Properties getDebug() { - Properties p = new Properties(); - p.put("CleanSession", new Boolean(isCleanSession())); - p.put("ConTimeout", new Integer(getConnectionTimeout())); - p.put("KeepAliveInterval", new Integer(getKeepAliveInterval())); - p.put("UserName", (getUserName()==null)?"null":getUserName()); - p.put("WillDestination", (getWillDestination()==null)?"null":getWillDestination()); - if (getSocketFactory()==null) { - p.put("SocketFactory", "null"); - } else { - p.put("SocketFactory", getSocketFactory()); - } - if (getSSLProperties()==null) { - p.put("SSLProperties", "null"); - } else { - p.put("SSLProperties", getSSLProperties()); - } - return p; - } - - public String toString() { - return Debug.dumpProperties(getDebug(), "Connection options"); - } -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttDeliveryToken.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttDeliveryToken.java deleted file mode 100644 index fe9b129..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttDeliveryToken.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3; - -/** - * Provides a mechanism to track the delivery progress of a message. - * - *

                        - * Used to track the the delivery progress of a message when a publish is - * executed in a non-blocking manner (run in the background)

                        - * - * @see MqttToken - */ -public class MqttDeliveryToken extends MqttToken implements IMqttDeliveryToken { - - - public MqttDeliveryToken() { - super(); - } - - public MqttDeliveryToken(String logContext) { - super(logContext); - } - - /** - * Returns the message associated with this token. - *

                        Until the message has been delivered, the message being delivered will - * be returned. Once the message has been delivered null will be - * returned. - * @return the message associated with this token or null if already delivered. - * @throws MqttException if there was a problem completing retrieving the message - */ - public MqttMessage getMessage() throws MqttException { - return internalTok.getMessage(); - } - - protected void setMessage(MqttMessage msg) { - internalTok.setMessage(msg); - } -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttException.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttException.java deleted file mode 100644 index 677b2d9..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttException.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3; - -import org.eclipse.paho.client.mqttv3.internal.MessageCatalog; - -/** - * Thrown if an error occurs communicating with the server. - */ -public class MqttException extends Exception { - private static final long serialVersionUID = 300L; - - /** - * Client encountered an exception. Use the {@link #getCause()} - * method to get the underlying reason. - */ - public static final short REASON_CODE_CLIENT_EXCEPTION = 0x00; - - // CONNACK return codes - /** The protocol version requested is not supported by the server. */ - public static final short REASON_CODE_INVALID_PROTOCOL_VERSION = 0x01; - /** The server has rejected the supplied client ID */ - public static final short REASON_CODE_INVALID_CLIENT_ID = 0x02; - /** The broker was not available to handle the request. */ - public static final short REASON_CODE_BROKER_UNAVAILABLE = 0x03; - /** Authentication with the server has failed, due to a bad user name or password. */ - public static final short REASON_CODE_FAILED_AUTHENTICATION = 0x04; - /** Not authorized to perform the requested operation */ - public static final short REASON_CODE_NOT_AUTHORIZED = 0x05; - - /** An unexpected error has occurred. */ - public static final short REASON_CODE_UNEXPECTED_ERROR = 0x06; - - /** - * Client timed out while waiting for a response from the server. - * The server is no longer responding to keep-alive messages. - */ - public static final short REASON_CODE_CLIENT_TIMEOUT = 32000; - /** - * Internal error, caused by no new message IDs being available. - */ - public static final short REASON_CODE_NO_MESSAGE_IDS_AVAILABLE = 32001; - - /** - * The client is already connected. - */ - public static final short REASON_CODE_CLIENT_CONNECTED = 32100; - /** - * The client is already disconnected. - */ - public static final short REASON_CODE_CLIENT_ALREADY_DISCONNECTED = 32101; - /** - * The client is currently disconnecting and cannot accept any new work. - * This can occur when waiting on a token, and then disconnecting the client. - * If the message delivery does not complete within the quiesce timeout - * period, then the waiting token will be notified with an exception. - */ - public static final short REASON_CODE_CLIENT_DISCONNECTING = 32102; - - /** Unable to connect to server */ - public static final short REASON_CODE_SERVER_CONNECT_ERROR = 32103; - - /** - * The client is not connected to the server. The {@link MqttClient#connect()} - * or {@link MqttClient#connect(MqttConnectOptions)} method must be called - * first. It is also possible that the connection was lost - see - * {@link MqttClient#setCallback(MqttCallback)} for a way to track lost - * connections. - */ - public static final short REASON_CODE_CLIENT_NOT_CONNECTED = 32104; - - /** - * Server URI and supplied SocketFactory do not match. - * URIs beginning tcp:// must use a javax.net.SocketFactory, - * and URIs beginning ssl:// must use a javax.net.ssl.SSLSocketFactory. - */ - public static final short REASON_CODE_SOCKET_FACTORY_MISMATCH = 32105; - - /** - * SSL configuration error. - */ - public static final short REASON_CODE_SSL_CONFIG_ERROR = 32106; - - /** - * Thrown when an attempt to call {@link MqttClient#disconnect()} has been - * made from within a method on {@link MqttCallback}. These methods are invoked - * by the client's thread, and must not be used to control disconnection. - * - * @see MqttCallback#messageArrived(String, MqttMessage) - */ - public static final short REASON_CODE_CLIENT_DISCONNECT_PROHIBITED = 32107; - - /** - * Protocol error: the message was not recognized as a valid MQTT packet. - * Possible reasons for this include connecting to a non-MQTT server, or - * connecting to an SSL server port when the client isn't using SSL. - */ - public static final short REASON_CODE_INVALID_MESSAGE = 32108; - - /** - * The client has been unexpectedly disconnected from the server. The {@link #getCause() cause} - * will provide more details. - */ - public static final short REASON_CODE_CONNECTION_LOST = 32109; - - /** - * A connect operation in already in progress, only one connect can happen - * at a time. - */ - public static final short REASON_CODE_CONNECT_IN_PROGRESS = 32110; - - /** - * The client is closed - no operations are permitted on the client in this - * state. New up a new client to continue. - */ - public static final short REASON_CODE_CLIENT_CLOSED = 32111; - - /** - * A request has been made to use a token that is already associated with - * another action. If the action is complete the reset() can ve called on the - * token to allow it to be reused. - */ - public static final short REASON_CODE_TOKEN_INUSE = 32201; - - /** - * A request has been made to send a message but the maximum number of inflight - * messages has already been reached. Once one or more messages have been moved - * then new messages can be sent. - */ - public static final short REASON_CODE_MAX_INFLIGHT = 32202; - - private int reasonCode; - private Throwable cause; - - /** - * Constructs a new MqttException with the specified code - * as the underlying reason. - * @param reasonCode the reason code for the exception. - */ - public MqttException(int reasonCode) { - super(); - this.reasonCode = reasonCode; - } - - /** - * Constructs a new MqttException with the specified - * Throwable as the underlying reason. - * @param cause the underlying cause of the exception. - */ - public MqttException(Throwable cause) { - super(); - this.reasonCode = REASON_CODE_CLIENT_EXCEPTION; - this.cause = cause; - } - - /** - * Constructs a new MqttException with the specified - * Throwable as the underlying reason. - * @param reason the reason code for the exception. - * @param cause the underlying cause of the exception. - */ - public MqttException(int reason, Throwable cause) { - super(); - this.reasonCode = reason; - this.cause = cause; - } - - - /** - * Returns the reason code for this exception. - * @return the code representing the reason for this exception. - */ - public int getReasonCode() { - return reasonCode; - } - - /** - * Returns the underlying cause of this exception, if available. - * @return the Throwable that was the root cause of this exception, - * which may be null. - */ - public Throwable getCause() { - return cause; - } - - /** - * Returns the detail message for this exception. - * @return the detail message, which may be null. - */ - public String getMessage() { - return MessageCatalog.getMessage(reasonCode); - } - - /** - * Returns a String representation of this exception. - * @return a String representation of this exception. - */ - public String toString() { - String result = getMessage() + " (" + reasonCode + ")"; - if (cause != null) { - result = result + " - " + cause.toString(); - } - return result; - } -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttMessage.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttMessage.java deleted file mode 100644 index 4c5f88f..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttMessage.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3; - -/** - * An MQTT message holds the application payload and options - * specifying how the message is to be delivered - * The message includes a "payload" (the body of the message) - * represented as a byte[]. - */ -public class MqttMessage { - - private boolean mutable = true; - private byte[] payload; - private int qos = 1; - private boolean retained = false; - private boolean dup = false; - - /** - * Utility method to validate the supplied QoS value. - * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2. - */ - public static void validateQos(int qos) { - if ((qos < 0) || (qos > 2)) { - throw new IllegalArgumentException(); - } - } - - /** - * Constructs a message with an empty payload, and all other values - * set to defaults. - * - * The defaults are: - *

                          - *
                        • Message QoS set to 1
                        • - *
                        • Message will not be "retained" by the server
                        • - *
                        - */ - public MqttMessage() { - setPayload(new byte[]{}); - } - - /** - * Constructs a message with the specified byte array as a payload, - * and all other values set to defaults. - */ - public MqttMessage(byte[] payload) { - setPayload(payload); - } - - /** - * Returns the payload as a byte array. - * - * @return the payload as a byte array. - */ - public byte[] getPayload() { - return payload; - } - - /** - * Clears the payload, resetting it to be empty. - * @throws IllegalStateException if this message cannot be edited - */ - public void clearPayload() { - checkMutable(); - this.payload = new byte[] {}; - } - - /** - * Sets the payload of this message to be the specified byte array. - * - * @param payload the payload for this message. - * @throws IllegalStateException if this message cannot be edited - * @throws NullPointerException if no payload is provided - */ - public void setPayload(byte[] payload) { - checkMutable(); - if (payload == null) { - throw new NullPointerException(); - } - this.payload = payload; - } - - /** - * Returns whether or not this message should be/was retained by the server. - * For messages received from the server, this method returns whether or not - * the message was from a current publisher, or was "retained" by the server as - * the last message published on the topic. - * - * @return true if the message should be, or was, retained by - * the server. - * @see #setRetained(boolean) - */ - public boolean isRetained() { - return retained; - } - - /** - * Whether or not the publish message should be retained by the messaging engine. - * Sending a message with the retained set to false will clear the - * retained message from the server. The default value is false - * - * @param retained whether or not the messaging engine should retain the message. - * @throws IllegalStateException if this message cannot be edited - */ - public void setRetained(boolean retained) { - checkMutable(); - this.retained = retained; - } - - /** - * Returns the quality of service for this message. - * @return the quality of service to use, either 0, 1, or 2. - * @see #setQos(int) - */ - public int getQos() { - return qos; - } - - /** - * Sets the quality of service for this message. - *
                          - *
                        • Quality of service 0 - indicates that a message should - * be delivered at most once (zero or one times). The message will not be persisted to disk, - * and will not be acknowledged across the network. This QoS is the fastest, - * but should only be used for messages which are not valuable - note that - * if the server cannot process the message (for example, there - * is an authorization problem), then an - * {@link MqttCallback#deliveryComplete(IMqttDeliveryToken)}. - * Also known as "fire and forget".
                        • - * - *
                        • Quality of service 1 - indicates that a message should - * be delivered at least once (one or more times). The message can only be delivered safely if - * it can be persisted, so the application must supply a means of - * persistence using MqttConnectOptions. - * If a persistence mechanism is not specified, the message will not be - * delivered in the event of a client failure. - * The message will be acknowledged across the network. - * This is the default QoS.
                        • - * - *
                        • Quality of service 2 - indicates that a message should - * be delivered once. The message will be persisted to disk, and will - * be subject to a two-phase acknowledgement across the network. - * The message can only be delivered safely if - * it can be persisted, so the application must supply a means of - * persistence using MqttConnectOptions. - * If a persistence mechanism is not specified, the message will not be - * delivered in the event of a client failure.
                        • - * - * If persistence is not configured, QOS 1 and 2 messages will still be delivered - * in the event of a network or server problem as the client will hold state in memory. - * If the MQTT client is shutdown or fails and persistence is not configured then - * delivery of QOS 1 and 2 messages can not be maintained as client side state will - * be lost. - * - * @param qos the "quality of service" to use. Set to 0, 1, 2. - * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2. - * @throws IllegalStateException if this message cannot be edited - */ - public void setQos(int qos) { - checkMutable(); - validateQos(qos); - this.qos = qos; - } - - /** - * Returns a string representation of this messages payload. - * Makes an attempt to return the payload as a string. As the - * MQTT client has no control over the content of the payload - * it may fail. - * @return a string representation of this message. - */ - public String toString() { - return new String(payload); - } - - /** - * Sets the mutability of this object (whether or not its values can be - * changed. - * @param mutable true if the values can be changed, - * false to prevent them from being changed. - */ - protected void setMutable(boolean mutable) { - this.mutable = mutable; - } - - protected void checkMutable() throws IllegalStateException { - if (!mutable) { - throw new IllegalStateException(); - } - } - - protected void setDuplicate(boolean dup) { - this.dup = dup; - } - - /** - * Returns whether or not this message might be a duplicate of one which has - * already been received. This will only be set on messages received from - * the server. - * @return true if the message might be a duplicate. - */ - public boolean isDuplicate() { - return this.dup; - } -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttPersistable.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttPersistable.java deleted file mode 100644 index 5508c52..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttPersistable.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3; - -/** - * Represents an object used to pass data to be persisted across the - * {@link org.eclipse.paho.client.mqttv3.MqttClientPersistence MqttClientPersistence} - * interface. - *

                          - * When data is passed across the interface the header and payload are - * separated, so that unnecessary message copies may be avoided. - * For example, if a 10 MB payload was published it would be inefficient - * to create a byte array a few bytes larger than 10 MB and copy the - * MQTT message header and payload into a contiguous byte array.

                          - *

                          - * When the request to persist data is made a separate byte array and offset - * is passed for the header and payload. Only the data between - * offset and length need be persisted. - * So for example, a message to be persisted consists of a header byte - * array starting at offset 1 and length 4, plus a payload byte array - * starting at offset 30 and length 40000. There are three ways in which - * the persistence implementation may return data to the client on - * recovery: - *

                            - *
                          • It could return the data as it was passed in - * originally, with the same byte arrays and offsets.
                          • - *
                          • It could safely just persist and return the bytes from the offset - * for the specified length. For example, return a header byte array with - * offset 0 and length 4, plus a payload byte array with offset 0 and length - * 40000
                          • - *
                          • It could return the header and payload as a contiguous byte array - * with the header bytes preceeding the payload. The contiguous byte array - * should be set as the header byte array, with the payload byte array being - * null. For example, return a single byte array with offset 0 - * and length 40004. - * This is useful when recovering from a file where the header and payload - * could be written as a contiguous stream of bytes.
                          • - *
                          - *

                          - */ -public interface MqttPersistable { - - /** - * Returns the header bytes in an array. - * The bytes start at {@link #getHeaderOffset()} - * and continue for {@link #getHeaderLength()}. - * @return the header bytes. - */ - public byte[] getHeaderBytes() throws MqttPersistenceException; - - /** - * Returns the length of the header. - * @return the header length - */ - public int getHeaderLength() throws MqttPersistenceException; - - /** - * Returns the offset of the header within the byte array returned by {@link #getHeaderBytes()}. - * @return the header offset. - * - */ - public int getHeaderOffset() throws MqttPersistenceException; - - /** - * Returns the payload bytes in an array. - * The bytes start at {@link #getPayloadOffset()} - * and continue for {@link #getPayloadLength()}. - * @return the payload bytes. - */ - public byte[] getPayloadBytes() throws MqttPersistenceException; - - /** - * Returns the length of the payload. - * @return the payload length. - */ - public int getPayloadLength() throws MqttPersistenceException; - - /** - * Returns the offset of the payload within the byte array returned by {@link #getPayloadBytes()}. - * @return the payload offset. - * - */ - public int getPayloadOffset() throws MqttPersistenceException; -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttPersistenceException.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttPersistenceException.java deleted file mode 100644 index 7525080..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttPersistenceException.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3; - -/** - * This exception is thrown by the implementor of the persistence - * interface if there is a problem reading or writing persistent data. - */ -public class MqttPersistenceException extends MqttException { - private static final long serialVersionUID = 300L; - - /** Persistence is already being used by another client. */ - public static final short REASON_CODE_PERSISTENCE_IN_USE = 32200; - - /** - * Constructs a new MqttPersistenceException - */ - public MqttPersistenceException() { - super(REASON_CODE_CLIENT_EXCEPTION); - } - - /** - * Constructs a new MqttPersistenceException with the specified code - * as the underlying reason. - * @param reasonCode the reason code for the exception. - */ - public MqttPersistenceException(int reasonCode) { - super(reasonCode); - } - /** - * Constructs a new MqttPersistenceException with the specified - * Throwable as the underlying reason. - * @param cause the underlying cause of the exception. - */ - public MqttPersistenceException(Throwable cause) { - super(cause); - } - /** - * Constructs a new MqttPersistenceException with the specified - * Throwable as the underlying reason. - * @param reason the reason code for the exception. - * @param cause the underlying cause of the exception. - */ - public MqttPersistenceException(int reason, Throwable cause) { - super(reason, cause); - } -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttSecurityException.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttSecurityException.java deleted file mode 100644 index 9069e41..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttSecurityException.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3; - -/** - * Thrown when a client is not authorized to perform an operation, or - * if there is a problem with the security configuration. - */ -public class MqttSecurityException extends MqttException { - private static final long serialVersionUID = 300L; - - /** - * Constructs a new MqttSecurityException with the specified code - * as the underlying reason. - * @param reasonCode the reason code for the exception. - */ - public MqttSecurityException(int reasonCode) { - super(reasonCode); - } - - /** - * Constructs a new MqttSecurityException with the specified - * Throwable as the underlying reason. - * @param cause the underlying cause of the exception. - */ - public MqttSecurityException(Throwable cause) { - super(cause); - } - /** - * Constructs a new MqttSecurityException with the specified - * code and Throwable as the underlying reason. - * @param reasonCode the reason code for the exception. - * @param cause the underlying cause of the exception. - */ - public MqttSecurityException(int reasonCode, Throwable cause) { - super(reasonCode, cause); - } -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttToken.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttToken.java deleted file mode 100644 index 1fc826b..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttToken.java +++ /dev/null @@ -1,71 +0,0 @@ -package org.eclipse.paho.client.mqttv3; - -import org.eclipse.paho.client.mqttv3.internal.Token; - -/** - * Provides a mechanism for tracking the completion of an asynchronous action. - *

                          - * A token that implements the ImqttToken interface is returned from all non-blocking - * method with the exception of publish. - *

                          - * - * @see IMqttToken - */ - -public class MqttToken implements IMqttToken { - /** - * A reference to the the class that provides most of the implementation of the - * MqttToken. MQTT application programs must not use the internal class. - */ - public Token internalTok = null; - - public MqttToken() { - } - - public MqttToken(String logContext) { - internalTok = new Token(logContext); - } - - public MqttException getException() { - return internalTok.getException(); - } - - public boolean isComplete() { - return internalTok.isComplete(); - } - - public void setActionCallback(IMqttActionListener listener) { - internalTok.setActionCallback(listener); - - } - public IMqttActionListener getActionCallback() { - return internalTok.getActionCallback(); - } - - public void waitForCompletion() throws MqttException { - internalTok.waitForCompletion(-1); - } - - public void waitForCompletion(long timeout) throws MqttException { - internalTok.waitForCompletion(timeout); - } - - public IMqttAsyncClient getClient() { - return internalTok.getClient(); - } - - public String[] getTopics() { - return internalTok.getTopics(); - } - - public Object getUserContext() { - return internalTok.getUserContext(); - } - - public void setUserContext(Object userContext) { - internalTok.setUserContext(userContext); } - - public int getMessageId() { - return internalTok.getMessageID(); - } -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttTopic.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttTopic.java deleted file mode 100644 index a486f7b..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttTopic.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3; - -import org.eclipse.paho.client.mqttv3.internal.ClientComms; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttPublish; - -/** - * Represents a topic destination, used for publish/subscribe messaging. - */ -public class MqttTopic { - - private ClientComms comms; - private String name; - - public MqttTopic(String name, ClientComms comms) { - this.comms = comms; - this.name = name; - } - - /** - * Publishes a message on the topic. This is a convenience method, which will - * create a new {@link MqttMessage} object with a byte array payload and the - * specified QoS, and then publish it. All other values in the - * message will be set to the defaults. - - * @param payload the byte array to use as the payload - * @param qos the Quality of Service. Valid values are 0, 1 or 2. - * @param retained whether or not this message should be retained by the server. - * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2. - * @see #publish(MqttMessage) - * @see MqttMessage#setQos(int) - * @see MqttMessage#setRetained(boolean) - */ - public MqttDeliveryToken publish(byte[] payload, int qos, boolean retained) throws MqttException, MqttPersistenceException { - MqttMessage message = new MqttMessage(payload); - message.setQos(qos); - message.setRetained(retained); - return this.publish(message); - } - - /** - * Publishes the specified message to this topic, but does not wait for delivery - * of the message to complete. The returned {@link MqttDeliveryToken token} can be used - * to track the delivery status of the message. Once this method has - * returned cleanly, the message has been accepted for publication by the - * client. Message delivery will be completed in the background when a connection - * is available. - * - * @param message the message to publish - * @return an MqttDeliveryToken for tracking the delivery of the message - */ - public MqttDeliveryToken publish(MqttMessage message) throws MqttException, MqttPersistenceException { - MqttDeliveryToken token = new MqttDeliveryToken(comms.getClient().getClientId()); - token.setMessage(message); - comms.sendNoWait(createPublish(message), token); - token.internalTok.waitUntilSent(); - return token; - } - - /** - * Returns the name of the queue or topic. - * - * @return the name of this destination. - */ - public String getName() { - return name; - } - - /** - * Create a PUBLISH packet from the specified message. - */ - private MqttPublish createPublish(MqttMessage message) { - return new MqttPublish(this.getName(), message); - } - - /** - * Returns a string representation of this topic. - * @return a string representation of this topic. - */ - public String toString() { - return getName(); - } - -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/ClientComms.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/ClientComms.java deleted file mode 100644 index f657e31..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/ClientComms.java +++ /dev/null @@ -1,582 +0,0 @@ -/* -* Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal; - -import java.util.Enumeration; -import java.util.Properties; -import java.util.Vector; - -import org.eclipse.paho.client.mqttv3.IMqttAsyncClient; -import org.eclipse.paho.client.mqttv3.MqttCallback; -import org.eclipse.paho.client.mqttv3.MqttClientPersistence; -import org.eclipse.paho.client.mqttv3.MqttConnectOptions; -import org.eclipse.paho.client.mqttv3.MqttDeliveryToken; -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.MqttPersistenceException; -import org.eclipse.paho.client.mqttv3.MqttToken; -import org.eclipse.paho.client.mqttv3.MqttTopic; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttConnack; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttConnect; -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.MqttWireMessage; -import org.eclipse.paho.client.mqttv3.logging.Logger; -import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; - -/** - * Handles client communications with the server. Sends and receives MQTT V3 - * messages. - */ -public class ClientComms { - public static String VERSION = "@@VERSION@@"; - public static String BUILD_LEVEL = "@@BUILDLEVEL@@"; - - private IMqttAsyncClient client; - NetworkModule networkModule; - CommsReceiver receiver; - CommsSender sender; - CommsCallback callback; - ClientState clientState; - MqttConnectOptions conOptions; - private MqttClientPersistence persistence; - CommsTokenStore tokenStore; - boolean stoppingComms = false; - - final static byte CONNECTED =0; - final static byte CONNECTING =1; - final static byte DISCONNECTING =2; - final static byte DISCONNECTED =3; - final static byte CLOSED =4; - - private byte conState = DISCONNECTED; - Object conLock = new Object(); // Used to syncrhonize connection state - private boolean closePending = false; - - final static String className = ClientComms.class.getName(); - Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className); - - - /** - * Creates a new ClientComms object, using the specified module to handle - * the network calls. - */ - public ClientComms(IMqttAsyncClient client, MqttClientPersistence persistence) throws MqttException { - this.conState = DISCONNECTED; - this.client = client; - this.persistence = persistence; - this.tokenStore = new CommsTokenStore(getClient().getClientId()); - this.callback = new CommsCallback(this); - this.clientState = new ClientState(persistence, tokenStore, this.callback, this); - - callback.setClientState(clientState); - log.setResourceName(getClient().getClientId()); - } - - /** - * Sends a message to the server. Does not check if connected this validation must be done - * by invoking routines. - * @param message - * @param token - * @throws MqttException - */ - void internalSend(MqttWireMessage message, MqttToken token) throws MqttException { - final String methodName = "internalSend"; - //@TRACE 200=internalSend key={0} message={1} token={2} - log.fine(className, methodName, "200", new Object[]{message.getKey(), message, token}); - - if (token.getClient() == null ) { - // Associate the client with the token - also marks it as in use. - token.internalTok.setClient(getClient()); - } else { - // Token is already in use - cannot reuse - //@TRACE 213=fail: token in use: key={0} message={1} token={2} - log.fine(className, methodName, "213", new Object[]{message.getKey(), message, token}); - - throw new MqttException(MqttException.REASON_CODE_TOKEN_INUSE); - } - - try { - // Persist if needed and send the message - this.clientState.send(message, token); - } catch(MqttException e) { - if (message instanceof MqttPublish) { - this.clientState.undo((MqttPublish)message); - } - throw e; - } - } - - /** - * Sends a message to the broker if in connected state, but only waits for the message to be - * stored, before returning. - */ - public void sendNoWait(MqttWireMessage message, MqttToken token) throws MqttException { - final String methodName = "sendNoWait"; - if (isConnected() || - (!isConnected() && message instanceof MqttConnect) || - (isDisconnecting() && message instanceof MqttDisconnect)) { - this.internalSend(message, token); - } else { - //@TRACE 208=failed: not connected - log.fine(className, methodName, "208"); - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_NOT_CONNECTED); - } - } - - /** - * Tidy up - * - call each main class and let it tidy up e.g. releasing the token - * store which normally survives a disconnect. - * @throws MqttException if not disconnected - */ - public void close() throws MqttException { - final String methodName = "close"; - synchronized (conLock) { - if (!isClosed()) { - // Must be disconnected before close can take place - if (!isDisconnected()) { - //@TRACE 224=failed: not disconnected - log.fine(className, methodName, "224"); - - if (isConnecting()) { - throw new MqttException(MqttException.REASON_CODE_CONNECT_IN_PROGRESS); - } else if (isConnected()) { - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_CONNECTED); - } else if (isDisconnecting()) { - closePending = true; - return; - } - } - - conState = CLOSED; - - // ShutdownConnection has already cleaned most things - clientState.close(); - clientState = null; - callback = null; - persistence = null; - sender = null; - receiver = null; - networkModule = null; - conOptions = null; - tokenStore = null; - } - } - } - - /** - * Sends a connect message and waits for an ACK or NACK. - * Connecting is a special case which will also start up the - * network connection, receive thread, and keep alive thread. - */ - public void connect(MqttConnectOptions options, MqttToken token) throws MqttException { - final String methodName = "connect"; - synchronized (conLock) { - if (isDisconnected() && !closePending) { - //@TRACE 214=state=CONNECTING - log.fine(className,methodName,"214"); - - conState = CONNECTING; - - this.conOptions = options; - - MqttConnect connect = new MqttConnect(client.getClientId(), - options.isCleanSession(), - options.getKeepAliveInterval(), - options.getUserName(), - options.getPassword(), - options.getWillMessage(), - options.getWillDestination()); - - this.clientState.setKeepAliveSecs(options.getKeepAliveInterval()); - this.clientState.setCleanSession(options.isCleanSession()); - - tokenStore.open(); - ConnectBG conbg = new ConnectBG(this, token, connect); - conbg.start(); - } - else { - // @TRACE 207=connect failed: not disconnected {0} - log.fine(className,methodName,"207", new Object[] {new Byte(conState)}); - if (isClosed() || closePending) { - throw new MqttException(MqttException.REASON_CODE_CLIENT_CLOSED); - } else if (isConnecting()) { - throw new MqttException(MqttException.REASON_CODE_CONNECT_IN_PROGRESS); - } else if (isDisconnecting()) { - throw new MqttException(MqttException.REASON_CODE_CLIENT_DISCONNECTING); - } else { - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_CONNECTED); - } - } - } - } - - public void connectComplete( MqttConnack cack, MqttException mex) throws MqttException { - final String methodName = "connectComplete"; - int rc = cack.getReturnCode(); - synchronized (conLock) { - if (rc == 0) { - // We've successfully connected - // @TRACE 215=state=CONNECTED - log.fine(className,methodName,"215"); - - conState = CONNECTED; - return; - } - } - - // @TRACE 204=connect failed: rc={0} - log.fine(className,methodName,"204", new Object[]{new Integer(rc)}); - throw mex; - } - - /** - * Shuts down the connection to the server. - * This may have been invoked as a result of a user calling disconnect or - * an abnormal disconnection. The method may be invoked multiple times - * in parallel as each thread when it receives an error uses this method - * to ensure that shutdown completes successfully. - */ - public void shutdownConnection(MqttToken token, MqttException reason) { - final String methodName = "shutdownConnection"; - boolean wasConnected; - MqttToken endToken = null; //Token to notify after disconnect completes - - // This method could concurrently be invoked from many places only allow it - // to run once. - synchronized(conLock) { - if (stoppingComms || closePending) { - return; - } - stoppingComms = true; - - //@TRACE 216=state=DISCONNECTING - log.fine(className,methodName,"216"); - - wasConnected = (isConnected() || isDisconnecting()); - conState = DISCONNECTING; - } - - // Update the token with the reason for shutdown if it - // is not already complete. - if (token != null && !token.isComplete()) { - token.internalTok.setException(reason); - } - - // Stop the thread that is used to call the user back - // when actions complete - if (callback!= null) {callback.stop(); } - - // Stop the network module, send and receive now not possible - try { - if (networkModule != null) {networkModule.stop();} - }catch(Exception ioe) { - // Ignore as we are shutting down - } - - // Stop the thread that handles inbound work from the network - if (receiver != null) {receiver.stop();} - - // Stop any new tokens being saved by app and throwing an exception if they do - tokenStore.quiesce(new MqttException(MqttException.REASON_CODE_CLIENT_DISCONNECTING)); - - // Notify any outstanding tokens with the exception of - // con or discon which may be returned and will be notified at - // the end - endToken = handleOldTokens(token, reason); - - try { - // Clean session handling and tidy up - clientState.disconnected(reason); - }catch(Exception ex) { - // Ignore as we are shutting down - } - - if (sender != null) { sender.stop(); } - - try { - if (persistence != null) {persistence.close();} - }catch(Exception ex) { - // Ignore as we are shutting down - } - // All disconnect logic has been completed allowing the - // client to be marked as disconnected. - synchronized(conLock) { - //@TRACE 217=state=DISCONNECTED - log.fine(className,methodName,"217"); - - conState = DISCONNECTED; - stoppingComms = false; - } - - // Internal disconnect processing has completed. If there - // is a disconnect token or a connect in error notify - // it now. This is done at the end to allow a new connect - // to be processed and now throw a currently disconnecting error. - // any outstanding tokens and unblock any waiters - if (endToken != null & callback != null) { - callback.asyncOperationComplete(endToken); - } - - if (wasConnected && callback != null) { - // Let the user know client has disconnected either normally or abnormally - callback.connectionLost(reason); - } - - // While disconnecting, close may have been requested - try it now - synchronized(conLock) { - if (closePending) { - try { - close(); - } catch (Exception e) { // ignore any errors as closing - } - } - } - } - - // Tidy up. There may be tokens outstanding as the client was - // not disconnected/quiseced cleanly! Work out what tokens still - // need to be notified and waiters unblocked. Store the - // disconnect or connect token to notify after disconnect is - // complete. - private MqttToken handleOldTokens(MqttToken token, MqttException reason) { - final String methodName = "handleOldTokens"; - //@TRACE 222=> - log.fine(className,methodName,"222"); - - MqttToken tokToNotifyLater = null; - try { - // First the token that was related to the disconnect / shutdown may - // not be in the token table - temporarily add it if not - if (token != null) { - if (tokenStore.getToken(token.internalTok.getKey())==null) { - tokenStore.saveToken(token, token.internalTok.getKey()); - } - } - - Vector toksToNot = clientState.resolveOldTokens(reason); - Enumeration toksToNotE = toksToNot.elements(); - while(toksToNotE.hasMoreElements()) { - MqttToken tok = (MqttToken)toksToNotE.nextElement(); - - if (tok.internalTok.getKey().equals(MqttDisconnect.KEY) || - tok.internalTok.getKey().equals(MqttConnect.KEY)) { - // Its con or discon so remember and notify @ end of disc routine - tokToNotifyLater = tok; - } else { - // notify waiters and callbacks of outstanding tokens - // that a problem has occurred and disconnect is in - // progress - callback.asyncOperationComplete(tok); - } - } - }catch(Exception ex) { - // Ignore as we are shutting down - } - return tokToNotifyLater; - } - - public void disconnect(MqttDisconnect disconnect, long quiesceTimeout, MqttToken token) throws MqttException { - final String methodName = "disconnect"; - synchronized (conLock){ - if (isClosed()) { - //@TRACE 223=failed: in closed state - log.fine(className,methodName,"223"); - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_CLOSED); - } else if (isDisconnected()) { - //@TRACE 211=failed: already disconnected - log.fine(className,methodName,"211"); - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_ALREADY_DISCONNECTED); - } else if (isDisconnecting()) { - //@TRACE 219=failed: already disconnecting - log.fine(className,methodName,"219"); - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_DISCONNECTING); - } else if (Thread.currentThread() == callback.getThread()) { - //@TRACE 210=failed: called on callback thread - log.fine(className,methodName,"210"); - // Not allowed to call disconnect() from the callback, as it will deadlock. - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_DISCONNECT_PROHIBITED); - } - - //@TRACE 218=state=DISCONNECTING - log.fine(className,methodName,"218"); - conState = DISCONNECTING; - DisconnectBG discbg = new DisconnectBG(disconnect,quiesceTimeout,token); - discbg.start(); - } - } - - public boolean isConnected() { - return conState == CONNECTED; - } - - public boolean isConnecting() { - return conState == CONNECTING; - } - public boolean isDisconnected() { - return conState == DISCONNECTED; - } - - public boolean isDisconnecting() { - return conState == DISCONNECTING; - } - public boolean isClosed() { - return conState == CLOSED; - } - - - public void setCallback(MqttCallback mqttCallback) { - this.callback.setCallback(mqttCallback); - } - - protected MqttTopic getTopic(String topic) { - return new MqttTopic(topic, this); - } - public void setNetworkModule(NetworkModule networkModule) { - this.networkModule = networkModule; - } - public MqttDeliveryToken[] getPendingDeliveryTokens() { - return tokenStore.getOutstandingDelTokens(); - } - protected void deliveryComplete(MqttPublish msg) throws MqttPersistenceException { - this.clientState.deliveryComplete(msg); - } - - public IMqttAsyncClient getClient() { - return client; - } - - public long getKeepAlive() { - return this.clientState.getKeepAlive(); - } - - public ClientState getClientState() { - return clientState; - } - - public MqttConnectOptions getConOptions() { - return conOptions; - } - - public Properties getDebug() { - Properties props = new Properties(); - props.put("conState", new Integer(conState)); - props.put("serverURI", getClient().getServerURI()); - props.put("callback", callback); - props.put("stoppingComms", new Boolean(stoppingComms)); - return props; - } - - - - // Kick off the connect processing in the background so that it does not block. For instance - // the socket could take time to create. - private class ConnectBG implements Runnable { - ClientComms clientComms = null; - Thread cBg = null; - MqttToken conToken; - MqttConnect conPacket; - - ConnectBG(ClientComms cc, MqttToken cToken, MqttConnect cPacket) { - clientComms = cc; - conToken = cToken; - conPacket = cPacket; - cBg = new Thread(this, "MQTT Con: "+getClient().getClientId()); - } - - void start() { - cBg.start(); - } - - public void run() { - final String methodName = "connectBG:run"; - MqttException mqttEx = null; - //@TRACE 220=> - log.fine(className, methodName, "220"); - - try { - // Reset an exception on existing delivery tokens. - // This will have been set if disconnect occured before delivery was - // fully processed. - MqttDeliveryToken[] toks = tokenStore.getOutstandingDelTokens(); - for (int i=0; i - log.fine(className, methodName, "221"); - - // Allow current inbound and outbound work to complete - clientState.quiesce(quiesceTimeout); - try { - internalSend(disconnect, token); - token.internalTok.waitUntilSent(); - } - catch (MqttException ex) { - } - finally { - token.internalTok.markComplete(null, null); - shutdownConnection(token, null); - } - } - } -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/ClientDefaults.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/ClientDefaults.java deleted file mode 100644 index 996ce0b..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/ClientDefaults.java +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal; - -public class ClientDefaults { - public static final int MAX_MSG_SIZE = 1024 * 1024 * 256; // 256 MB -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/ClientState.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/ClientState.java deleted file mode 100644 index b39c5ef..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/ClientState.java +++ /dev/null @@ -1,1146 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal; - -import java.io.EOFException; -import java.util.Enumeration; -import java.util.Hashtable; -import java.util.Properties; -import java.util.Vector; - -import org.eclipse.paho.client.mqttv3.MqttClientPersistence; -import org.eclipse.paho.client.mqttv3.MqttDeliveryToken; -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.MqttMessage; -import org.eclipse.paho.client.mqttv3.MqttPersistable; -import org.eclipse.paho.client.mqttv3.MqttPersistenceException; -import org.eclipse.paho.client.mqttv3.MqttToken; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttAck; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttConnack; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttConnect; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttPingReq; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttPingResp; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttPubAck; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttPubComp; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttPubRec; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttPubRel; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttPublish; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage; -import org.eclipse.paho.client.mqttv3.logging.Logger; -import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; - -/** - * The core of the client, which holds the state information for pending and - * in-flight messages. - * - * Messages that have been accepted for delivery are moved between several objects - * while being delivered. - * - * 1) When the client is not running messages are stored in a persistent store that - * implements the MqttClientPersistent Interface. The default is MqttDefaultFilePersistencew - * which stores messages safely across failures and system restarts. If no persistence - * is specified there is a fall back to MemoryPersistence which will maintain the messages - * while the Mqtt client is instantiated. - * - * 2) When the client or specifically ClientState is instantiated the messages are - * read from the persistent store into: - * - outboundqos2 hashtable if a qos 2 publish or pubrel - * - outboundqos1 hashtable if a qos 1 publish - * (see restoreState) - * - * 3) On Connect, copy messages from the outbound hashtables to the pendingMessages or - * pendingFlows vector in messageid order. - * - Initial message publish goes onto the pendingmessages buffer. - * - Pubrel goes onto the pendingflows buffer - * (see restoreInflightMessages) - * - * 4) Sender thread reads messages from the pendingflows and pendingmessages buffer - * one at a time. The message is removed from the pendingbuffer but remains on the - * outbound* hashtable. The hashtable is the place where the full set of outstanding - * messages are stored in memory. (Persistence is only used at start up) - * - * 5) Receiver thread - receives wire messages: - * - if QOS 1 then remove from persistence and outboundqos1 - * - if QOS 2 pubrec send pubrel. Updating the outboundqos2 entry with the pubrel - * and update persistence. - * - if QOS 2 pubcomp remove from persistence and outboundqos2 - * - * Notes: - * because of the multi threaded nature of the client it is vital that any changes to this - * class take concurrency into account. For instance as soon as a flow / message is put on - * the wire it is possible for the receiving thread to receive the ack and to be processing - * the response before the sending side has finished processing. For instance a connect may - * be sent, the conack received before the connect notify send has been processed! - * - */ -public class ClientState { - private static final String PERSISTENCE_SENT_PREFIX = "s-"; - private static final String PERSISTENCE_CONFIRMED_PREFIX = "sc-"; - private static final String PERSISTENCE_RECEIVED_PREFIX = "r-"; - - private static final int MIN_MSG_ID = 1; // Lowest possible MQTT message ID to use - private static final int MAX_MSG_ID = 65535; // Highest possible MQTT message ID to use - private int nextMsgId = MIN_MSG_ID - 1; // The next available message ID to use - private Hashtable inUseMsgIds; // Used to store a set of in-use message IDs - - volatile private Vector pendingMessages; - volatile private Vector pendingFlows; - - private CommsTokenStore tokenStore; - private ClientComms clientComms = null; - private CommsCallback callback = null; - private long keepAlive; - private boolean cleanSession; - private MqttClientPersistence persistence; - - private int maxInflight = 10; - private int actualInFlight = 0; - private int inFlightPubRels = 0; - - private Object queueLock = new Object(); - private Object quiesceLock = new Object(); - private boolean quiescing = false; - - private long lastOutboundActivity = 0; - private long lastInboundActivity = 0; - private long lastPing = 0; - private MqttWireMessage pingCommand; - private boolean pingOutstanding = false; - - private boolean connected = false; - - private Hashtable outboundQoS2 = null; - private Hashtable outboundQoS1 = null; - private Hashtable inboundQoS2 = null; - - private final static String className = ClientState.class.getName(); - private Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className); - - protected ClientState(MqttClientPersistence persistence, CommsTokenStore tokenStore, - CommsCallback callback, ClientComms clientComms) throws MqttException { - - log.setResourceName(clientComms.getClient().getClientId()); - log.finer(className, "", "" ); - - inUseMsgIds = new Hashtable(); - pendingMessages = new Vector(this.maxInflight); - pendingFlows = new Vector(); - outboundQoS2 = new Hashtable(); - outboundQoS1 = new Hashtable(); - inboundQoS2 = new Hashtable(); - pingCommand = new MqttPingReq(); - inFlightPubRels = 0; - actualInFlight = 0; - - this.persistence = persistence; - this.callback = callback; - this.tokenStore = tokenStore; - this.clientComms = clientComms; - - restoreState(); - } - - protected void setKeepAliveSecs(long keepAliveSecs) { - this.keepAlive = keepAliveSecs*1000; - } - protected long getKeepAlive() { - return this.keepAlive; - } - protected void setCleanSession(boolean cleanSession) { - this.cleanSession = cleanSession; - } - - private String getSendPersistenceKey(MqttWireMessage message) { - return PERSISTENCE_SENT_PREFIX + message.getMessageId(); - } - - private String getSendConfirmPersistenceKey(MqttWireMessage message) { - return PERSISTENCE_CONFIRMED_PREFIX + message.getMessageId(); - } - - private String getReceivedPersistenceKey(MqttWireMessage message) { - return PERSISTENCE_RECEIVED_PREFIX + message.getMessageId(); - } - - protected void clearState() throws MqttException { - final String methodName = "clearState"; - //@TRACE 603=clearState - log.fine(className, methodName,">"); - - persistence.clear(); - inUseMsgIds.clear(); - pendingMessages.clear(); - pendingFlows.clear(); - outboundQoS2.clear(); - outboundQoS1.clear(); - inboundQoS2.clear(); - tokenStore.clear(); - } - - private MqttWireMessage restoreMessage(String key, MqttPersistable persistable) throws MqttException { - final String methodName = "restoreMessage"; - MqttWireMessage message = null; - - try { - message = MqttWireMessage.createWireMessage(persistable); - } - catch (MqttException ex) { - //@TRACE 602=key={0} exception - log.fine(className, methodName, "602", new Object[] {key}, ex); - if (ex.getCause() instanceof EOFException) { - // Premature end-of-file means that the message is corrupted - if (key != null) { - persistence.remove(key); - } - } - else { - throw ex; - } - } - //@TRACE 601=key={0} message={1} - log.fine(className, methodName, "601", new Object[]{key,message}); - return message; - } - - /** - * Inserts a new message to the list, ensuring that list is ordered from lowest to highest in terms of the message id's. - * @param list the list to insert the message into - * @param newMsg the message to insert into the list - */ - private void insertInOrder(Vector list, MqttWireMessage newMsg) { - int newMsgId = newMsg.getMessageId(); - for (int i = 0; i < list.size(); i++) { - MqttWireMessage otherMsg = (MqttWireMessage) list.elementAt(i); - int otherMsgId = otherMsg.getMessageId(); - if (otherMsgId > newMsgId) { - list.insertElementAt(newMsg, i); - return; - } - } - list.addElement(newMsg); - } - - /** - * Produces a new list with the messages properly ordered according to their message id's. - * @param list the list containing the messages to produce a new reordered list for - * - this will not be modified or replaced, i.e., be read-only to this method - * @return a new reordered list - */ - private Vector reOrder(Vector list) { - - // here up the new list - Vector newList = new Vector(); - - if (list.size() == 0) { - return newList; // nothing to reorder - } - - int previousMsgId = 0; - int largestGap = 0; - int largestGapMsgIdPosInList = 0; - for (int i = 0; i < list.size(); i++) { - int currentMsgId = ((MqttWireMessage) list.elementAt(i)).getMessageId(); - if (currentMsgId - previousMsgId > largestGap) { - largestGap = currentMsgId - previousMsgId; - largestGapMsgIdPosInList = i; - } - previousMsgId = currentMsgId; - } - int lowestMsgId = ((MqttWireMessage) list.elementAt(0)).getMessageId(); - int highestMsgId = previousMsgId; // last in the sorted list - - // we need to check that the gap after highest msg id to the lowest msg id is not beaten - if (MAX_MSG_ID - highestMsgId + lowestMsgId > largestGap) { - largestGapMsgIdPosInList = 0; - } - - // starting message has been located, let's start from this point on - for (int i = largestGapMsgIdPosInList; i < list.size(); i++) { - newList.addElement(list.elementAt(i)); - } - - // and any wrapping back to the beginning - for (int i = 0; i < largestGapMsgIdPosInList; i++) { - newList.addElement(list.elementAt(i)); - } - - return newList; - } - - /** - * Restores the state information from persistence. - */ - protected void restoreState() throws MqttException { - final String methodName = "restoreState"; - Enumeration messageKeys = persistence.keys(); - MqttPersistable persistable; - String key; - int highestMsgId = nextMsgId; - Vector orphanedPubRels = new Vector(); - //@TRACE 600=> - log.fine(className, methodName, "600"); - - while (messageKeys.hasMoreElements()) { - key = (String) messageKeys.nextElement(); - persistable = persistence.get(key); - MqttWireMessage message = restoreMessage(key, persistable); - if (message != null) { - if (key.startsWith(PERSISTENCE_RECEIVED_PREFIX)) { - //@TRACE 604=inbound QoS 2 publish key={0} message={1} - log.fine(className,methodName,"604", new Object[]{key,message}); - - // The inbound messages that we have persisted will be QoS 2 - inboundQoS2.put(new Integer(message.getMessageId()),message); - } else if (key.startsWith(PERSISTENCE_SENT_PREFIX)) { - MqttPublish sendMessage = (MqttPublish) message; - highestMsgId = Math.max(sendMessage.getMessageId(), highestMsgId); - if (persistence.containsKey(getSendConfirmPersistenceKey(sendMessage))) { - MqttPersistable persistedConfirm = persistence.get(getSendConfirmPersistenceKey(sendMessage)); - // QoS 2, and CONFIRM has already been sent... - MqttPubRel confirmMessage = (MqttPubRel) restoreMessage(key, persistedConfirm); - if (confirmMessage != null) { - confirmMessage.setDuplicate(true); - //@TRACE 605=outbound QoS 2 pubrel key={0} message={1} - log.fine(className,methodName, "605", new Object[]{key,message}); - - outboundQoS2.put(new Integer(confirmMessage.getMessageId()), confirmMessage); - } else { - //@TRACE 606=outbound QoS 2 completed key={0} message={1} - log.fine(className,methodName, "606", new Object[]{key,message}); - } - } else { - // QoS 1 or 2, with no CONFIRM sent... - // Put the SEND to the list of pending messages, ensuring message ID ordering... - sendMessage.setDuplicate(true); - if (sendMessage.getMessage().getQos() == 2) { - //@TRACE 607=outbound QoS 2 publish key={0} message={1} - log.fine(className,methodName, "607", new Object[]{key,message}); - - outboundQoS2.put(new Integer(sendMessage.getMessageId()),sendMessage); - } else { - //@TRACE 608=outbound QoS 1 publish key={0} message={1} - log.fine(className,methodName, "608", new Object[]{key,message}); - - outboundQoS1.put(new Integer(sendMessage.getMessageId()),sendMessage); - } - } - MqttDeliveryToken tok = tokenStore.restoreToken(sendMessage); - tok.internalTok.setClient(clientComms.getClient()); - inUseMsgIds.put(new Integer(sendMessage.getMessageId()),new Integer(sendMessage.getMessageId())); - } - else if (key.startsWith(PERSISTENCE_CONFIRMED_PREFIX)) { - MqttPubRel pubRelMessage = (MqttPubRel) message; - if (!persistence.containsKey(getSendPersistenceKey(pubRelMessage))) { - orphanedPubRels.addElement(key); - } - } - } - } - - messageKeys = orphanedPubRels.elements(); - while(messageKeys.hasMoreElements()) { - key = (String) messageKeys.nextElement(); - //@TRACE 609=removing orphaned pubrel key={0} - log.fine(className,methodName, "609", new Object[]{key}); - - persistence.remove(key); - } - - nextMsgId = highestMsgId; - } - - private void restoreInflightMessages() { - final String methodName = "restoreInflightMessages"; - pendingMessages = new Vector(this.maxInflight); - pendingFlows = new Vector(); - - Enumeration keys = outboundQoS2.keys(); - while (keys.hasMoreElements()) { - Object key = keys.nextElement(); - Object msg = outboundQoS2.get(key); - if (msg instanceof MqttPublish) { - //@TRACE 610=QoS 2 publish key={0} - log.fine(className,methodName, "610", new Object[]{key}); - - insertInOrder(pendingMessages, (MqttPublish)msg); - } else if (msg instanceof MqttPubRel) { - //@TRACE 611=QoS 2 pubrel key={0} - log.fine(className,methodName, "611", new Object[]{key}); - - insertInOrder(pendingFlows, (MqttPubRel)msg); - } - } - keys = outboundQoS1.keys(); - while (keys.hasMoreElements()) { - Object key = keys.nextElement(); - MqttPublish msg = (MqttPublish)outboundQoS1.get(key); - //@TRACE 612=QoS 1 publish key={0} - log.fine(className,methodName, "612", new Object[]{key}); - - insertInOrder(pendingMessages, msg); - } - - this.pendingFlows = reOrder(pendingFlows); - this.pendingMessages = reOrder(pendingMessages); - } - - /** - * Submits a message for delivery. This method will block until there is - * room in the inFlightWindow for the message. The message is put into - * persistence before returning. - * - * @param message the message to send - * @param token the token that can be used to track delivery of the message - * @throws MqttException - */ - public void send(MqttWireMessage message, MqttToken token) throws MqttException { - final String methodName = "send"; - if (message.isMessageIdRequired() && (message.getMessageId() == 0)) { - message.setMessageId(getNextMessageId()); - } - if (token != null ) { - try { - token.internalTok.setMessageID(message.getMessageId()); - } catch (Exception e) { - } - } - - if (message instanceof MqttPublish) { - synchronized (queueLock) { - if (actualInFlight >= this.maxInflight) { - //@TRACE 613= sending {0} msgs at max inflight window - log.fine(className, methodName, "613", new Object[]{new Integer(actualInFlight)}); - - throw new MqttException(MqttException.REASON_CODE_MAX_INFLIGHT); - } - - MqttMessage innerMessage = ((MqttPublish) message).getMessage(); - //@TRACE 628=pending publish key={0} qos={1} message={2} - log.fine(className,methodName,"628", new Object[]{new Integer(message.getMessageId()), new Integer(innerMessage.getQos()), message}); - - switch(innerMessage.getQos()) { - case 2: - outboundQoS2.put(new Integer(message.getMessageId()), message); - persistence.put(getSendPersistenceKey(message), (MqttPublish) message); - break; - case 1: - outboundQoS1.put(new Integer(message.getMessageId()), message); - persistence.put(getSendPersistenceKey(message), (MqttPublish) message); - break; - } - tokenStore.saveToken(token, message); - pendingMessages.addElement(message); - queueLock.notifyAll(); - } - } else { - //@TRACE 615=pending send key={0} message {1} - log.fine(className,methodName,"615", new Object[]{new Integer(message.getMessageId()), message}); - - if (message instanceof MqttConnect) { - synchronized (queueLock) { - // Add the connect action at the head of the pending queue ensuring it jumps - // ahead of any of other pending actions. - tokenStore.saveToken(token, message); - pendingFlows.insertElementAt(message,0); - queueLock.notifyAll(); - } - } else { - if (message instanceof MqttPingReq) { - this.pingCommand = message; - } - else if (message instanceof MqttPubRel) { - outboundQoS2.put(new Integer(message.getMessageId()), message); - persistence.put(getSendConfirmPersistenceKey(message), (MqttPubRel) message); - } - else if (message instanceof MqttPubComp) { - persistence.remove(getReceivedPersistenceKey(message)); - } - - synchronized (queueLock) { - if ( !(message instanceof MqttAck )) { - tokenStore.saveToken(token, message); - } - pendingFlows.addElement(message); - queueLock.notifyAll(); - } - } - } - } - - /** - * This removes the MqttSend message from the outbound queue and persistence. - * @param message - * @throws MqttPersistenceException - */ - protected void undo(MqttPublish message) throws MqttPersistenceException { - final String methodName = "undo"; - synchronized (queueLock) { - //@TRACE 618=key={0} QoS={1} - log.fine(className,methodName,"618", new Object[]{new Integer(message.getMessageId()), new Integer(message.getMessage().getQos())}); - - if (message.getMessage().getQos() == 1) { - outboundQoS1.remove(new Integer(message.getMessageId())); - } else { - outboundQoS2.remove(new Integer(message.getMessageId())); - } - pendingMessages.removeElement(message); - persistence.remove(getSendPersistenceKey(message)); - tokenStore.removeToken(message); - checkQuiesceLock(); - } - } - - /** - * Check and send a ping if needed and check for ping timeout. - * Need to send a ping if nothing has been sent or received - * in the last keepalive interval. It is important to check for - * both sent and received packets in order to catch the case where an - * app is solely sending QOS 0 messages or receiving QOS 0 messages. - * QOS 0 message are not good enough for checking a connection is - * alive as they are one way messages. - * - * If a ping has been sent but no data has been received in the - * last keepalive interval then the connection is deamed to be broken. - */ - private void checkForActivity() throws MqttException { - final String methodName = "checkForActivity"; - - if (connected && this.keepAlive > 0) { - long time = System.currentTimeMillis(); - - if (!pingOutstanding) { - // Is a ping required? - if (time - lastOutboundActivity >= this.keepAlive || - time - lastInboundActivity >= this.keepAlive) { - - //@TRACE 620=ping needed. keepAlive={0} lastOutboundActivity={1} lastInboundActivity={2} - log.fine(className,methodName,"620", new Object[]{new Long(this.keepAlive),new Long(lastOutboundActivity),new Long(lastInboundActivity)}); - - pingOutstanding = true; - lastPing = time; - MqttToken token = new MqttToken(clientComms.getClient().getClientId()); - tokenStore.saveToken(token, pingCommand); - pendingFlows.insertElementAt(pingCommand, 0); - } - } else if (time - lastPing >= this.keepAlive) { - // A ping is outstanding but no packet has been received in KA so connection is deemed broken - //@TRACE 619=Timed out as no activity, keepAlive={0} lastOutboundActivity={1} lastInboundActivity={2} - log.severe(className,methodName,"619", new Object[]{new Long(this.keepAlive),new Long(lastOutboundActivity),new Long(lastInboundActivity)}); - - // A ping has already been sent. At this point, assume that the - // broker has hung and the TCP layer hasn't noticed. - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_TIMEOUT); - } - } - } - - /** - * This returns the next piece of work, ie message, for the CommsSender - * to send over the network. - * Calls to this method block until either: - * - there is a message to be sent - * - the keepAlive interval is exceeded, which triggers a ping message - * to be returned - * - {@link #disconnected(MqttException, boolean)} is called - * @return the next message to send, or null if the client is disconnected - */ - protected MqttWireMessage get() throws MqttException { - final String methodName = "get"; - MqttWireMessage result = null; - - synchronized (queueLock) { - while (result == null) { - if (pendingMessages.isEmpty() && pendingFlows.isEmpty()) { - try { - long ttw = getTimeUntilPing(); - //@TRACE 644=nothing to send, wait for {0} ms - log.fine(className,methodName, "644", new Object[] {new Long(ttw)}); - - queueLock.wait(getTimeUntilPing()); - } catch (InterruptedException e) { - } - } - - // Handle the case where not connected. This should only be the case if: - // - in the process of disconnecting / shutting down - // - in the process of connecting - if (!connected && - (pendingFlows.isEmpty() || !((MqttWireMessage)pendingFlows.elementAt(0) instanceof MqttConnect))) { - //@TRACE 621=no outstanding flows and not connected - log.fine(className,methodName,"621"); - - return null; - } - - // Check if there is a need to send a ping to keep the session alive. - // Note this check is done before processing messages. If not done first - // an app that only publishes QOS 0 messages will prevent keepalive processing - // from functioning. - checkForActivity(); - - // Now process any queued flows or messages - if (!pendingFlows.isEmpty()) { - // Process the first "flow" in the queue - result = (MqttWireMessage)pendingFlows.elementAt(0); - pendingFlows.removeElementAt(0); - if (result instanceof MqttPubRel) { - inFlightPubRels++; - - //@TRACE 617=+1 inflightpubrels={0} - log.fine(className,methodName,"617", new Object[]{new Integer(inFlightPubRels)}); - } - - checkQuiesceLock(); - } else if (!pendingMessages.isEmpty()) { - // If the inflight window is full then messages are not - // processed until the inflight window has space. - if (actualInFlight < this.maxInflight) { - // The in flight window is not full so process the - // first message in the queue - result = (MqttWireMessage)pendingMessages.elementAt(0); - pendingMessages.removeElementAt(0); - actualInFlight++; - - //@TRACE 623=+1 actualInFlight={0} - log.fine(className,methodName,"623",new Object[]{new Integer(actualInFlight)}); - } else { - //@TRACE 622=inflight window full - log.fine(className,methodName,"622"); - } - } - } - } - return result; - } - - public void setKeepAliveInterval(long interval) { - this.keepAlive = interval; - } - - /** - * Deduce how long to to wait until a ping is required. - * - * In order to keep the connection alive the server must see activity within - * the keepalive interval. If the application is not sending / receiving - * any messages then the client will send a ping. This method works out - * the next time that a ping must be sent in order for the server to - * know the client is alive. - * @return time before a ping needs to be sent to keep alive the connection - */ - long getTimeUntilPing() { - long pingin = getKeepAlive(); - // If KA is zero which means just wait for work or - // if a ping is outstanding return the KA value - if (connected && (getKeepAlive() > 0) && !pingOutstanding) { - - long time = System.currentTimeMillis(); - long timeSinceOut = (time-lastOutboundActivity); - long timeSinceIn = (time-lastInboundActivity); - - if (timeSinceOut > timeSinceIn) { - pingin = (getKeepAlive()-timeSinceOut); - } else { - pingin = (getKeepAlive()-timeSinceIn); - } - - // Unlikely to be negative or zero but in the case it is return a - // small value > 0 to cause a ping to occur - if (pingin <= 0) { - pingin = 10; - } - } - return (pingin); - } - - /** - * Called by the CommsSender when a message has been sent - * @param message - */ - protected void notifySent(MqttWireMessage message) { - final String methodName = "notifySent"; - - this.lastOutboundActivity = System.currentTimeMillis(); - //@TRACE 625=key={0} - log.fine(className,methodName,"625",new Object[]{message.getKey()}); - - MqttToken token = tokenStore.getToken(message); - token.internalTok.notifySent(); - if (message instanceof MqttPublish) { - if (((MqttPublish)message).getMessage().getQos() == 0) { - // once a QOS 0 message is sent we can clean up its records straight away as - // we won't be hearing about it again - token.internalTok.markComplete(null, null); - callback.asyncOperationComplete(token); - decrementInFlight(); - releaseMessageId(message.getMessageId()); - tokenStore.removeToken(message); - checkQuiesceLock(); - } - } - } - - private void decrementInFlight() { - final String methodName = "decrementInFlight"; - synchronized (queueLock) { - actualInFlight--; - //@TRACE 646=-1 actualInFlight={0} - log.fine(className,methodName,"646",new Object[]{new Integer(actualInFlight)}); - - if (!checkQuiesceLock()) { - queueLock.notifyAll(); - } - } - } - - protected boolean checkQuiesceLock() { - final String methodName = "checkQuiesceLock"; -// if (quiescing && actualInFlight == 0 && pendingFlows.size() == 0 && inFlightPubRels == 0 && callback.isQuiesced()) { - int tokC = tokenStore.count(); - if (quiescing && tokC == 0 && pendingFlows.size() == 0 && callback.isQuiesced()) { - //@TRACE 626=quiescing={0} actualInFlight={1} pendingFlows={2} inFlightPubRels={3} callbackQuiesce={4} tokens={5} - log.fine(className,methodName,"626",new Object[]{new Boolean(quiescing), new Integer(actualInFlight), new Integer(pendingFlows.size()), new Integer(inFlightPubRels), new Boolean(callback.isQuiesced()), new Integer(tokC)}); - synchronized (quiesceLock) { - quiesceLock.notifyAll(); - } - return true; - } - return false; - } - - /** - * Called by the CommsReceiver when an ack has arrived. - * - * @param message - * @throws MqttException - */ - protected void notifyReceivedAck(MqttAck ack) throws MqttException { - final String methodName = "notifyReceivedAck"; - this.lastInboundActivity = System.currentTimeMillis(); - - // @TRACE 627=received key={0} message={1} - log.fine(className, methodName, "627", new Object[] { - new Integer(ack.getMessageId()), ack }); - - MqttToken token = tokenStore.getToken(ack); - MqttException mex = null; - - if (ack instanceof MqttPubRec) { - // Complete the QOS 2 flow. Unlike all other - // flows, QOS is a 2 phase flow. The second phase sends a - // pubrel - the operation is not complete until a pubcomp - // is received - MqttPubRel rel = new MqttPubRel((MqttPubRec) ack); - this.send(rel, token); - } else if (ack instanceof MqttPubAck || ack instanceof MqttPubComp) { - // QoS 1 & 2 notify users of result before removing from - // persistence - notifyResult(ack, token, mex); - // Do not remove publish / delivery token at this stage - // do this when the persistence is removed later - } else if (ack instanceof MqttPingResp) { - pingOutstanding = false; - notifyResult(ack, token, mex); - tokenStore.removeToken(ack); - } else if (ack instanceof MqttConnack) { - int rc = ((MqttConnack) ack).getReturnCode(); - if (rc == 0) { - synchronized (queueLock) { - if (cleanSession) { - clearState(); - // Add the connect token back in so that users can be - // notified when connect completes. - tokenStore.saveToken(token,ack); - } - inFlightPubRels = 0; - actualInFlight = 0; - restoreInflightMessages(); - connected(); - } - } else { - mex = ExceptionHelper.createMqttException(rc); - throw mex; - } - - clientComms.connectComplete((MqttConnack) ack, mex); - notifyResult(ack, token, mex); - tokenStore.removeToken(ack); - - // Notify the sender thread that there maybe work for it to do now - synchronized (queueLock) { - queueLock.notifyAll(); - } - } else { - // Sub ack or unsuback - notifyResult(ack, token, mex); - releaseMessageId(ack.getMessageId()); - tokenStore.removeToken(ack); - } - - checkQuiesceLock(); - } - - /** - * Called by the CommsReceiver when a message has been received. - * Handles inbound messages and other flows such as pubrel. - * - * @param message - * @throws MqttException - */ - protected void notifyReceivedMsg(MqttWireMessage message) throws MqttException { - final String methodName = "notifyReceivedMsg"; - this.lastInboundActivity = System.currentTimeMillis(); - - // @TRACE 651=received key={0} message={1} - log.fine(className, methodName, "651", new Object[] { - new Integer(message.getMessageId()), message }); - - if (!quiescing) { - if (message instanceof MqttPublish) { - MqttPublish send = (MqttPublish) message; - switch (send.getMessage().getQos()) { - case 0: - case 1: - if (callback != null) { - callback.messageArrived(send); - } - break; - case 2: - persistence.put(getReceivedPersistenceKey(message), - (MqttPublish) message); - inboundQoS2.put(new Integer(send.getMessageId()), send); - this.send(new MqttPubRec(send), null); - } - } else if (message instanceof MqttPubRel) { - MqttPublish sendMsg = (MqttPublish) inboundQoS2 - .get(new Integer(message.getMessageId())); - if (sendMsg != null) { - if (callback != null) { - callback.messageArrived(sendMsg); - } - } else { - // Original publish has already been delivered. - MqttPubComp pubComp = new MqttPubComp(message - .getMessageId()); - this.send(pubComp, null); - } - } - } - } - - - /** - * Called when waiters and callbacks have processed the message. For - * messages where delivery is complete the message can be removed from - * persistence and counters adjusted accordingly. Also tidy up by removing - * token from store... - * - * @param message - * @throws MqttException - */ - protected void notifyComplete(MqttToken token) throws MqttException { - final String methodName = "notifyComplete"; - - MqttWireMessage message = token.internalTok.getWireMessage(); - - if (message != null && message instanceof MqttAck) { - // @TRACE 629=received key={0} token={1} message={2} - log.fine(className, methodName, "629", new Object[] { - new Integer(message.getMessageId()), token, message }); - - MqttAck ack = (MqttAck) message; - - if (ack instanceof MqttPubAck) { - // QoS 1 - user notified now remove from persistence... - persistence.remove(getSendPersistenceKey(message)); - outboundQoS1.remove(new Integer(ack.getMessageId())); - decrementInFlight(); - releaseMessageId(message.getMessageId()); - tokenStore.removeToken(message); - // @TRACE 650=removed Qos 1 publish. key={0} - log.fine(className, methodName, "650", - new Object[] { new Integer(ack.getMessageId()) }); - } else if (ack instanceof MqttPubComp) { - // QoS 2 - user notified now remove from persistence... - persistence.remove(getSendPersistenceKey(message)); - persistence.remove(getSendConfirmPersistenceKey(message)); - outboundQoS2.remove(new Integer(ack.getMessageId())); - - inFlightPubRels--; - decrementInFlight(); - releaseMessageId(message.getMessageId()); - tokenStore.removeToken(message); - - // @TRACE 645=removed QoS 2 publish/pubrel. key={0}, -1 inFlightPubRels={1} - log.fine(className, methodName, "645", new Object[] { - new Integer(ack.getMessageId()), - new Integer(inFlightPubRels) }); - } - - checkQuiesceLock(); - } - } - - protected void notifyResult(MqttWireMessage ack, MqttToken token, MqttException ex) { - final String methodName = "notifyResult"; - // unblock any threads waiting on the token - token.internalTok.markComplete(ack, ex); - - // Let the user know an async operation has completed and then remove the token - if (ack != null && ack instanceof MqttAck && !(ack instanceof MqttPubRec)) { - //@TRACE 648=key{0}, msg={1}, excep={2} - log.fine(className,methodName, "648", new Object [] {token.internalTok.getKey(), ack, ex}); - callback.asyncOperationComplete(token); - } - // There are cases where there is no ack as the operation failed before - // an ack was received - if (ack == null ) { - //@TRACE 649=key={0},excep={1} - log.fine(className,methodName, "649", new Object [] { token.internalTok.getKey(), ex}); - callback.asyncOperationComplete(token); - } - } - - /** - * Called when the client has successfully connected to the broker - */ - public void connected() { - final String methodName = "connected"; - //@TRACE 631=connected - log.fine(className, methodName, "631"); - this.connected = true; - } - - /** - * - * Called during shutdown to work out if there are any tokens still - * to be notified and waiters to be unblocked. Notifying and unblocking - * takes place after most shutdown processing has completed. The tokenstore - * is tidied up so it only contains outstanding delivery tokens which are - * valid after reconnect (if clean session is false) - * @param reason The root cause of the disconnection, or null if it is a clean disconnect - */ - public Vector resolveOldTokens(MqttException reason) { - final String methodName = "resolveOldTokens"; - //@TRACE 632=reason {0} - log.fine(className,methodName,"632", new Object[] {reason}); - - // If any outstanding let the user know the reason why it is still - // outstanding by putting the reason shutdown is occurring into the - // token. - MqttException shutReason = reason; - if (reason == null) { - shutReason = new MqttException(MqttException.REASON_CODE_CLIENT_DISCONNECTING); - } - - // Set the token up so it is ready to be notified after disconnect - // processing has completed. Do not - // remove the token from the store if it is a delivery token, it is - // valid after a reconnect. - Vector outT = tokenStore.getOutstandingTokens(); - Enumeration outTE = outT.elements(); - while (outTE.hasMoreElements()) { - MqttToken tok = (MqttToken)outTE.nextElement(); - synchronized (tok) { - if (!tok.isComplete() && !tok.internalTok.isCompletePending() && tok.getException() == null) { - tok.internalTok.setException(shutReason); - } - } - if (!(tok instanceof MqttDeliveryToken)) { - // If not a delivery token it is not valid on - // restart so remove - tokenStore.removeToken(tok.internalTok.getKey()); - } - } - return outT; - } - - /** - * Called when the client has been disconnected from the broker. - * @param reason The root cause of the disconnection, or null if it is a clean disconnect - */ - public void disconnected(MqttException reason) { - final String methodName = "disconnected"; - //@TRACE 633=disconnected - log.fine(className,methodName,"633", new Object[] {reason}); - - this.connected = false; - - try { - if (cleanSession) { - clearState(); - } - - pendingMessages.clear(); - pendingFlows.clear(); - // Reset pingOutstanding to allow reconnects to assume no previous ping. - pingOutstanding = false; - - } catch (MqttException e) { - // Ignore as we have disconnected at this point - } - } - - /** - * Releases a message ID back into the pool of available message IDs. - * If the supplied message ID is not in use, then nothing will happen. - * - * @param msgId A message ID that can be freed up for re-use. - */ - private synchronized void releaseMessageId(int msgId) { - inUseMsgIds.remove(new Integer(msgId)); - } - - /** - * Get the next MQTT message ID that is not already in use, and marks - * it as now being in use. - * - * @return the next MQTT message ID to use - */ - private synchronized int getNextMessageId() throws MqttException { - int startingMessageId = nextMsgId; - // Allow two complete passes of the message ID range. This gives - // any asynchronous releases a chance to occur - int loopCount = 0; - do { - nextMsgId++; - if ( nextMsgId > MAX_MSG_ID ) { - nextMsgId = MIN_MSG_ID; - } - if (nextMsgId == startingMessageId) { - loopCount++; - if (loopCount == 2) { - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_NO_MESSAGE_IDS_AVAILABLE); - } - } - } while( inUseMsgIds.containsKey( new Integer(nextMsgId) ) ); - Integer id = new Integer(nextMsgId); - inUseMsgIds.put(id, id); - return nextMsgId; - } - - /** - * Quiesce the client state, preventing any new messages getting sent, - * and preventing the callback on any newly received messages. - * After the timeout expires, delete any pending messages except for - * outbound ACKs, and wait for those ACKs to complete. - */ - public void quiesce(long timeout) { - final String methodName = "quiesce"; - // If the timeout is greater than zero t - if (timeout > 0 ) { - //@TRACE 637=timeout={0} - log.fine(className,methodName, "637",new Object[]{new Long(timeout)}); - synchronized (queueLock) { - this.quiescing = true; - } - // We don't want to handle any new inbound messages - callback.quiesce(); - notifyQueueLock(); - - synchronized (quiesceLock) { - try { - // If token count is not zero there is outbound work to process and - // if pending flows is not zero there is outstanding work to complete and - // if call back is not quiseced there it needs to complete. - int tokc = tokenStore.count(); - if (tokc > 0 || pendingFlows.size() >0 || !callback.isQuiesced()) { - //@TRACE 639=wait for outstanding: actualInFlight={0} pendingFlows={1} inFlightPubRels={2} tokens={3} - log.fine(className, methodName,"639", new Object[]{new Integer(actualInFlight), new Integer(pendingFlows.size()), new Integer(inFlightPubRels), new Integer(tokc)}); - - // wait for outstanding in flight messages to complete and - // any pending flows to complete - quiesceLock.wait(timeout); - } - } - catch (InterruptedException ex) { - // Don't care, as we're shutting down anyway - } - } - - // Quiesce time up or inflight messsages delivered. Ensure pending delivery - // vectors are cleared ready for disconnect to be sent as the final flow. - synchronized (queueLock) { - pendingMessages.clear(); - pendingFlows.clear(); - quiescing = false; - actualInFlight = 0; - } - //@TRACE 640=finished - log.fine(className, methodName, "640"); - } - } - - protected void notifyQueueLock() { - final String methodName = "notifyQueueLock"; - synchronized (queueLock) { - //@TRACE 638=notifying queueLock holders - log.fine(className,methodName,"638"); - queueLock.notifyAll(); - } - } - - protected void deliveryComplete(MqttPublish message) throws MqttPersistenceException { - final String methodName = "deliveryComplete"; - - //@TRACE 641=remove publish from persistence. key={0} - log.fine(className,methodName,"641", new Object[]{new Integer(message.getMessageId())}); - - persistence.remove(getReceivedPersistenceKey(message)); - inboundQoS2.remove(new Integer(message.getMessageId())); - } - - /** - * Tidy up - * - ensure that tokens are released as they are maintained over a - * disconnect / connect cycle. - */ - protected void close() { - inUseMsgIds.clear(); - pendingMessages.clear(); - pendingFlows.clear(); - outboundQoS2.clear(); - outboundQoS1.clear(); - inboundQoS2.clear(); - tokenStore.clear(); - inUseMsgIds = null; - pendingMessages = null; - pendingFlows = null; - outboundQoS2 = null; - outboundQoS1 = null; - inboundQoS2 = null; - tokenStore = null; - callback = null; - clientComms = null; - persistence = null; - pingCommand = null; - } - - public Properties getDebug() { - Properties props = new Properties(); - props.put("In use msgids", inUseMsgIds); - props.put("pendingMessages", pendingMessages); - props.put("pendingFlows", pendingFlows); - props.put("maxInflight", new Integer(maxInflight)); - props.put("nextMsgID", new Integer(nextMsgId)); - props.put("actualInFlight", new Integer(actualInFlight)); - props.put("inFlightPubRels", new Integer(inFlightPubRels)); - props.put("quiescing", new Boolean(quiescing)); - props.put("pingoutstanding", new Boolean(pingOutstanding)); - props.put("lastOutboundActivity", new Long(lastOutboundActivity)); - props.put("lastInboundActivity", new Long(lastInboundActivity)); - props.put("outboundQoS2", outboundQoS2); - props.put("outboundQoS1", outboundQoS1); - props.put("inboundQoS2", inboundQoS2); - props.put("tokens", tokenStore); - return props; - } -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/CommsCallback.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/CommsCallback.java deleted file mode 100644 index 26369f9..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/CommsCallback.java +++ /dev/null @@ -1,384 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal; - -import java.util.Vector; - -import org.eclipse.paho.client.mqttv3.IMqttActionListener; -import org.eclipse.paho.client.mqttv3.MqttCallback; -import org.eclipse.paho.client.mqttv3.MqttDeliveryToken; -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.MqttToken; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttPubAck; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttPubComp; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttPublish; -import org.eclipse.paho.client.mqttv3.logging.Logger; -import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; - -/** - * Bridge between Receiver and the external API. This class gets called by - * Receiver, and then converts the comms-centric MQTT message objects into ones - * understood by the external API. - */ -public class CommsCallback implements Runnable { - private static int INBOUND_QUEUE_SIZE = 10; - private MqttCallback mqttCallback; - private ClientComms clientComms; - private Vector messageQueue; - private Vector completeQueue; - public boolean running = false; - private boolean quiescing = false; - private Object lifecycle = new Object(); - private Thread callbackThread; - private Object workAvailable = new Object(); - private Object spaceAvailable = new Object(); - private ClientState clientState; - - final static String className = CommsCallback.class.getName(); - Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT, className); - - CommsCallback(ClientComms clientComms) { - this.clientComms = clientComms; - this.messageQueue = new Vector(INBOUND_QUEUE_SIZE); - this.completeQueue = new Vector(INBOUND_QUEUE_SIZE); - log.setResourceName(clientComms.getClient().getClientId()); - } - - public void setClientState(ClientState clientState) { - this.clientState = clientState; - } - - /** - * Starts up the Callback thread. - */ - public void start(String threadName) { - synchronized (lifecycle) { - if (running == false) { - // Praparatory work before starting the background thread. - // For safety ensure any old events are cleared. - messageQueue.clear(); - completeQueue.clear(); - - running = true; - quiescing = false; - callbackThread = new Thread(this, threadName); - callbackThread.start(); - } - } - } - - /** - * Stops the callback thread. - * This call will block until stop has completed. - */ - public void stop() { - final String methodName = "stop"; - synchronized (lifecycle) { - if (running) { - // @TRACE 700=stopping - log.fine(className, methodName, "700"); - running = false; - if (!Thread.currentThread().equals(callbackThread)) { - try { - synchronized (workAvailable) { - // @TRACE 701=notify workAvailable and wait for run - // to finish - log.fine(className, methodName, "701"); - workAvailable.notifyAll(); - } - // Wait for the thread to finish. - callbackThread.join(); - } catch (InterruptedException ex) { - } - } - } - callbackThread = null; - // @TRACE 703=stopped - log.fine(className, methodName, "703"); - } - } - - public void setCallback(MqttCallback mqttCallback) { - this.mqttCallback = mqttCallback; - } - - public void run() { - final String methodName = "run"; - while (running) { - try { - // If no work is currently available, then wait until there is some... - try { - synchronized (workAvailable) { - if (running & messageQueue.isEmpty() - && completeQueue.isEmpty()) { - // @TRACE 704=wait for workAvailable - log.fine(className, methodName, "704"); - workAvailable.wait(); - } - } - } catch (InterruptedException e) { - } - - if (running) { - // Check for deliveryComplete callbacks... - if (!completeQueue.isEmpty()) { - // First call the delivery arrived callback if needed - MqttToken token = (MqttToken) completeQueue.elementAt(0); - handleActionComplete(token); - completeQueue.removeElementAt(0); - } - - // Check for messageArrived callbacks... - if (!messageQueue.isEmpty()) { - // Note, there is a window on connect where a publish - // could arrive before we've - // finished the connect logic. - MqttPublish message = (MqttPublish) messageQueue - .elementAt(0); - - handleMessage(message); - messageQueue.removeElementAt(0); - } - } - - if (quiescing) { - clientState.checkQuiesceLock(); - } - - synchronized (spaceAvailable) { - // Notify the spaceAvailable lock, to say that there's now - // some space on the queue... - - // @TRACE 706=notify spaceAvailable - log.fine(className, methodName, "706"); - spaceAvailable.notifyAll(); - } - } catch (Throwable ex) { - // Users code could throw an Error or Exception e.g. in the case - // of class NoClassDefFoundError - // @TRACE 714=callback threw exception - log.fine(className, methodName, "714", null, ex); - running = false; - clientComms.shutdownConnection(null, new MqttException(ex)); - } - } - } - - private void handleActionComplete(MqttToken token) - throws MqttException { - final String methodName = "handleActionComplete"; - synchronized (token) { - // @TRACE 705=callback and notify for key={0} - log.fine(className, methodName, "705", new Object[] { token.internalTok.getKey() }); - - // Unblock any waiters and if pending complete now set completed - token.internalTok.notifyComplete(); - - if (!token.internalTok.isNotified()) { - // If a callback is registered and delivery has finished - // call delivery complete callback. - if ( mqttCallback != null - && token instanceof MqttDeliveryToken - && token.isComplete()) { - mqttCallback.deliveryComplete((MqttDeliveryToken) token); - } - // Now call async action completion callbacks - fireActionEvent(token); - } - - // Set notified so we don't tell the user again about this action. - if ( token instanceof MqttDeliveryToken && token.isComplete()) { - token.internalTok.setNotified(true); - } - - if (token.isComplete()) { - // Finish by doing any post processing such as delete - // from persistent store but only do so if the action - // is complete - clientState.notifyComplete(token); - } - } - } - - /** - * This method is called when the connection to the server is lost. If there - * is no cause then it was a clean disconnect. The connectionLost callback - * will be invoked if registered and run on the thread that requested - * shutdown e.g. receiver or sender thread. If the request was a user - * initiated disconnect then the disconnect token will be notified. - * - * @param cause the reason behind the loss of connection. - */ - public void connectionLost(MqttException cause) { - final String methodName = "connectionLost"; - // If there was a problem and a client callback has been set inform - // the connection lost listener of the problem. - try { - if (mqttCallback != null && cause != null) { - // @TRACE 708=call connectionLost - log.fine(className, methodName, "708", new Object[] { cause }); - mqttCallback.connectionLost(cause); - } - } catch (java.lang.Throwable t) { - // Just log the fact that a throwable has caught connection lost - // is called during shutdown processing so no need to do anything else - // @TRACE 720=exception from connectionLost {0} - log.fine(className, methodName, "720", new Object[] { t }); - } - } - - /** - * An action has completed - if a completion listener has been set on the - * token then invoke it with the outcome of the action. - * - * @param token - */ - public void fireActionEvent(MqttToken token) { - final String methodName = "fireActionEvent"; - - if (token != null) { - IMqttActionListener asyncCB = token.getActionCallback(); - if (asyncCB != null) { - if (token.getException() == null) { - // @TRACE 716=call onSuccess key={0} - log.fine(className, methodName, "716", - new Object[] { token.internalTok.getKey() }); - asyncCB.onSuccess(token); - } else { - // @TRACE 717=call onFailure key {0} - log.fine(className, methodName, "716", - new Object[] { token.internalTok.getKey() }); - asyncCB.onFailure(token, token.getException()); - } - } - } - } - - /** - * This method is called when a message arrives on a topic. Messages are - * only added to the queue for inbound messages if the client is not - * quiescing. - * - * @param sendMessage - * the MQTT SEND message. - */ - public void messageArrived(MqttPublish sendMessage) { - final String methodName = "messageArrived"; - if (mqttCallback != null) { - // If we already have enough messages queued up in memory, wait - // until some more queue space becomes available. This helps - // the client protect itself from getting flooded by messages - // from the server. - synchronized (spaceAvailable) { - if (!quiescing && messageQueue.size() >= INBOUND_QUEUE_SIZE) { - try { - // @TRACE 709=wait for spaceAvailable - log.fine(className, methodName, "709"); - spaceAvailable.wait(); - } catch (InterruptedException ex) { - } - } - } - if (!quiescing) { - messageQueue.addElement(sendMessage); - // Notify the CommsCallback thread that there's work to do... - synchronized (workAvailable) { - // @TRACE 710=new msg avail, notify workAvailable - log.fine(className, methodName, "710"); - workAvailable.notifyAll(); - } - } - } - } - - /** - * Let the call back thread quiesce. Prevent new inbound messages being - * added to the process queue and let existing work quiesce. (until the - * thread is told to shutdown). - */ - public void quiesce() { - final String methodName = "quiesce"; - this.quiescing = true; - synchronized (spaceAvailable) { - // @TRACE 711=quiesce notify spaceAvailable - log.fine(className, methodName, "711"); - // Unblock anything waiting for space... - spaceAvailable.notifyAll(); - } - } - - public boolean isQuiesced() { - if (quiescing && completeQueue.size() == 0 && messageQueue.size() == 0) { - return true; - } - return false; - } - - private void handleMessage(MqttPublish publishMessage) - throws MqttException, Exception { - final String methodName = "handleMessage"; - // If quisecing process any pending messages. - if (mqttCallback != null) { - String destName = publishMessage.getTopicName(); - - // @TRACE 713=call messageArrived key={0} topic={1} - log.fine(className, methodName, "713", new Object[] { - new Integer(publishMessage.getMessageId()), destName }); - mqttCallback.messageArrived(destName, publishMessage.getMessage()); - if (publishMessage.getMessage().getQos() == 1) { - this.clientComms.internalSend(new MqttPubAck(publishMessage), - new MqttToken(clientComms.getClient().getClientId())); - } else if (publishMessage.getMessage().getQos() == 2) { - this.clientComms.deliveryComplete(publishMessage); - MqttPubComp pubComp = new MqttPubComp(publishMessage); - this.clientComms.internalSend(pubComp, new MqttToken(clientComms.getClient().getClientId())); - } - } - } - - public void asyncOperationComplete(MqttToken token) { - final String methodName = "asyncOperationComplete"; - - if (running) { - // invoke callbacks on callback thread - completeQueue.addElement(token); - synchronized (workAvailable) { - // @TRACE 715=new workAvailable. key={0} - log.fine(className, methodName, "715", new Object[] { token.internalTok.getKey() }); - workAvailable.notifyAll(); - } - } else { - // invoke async callback on invokers thread - try { - handleActionComplete(token); - } catch (Throwable ex) { - // Users code could throw an Error or Exception e.g. in the case - // of class NoClassDefFoundError - // @TRACE 719=callback threw ex: - log.fine(className, methodName, "719", null, ex); - - // Shutdown likely already in progress but no harm to confirm - System.err.println("problem in asyncopcomplete "+ex); - ex.printStackTrace(); - clientComms.shutdownConnection(null, new MqttException(ex)); - } - - } - } - - /** - * Returns the thread used by this callback. - */ - protected Thread getThread() { - return callbackThread; - } -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/CommsReceiver.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/CommsReceiver.java deleted file mode 100644 index 84c49cd..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/CommsReceiver.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal; - -import java.io.IOException; -import java.io.InputStream; - -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.MqttToken; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttAck; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttInputStream; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage; -import org.eclipse.paho.client.mqttv3.logging.Logger; -import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; - -/** - * Receives MQTT packets from the server. - */ -public class CommsReceiver implements Runnable { - private boolean running = false; - private Object lifecycle = new Object(); - private ClientState clientState = null; - private ClientComms clientComms = null; - private MqttInputStream in; - private CommsTokenStore tokenStore = null; - private Thread recThread = null; - - private final static String className = CommsReceiver.class.getName(); - private Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className); - - public CommsReceiver(ClientComms clientComms, ClientState clientState,CommsTokenStore tokenStore, InputStream in) { - this.in = new MqttInputStream(in); - this.clientComms = clientComms; - this.clientState = clientState; - this.tokenStore = tokenStore; - log.setResourceName(clientComms.getClient().getClientId()); - } - - /** - * Starts up the Receiver's thread. - */ - public void start(String threadName) { - final String methodName = "start"; - //@TRACE 855=starting - log.fine(className,methodName, "855"); - synchronized (lifecycle) { - if (running == false) { - running = true; - recThread = new Thread(this, threadName); - recThread.start(); - } - } - } - - /** - * Stops the Receiver's thread. This call will block. - */ - public void stop() { - final String methodName = "stop"; - synchronized (lifecycle) { - //@TRACE 850=stopping - log.fine(className,methodName, "850"); - if (running) { - running = false; - if (!Thread.currentThread().equals(recThread)) { - try { - // Wait for the thread to finish. - recThread.join(); - } - catch (InterruptedException ex) { - } - } - } - } - recThread = null; - //@TRACE 851=stopped - log.fine(className,methodName,"851"); - } - - /** - * Run loop to receive messages from the server. - */ - public void run() { - final String methodName = "run"; - MqttToken token = null; - - while (running && (in != null)) { - try { - //@TRACE 852=network read message - log.fine(className,methodName,"852"); - MqttWireMessage message = in.readMqttWireMessage(); - - if (message instanceof MqttAck) { - token = tokenStore.getToken(message); - if (token!=null) { - synchronized (token) { - // Ensure the notify processing is done under a lock on the token - // This ensures that the send processing can complete before the - // receive processing starts! ( request and ack and ack processing - // can occur before request processing is complete if not! - clientState.notifyReceivedAck((MqttAck)message); - } - } else { - // It its an ack and there is no token then something is not right. - // An ack should always have a token assoicated with it. - throw new MqttException(MqttException.REASON_CODE_UNEXPECTED_ERROR); - } - } else { - // A new message has arrived - clientState.notifyReceivedMsg(message); - } - } - catch (MqttException ex) { - //@TRACE 856=Stopping, MQttException - log.fine(className,methodName,"856",null,ex); - running = false; - // Token maybe null but that is handled in shutdown - clientComms.shutdownConnection(token, ex); - } - catch (IOException ioe) { - //@TRACE 853=Stopping due to IOException - log.fine(className,methodName,"853"); - - running = false; - // An EOFException could be raised if the broker processes the - // DISCONNECT and ends the socket before we complete. As such, - // only shutdown the connection if we're not already shutting down. - if (!clientComms.isDisconnecting()) { - clientComms.shutdownConnection(token, new MqttException(MqttException.REASON_CODE_CONNECTION_LOST, ioe)); - } // else { - } - } - - //@TRACE 854=< - log.fine(className,methodName,"854"); - } - - public boolean isRunning() { - return running; - } -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/CommsSender.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/CommsSender.java deleted file mode 100644 index 44781fc..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/CommsSender.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal; - -import java.io.OutputStream; -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.MqttToken; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttAck; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttOutputStream; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage; -import org.eclipse.paho.client.mqttv3.logging.Logger; -import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; - - -public class CommsSender implements Runnable { - /** - * Sends MQTT packets to the server on its own thread - */ - private boolean running = false; - private Object lifecycle = new Object(); - private ClientState clientState = null; - private MqttOutputStream out; - private ClientComms clientComms = null; - private CommsTokenStore tokenStore = null; - private Thread sendThread = null; - - private final static String className = CommsSender.class.getName(); - private Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT, className); - - public CommsSender(ClientComms clientComms, ClientState clientState, CommsTokenStore tokenStore, OutputStream out) { - this.out = new MqttOutputStream(out); - this.clientComms = clientComms; - this.clientState = clientState; - this.tokenStore = tokenStore; - log.setResourceName(clientComms.getClient().getClientId()); - } - - /** - * Starts up the Sender thread. - */ - public void start(String threadName) { - synchronized (lifecycle) { - if (running == false) { - running = true; - sendThread = new Thread(this, threadName); - sendThread.start(); - } - } - } - - /** - * Stops the Sender's thread. This call will block. - */ - public void stop() { - final String methodName = "stop"; - - synchronized (lifecycle) { - //@TRACE 800=stopping sender - log.fine(className,methodName,"800"); - if (running) { - running = false; - if (!Thread.currentThread().equals(sendThread)) { - try { - // first notify get routine to finish - clientState.notifyQueueLock(); - // Wait for the thread to finish. - sendThread.join(); - } - catch (InterruptedException ex) { - } - } - } - sendThread=null; - //@TRACE 801=stopped - log.fine(className,methodName,"801"); - } - } - - public void run() { - final String methodName = "run"; - MqttWireMessage message = null; - while (running && (out != null)) { - try { - message = clientState.get(); - if (message != null) { - //@TRACE 802=network send key={0} msg={1} - log.fine(className,methodName,"802", new Object[] {message.getKey(),message}); - - if (message instanceof MqttAck) { - out.write(message); - out.flush(); - } else { - MqttToken token = tokenStore.getToken(message); - // While quiescing the tokenstore can be cleared so need - // to check for null for the case where clear occurs - // while trying to send a message. - if (token != null) { - synchronized (token) { - out.write(message); - out.flush(); - clientState.notifySent(message); - } - } - } - } else { // null message - //@TRACE 803=get message returned null, stopping} - log.fine(className,methodName,"803"); - - running = false; - } - } catch (MqttException me) { - handleRunException(message, me); - } catch (Exception ex) { - handleRunException(message, ex); - } - } // end while - - //@TRACE 805=< - log.fine(className, methodName,"805"); - - } - - private void handleRunException(MqttWireMessage message, Exception ex) { - final String methodName = "handleRunException"; - //@TRACE 804=exception - log.fine(className,methodName,"804",null, ex); - MqttException mex; - if ( !(ex instanceof MqttException)) { - mex = new MqttException(MqttException.REASON_CODE_CONNECTION_LOST, ex); - } else { - mex = (MqttException)ex; - } - - running = false; - clientComms.shutdownConnection(null, mex); - } -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/CommsTokenStore.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/CommsTokenStore.java deleted file mode 100644 index e7a6f99..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/CommsTokenStore.java +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal; - -import java.util.Enumeration; -import java.util.Hashtable; -import java.util.Vector; - -import org.eclipse.paho.client.mqttv3.MqttDeliveryToken; -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.MqttToken; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttPublish; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage; -import org.eclipse.paho.client.mqttv3.logging.Logger; -import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; - - -/** - * Provides a "token" based system for storing and tracking actions across - * multiple threads. - * When a message is sent, a token is associated with the message - * and saved using the {@link #saveToken(MqttToken, MqttWireMessage)} method. Anyone interested - * in tacking the state can call one of the wait methods on the token or using - * the asynchronous listener callback method on the operation. - * The {@link CommsReceiver} class, on another thread, reads responses back from - * the network. It uses the response to find the relevant token, which it can then - * notify. - * - * Note: - * Ping, connect and disconnect do not have a unique message id as - * only one outstanding request of each type is allowed to be outstanding - */ -public class CommsTokenStore { - /** Maps message-specific data (usually message IDs) to tokens */ - private Hashtable tokens; - private String logContext; - private MqttException closedResponse = null; - - final static String className = CommsTokenStore.class.getName(); - Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT, className); - - public CommsTokenStore(String logContext) { - final String methodName = ""; - - log.setResourceName(logContext); - this.tokens = new Hashtable(); - this.logContext = logContext; - //@TRACE 308=<> - log.fine(className,methodName,"308");//,new Object[]{message}); - - } - - /** - * Based on the message type that has just been received return the associated - * token from the token store or null if one does not exist. - * @param message whose token is to be returned - * @return token for the requested message - */ - public MqttToken getToken(MqttWireMessage message) { - String key = message.getKey(); - return (MqttToken)tokens.get(key); - } - - public MqttToken getToken(String key) { - return (MqttToken)tokens.get(key); - } - - - public MqttToken removeToken(MqttWireMessage message) { - if (message != null) { - return removeToken(message.getKey()); - } - return null; - } - - public MqttToken removeToken(String key) { - final String methodName = "removeToken"; - //@TRACE 306=key={0} - log.fine(className,methodName,"306",new Object[]{key}); - - if (key != null) { - synchronized(tokens) { - MqttToken tok = (MqttToken)tokens.get(key); - if (tok != null) { - synchronized(tok) { - - return (MqttToken) tokens.remove(key); - } - } - } - } - return null; - } - - /** - * Restores a token after a client restart. This method could be called - * for a SEND of CONFIRM, but either way, the original SEND is what's - * needed to re-build the token. - */ - protected MqttDeliveryToken restoreToken(MqttPublish message) { - final String methodName = "restoreToken"; - MqttDeliveryToken token; - synchronized(tokens) { - String key = new Integer(message.getMessageId()).toString(); - if (this.tokens.containsKey(key)) { - token = (MqttDeliveryToken)this.tokens.get(key); - //@TRACE 302=existing key={0} message={1} token={2} - log.fine(className,methodName, "302",new Object[]{key, message,token}); - } else { - token = new MqttDeliveryToken(logContext); - token.internalTok.setKey(key); - this.tokens.put(key, token); - //@TRACE 303=creating new token key={0} message={1} token={2} - log.fine(className,methodName,"303",new Object[]{key, message, token}); - } - } - return token; - } - - // For outbound messages store the token in the token store - // For pubrel use the existing publish token - protected void saveToken(MqttToken token, MqttWireMessage message) throws MqttException { - final String methodName = "saveToken"; - - synchronized(tokens) { - if (closedResponse == null) { - String key = message.getKey(); - //@TRACE 300=key={0} message={1} - log.fine(className,methodName,"300",new Object[]{key, message}); - - saveToken(token,key); - } else { - throw closedResponse; - } - } - } - - protected void saveToken(MqttToken token, String key) { - final String methodName = "saveToken"; - - synchronized(tokens) { - //@TRACE 307=key={0} token={1} - log.fine(className,methodName,"307",new Object[]{key,token.toString()}); - token.internalTok.setKey(key); - this.tokens.put(key, token); - } - } - - protected void quiesce(MqttException quiesceResponse) { - final String methodName = "quiesce"; - - synchronized(tokens) { - //@TRACE 309=resp={0} - log.fine(className,methodName,"309",new Object[]{quiesceResponse}); - - closedResponse = quiesceResponse; - } - } - - public void open() { - final String methodName = "open"; - - synchronized(tokens) { - //@TRACE 310=> - log.fine(className,methodName,"310"); - - closedResponse = null; - } - } - - public MqttDeliveryToken[] getOutstandingDelTokens() { - final String methodName = "getOutstandingDelTokens"; - - synchronized(tokens) { - //@TRACE 311=> - log.fine(className,methodName,"311"); - - Vector list = new Vector(); - Enumeration enumeration = tokens.elements(); - MqttToken token; - while(enumeration.hasMoreElements()) { - token = (MqttToken)enumeration.nextElement(); - if (token != null - && token instanceof MqttDeliveryToken - && !token.internalTok.isNotified()) { - - list.addElement(token); - } - } - - MqttDeliveryToken[] result = new MqttDeliveryToken[list.size()]; - return (MqttDeliveryToken[]) list.toArray(result); - } - } - - public Vector getOutstandingTokens() { - final String methodName = "getOutstandingTokens"; - - synchronized(tokens) { - //@TRACE 312=> - log.fine(className,methodName,"312"); - - Vector list = new Vector(); - Enumeration enumeration = tokens.elements(); - MqttToken token; - while(enumeration.hasMoreElements()) { - token = (MqttToken)enumeration.nextElement(); - if (token != null) { - list.addElement(token); - } - } - return list; - } - } - - /** - * Empties the token store without notifying any of the tokens. - */ - public void clear() { - final String methodName = "clear"; - //@TRACE 305=> {0} tokens - log.fine(className, methodName, "305", new Object[] {new Integer(tokens.size())}); - synchronized(tokens) { - tokens.clear(); - } - } - - public int count() { - synchronized(tokens) { - return tokens.size(); - } - } - public String toString() { - String lineSep = System.getProperty("line.separator","\n"); - StringBuffer toks = new StringBuffer(); - synchronized(tokens) { - Enumeration enumeration = tokens.elements(); - MqttToken token; - while(enumeration.hasMoreElements()) { - token = (MqttToken)enumeration.nextElement(); - toks.append("{"+token.internalTok+"}"+lineSep); - } - return toks.toString(); - } - } -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/DestinationProvider.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/DestinationProvider.java deleted file mode 100644 index 4001b5e..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/DestinationProvider.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal; - -import org.eclipse.paho.client.mqttv3.MqttTopic; - -/** - * This interface exists to act as a common type for - * MqttClient and MqttMIDPClient so they can be passed to - * ClientComms without either client class need to know - * about the other. - * Specifically, this allows the MIDP client to work - * without the non-MIDP MqttClient/MqttConnectOptions - * classes being present. - */ -public interface DestinationProvider { - public MqttTopic getTopic(String topic); -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/ExceptionHelper.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/ExceptionHelper.java deleted file mode 100644 index fe5b037..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/ExceptionHelper.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal; - -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.MqttSecurityException; - -/** - * Utility class to help create exceptions of the correct type. - */ -public class ExceptionHelper { - public static MqttException createMqttException(int reasonCode) { - if ((reasonCode == MqttException.REASON_CODE_FAILED_AUTHENTICATION) || - (reasonCode == MqttException.REASON_CODE_NOT_AUTHORIZED)) { - return new MqttSecurityException(reasonCode); - } - - return new MqttException(reasonCode); - } - - public static MqttException createMqttException(Throwable cause) { - if (cause.getClass().getName().equals("java.security.GeneralSecurityException")) { - return new MqttSecurityException(cause); - } - return new MqttException(cause); - } - - /** - * Returns whether or not the specified class is available to the current - * class loader. This is used to protect the code against using Java SE - * APIs on Java ME. - */ - public static boolean isClassAvailable(String className) { - boolean result = false; - try { - Class.forName(className); - result = true; - } - catch (ClassNotFoundException ex) { - } - return result; - } -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/FileLock.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/FileLock.java deleted file mode 100644 index 576d860..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/FileLock.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal; -/** - * FileLock - used to obtain a lock that can be used to prevent other MQTT clients - * using the same persistent store. If the lock is already held then an exception - * is thrown. - * - * Some Java runtimes such as JME MIDP do not support file locking or even - * the Java classes that support locking. The class is coded to both compile - * and work on all Java runtimes. In Java runtimes that do not support - * locking it will look as though a lock has been obtained but in reality - * no lock has been obtained. - */ -import java.io.File; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.lang.reflect.Method; - -public class FileLock { - private File lockFile; - private RandomAccessFile file; - private Object fileLock; - - /** - * Creates an NIO FileLock on the specified file if on a suitable Java runtime. - * @param clientDir the a File of the directory to contain the lock file. - * @param lockFilename name of the the file to lock - * @throws Exception if the lock could not be obtained for any reason - */ - public FileLock(File clientDir, String lockFilename) throws Exception { - // Create a file to obtain a lock on. - lockFile = new File(clientDir,lockFilename); - if (ExceptionHelper.isClassAvailable("java.nio.channels.FileLock")) { - try { - this.file = new RandomAccessFile(lockFile,"rw"); - Method m = file.getClass().getMethod("getChannel",new Class[]{}); - Object channel = m.invoke(file,new Object[]{}); - m = channel.getClass().getMethod("tryLock",new Class[]{}); - this.fileLock = m.invoke(channel, new Object[]{}); - } catch(NoSuchMethodException nsme) { - this.fileLock = null; - } catch(IllegalArgumentException iae) { - this.fileLock = null; - } catch(IllegalAccessException iae) { - this.fileLock = null; - } - if (fileLock == null) { - // Lock not obtained - release(); - throw new Exception("Problem obtaining file lock"); - } - } - } - - /** - * Releases the lock. - */ - public void release() { - try { - if (fileLock != null) { - Method m = fileLock.getClass().getMethod("release",new Class[]{}); - m.invoke(fileLock, new Object[]{}); - fileLock = null; - } - } catch (Exception e) { - // Ignore exceptions - } - if (file != null) { - try { - file.close(); - } catch (IOException e) { - } - file = null; - } - - if (lockFile != null && lockFile.exists()) { - lockFile.delete(); - } - lockFile = null; - } - -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/LocalNetworkModule.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/LocalNetworkModule.java deleted file mode 100644 index cb4c7cb..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/LocalNetworkModule.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.lang.reflect.Method; - -import org.eclipse.paho.client.mqttv3.MqttException; - - -/** - * Special comms class that allows an MQTT client to use a non TCP / optimised - * mechanism to talk to an MQTT server when running in the same JRE instance as the - * MQTT server. - * - * This class checks for the existence of the optimised comms adatper class i.e. the one - * that provides the optimised communication mechanism. If not available the request - * to connect using the optimised mechanism is rejected. - * - * The only known server that implements this is the microbroker:- an MQTT server that - * ships with a number of IBM products. - */ -public class LocalNetworkModule implements NetworkModule { - private Class LocalListener; - private String brokerName; - private Object localAdapter; - - public LocalNetworkModule(String brokerName) { - this.brokerName = brokerName; - } - - public void start() throws IOException, MqttException{ - if (!ExceptionHelper.isClassAvailable("com.ibm.mqttdirect.modules.local.bindings.LocalListener")) { - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_SERVER_CONNECT_ERROR); - } - try { - LocalListener = Class.forName("com.ibm.mqttdirect.modules.local.bindings.LocalListener"); - Method connect_m = LocalListener.getMethod("connect", new Class[]{ java.lang.String.class }); - localAdapter = connect_m.invoke(null,new Object[]{ brokerName }); - } catch(Exception e) { - } - if(localAdapter == null) { - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_SERVER_CONNECT_ERROR); - } - } - - public InputStream getInputStream() throws IOException { - InputStream stream = null; - try { - Method m = LocalListener.getMethod("getClientInputStream",new Class[]{}); - stream = (InputStream)m.invoke(this.localAdapter,new Object[]{}); - } catch(Exception e) { - } - return stream; - } - - public OutputStream getOutputStream() throws IOException { - OutputStream stream = null; - try { - Method m = LocalListener.getMethod("getClientOutputStream",new Class[]{}); - stream = (OutputStream)m.invoke(this.localAdapter,new Object[]{}); - } catch(Exception e) { - } - return stream; - } - - public void stop() throws IOException { - if (localAdapter != null) { - try { - Method m = LocalListener.getMethod("close",new Class[]{}); - m.invoke(this.localAdapter,new Object[]{}); - } catch(Exception e) { - } - } - } -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/MessageCatalog.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/MessageCatalog.java deleted file mode 100644 index 295b15c..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/MessageCatalog.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal; - -/** - * Catalog of human readable error messages. - */ -public abstract class MessageCatalog { - private static MessageCatalog INSTANCE = null; - - public static final String getMessage(int id) { - if (INSTANCE == null) { - if (ExceptionHelper.isClassAvailable("java.util.ResourceBundle")) { - try { - // Hide this class reference behind reflection so that the class does not need to - // be present when compiled on midp - INSTANCE = (MessageCatalog)Class.forName("org.eclipse.paho.client.mqttv3.internal.ResourceBundleCatalog").newInstance(); - } catch (Exception e) { - return ""; - } - } else if (ExceptionHelper.isClassAvailable("org.eclipse.paho.client.mqttv3.internal.MIDPCatalog")){ - try { - INSTANCE = (MessageCatalog)Class.forName("org.eclipse.paho.client.mqttv3.internal.MIDPCatalog").newInstance(); - } catch (Exception e) { - return ""; - } - } - } - return INSTANCE.getLocalizedMessage(id); - } - - protected abstract String getLocalizedMessage(int id); -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/MqttPersistentData.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/MqttPersistentData.java deleted file mode 100644 index c56cd5b..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/MqttPersistentData.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal; - -import org.eclipse.paho.client.mqttv3.MqttPersistable; - -public class MqttPersistentData implements MqttPersistable { - // Message key - private String key = null; - - // Message header - private byte[] header = null; - private int hOffset = 0; - private int hLength = 0; - - // Message payload - private byte[] payload = null; - private int pOffset = 0; - private int pLength = 0; - - /** - * Construct a data object to pass across the MQTT client persistence - * interface.
                          - * When this Object is passed to the persistence implementation the key is - * used by the client to identify the persisted data to which further - * update or deletion requests are targeted.
                          - * When this Object is created for returning to the client when it is - * recovering its state from persistence the key is not required to be set. - * The client can determine the key from the data. - * @param key The key which identifies this data - * @param header The message header - * @param hOffset The start offset of the header bytes in header. - * @param hLength The length of the header in the header bytes array. - * @param payload The message payload - * @param pOffset The start offset of the payload bytes in payload. - * @param pLength The length of the payload in the payload bytes array - * when persisting the message. - */ - public MqttPersistentData( String key, - byte[] header, - int hOffset, - int hLength, - byte[] payload, - int pOffset, - int pLength) { - this.key = key; - this.header = header; - this.hOffset = hOffset; - this.hLength = hLength; - this.payload = payload; - this.pOffset = pOffset; - this.pLength = pLength; - } - - public String getKey() { - return key; - } - - public byte[] getHeaderBytes() { - return header; - } - - public int getHeaderLength() { - return hLength; - } - - public int getHeaderOffset() { - return hOffset; - } - - public byte[] getPayloadBytes() { - return payload; - } - - public int getPayloadLength() { - if ( payload == null ) { - return 0; - } - return pLength; - } - - public int getPayloadOffset() { - return pOffset; - } -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/NetworkModule.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/NetworkModule.java deleted file mode 100644 index b6db39a..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/NetworkModule.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import org.eclipse.paho.client.mqttv3.MqttException; - - -public interface NetworkModule { - public void start() throws IOException, MqttException; - - public InputStream getInputStream() throws IOException; - - public OutputStream getOutputStream() throws IOException; - - public void stop() throws IOException; -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/ResourceBundleCatalog.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/ResourceBundleCatalog.java deleted file mode 100644 index c93e22b..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/ResourceBundleCatalog.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal; - -import java.util.MissingResourceException; -import java.util.ResourceBundle; - -public class ResourceBundleCatalog extends MessageCatalog { - - private ResourceBundle bundle; - - public ResourceBundleCatalog() throws MissingResourceException { - bundle = ResourceBundle.getBundle("org.eclipse.paho.client.mqttv3.internal.nls.messages"); - } - - protected String getLocalizedMessage(int id) { - try { - return bundle.getString(Integer.toString(id)); - } catch(MissingResourceException mre) { - return "MqttException"; - } - } -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/SSLNetworkModule.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/SSLNetworkModule.java deleted file mode 100644 index 4a653a4..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/SSLNetworkModule.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal; - -import java.io.IOException; - -import javax.net.ssl.SSLSocket; -import javax.net.ssl.SSLSocketFactory; - -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.logging.Logger; -import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; - -/** - * A network module for connecting over SSL. - */ -public class SSLNetworkModule extends TCPNetworkModule { - private String[] enabledCiphers; - private int handshakeTimeoutSecs; - - final static String className = SSLNetworkModule.class.getName(); - Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className); - - /** - * Constructs a new SSLNetworkModule using the specified host and - * port. The supplied SSLSocketFactory is used to supply the network - * socket. - */ - public SSLNetworkModule(SSLSocketFactory factory, String host, int port, String resourceContext) { - super(factory, host, port, resourceContext); - log.setResourceName(resourceContext); - } - - /** - * Returns the enabled cipher suites. - */ - public String[] getEnabledCiphers() { - return enabledCiphers; - } - - /** - * Sets the enabled cipher suites on the underlying network socket. - */ - public void setEnabledCiphers(String[] enabledCiphers) { - final String methodName = "setEnabledCiphers"; - this.enabledCiphers = enabledCiphers; - if ((socket != null) && (enabledCiphers != null)) { - if (log.isLoggable(Logger.FINE)) { - String ciphers = ""; - for (int i=0;i0) { - ciphers+=","; - } - ciphers+=enabledCiphers[i]; - } - //@TRACE 260=setEnabledCiphers ciphers={0} - log.fine(className,methodName,"260",new Object[]{ciphers}); - } - ((SSLSocket) socket).setEnabledCipherSuites(enabledCiphers); - } - } - - public void setSSLhandshakeTimeout(int timeout) { - this.handshakeTimeoutSecs = timeout; - } - - public void start() throws IOException, MqttException { - super.start(); - setEnabledCiphers(enabledCiphers); - int soTimeout = socket.getSoTimeout(); - if ( soTimeout == 0 ) { - // RTC 765: Set a timeout to avoid the SSL handshake being blocked indefinitely - socket.setSoTimeout(this.handshakeTimeoutSecs*1000); - } - ((SSLSocket)socket).startHandshake(); - // reset timeout to default value - socket.setSoTimeout(soTimeout); - } -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/TCPNetworkModule.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/TCPNetworkModule.java deleted file mode 100644 index 49547c9..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/TCPNetworkModule.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.ConnectException; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.net.SocketAddress; - -import javax.net.SocketFactory; - -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.logging.Logger; -import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; - -/** - * A network module for connecting over TCP. - */ -public class TCPNetworkModule implements NetworkModule { - protected Socket socket; - private SocketFactory factory; - private String host; - private int port; - private int conTimeout; - - final static String className = TCPNetworkModule.class.getName(); - Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className); - - /** - * Constructs a new TCPNetworkModule using the specified host and - * port. The supplied SocketFactory is used to supply the network - * socket. - */ - public TCPNetworkModule(SocketFactory factory, String host, int port, String resourceContext) { - log.setResourceName(resourceContext); - this.factory = factory; - this.host = host; - this.port = port; - - } - - /** - * Starts the module, by creating a TCP socket to the server. - */ - public void start() throws IOException, MqttException { - final String methodName = "start"; - try { -// InetAddress localAddr = InetAddress.getLocalHost(); -// socket = factory.createSocket(host, port, localAddr, 0); - // @TRACE 252=connect to host {0} port {1} timeout {2} - log.fine(className,methodName, "252", new Object[] {host, new Integer(port), new Long(conTimeout*1000)}); - SocketAddress sockaddr = new InetSocketAddress(host, port); - socket = factory.createSocket(); - socket.connect(sockaddr, conTimeout*1000); - - // SetTcpNoDelay was originally set ot true disabling Nagle's algorithm. - // This should not be required. -// socket.setTcpNoDelay(true); // TCP_NODELAY on, which means we do not use Nagle's algorithm - } - catch (ConnectException ex) { - //@TRACE 250=Failed to create TCP socket - log.fine(className,methodName,"250",null,ex); - throw new MqttException(MqttException.REASON_CODE_SERVER_CONNECT_ERROR, ex); - } - } - - public InputStream getInputStream() throws IOException { - return socket.getInputStream(); - } - - public OutputStream getOutputStream() throws IOException { - return socket.getOutputStream(); - } - - /** - * Stops the module, by closing the TCP socket. - */ - public void stop() throws IOException { - if (socket != null) { - socket.close(); - } - } - - /** - * Set the maximum time to wait for a socket to be established - * @param timeout - */ - public void setConnectTimeout(int timeout) { - this.conTimeout = timeout; - } -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/Token.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/Token.java deleted file mode 100644 index b966510..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/Token.java +++ /dev/null @@ -1,348 +0,0 @@ -package org.eclipse.paho.client.mqttv3.internal; - -import org.eclipse.paho.client.mqttv3.IMqttActionListener; -import org.eclipse.paho.client.mqttv3.IMqttAsyncClient; -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.MqttMessage; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttAck; -import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage; -import org.eclipse.paho.client.mqttv3.logging.Logger; -import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; - -public class Token { - volatile private boolean completed = false; - private boolean pendingComplete = false; - private boolean sent = false; - - private Object responseLock = new Object(); - private Object sentLock = new Object(); - - protected MqttMessage message = null; - private MqttWireMessage response = null; - private MqttException exception = null; - private String[] topics = null; - - private String key; - - private IMqttAsyncClient client = null; - private IMqttActionListener callback = null; - - private Object userContext = null; - - public int messageID = 0; - public boolean notified = false; - - public Token(String logContext) { - log.setResourceName(logContext); - } - - public int getMessageID() { - return messageID; - } - - public void setMessageID(int messageID) { - this.messageID = messageID; - } - - final static String className = Token.class.getName(); - Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className); - - public boolean checkResult() throws MqttException { - if ( getException() != null) { - throw getException(); - } - return true; - } - - public MqttException getException() { - return exception; - } - - public boolean isComplete() { - return completed; - } - - protected boolean isCompletePending() { - return pendingComplete; - } - - protected boolean isInUse() { - return (getClient() != null && !isComplete()); - } - - public void setActionCallback(IMqttActionListener listener) { - this.callback = listener; - - } - public IMqttActionListener getActionCallback() { - return callback; - } - - public void waitForCompletion() throws MqttException { - waitForCompletion(-1); - } - - public void waitForCompletion(long timeout) throws MqttException { - final String methodName = "waitForCompletion"; - //@TRACE 407=key={0} wait max={1} token={2} - log.fine(className,methodName, "407",new Object[]{getKey(), new Long(timeout), this}); - - MqttWireMessage resp = waitForResponse(timeout); - if (resp == null && !completed) { - //@TRACE 406=key={0} timed out token={1} - log.fine(className,methodName, "406",new Object[]{getKey(), this}); - throw new MqttException(MqttException.REASON_CODE_CLIENT_TIMEOUT); - } - checkResult(); - } - - /** - * Waits for the message delivery to complete, but doesn't throw an exception - * in the case of a NACK. It does still throw an exception if something else - * goes wrong (e.g. an IOException). This is used for packets like CONNECT, - * which have useful information in the ACK that needs to be accessed. - */ - protected MqttWireMessage waitForResponse() throws MqttException { - return waitForResponse(-1); - } - - protected MqttWireMessage waitForResponse(long timeout) throws MqttException { - final String methodName = "waitForResponse"; - synchronized (responseLock) { - //@TRACE 400=>key={0} timeout={1} sent={2} completed={3} hasException={4} response={5} token={6} - log.fine(className, methodName, "400",new Object[]{getKey(), new Long(timeout),new Boolean(sent),new Boolean(completed),(exception==null)?"false":"true",response,this},exception); - - if (!this.completed) { - if (this.exception == null) { - try { - //@TRACE 408=key={0} wait max={1} - log.fine(className,methodName,"408",new Object[] {getKey(),new Long(timeout)}); - - if (timeout == -1) { - responseLock.wait(); - } else { - responseLock.wait(timeout); - } - } catch (InterruptedException e) { - exception = new MqttException(e); - } - } - if (!this.completed) { - if (this.exception != null) { - //@TRACE 401=failed with exception - log.fine(className,methodName,"401",null,exception); - throw exception; - } - } - } - } - //@TRACE 402=key={0} response={1} - log.fine(className,methodName, "402",new Object[]{getKey(), this.response}); - return this.response; - } - - /** - * Mark the token as complete and ready for users to be notified. - * @param msg response message. Optional - there are no response messages for some flows - * @param ex if there was a problem store the exception in the token. - */ - protected void markComplete(MqttWireMessage msg, MqttException ex) { - final String methodName = "markComplete"; - //@TRACE 404=>key={0} response={1} excep={2} - log.fine(className,methodName,"404",new Object[]{getKey(),msg,ex}); - - synchronized(responseLock) { - // ACK means that everything was OK, so mark the message for garbage collection. - if (msg instanceof MqttAck) { - this.message = null; - } - this.pendingComplete = true; - this.response = msg; - this.exception = ex; - } - } - /** - * Notifies this token that a response message (an ACK or NACK) has been - * received. - */ - protected void notifyComplete() { - final String methodName = "notifyComplete"; - //@TRACE 411=>key={0} response={1} excep={2} - log.fine(className,methodName,"404",new Object[]{getKey(),this.response, this.exception}); - - synchronized (responseLock) { - // If pending complete is set then normally the token can be marked - // as complete and users notified. An abnormal error may have - // caused the client to shutdown beween pending complete being set - // and notifying the user. In this case - the action must be failed. - if (exception == null && pendingComplete) { - completed = true; - pendingComplete = false; - } else { - pendingComplete = false; - } - - responseLock.notifyAll(); - } - synchronized (sentLock) { - sent=true; - sentLock.notifyAll(); - } - } - -// /** -// * Notifies this token that an exception has occurred. This is only -// * used for things like IOException, and not for MQTT NACKs. -// */ -// protected void notifyException() { -// final String methodName = "notifyException"; -// //@TRACE 405=token={0} excep={1} -// log.fine(className,methodName, "405",new Object[]{this,this.exception}); -// synchronized (responseLock) { -// responseLock.notifyAll(); -// } -// synchronized (sentLock) { -// sentLock.notifyAll(); -// } -// } - - public void waitUntilSent() throws MqttException { - final String methodName = "waitUntilSent"; - synchronized (sentLock) { - synchronized (responseLock) { - if (this.exception != null) { - throw this.exception; - } - } - if (!sent) { - try { - //@TRACE 409=wait key={0} - log.fine(className,methodName, "409",new Object[]{getKey()}); - - sentLock.wait(); - } catch (InterruptedException e) { - } - } - - if (!sent) { - if (this.exception == null) { - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_UNEXPECTED_ERROR); - } - throw this.exception; - } - } - } - - /** - * Notifies this token that the associated message has been sent - * (i.e. written to the TCP/IP socket). - */ - protected void notifySent() { - final String methodName = "notifySent"; - //@TRACE 403=> key={0} - log.fine(className, methodName, "403",new Object[]{getKey()}); - synchronized (responseLock) { - this.response = null; - this.completed = false; - } - synchronized (sentLock) { - sent = true; - sentLock.notifyAll(); - } - } - - public IMqttAsyncClient getClient() { - return client; - } - - protected void setClient(IMqttAsyncClient client) { - this.client = client; - } - - public void reset() throws MqttException { - final String methodName = "reset"; - if (isInUse() ) { - // Token is already in use - cannot reset - throw new MqttException(MqttException.REASON_CODE_TOKEN_INUSE); - } - //@TRACE 410=> key={0} - log.fine(className, methodName, "410",new Object[]{getKey()}); - - client = null; - completed = false; - response = null; - sent = false; - exception = null; - userContext = null; - } - - public MqttMessage getMessage() { - return message; - } - - public MqttWireMessage getWireMessage() { - return response; - } - - - public void setMessage(MqttMessage msg) { - this.message = msg; - } - - public String[] getTopics() { - return topics; - } - - public void setTopics(String[] topics) { - this.topics = topics; - } - - public Object getUserContext() { - return userContext; - } - - public void setUserContext(Object userContext) { - this.userContext = userContext; - } - - public void setKey(String key) { - this.key = key; - } - - public String getKey() { - return key; - } - - public void setException(MqttException exception) { - synchronized(responseLock) { - this.exception = exception; - } - } - - public boolean isNotified() { - return notified; - } - - public void setNotified(boolean notified) { - this.notified = notified; - } - - public String toString() { - StringBuffer tok = new StringBuffer(); - tok.append("key="+getKey()); - tok.append(" ,topics="); - if (getTopics() != null) { - for (int i=0; i - * The SSLSocketFacotryFactory can be reconfigured at any time. A - * reconfiguration does not affect existing socket factories. - *

                          - * All properties share the same key space; i.e. the configuration ID is not - * part of the property keys. - *

                          - * The methods should be called in the following order: - *

                            - *
                          1. isSupportedOnJVM(): to check whether this class is supported on - * the runtime platform. Not all runtimes support SSL/TLS.
                          2. - *
                          3. SSLSocketFactoryFactory(): the constructor. Clients - * (in the same JVM) may share an SSLSocketFactoryFactory, or have one each.
                          4. - *
                          5. initialize(properties, configID): to initialize this object with - * the required SSL properties for a configuration. This may be called multiple - * times, once for each required configuration.It may be called again to change the required SSL - * properties for a particular configuration
                          6. - *
                          7. getEnabledCipherSuites(configID): to later set the enabled - * cipher suites on the socket [see below].
                          8. - *
                          - *
                            - *
                          • For an MQTT server:
                          • - *
                              - *
                            1. getKeyStore(configID): Optionally, to check that if there is no - * keystore, then that all the enabled cipher suits are anonymous.
                            2. - *
                            3. createServerSocketFactory(configID): to create an - * SSLServerSocketFactory.
                            4. - *
                            5. getClientAuthentication(configID): to later set on the - * SSLServerSocket (itself created from the SSLServerSocketFactory) whether - * client authentication is needed.
                            6. - *
                            - *
                          • For an MQTT client:
                          • - *
                              - *
                            1. createSocketFactory(configID): to create an SSLSocketFactory.
                            2. - *
                            - *
                          - */ -public class SSLSocketFactoryFactory { - private final static String CLASS_NAME = "org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory"; - /** - * Property keys specific to the client). - */ - public final static String SSLPROTOCOL="com.ibm.ssl.protocol"; - public final static String JSSEPROVIDER="com.ibm.ssl.contextProvider"; - public final static String KEYSTORE="com.ibm.ssl.keyStore"; - public final static String KEYSTOREPWD="com.ibm.ssl.keyStorePassword"; - public final static String KEYSTORETYPE="com.ibm.ssl.keyStoreType"; - public final static String KEYSTOREPROVIDER="com.ibm.ssl.keyStoreProvider"; - public final static String KEYSTOREMGR="com.ibm.ssl.keyManager"; - public final static String TRUSTSTORE="com.ibm.ssl.trustStore"; - public final static String TRUSTSTOREPWD="com.ibm.ssl.trustStorePassword"; - public final static String TRUSTSTORETYPE="com.ibm.ssl.trustStoreType"; - public final static String TRUSTSTOREPROVIDER="com.ibm.ssl.trustStoreProvider"; - public final static String TRUSTSTOREMGR="com.ibm.ssl.trustManager"; - public final static String CIPHERSUITES="com.ibm.ssl.enabledCipherSuites"; - public final static String CLIENTAUTH="com.ibm.ssl.clientAuthentication"; - - /** - * Property keys used for java system properties - */ - public final static String SYSKEYSTORE="javax.net.ssl.keyStore"; - public final static String SYSKEYSTORETYPE="javax.net.ssl.keyStoreType"; - public final static String SYSKEYSTOREPWD="javax.net.ssl.keyStorePassword"; - public final static String SYSTRUSTSTORE="javax.net.ssl.trustStore"; - public final static String SYSTRUSTSTORETYPE="javax.net.ssl.trustStoreType"; - public final static String SYSTRUSTSTOREPWD="javax.net.ssl.trustStorePassword"; - public final static String SYSKEYMGRALGO="ssl.KeyManagerFactory.algorithm"; - public final static String SYSTRUSTMGRALGO="ssl.TrustManagerFactory.algorithm"; - - - public final static String DEFAULT_PROTOCOL = "TLS"; // "SSL_TLS" is not supported by DesktopEE - - private final static String propertyKeys[] = { SSLPROTOCOL, JSSEPROVIDER, - KEYSTORE, KEYSTOREPWD, KEYSTORETYPE, KEYSTOREPROVIDER, KEYSTOREMGR, - TRUSTSTORE, TRUSTSTOREPWD, TRUSTSTORETYPE, TRUSTSTOREPROVIDER, - TRUSTSTOREMGR, CIPHERSUITES, CLIENTAUTH}; - - private Hashtable configs; // a hashtable that maps configIDs to properties. - - private Properties defaultProperties; - - private static final byte[] key = { (byte) 0x9d, (byte) 0xa7, (byte) 0xd9, - (byte) 0x80, (byte) 0x05, (byte) 0xb8, (byte) 0x89, (byte) 0x9c }; - - private static final String xorTag = "{xor}"; - - private Logger logger = null; - - - /** - * Not all of the JVM/Platforms support all of its - * security features. This method determines if is supported. - * - * @return whether dependent classes can be instantiated on the current - * JVM/platform. - * - * @throws Error - * if any unexpected error encountered whilst checking. Note - * this should not be a ClassNotFoundException, which should - * cause the method to return false. - */ - public static boolean isSupportedOnJVM() throws LinkageError, ExceptionInInitializerError { - String requiredClassname = "javax.net.ssl.SSLServerSocketFactory"; - try { - Class.forName(requiredClassname); - } catch (ClassNotFoundException e) { - return false; - } - return true; - } - - - /** - * Create new instance of class. - * Constructor used by clients. - */ - public SSLSocketFactoryFactory() { - configs = new Hashtable(); - } - - /** - * Create new instance of class. - * Constructor used by the broker. - */ - public SSLSocketFactoryFactory(Logger logger) { - this(); - this.logger = logger; - } - - /** - * Checks whether a key belongs to the supported IBM SSL property keys. - * - * @param key - * @return whether a key belongs to the supported IBM SSL property keys. - */ - private boolean keyValid(String key) { - int i = 0; - while (i < propertyKeys.length) { - if (propertyKeys[i].equals(key)) { - break; - } - ++i; - } - return i < propertyKeys.length; - } - - /** - * Checks whether the property keys belong to the supported IBM SSL property - * key set. - * - * @param properties - * @throws IllegalArgumentException - * if any of the properties is not a valid IBM SSL property key. - */ - private void checkPropertyKeys(Properties properties) - throws IllegalArgumentException { - Set keys = properties.keySet(); - Iterator i = keys.iterator(); - while (i.hasNext()) { - String k = (String) i.next(); - if (!keyValid(k)) { - throw new IllegalArgumentException(k + " is not a valid IBM SSL property key."); - } - } - } - - /** - * Convert byte array to char array, where each char is constructed from two - * bytes. - * - * @param b - * byte array - * @return char array - */ - public static char[] toChar(byte[] b) { - if(b==null) return null; - char[] c= new char[b.length/2]; - int i=0; int j=0; - while(i> 8)& 0xFF); - } - return b; - } - - /** - * Obfuscates the password using a simple and not very secure XOR mechanism. - * This should not be used for cryptographical purpose, it's a simple - * scrambler to obfuscate clear-text passwords. - * - * @see org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory#deObfuscate - * - * @param password - * The password to be encrypted, as a char[] array. - * @return An obfuscated password as a String. - */ - public static String obfuscate(char[] password) { - if (password == null) - return null; - byte[] bytes = toByte(password); - for (int i = 0; i < bytes.length; i++) { - bytes[i] = (byte) ((bytes[i] ^ key[i % key.length]) & 0x00ff); - } - String encryptedValue = xorTag - + new String(SimpleBase64Encoder.encode(bytes)); - return encryptedValue; - } - - /** - * The inverse operation of obfuscate: returns a cleartext password that was - * previously obfuscated using the XOR scrambler. - * - * @see org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory#obfuscate - * - * @param ePassword - * An obfuscated password. - * @return An array of char, containing the clear text password. - */ - public static char[] deObfuscate(String ePassword) { - if (ePassword == null) - return null; - byte[] bytes = null; - try { - bytes = SimpleBase64Encoder.decode(ePassword.substring(xorTag - .length())); - } catch (Exception e) { - return null; - } - - for (int i = 0; i < bytes.length; i++) { - bytes[i] = (byte) ((bytes[i] ^ key[i % key.length]) & 0x00ff); - } - return toChar(bytes); - } - - /** - * Converts an array of ciphers into a single String. - * - * @param ciphers - * The array of cipher names. - * @return A string containing the name of the ciphers, separated by comma. - */ - public static String packCipherSuites(String[] ciphers) { - String cipherSet=null; - if (ciphers != null) { - StringBuffer buf = new StringBuffer(); - for (int i = 0; i < ciphers.length; i++) { - buf.append(ciphers[i]); - if (i < ciphers.length - 1) { - buf.append(','); - } - } - cipherSet = buf.toString(); - } - return cipherSet; - } - - /** - * Inverse operation of packCipherSuites: converts a string of cipher names - * into an array of cipher names - * - * @param ciphers - * A list of ciphers, separated by comma. - * @return An array of string, each string containing a single cipher name. - */ - public static String[] unpackCipherSuites(String ciphers) { - // can't use split as split is not available on all java platforms. - if(ciphers==null) return null; - Vector c=new Vector(); - int i=ciphers.indexOf(','); - int j=0; - // handle all commas. - while(i>-1) { - // add stuff before and up to (but not including) the comma. - c.add(ciphers.substring(j, i)); - j=i+1; // skip the comma. - i=ciphers.indexOf(',',j); - } - // add last element after the comma or only element if no comma is present. - c.add(ciphers.substring(j)); - String[] s = new String[c.size()]; - c.toArray(s); - return s; - } - - /** - * Obfuscate any key & trust store passwords within the given properties. - * - * @see org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory#obfuscate - * - * @param p - * properties - */ - private void convertPassword(Properties p) { - String pw = p.getProperty(KEYSTOREPWD); - if (pw != null && !pw.startsWith(xorTag)) { - String epw = obfuscate(pw.toCharArray()); - p.put(KEYSTOREPWD, epw); - } - pw = p.getProperty(TRUSTSTOREPWD); - if (pw != null && !pw.startsWith(xorTag)) { - String epw = obfuscate(pw.toCharArray()); - p.put(TRUSTSTOREPWD, epw); - } - } - - /** - * Returns the properties object for configuration configID or creates a new - * one if required. - * - * @param configID - * The configuration identifier for selecting a configuration or - * null for the default configuration. - * @return the properties object for configuration configID - */ -// private Properties getOrCreate(String configID) { -// Properties res = null; -// if (configID == null) { -// if (this.defaultProperties == null) { -// this.defaultProperties = new Properties(); -// } -// res = this.defaultProperties; -// } else { -// res = (Properties) this.configs.get(configID); -// if (res == null) { -// res = new Properties(); -// this.configs.put(configID, res); -// } -// } -// return res; -// } - - /** - * Initializes the SSLSocketFactoryFactory with the provided properties for - * the provided configuration. - * - * @param props - * A properties object containing IBM SSL properties that are - * qualified by one or more configuration identifiers. - * @param configID - * The configuration identifier for selecting a configuration or - * null for the default configuration. - * @throws IllegalArgumentException - * if any of the properties is not a valid IBM SSL property key. - */ - public void initialize(Properties props, String configID) - throws IllegalArgumentException { - checkPropertyKeys(props); - // copy the properties. - Properties p = new Properties(); - p.putAll(props); - convertPassword(p); - if (configID != null) { - this.configs.put(configID, p); - } else { - this.defaultProperties = p; - } - } - - /** - * Merges the given IBM SSL properties into the existing configuration, - * overwriting existing properties. This method is used to selectively - * change properties for a given configuration. The method throws an - * IllegalArgumentException if any of the properties is not a valid IBM SSL - * property key. - * - * @param props - * A properties object containing IBM SSL properties - * @param configID - * The configuration identifier for selecting a configuration or - * null for the default configuration. - * @throws IllegalArgumentException - * if any of the properties is not a valid IBM SSL property key. - */ - public void merge(Properties props, String configID) - throws IllegalArgumentException { - checkPropertyKeys(props); - Properties p = this.defaultProperties; - if (configID == null) { - p = (Properties) this.configs.get(configID); - } - if (p == null) { - p = new Properties(); - } - convertPassword(props); - p.putAll(props); - if (configID != null) { - this.configs.put(configID, p); - } else { - this.defaultProperties = p; - } - - } - - /** - * Remove the configuration of a given configuration identifier. - * - * @param configID - * The configuration identifier for selecting a configuration or - * null for the default configuration. - * @return true, if the configuation could be removed. - */ - public boolean remove(String configID) { - boolean res = false; - if (configID != null) { - res = this.configs.remove(configID) != null; - } else { - if(null != this.defaultProperties) { - res = true; - this.defaultProperties = null; - } - } - return res; - } - - /** - * Returns the configuration of the SSLSocketFactoryFactory for a given - * configuration. Note that changes in the property are reflected in the - * SSLSocketFactoryFactory. - * - * @param configID - * The configuration identifier for selecting a configuration or - * null for the default configuration. - * @return A property object containing the current configuration of the - * SSLSocketFactoryFactory. Note that it could be null. - */ - public Properties getConfiguration(String configID) { - return (Properties) (configID == null ? this.defaultProperties - : this.configs.get(configID)); - } - - /** - * @return Returns the set of configuration IDs that exist in the SSLSocketFactoryFactory. - */ -// public String[] getConfigurationIDs() { -// Set s = this.configs.keySet(); -// String[] configs = new String[s.size()]; -// configs = (String[]) s.toArray(configs); -// return configs; -// } - - /** - * If the value is not null, then put it in the properties object using the - * key. If the value is null, then remove the entry in the properties object - * with the key. - * - * @param p - * @param key - * @param value - */ -// private final void putOrRemove(Properties p, String key, String value) { -// if (value == null) { -// p.remove(key); -// } else { -// p.put(key, value); -// } -// } - - /** - * Sets the SSL protocol variant. If protocol is NULL then an existing value - * will be removed. - * - * @param configID - * The configuration identifier for selecting a configuration or - * null for the default configuration. - * @param protocol - * One of SSL, SSLv3, TLS, TLSv1, SSL_TLS - */ -// public void setSSLProtocol(String configID, String protocol) { -// Properties p = getOrCreate(configID); -// putOrRemove(p, SSLPROTOCOL, protocol); -// } - - /** - * Sets the JSSE context provider. If provider is null, then an existing - * value will be removed. - * - * @param configID - * The configuration identifier for selecting a configuration or - * null for the default configuration. - * @param provider - * The JSSE provider. For example "IBMJSSE2" or "SunJSSE". - */ -// public void setJSSEProvider(String configID, String provider) { -// Properties p = getOrCreate(configID); -// putOrRemove(p, JSSEPROVIDER, provider); -// } - - /** - * Sets the filename of the keyStore object. A null value is ignored. - * - * @param configID - * The configuration identifier for selecting a configuration or - * null for the default configuration. - * @param keyStore - * A filename that points to a valid keystore. - */ -// public void setKeyStore(String configID, String keyStore) { -// if (keyStore == null) -// return; -// Properties p = getOrCreate(configID); -// putOrRemove(p, KEYSTORE, keyStore); -// } - - /** - * Sets the password that is used for the keystore. The password must be - * provided in plain text, but it will be stored internally in a scrambled - * XOR format. - * - * @see org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory#obfuscate - * - * @param configID - * The configuration identifier for selecting a configuration or - * null for the default configuration. - * @param password - * The keystore password - */ -// public void setKeyStorePassword(String configID, char[] password) { -// if (password == null) -// return; -// Properties p = getOrCreate(configID); -// // convert password, using XOR-based scrambling. -// String ePasswd = obfuscate(password); -// for(int i=0;i=3){ - encoded.append(to64((((bytes[i] & 0xff) << 16) - | (int) ((bytes[i+1] & 0xff) << 8) | (int) (bytes[i+2] & 0xff)),4)); - i+=3; - j-=3; - } - // j==2 | j==1 | j==0 - if(j==2) { - // there is a rest of 2 bytes. This encodes into 3 chars. - encoded.append(to64(((bytes[i] &0xff)<<8) | ((bytes[i+1] & 0xff)),3)); - } - if(j==1) { - // there is a rest of 1 byte. This encodes into 1 char. - encoded.append(to64(((bytes[i] & 0xff)),2)); - } - return encoded.toString(); - } - - public static byte[] decode(String string) { - byte[] encoded=string.getBytes(); - int len=encoded.length; - byte[] decoded=new byte[len*3/4]; - int i=0; - int j=len; - int k=0; - while(j>=4) { - long d=from64(encoded, i, 4); - j-=4; - i+=4; - for(int l=2;l>=0;l--) { - decoded[k+l]=(byte) (d & 0xff); - d=d >>8; - } - k+=3; - } - // j==3 | j==2 - if(j==3) { - long d=from64(encoded, i, 3); - for(int l=1;l>=0;l--) { - decoded[k+l]=(byte) (d & 0xff); - d=d >>8; - } - } - if(j==2) { - long d=from64(encoded, i, 2); - decoded[k]=(byte) (d & 0xff); - } - return decoded; - } - - /* the core conding routine. Translates an input integer into - * a string of the given length.*/ - private final static String to64(long input, int size) { - final StringBuffer result = new StringBuffer(size); - while (size > 0) { - size--; - result.append(PWDCHARS_ARRAY[((int) (input & 0x3f))]); - input = input >> 6; - } - return result.toString(); - } - - /* - * The reverse operation of to64 - */ - private final static long from64(byte[] encoded, int idx, int size) { - long res=0; - int f=0; - while(size>0) { - size--; - long r=0; - // convert encoded[idx] back into a 6-bit value. - byte d=encoded[idx++]; - if(d=='/') { - r=1; - } - if(d>='0' && d<='9') { - r=2+d-'0'; - } - if(d>='A' && d<='Z') { - r=12+d-'A'; - } - if(d>='a' && d<='z') { - r=38+d-'a'; - } - res=res+((long)r << f); - f+=6; - } - return res; - } - -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/CountingInputStream.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/CountingInputStream.java deleted file mode 100644 index 524c62f..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/CountingInputStream.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import java.io.IOException; -import java.io.InputStream; - -/** - * An input stream that counts the bytes read from it. - */ -public class CountingInputStream extends InputStream { - private InputStream in; - private int counter; - - /** - * Constructs a new CountingInputStream wrapping the supplied - * input stream. - */ - public CountingInputStream(InputStream in) { - this.in = in; - this.counter = 0; - } - - public int read() throws IOException { - int i = in.read(); - if (i != -1) { - counter++; - } - return i; - } - - /** - * Returns the number of bytes read since the last reset. - */ - public int getCounter() { - return counter; - } - - /** - * Resets the counter to zero. - */ - public void resetCounter() { - counter = 0; - } -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttAck.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttAck.java deleted file mode 100644 index 13ce622..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttAck.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - - -/** - * Abstract super-class of all acknowledgement messages. - */ -public abstract class MqttAck extends MqttWireMessage { - public MqttAck(byte type) { - super(type); - } - - protected byte getMessageInfo() { - return 0; - } - -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttConnack.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttConnack.java deleted file mode 100644 index 612c321..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttConnack.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import java.io.ByteArrayInputStream; -import java.io.DataInputStream; -import java.io.IOException; - -import org.eclipse.paho.client.mqttv3.MqttException; - - -/** - * An on-the-wire representation of an MQTT CONNACK. - */ -public class MqttConnack extends MqttAck { - private int returnCode; - - public MqttConnack(byte info, byte[] variableHeader) throws IOException { - super(MqttWireMessage.MESSAGE_TYPE_CONNACK); - ByteArrayInputStream bais = new ByteArrayInputStream(variableHeader); - DataInputStream dis = new DataInputStream(bais); - dis.readByte(); - returnCode = dis.readUnsignedByte(); - dis.close(); - } - - public int getReturnCode() { - return returnCode; - } - - protected byte[] getVariableHeader() throws MqttException { - // Not needed, as the client never encodes a CONNACK - return new byte[0]; - } - - /** - * Returns whether or not this message needs to include a message ID. - */ - public boolean isMessageIdRequired() { - return false; - } - - public String getKey() { - return new String("Con"); - } -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttConnect.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttConnect.java deleted file mode 100644 index 7c42e1f..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttConnect.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; -import java.io.IOException; - -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.MqttMessage; - -/** - * An on-the-wire representation of an MQTT CONNECT message. - */ -public class MqttConnect extends MqttWireMessage { - private String clientId; - private boolean cleanSession; - private MqttMessage willMessage; - private String userName; - private char[] password; - private int keepAliveInterval; - private String willDestination; - public static String KEY="Con"; - - - public MqttConnect(String clientId, - boolean cleanSession, - int keepAliveInterval, - String userName, - char[] password, - MqttMessage willMessage, - String willDestination) { - super(MqttWireMessage.MESSAGE_TYPE_CONNECT); - this.clientId = clientId; - this.cleanSession = cleanSession; - this.keepAliveInterval = keepAliveInterval; - this.userName = userName; - this.password = password; - this.willMessage = willMessage; - this.willDestination = willDestination; - } - - protected byte getMessageInfo() { - return (byte) 0; - } - - public boolean isCleanSession() { - return cleanSession; - } - - protected byte[] getVariableHeader() throws MqttException { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - DataOutputStream dos = new DataOutputStream(baos); - this.encodeUTF8(dos,"MQIsdp"); - dos.write(3); - byte connectFlags = 0; - - if (cleanSession) { - connectFlags |= 0x02; - } - - if (willMessage != null ) { - connectFlags |= 0x04; - connectFlags |= (willMessage.getQos()<<3); - if (willMessage.isRetained()) { - connectFlags |= 0x20; - } - } - - if (userName != null) { - connectFlags |= 0x80; - if (password != null) { - connectFlags |= 0x40; - } - } - dos.write(connectFlags); - dos.writeShort(keepAliveInterval); - dos.flush(); - return baos.toByteArray(); - } catch(IOException ioe) { - throw new MqttException(ioe); - } - } - - public byte[] getPayload() throws MqttException { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - DataOutputStream dos = new DataOutputStream(baos); - this.encodeUTF8(dos,clientId); - - if (willMessage != null) { - this.encodeUTF8(dos,willDestination); - dos.writeShort(willMessage.getPayload().length); - dos.write(willMessage.getPayload()); - } - - if (userName != null) { - this.encodeUTF8(dos,userName); - if (password != null) { - this.encodeUTF8(dos,new String(password)); - } - } - dos.flush(); - return baos.toByteArray(); - } - catch (IOException ex) { - throw new MqttException(ex); - } - } - - /** - * Returns whether or not this message needs to include a message ID. - */ - public boolean isMessageIdRequired() { - return false; - } - - public String getKey() { - return new String(KEY); - } -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttDisconnect.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttDisconnect.java deleted file mode 100644 index 1c48d47..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttDisconnect.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import org.eclipse.paho.client.mqttv3.MqttException; - -/** - * An on-the-wire representation of an MQTT DISCONNECT message. - */ -public class MqttDisconnect extends MqttWireMessage { - public static String KEY="Disc"; - - public MqttDisconnect() { - super(MqttWireMessage.MESSAGE_TYPE_DISCONNECT); - } - - protected byte getMessageInfo() { - return (byte) 0; - } - - protected byte[] getVariableHeader() throws MqttException { - return new byte[0]; - } - - /** - * Returns whether or not this message needs to include a message ID. - */ - public boolean isMessageIdRequired() { - return false; - } - - public String getKey() { - return new String(KEY); - } -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttInputStream.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttInputStream.java deleted file mode 100644 index 9eaec05..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttInputStream.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.IOException; -import java.io.InputStream; - -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.internal.ExceptionHelper; - - -/** - * An MqttInputStream lets applications read instances of - * MqttWireMessage. - */ -public class MqttInputStream extends InputStream { - private DataInputStream in; - - public MqttInputStream(InputStream in) { - this.in = new DataInputStream(in); - } - - public int read() throws IOException { - return in.read(); - } - - public int available() throws IOException { - return in.available(); - } - - public void close() throws IOException { - in.close(); - } - - /** - * Reads an MqttWireMessage from the stream. - */ - public MqttWireMessage readMqttWireMessage() throws IOException, MqttException { - ByteArrayOutputStream bais = new ByteArrayOutputStream(); - byte first = in.readByte(); - byte type = (byte) ((first >>> 4) & 0x0F); - if ((type < MqttWireMessage.MESSAGE_TYPE_CONNECT) || - (type > MqttWireMessage.MESSAGE_TYPE_DISCONNECT)) { - // Invalid MQTT message type... - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_INVALID_MESSAGE); - } - long remLen = MqttWireMessage.readMBI(in).getValue(); - bais.write(first); - // bit silly, we decode it then encode it - bais.write(MqttWireMessage.encodeMBI(remLen)); - byte[] packet = new byte[(int)(bais.size()+remLen)]; - in.readFully(packet,bais.size(),packet.length - bais.size()); - byte[] header = bais.toByteArray(); - System.arraycopy(header,0,packet,0, header.length); - MqttWireMessage message = MqttWireMessage.createWireMessage(packet); - return message; - } -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttOutputStream.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttOutputStream.java deleted file mode 100644 index 5df98c6..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttOutputStream.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import java.io.BufferedOutputStream; -import java.io.IOException; -import java.io.OutputStream; - -import org.eclipse.paho.client.mqttv3.MqttException; - - -/** - * An MqttOutputStream lets applications write instances of - * MqttWireMessage. - */ -public class MqttOutputStream extends OutputStream { - private BufferedOutputStream out; - - public MqttOutputStream(OutputStream out) { - this.out = new BufferedOutputStream(out); - } - - public void close() throws IOException { - out.close(); - } - - public void flush() throws IOException { - out.flush(); - } - - public void write(byte[] b) throws IOException { - out.write(b); - } - - public void write(byte[] b, int off, int len) throws IOException { - out.write(b, off, len); - } - - public void write(int b) throws IOException { - out.write(b); - } - - /** - * Writes an MqttWireMessage to the stream. - */ - public void write(MqttWireMessage message) throws IOException, MqttException { - byte[] bytes = message.getHeader(); - byte[] pl = message.getPayload(); -// out.write(message.getHeader()); -// out.write(message.getPayload()); - out.write(bytes,0,bytes.length); - out.write(pl,0,pl.length); - } -} - diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPersistableWireMessage.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPersistableWireMessage.java deleted file mode 100644 index bc2d39f..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPersistableWireMessage.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.MqttPersistable; -import org.eclipse.paho.client.mqttv3.MqttPersistenceException; - -public abstract class MqttPersistableWireMessage extends MqttWireMessage - implements MqttPersistable { - - public MqttPersistableWireMessage(byte type) { - super(type); - } - - public byte[] getHeaderBytes() throws MqttPersistenceException { - try { - return getHeader(); - } - catch (MqttException ex) { - throw new MqttPersistenceException(ex.getCause()); - } - } - - public int getHeaderLength() throws MqttPersistenceException { - return getHeaderBytes().length; - } - - public int getHeaderOffset() throws MqttPersistenceException{ - return 0; - } - -// public String getKey() throws MqttPersistenceException { -// return new Integer(getMessageId()).toString(); -// } - - public byte[] getPayloadBytes() throws MqttPersistenceException { - try { - return getPayload(); - } - catch (MqttException ex) { - throw new MqttPersistenceException(ex.getCause()); - } - } - - public int getPayloadLength() throws MqttPersistenceException { - return 0; - } - - public int getPayloadOffset() throws MqttPersistenceException { - return 0; - } - -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPingReq.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPingReq.java deleted file mode 100644 index 4f397fd..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPingReq.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import org.eclipse.paho.client.mqttv3.MqttException; - -/** - * An on-the-wire representation of an MQTT PINGREQ message. - */ -public class MqttPingReq extends MqttWireMessage { - public MqttPingReq() { - super(MqttWireMessage.MESSAGE_TYPE_PINGREQ); - } - - /** - * Returns false as message IDs are not required for MQTT - * PINGREQ messages. - */ - public boolean isMessageIdRequired() { - return false; - } - - protected byte[] getVariableHeader() throws MqttException { - return new byte[0]; - } - - protected byte getMessageInfo() { - return 0; - } - - public String getKey() { - return new String("Ping"); - } -} - diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPingResp.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPingResp.java deleted file mode 100644 index abb28d3..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPingResp.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import org.eclipse.paho.client.mqttv3.MqttException; - - -/** - * An on-the-wire representation of an MQTT PINGRESP. - */ -public class MqttPingResp extends MqttAck { - public MqttPingResp(byte info, byte[] variableHeader) { - super(MqttWireMessage.MESSAGE_TYPE_PINGRESP); - } - - protected byte[] getVariableHeader() throws MqttException { - // Not needed, as the client never encodes a PINGRESP - return new byte[0]; - } - - /** - * Returns whether or not this message needs to include a message ID. - */ - public boolean isMessageIdRequired() { - return false; - } - - public String getKey() { - return new String("Ping"); - } -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubAck.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubAck.java deleted file mode 100644 index 4b92a43..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubAck.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import java.io.ByteArrayInputStream; -import java.io.DataInputStream; -import java.io.IOException; - -import org.eclipse.paho.client.mqttv3.MqttException; - - - -/** - * An on-the-wire representation of an MQTT PUBACK message. - */ -public class MqttPubAck extends MqttAck { - public MqttPubAck(byte info, byte[] data) throws IOException { - super(MqttWireMessage.MESSAGE_TYPE_PUBACK); - ByteArrayInputStream bais = new ByteArrayInputStream(data); - DataInputStream dis = new DataInputStream(bais); - msgId = dis.readUnsignedShort(); - dis.close(); - } - - public MqttPubAck(MqttPublish publish) { - super(MqttWireMessage.MESSAGE_TYPE_PUBACK); - msgId = publish.getMessageId(); - } - - protected byte[] getVariableHeader() throws MqttException { - return encodeMessageId(); - } -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubComp.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubComp.java deleted file mode 100644 index 3176e0d..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubComp.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import java.io.ByteArrayInputStream; -import java.io.DataInputStream; -import java.io.IOException; - -import org.eclipse.paho.client.mqttv3.MqttException; - - - -/** - * An on-the-wire representation of an MQTT PUBCOMP message. - */ -public class MqttPubComp extends MqttAck { - public MqttPubComp(byte info, byte[] data) throws IOException { - super(MqttWireMessage.MESSAGE_TYPE_PUBCOMP); - ByteArrayInputStream bais = new ByteArrayInputStream(data); - DataInputStream dis = new DataInputStream(bais); - msgId = dis.readUnsignedShort(); - dis.close(); - } - - public MqttPubComp(MqttPublish publish) { - super(MqttWireMessage.MESSAGE_TYPE_PUBCOMP); - this.msgId = publish.getMessageId(); - } - - public MqttPubComp(int msgId) { - super(MqttWireMessage.MESSAGE_TYPE_PUBCOMP); - this.msgId = msgId; - } - - protected byte[] getVariableHeader() throws MqttException { - return encodeMessageId(); - } -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubRec.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubRec.java deleted file mode 100644 index 864da02..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubRec.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import java.io.ByteArrayInputStream; -import java.io.DataInputStream; -import java.io.IOException; - -import org.eclipse.paho.client.mqttv3.MqttException; - - - -/** - * An on-the-wire representation of an MQTT PUBREC message. - */ -public class MqttPubRec extends MqttAck { - public MqttPubRec(byte info, byte[] data) throws IOException { - super(MqttWireMessage.MESSAGE_TYPE_PUBREC); - ByteArrayInputStream bais = new ByteArrayInputStream(data); - DataInputStream dis = new DataInputStream(bais); - msgId = dis.readUnsignedShort(); - dis.close(); - } - - public MqttPubRec(MqttPublish publish) { - super(MqttWireMessage.MESSAGE_TYPE_PUBREC); - msgId = publish.getMessageId(); - } - - protected byte[] getVariableHeader() throws MqttException { - return encodeMessageId(); - } -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubRel.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubRel.java deleted file mode 100644 index 8c12d4c..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubRel.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import java.io.ByteArrayInputStream; -import java.io.DataInputStream; -import java.io.IOException; - -import org.eclipse.paho.client.mqttv3.MqttException; - -/** - * An on-the-wire representation of an MQTT PUBREL message. - */ -public class MqttPubRel extends MqttPersistableWireMessage { - - /** - * Createa a pubrel message based on a pubrec - * @param pubRec - */ - public MqttPubRel(MqttPubRec pubRec) { - super(MqttWireMessage.MESSAGE_TYPE_PUBREL); - this.setMessageId(pubRec.getMessageId()); - } - - /** - * Creates a pubrel based on a pubrel set of bytes read fro the network - * @param info - * @param data - * @throws IOException - */ - public MqttPubRel(byte info, byte[] data) throws IOException { - super(MqttWireMessage.MESSAGE_TYPE_PUBREL); - ByteArrayInputStream bais = new ByteArrayInputStream(data); - DataInputStream dis = new DataInputStream(bais); - msgId = dis.readUnsignedShort(); - dis.close(); - } - - protected byte[] getVariableHeader() throws MqttException { - return encodeMessageId(); - } - - protected byte getMessageInfo() { - return (byte)( 2 | (this.duplicate?8:0)); - } - -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPublish.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPublish.java deleted file mode 100644 index 7c9d620..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPublish.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; - -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.MqttMessage; - - -/** - * An on-the-wire representation of an MQTT SEND message. - */ -public class MqttPublish extends MqttPersistableWireMessage { - - private MqttMessage message; - private String topicName; - - private byte[] encodedPayload = null; - - public MqttPublish(String name, MqttMessage message) { - super(MqttWireMessage.MESSAGE_TYPE_PUBLISH); - this.topicName = name; - this.message = message; - } - - /** - * Constructs a new MqttPublish object. - * @param info the message info byte - * @param data the variable header and payload bytes - */ - public MqttPublish(byte info, byte[] data) throws MqttException, IOException { - super(MqttWireMessage.MESSAGE_TYPE_PUBLISH); - this.message = new MqttReceivedMessage(); - message.setQos((info >> 1) & 0x03); - if ((info & 0x01) == 0x01) { - message.setRetained(true); - } - if ((info & 0x08) == 0x08) { - ((MqttReceivedMessage) message).setDuplicate(true); - } - - ByteArrayInputStream bais = new ByteArrayInputStream(data); - CountingInputStream counter = new CountingInputStream(bais); - DataInputStream dis = new DataInputStream(counter); - topicName = this.decodeUTF8(dis); - if (message.getQos() > 0) { - msgId = dis.readUnsignedShort(); - } - byte[] payload = new byte[data.length-counter.getCounter()]; - dis.readFully(payload); - dis.close(); - message.setPayload(payload); - } - - protected byte getMessageInfo() { - byte info = (byte) (message.getQos() << 1); - if (message.isRetained()) { - info |= 0x01; - } - if (message.isDuplicate() || duplicate ) { - info |= 0x08; - } - - return info; - } - - public String getTopicName() { - return topicName; - } - - public MqttMessage getMessage() { - return message; - } - - protected static byte[] encodePayload(MqttMessage message) { - return message.getPayload(); - } - - public byte[] getPayload() throws MqttException { - if (encodedPayload == null) { - encodedPayload = encodePayload(message); - } - return encodedPayload; - } - - public int getPayloadLength() { - int length = 0; - try { - length = getPayload().length; - } catch(MqttException me) { - } - return length; - } - - public void setMessageId(int msgId) { - super.setMessageId(msgId); - if (message instanceof MqttReceivedMessage) { - ((MqttReceivedMessage)message).setMessageId(msgId); - } - } - - protected byte[] getVariableHeader() throws MqttException { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - DataOutputStream dos = new DataOutputStream(baos); - this.encodeUTF8(dos, topicName); - if (message.getQos() > 0) { - dos.writeShort(msgId); - } - dos.flush(); - return baos.toByteArray(); - } - catch (IOException ex) { - throw new MqttException(ex); - } - } - - public boolean isMessageIdRequired() { - // all publishes require a message ID as it's used as the key to the token store - return true; - } -} \ No newline at end of file diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttReceivedMessage.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttReceivedMessage.java deleted file mode 100644 index 68773d0..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttReceivedMessage.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import org.eclipse.paho.client.mqttv3.MqttMessage; - -public class MqttReceivedMessage extends MqttMessage { - - private int messageId; - - public void setMessageId(int msgId) { - this.messageId = msgId; - } - - public int getMessageId() { - return messageId; - } - - // This method exists here to get around the protected visibility of the - // super class method. - public void setDuplicate(boolean value) { - super.setDuplicate(value); - } -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttSuback.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttSuback.java deleted file mode 100644 index 39ef1f0..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttSuback.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import java.io.ByteArrayInputStream; -import java.io.DataInputStream; -import java.io.IOException; - -import org.eclipse.paho.client.mqttv3.MqttException; - - -/** - * An on-the-wire representation of an MQTT SUBACK. - */ -public class MqttSuback extends MqttAck { - private int[] grantedQos; // Not currently made available to anyone. - - public MqttSuback(byte info, byte[] data) throws IOException { - super(MqttWireMessage.MESSAGE_TYPE_SUBACK); - ByteArrayInputStream bais = new ByteArrayInputStream(data); - DataInputStream dis = new DataInputStream(bais); - msgId = dis.readUnsignedShort(); - int index = 0; - grantedQos = new int[data.length-2]; - int qos = dis.read(); - while (qos != -1) { - grantedQos[index] = qos; - index++; - qos = dis.read(); - } - dis.close(); - } - - protected byte[] getVariableHeader() throws MqttException { - // Not needed, as the client never encodes a SUBACK - return new byte[0]; - } -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttSubscribe.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttSubscribe.java deleted file mode 100644 index 0eb68fe..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttSubscribe.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; -import java.io.IOException; - -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.MqttMessage; - - -/** - * An on-the-wire representation of an MQTT SUBSCRIBE message. - */ -public class MqttSubscribe extends MqttWireMessage { - private String[] names; - private int[] qos; - - /** - * Constructor for on an on hte wire MQTT subscribe message - * @param names - one or more topics to subscribe to - * @param qos - the max QOS that each each topic will be subscribed at - */ - public MqttSubscribe(String[] names, int[] qos) { - super(MqttWireMessage.MESSAGE_TYPE_SUBSCRIBE); - this.names = names; - this.qos = qos; - - if (names.length != qos.length) { - throw new IllegalArgumentException(); - } - - for (int i=0;i> 4); - byte info = (byte) (first &= 0x0f); - long remLen = readMBI(in).getValue(); - long totalToRead = counter.getCounter() + remLen; - - MqttWireMessage result; - long remainder = totalToRead - counter.getCounter(); - byte[] data = new byte[0]; - // The remaining bytes must be the payload... - if (remainder > 0) { - data = new byte[(int) remainder]; - in.readFully(data, 0, data.length); - } - - if (type == MqttWireMessage.MESSAGE_TYPE_PUBLISH) { - result = new MqttPublish(info, data); - } - else if (type == MqttWireMessage.MESSAGE_TYPE_PUBACK) { - result = new MqttPubAck(info, data); - } - else if (type == MqttWireMessage.MESSAGE_TYPE_PUBCOMP) { - result = new MqttPubComp(info, data); - } - else if (type == MqttWireMessage.MESSAGE_TYPE_CONNACK) { - result = new MqttConnack(info, data); - } - else if (type == MqttWireMessage.MESSAGE_TYPE_PINGRESP) { - result = new MqttPingResp(info, data); - } - else if (type == MqttWireMessage.MESSAGE_TYPE_SUBACK) { - result = new MqttSuback(info, data); - } - else if (type == MqttWireMessage.MESSAGE_TYPE_UNSUBACK) { - result = new MqttUnsubAck(info, data); - } - else if (type == MqttWireMessage.MESSAGE_TYPE_PUBREL) { - result = new MqttPubRel(info, data); - } - else if (type == MqttWireMessage.MESSAGE_TYPE_PUBREC) { - result = new MqttPubRec(info, data); - } - else { - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_UNEXPECTED_ERROR); - } - return result; - } catch(IOException io) { - throw new MqttException(io); - } - } - - protected static byte[] encodeMBI( long number) { - int numBytes = 0; - long no = number; - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - // Encode the remaining length fields in the four bytes - do { - byte digit = (byte)(no % 128); - no = no / 128; - if (no > 0) { - digit |= 0x80; - } - bos.write(digit); - numBytes++; - } while ( (no > 0) && (numBytes<4) ); - - return bos.toByteArray(); - } - - /** - * Decodes an MQTT Multi-Byte Integer from the given stream. - */ - protected static MultiByteInteger readMBI(DataInputStream in) throws IOException { - byte digit; - long msgLength = 0; - int multiplier = 1; - int count = 0; - - do { - digit = in.readByte(); - count++; - msgLength += ((digit & 0x7F) * multiplier); - multiplier *= 128; - } while ((digit & 0x80) != 0); - - return new MultiByteInteger(msgLength, count); - } - - protected byte[] encodeMessageId() throws MqttException { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - DataOutputStream dos = new DataOutputStream(baos); - dos.writeShort(msgId); - dos.flush(); - return baos.toByteArray(); - } - catch (IOException ex) { - throw new MqttException(ex); - } - } - - public boolean isRetryable() { - return false; - } - - public void setDuplicate(boolean duplicate) { - this.duplicate = duplicate; - } - - /** - * Encodes a String given into UTF-8, before writing this to the DataOutputStream the length of the - * encoded string is encoded into two bytes and then written to the DataOutputStream. @link{DataOutputStream#writeUFT(String)} - * should be no longer used. @link{DataOutputStream#writeUFT(String)} does not correctly encode UTF-16 surrogate characters. - * - * @param dos The stream to write the encoded UTF-8 String to. - * @param stringToEncode The String to be encoded - * @throws MqttException Thrown when an error occurs with either the encoding or writing the data to the stream - */ - protected void encodeUTF8(DataOutputStream dos, String stringToEncode) throws MqttException - { - try { - - byte[] encodedString = stringToEncode.getBytes("UTF-8"); - byte byte1 = (byte) ((encodedString.length >>> 8) & 0xFF); - byte byte2 = (byte) ((encodedString.length >>> 0) & 0xFF); - - - dos.write(byte1); - dos.write(byte2); - dos.write(encodedString); - } - catch(UnsupportedEncodingException ex) - { - throw new MqttException(ex); - } catch (IOException ex) { - throw new MqttException(ex); - } - } - - /** - * Decodes a UTF-8 string from the DataInputStream provided. @link(DataInoutStream#readUTF()) should be no longer used, because @link(DataInoutStream#readUTF()) - * does not decode UTF-16 surrogate characters correctly. - * - * @param input The input stream from which to read the encoded string - * @return a decoded String from the DataInputStream - * @throws MqttException thrown when an error occurs with either reading from the stream or - * decoding the encoded string. - */ - protected String decodeUTF8(DataInputStream input) throws MqttException - { - int encodedLength; - try { - encodedLength = input.readUnsignedShort(); - - byte[] encodedString = new byte[encodedLength]; - input.readFully(encodedString); - - return new String(encodedString, "UTF-8"); - } catch (IOException ex) { - throw new MqttException(ex); - } - } - -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MultiByteArrayInputStream.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MultiByteArrayInputStream.java deleted file mode 100644 index 0519c82..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MultiByteArrayInputStream.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.internal.wire; - -import java.io.IOException; -import java.io.InputStream; - -public class MultiByteArrayInputStream extends InputStream { - - private byte[] bytesA; - private int offsetA; - private int lengthA; - private byte[] bytesB; - private int offsetB; - private int lengthB; - - private int pos = 0; - - public MultiByteArrayInputStream(byte[] bytesA, int offsetA, int lengthA, byte[] bytesB, int offsetB, int lengthB) { - this.bytesA = bytesA; - this.bytesB = bytesB; - this.offsetA = offsetA; - this.offsetB = offsetB; - this.lengthA = lengthA; - this.lengthB = lengthB; - } - public int read() throws IOException { - int result = -1; - if (posA sample java.util.logging properties file - jsr47min.properties is provided that demonstrates - * how to run with a memory based trace facility that runs with minimal performance - * overhead. The memory buffer can be dumped when a log/trace record is written matching - * the MemoryHandlers trigger level or when the push method is invoked on the MemoryHandler. - * {@link org.eclipse.paho.client.mqttv3.util.Debug Debug} provides method to make it easy - * to dump the memory buffer as well as other useful debug info. - */ -public class JSR47Logger implements Logger { - private java.util.logging.Logger julLogger = null; - private ResourceBundle logMessageCatalog = null; - private ResourceBundle traceMessageCatalog = null; - private String catalogID = null; - private String resourceName = null; - private String loggerName = null; - - /** - * - * @param logMsgCatalog The resource bundle associated with this logger - * @param loggerID The suffix for the loggerName (will be appeneded to org.eclipse.paho.client.mqttv3 - * @param resourceContext A context for the logger e.g. clientID or appName... - */ - public void initialise(ResourceBundle logMsgCatalog, String loggerID, String resourceContext ) { - this.traceMessageCatalog = logMessageCatalog; - this.resourceName = resourceContext; -// loggerName = "org.eclipse.paho.client.mqttv3." + ((null == loggerID || 0 == loggerID.length()) ? "internal" : loggerID); - loggerName = loggerID; - this.julLogger = java.util.logging.Logger.getLogger(loggerName); - this.logMessageCatalog = logMsgCatalog; - this.traceMessageCatalog = logMsgCatalog; - this.catalogID = logMessageCatalog.getString("0"); - - } - - public void setResourceName(String logContext) { - this.resourceName = logContext; - } - - public boolean isLoggable(int level) { - return julLogger.isLoggable(mapJULLevel(level)); // || InternalTracer.isLoggable(level); - } - - public void severe(String sourceClass, String sourceMethod, String msg) { - log(SEVERE, sourceClass, sourceMethod, msg, null, null); - } - - public void severe(String sourceClass, String sourceMethod, String msg, Object[] inserts) { - log(SEVERE, sourceClass, sourceMethod, msg, inserts, null); - } - - public void severe(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown) { - log(SEVERE, sourceClass, sourceMethod, msg, inserts, thrown); - } - - public void warning(String sourceClass, String sourceMethod, String msg) { - log(WARNING, sourceClass, sourceMethod, msg, null, null); - } - - public void warning(String sourceClass, String sourceMethod, String msg, Object[] inserts) { - log(WARNING, sourceClass, sourceMethod, msg, inserts, null); - } - - public void warning(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown) { - log(WARNING, sourceClass, sourceMethod, msg, inserts, thrown); - } - - public void info(String sourceClass, String sourceMethod, String msg) { - log(INFO, sourceClass, sourceMethod, msg, null, null); - } - - public void info(String sourceClass, String sourceMethod, String msg, Object[] inserts) { - log(INFO, sourceClass, sourceMethod, msg, inserts, null); - } - - public void info(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown) { - log(INFO, sourceClass, sourceMethod, msg, inserts, thrown); - } - - public void config(String sourceClass, String sourceMethod, String msg) { - log(CONFIG, sourceClass, sourceMethod, msg, null, null); - } - - public void config(String sourceClass, String sourceMethod, String msg, Object[] inserts) { - log(CONFIG, sourceClass, sourceMethod, msg, inserts, null); - } - - public void config(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown) { - log(CONFIG, sourceClass, sourceMethod, msg, inserts, thrown); - } - - public void log(int level, String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown) { -// InternalTracer.log(this.catalogID, level, sourceClass, sourceMethod, msg, inserts, thrown); - java.util.logging.Level julLevel = mapJULLevel(level); - if (julLogger.isLoggable(julLevel)) { - logToJsr47(julLevel, sourceClass, sourceMethod, this.catalogID, this.logMessageCatalog, msg, inserts, thrown); - } - } - -// public void setTrace(Trace trace) { -// InternalTracer.setTrace(trace); -// } - - public void fine(String sourceClass, String sourceMethod, String msg) { - trace(FINE, sourceClass, sourceMethod, msg, null, null); - } - - public void fine(String sourceClass, String sourceMethod, String msg, Object[] inserts) { - trace(FINE, sourceClass, sourceMethod, msg, inserts, null); - } - - public void fine(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex) { - trace(FINE, sourceClass, sourceMethod, msg, inserts, ex); - } - - public void finer(String sourceClass, String sourceMethod, String msg) { - trace(FINER, sourceClass, sourceMethod, msg, null, null); - } - - public void finer(String sourceClass, String sourceMethod, String msg, Object[] inserts) { - trace(FINER, sourceClass, sourceMethod, msg, inserts, null); - } - - public void finer(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex) { - trace(FINER, sourceClass, sourceMethod, msg, inserts, ex); - } - - public void finest(String sourceClass, String sourceMethod, String msg) { - trace(FINEST, sourceClass, sourceMethod, msg, null, null); - } - - public void finest(String sourceClass, String sourceMethod, String msg, Object[] inserts) { - trace(FINEST, sourceClass, sourceMethod, msg, inserts, null); - } - - public void finest(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex) { - trace(FINEST, sourceClass, sourceMethod, msg, inserts, ex); - } - - - public void trace(int level, String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex) { - java.util.logging.Level julLevel = mapJULLevel(level); - boolean isJULLoggable = julLogger.isLoggable(julLevel); -// if (FINE == level || isJULLoggable || InternalTracer.isLoggable(level)) { -// InternalTracer.traceForced(level, sourceClass, sourceMethod, msg, inserts); -// } - if (isJULLoggable) { - logToJsr47(julLevel, sourceClass, sourceMethod, this.catalogID, this.traceMessageCatalog, msg, inserts, ex); - } - } - - - private String getResourceMessage(ResourceBundle messageCatalog, String msg) { - String message; - try { - message = messageCatalog.getString(msg); - } catch (MissingResourceException e) { - // This is acceptable, simply return the given msg string. - message = msg; - } - return message; - } - - private void logToJsr47(java.util.logging.Level julLevel, String sourceClass, String sourceMethod, String catalogName, - ResourceBundle messageCatalog, String msg, Object[] inserts, Throwable thrown) { -// LogRecord logRecord = new LogRecord(julLevel, msg); - String formattedWithArgs = msg; - if (msg.indexOf("=====")== -1) { - formattedWithArgs = MessageFormat.format(getResourceMessage(messageCatalog, msg), inserts); - } - LogRecord logRecord = new LogRecord(julLevel, resourceName + ": " +formattedWithArgs); - - logRecord.setSourceClassName(sourceClass); - logRecord.setSourceMethodName(sourceMethod); - logRecord.setLoggerName(loggerName); -// logRecord.setResourceBundleName(catalogName); -// logRecord.setResourceBundle(messageCatalog); -// if (null != inserts) { -// logRecord.setParameters(inserts); -// } - if (null != thrown) { - logRecord.setThrown(thrown); - } - - julLogger.log(logRecord); - } - - private java.util.logging.Level mapJULLevel(int level) { - java.util.logging.Level julLevel = null; - - switch (level) { - case SEVERE: - julLevel = java.util.logging.Level.SEVERE; - break; - case WARNING: - julLevel = java.util.logging.Level.WARNING; - break; - case INFO: - julLevel = java.util.logging.Level.INFO; - break; - case CONFIG: - julLevel = java.util.logging.Level.CONFIG; - break; - case FINE: - julLevel = java.util.logging.Level.FINE; - break; - case FINER: - julLevel = java.util.logging.Level.FINER; - break; - case FINEST: - julLevel = java.util.logging.Level.FINEST; - break; - } - - return julLevel; - } - - public String formatMessage(String msg, Object[] inserts) { - String formatString; - try { - formatString = logMessageCatalog.getString(msg); - } catch (MissingResourceException e) { - formatString = msg; - } - return formatString; - } - - public void dumpTrace() { - dumpMemoryTrace47(julLogger); - } - - protected static void dumpMemoryTrace47(java.util.logging.Logger logger) { - MemoryHandler mHand = null; - - if (logger!= null) { - Handler[] handlers = logger.getHandlers(); - - for (int i=0; i - * The int levels define a set of standard logging levels that can be used to - * control logging output. The logging levels are ordered and are specified by - * ordered integers. Enabling logging at a given level also enables logging at - * all higher levels. - *

                          - * Clients should use the the convenience methods such as severe() and fine() or - * one of the predefined level constants such as Logger.SEVERE and Logger.FINE - * with the appropriate log(int level...) or trace(int level...) methods. - *

                          - * The levels in descending order are: - *

                            - *
                          • SEVERE (log - highest value)
                          • - *
                          • WARNING (log)
                          • - *
                          • INFO (log)
                          • - *
                          • CONFIG (log)
                          • - *
                          • FINE (trace)
                          • - *
                          • FINER (trace)
                          • - *
                          • FINEST (trace - lowest value)
                          • - *
                          - *

                          - */ -public interface Logger { - /** - * SEVERE is a message level indicating a serious failure. - *

                          - * In general SEVERE messages should describe events that are of - * considerable importance and which will prevent normal program execution. - * They should be reasonably intelligible to end users and to system - * administrators. - */ - public static final int SEVERE = 1; - /** - * WARNING is a message level indicating a potential problem. - *

                          - * In general WARNING messages should describe events that will be of - * interest to end users or system managers, or which indicate potential - * problems. - */ - public static final int WARNING = 2; - /** - * INFO is a message level for informational messages. - *

                          - * Typically INFO messages will be written to the console or its equivalent. - * So the INFO level should only be used for reasonably significant messages - * that will make sense to end users and system admins. - */ - public static final int INFO = 3; - /** - * CONFIG is a message level for static configuration messages. - *

                          - * CONFIG messages are intended to provide a variety of static configuration - * information, to assist in debugging problems that may be associated with - * particular configurations. For example, CONFIG message might include the - * CPU type, the graphics depth, the GUI look-and-feel, etc. - */ - public static final int CONFIG = 4; - /** - * FINE is a message level providing tracing information. - *

                          - * All of FINE, FINER, and FINEST are intended for relatively detailed - * tracing. The exact meaning of the three levels will vary between - * subsystems, but in general, FINEST should be used for the most voluminous - * detailed output, FINER for somewhat less detailed output, and FINE for - * the lowest volume (and most important) messages. - *

                          - * In general the FINE level should be used for information that will be - * broadly interesting to developers who do not have a specialized interest - * in the specific subsystem. - *

                          - * FINE messages might include things like minor (recoverable) failures. - * Issues indicating potential performance problems are also worth logging - * as FINE. - */ - public static final int FINE = 5; - /** - * FINER indicates a fairly detailed tracing message. By default logging - * calls for entering, returning, or throwing an exception are traced at - * this level. - */ - public static final int FINER = 6; - /** - * FINEST indicates a highly detailed tracing message. - */ - public static final int FINEST = 7; - - public void initialise(ResourceBundle messageCatalog, String loggerID, String resourceName); - - /** - * Set a name that can be used to provide context with each log record. - * This overrides the value passed in on initialise - */ - public void setResourceName(String logContext); - - /** - * Check if a message of the given level would actually be logged by this - * logger. This check is based on the Loggers effective level, which may be - * inherited from its parent. - * - * @param level - * a message logging level. - * @return true if the given message level is currently being logged. - */ - public boolean isLoggable(int level); - - /** - * Log a message, specifying source class and method, if the logger is - * currently enabled for the given message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message localization catalog for the message or - * the actual message itself. During formatting, if the logger - * has a mapping for the msg string, then the msg string is - * replaced by the localized value. Otherwise the original msg - * string is used. - */ - public void severe(String sourceClass, String sourceMethod, String msg); - - /** - * Log a message, specifying source class and method, with an array of - * object arguments, if the logger is currently enabled for the given - * message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message localization catalog for the message or - * the actual message itself. During formatting, if the logger - * has a mapping for the msg string, then the msg string is - * replaced by the localized value. Otherwise the original msg - * string is used. The formatter uses java.text.MessageFormat - * style formatting to format parameters, so for example a format - * string "{0} {1}" would format two inserts into the message. - * @param inserts - * Array of parameters to the message. - */ - public void severe(String sourceClass, String sourceMethod, String msg, Object[] inserts); - - /** - * Log a message, specifying source class and method, with an array of - * object arguments and a throwable, if the logger is currently enabled for - * the given message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message localization catalog for the message or - * the actual message itself. During formatting, if the logger - * has a mapping for the msg string, then the msg string is - * replaced by the localized value. Otherwise the original msg - * string is used. The formatter uses java.text.MessageFormat - * style formatting to format parameters, so for example a format - * string "{0} {1}" would format two inserts into the message. - * @param inserts - * Array of parameters to the message. - * @param thrown - * Throwable associated with log message. - */ - public void severe(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown); - - /** - * Log a message, specifying source class and method, if the logger is - * currently enabled for the given message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message localization catalog for the message or - * the actual message itself. During formatting, if the logger - * has a mapping for the msg string, then the msg string is - * replaced by the localized value. Otherwise the original msg - * string is used. - */ - public void warning(String sourceClass, String sourceMethod, String msg); - - /** - * Log a message, specifying source class and method, with an array of - * object arguments, if the logger is currently enabled for the given - * message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message localization catalog for the message or - * the actual message itself. During formatting, if the logger - * has a mapping for the msg string, then the msg string is - * replaced by the localized value. Otherwise the original msg - * string is used. The formatter uses java.text.MessageFormat - * style formatting to format parameters, so for example a format - * string "{0} {1}" would format two inserts into the message. - * @param inserts - * Array of parameters to the message. - */ - public void warning(String sourceClass, String sourceMethod, String msg, Object[] inserts); - - /** - * Log a message, specifying source class and method, with an array of - * object arguments and a throwable, if the logger is currently enabled for - * the given message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message localization catalog for the message or - * the actual message itself. During formatting, if the logger - * has a mapping for the msg string, then the msg string is - * replaced by the localized value. Otherwise the original msg - * string is used. The formatter uses java.text.MessageFormat - * style formatting to format parameters, so for example a format - * string "{0} {1}" would format two inserts into the message. - * @param inserts - * Array of parameters to the message. - * @param thrown - * Throwable associated with log message. - */ - public void warning(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown); - - /** - * Log a message, specifying source class and method, if the logger is - * currently enabled for the given message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message localization catalog for the message or - * the actual message itself. During formatting, if the logger - * has a mapping for the msg string, then the msg string is - * replaced by the localized value. Otherwise the original msg - * string is used. - */ - public void info(String sourceClass, String sourceMethod, String msg); - - /** - * Log a message, specifying source class and method, with an array of - * object arguments, if the logger is currently enabled for the given - * message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message localization catalog for the message or - * the actual message itself. During formatting, if the logger - * has a mapping for the msg string, then the msg string is - * replaced by the localized value. Otherwise the original msg - * string is used. The formatter uses java.text.MessageFormat - * style formatting to format parameters, so for example a format - * string "{0} {1}" would format two inserts into the message. - * @param inserts - * Array of parameters to the message. - */ - public void info(String sourceClass, String sourceMethod, String msg, Object[] inserts); - - /** - * Log a message, specifying source class and method, with an array of - * object arguments and a throwable, if the logger is currently enabled for - * the given message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message localization catalog for the message or - * the actual message itself. During formatting, if the logger - * has a mapping for the msg string, then the msg string is - * replaced by the localized value. Otherwise the original msg - * string is used. The formatter uses java.text.MessageFormat - * style formatting to format parameters, so for example a format - * string "{0} {1}" would format two inserts into the message. - * @param inserts - * Array of parameters to the message. - * @param thrown - * Throwable associated with log message. - */ - public void info(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown); - - /** - * Log a message, specifying source class and method, if the logger is - * currently enabled for the given message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message localization catalog for the message or - * the actual message itself. During formatting, if the logger - * has a mapping for the msg string, then the msg string is - * replaced by the localized value. Otherwise the original msg - * string is used. - */ - public void config(String sourceClass, String sourceMethod, String msg); - - /** - * Log a message, specifying source class and method, with an array of - * object arguments, if the logger is currently enabled for the given - * message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message localization catalog for the message or - * the actual message itself. During formatting, if the logger - * has a mapping for the msg string, then the msg string is - * replaced by the localized value. Otherwise the original msg - * string is used. The formatter uses java.text.MessageFormat - * style formatting to format parameters, so for example a format - * string "{0} {1}" would format two inserts into the message. - * @param inserts - * Array of parameters to the message. - */ - public void config(String sourceClass, String sourceMethod, String msg, Object[] inserts); - - /** - * Log a message, specifying source class and method, with an array of - * object arguments and a throwable, if the logger is currently enabled for - * the given message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message localization catalog for the message or - * the actual message itself. During formatting, if the logger - * has a mapping for the msg string, then the msg string is - * replaced by the localized value. Otherwise the original msg - * string is used. The formatter uses java.text.MessageFormat - * style formatting to format parameters, so for example a format - * string "{0} {1}" would format two inserts into the message. - * @param inserts - * Array of parameters to the message. - * @param thrown - * Throwable associated with log message. - */ - public void config(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown); - - /** - * Trace a message, specifying source class and method, if the logger is - * currently enabled for the given message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message catalog for the message or the actual - * message itself. During formatting, if the logger has a mapping - * for the msg string, then the msg string is replaced by the - * value. Otherwise the original msg string is used. - */ - public void fine(String sourceClass, String sourceMethod, String msg); - - /** - * Trace a message, specifying source class and method, with an array of - * object arguments, if the logger is currently enabled for the given - * message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message catalog for the message or the actual - * message itself. During formatting, if the logger has a mapping - * for the msg string, then the msg string is replaced by the - * value. Otherwise the original msg string is used. The - * formatter uses java.text.MessageFormat style formatting to - * format parameters, so for example a format string "{0} {1}" - * would format two inserts into the message. - * @param inserts - * Array of parameters to the message. - */ - public void fine(String sourceClass, String sourceMethod, String msg, Object[] inserts); - - public void fine(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex); - - /** - * Trace a message, specifying source class and method, if the logger is - * currently enabled for the given message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message catalog for the message or the actual - * message itself. During formatting, if the logger has a mapping - * for the msg string, then the msg string is replaced by the - * value. Otherwise the original msg string is used. - */ - public void finer(String sourceClass, String sourceMethod, String msg); - - /** - * Trace a message, specifying source class and method, with an array of - * object arguments, if the logger is currently enabled for the given - * message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message catalog for the message or the actual - * message itself. During formatting, if the logger has a mapping - * for the msg string, then the msg string is replaced by the - * value. Otherwise the original msg string is used. The - * formatter uses java.text.MessageFormat style formatting to - * format parameters, so for example a format string "{0} {1}" - * would format two inserts into the message. - * @param inserts - * Array of parameters to the message. - */ - public void finer(String sourceClass, String sourceMethod, String msg, Object[] inserts); - - public void finer(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex); - - /** - * Trace a message, specifying source class and method, if the logger is - * currently enabled for the given message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message catalog for the message or the actual - * message itself. During formatting, if the logger has a mapping - * for the msg string, then the msg string is replaced by the - * value. Otherwise the original msg string is used. - */ - public void finest(String sourceClass, String sourceMethod, String msg); - - /** - * Trace a message, specifying source class and method, with an array of - * object arguments, if the logger is currently enabled for the given - * message level. - * - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message catalog for the message or the actual - * message itself. During formatting, if the logger has a mapping - * for the msg string, then the msg string is replaced by the - * value. Otherwise the original msg string is used. The - * formatter uses java.text.MessageFormat style formatting to - * format parameters, so for example a format string "{0} {1}" - * would format two inserts into the message. - * @param inserts - * Array of parameters to the message. - */ - public void finest(String sourceClass, String sourceMethod, String msg, Object[] inserts); - - public void finest(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex); - - /** - * Log a message, specifying source class and method, with an array of - * object arguments and a throwable, if the logger is currently enabled for - * the given message level. - * - * @param level - * One of the message level identifiers, e.g. SEVERE. - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message localization catalog for the message or - * the actual message itself. During formatting, if the logger - * has a mapping for the msg string, then the msg string is - * replaced by the localized value. Otherwise the original msg - * string is used. The formatter uses java.text.MessageFormat - * style formatting to format parameters, so for example a format - * string "{0} {1}" would format two inserts into the message. - * @param inserts - * Array of parameters to the message, may be null. - * @param thrown - * Throwable associated with log message. - */ - public void log(int level, String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown); - - /** - * Log a trace message, specifying source class and method, with an array of - * object arguments and a throwable, if the logger is currently enabled for - * the given message level. - * - * @param level - * One of the message level identifiers, e.g. SEVERE. - * @param sourceClass - * Name of class that issued the logging request. - * @param sourceMethod - * Name of method that issued the logging request. - * @param msg - * The key in the message catalog for the message or the actual - * message itself. During formatting, if the logger has a mapping - * for the msg string, then the msg string is replaced by the - * value. Otherwise the original msg string is used. The - * formatter uses java.text.MessageFormat style formatting to - * format parameters, so for example a format string "{0} {1}" - * would format two inserts into the message. - * @param inserts - * Array of parameters to the message, may be null. - */ - public void trace(int level, String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex); - - /** - * Format a log message without causing it to be written to the log. - * - * @param msg - * The key in the message localization catalog for the message or - * the actual message itself. During formatting, if the logger - * has a mapping for the msg string, then the msg string is - * replaced by the localized value. Otherwise the original msg - * string is used. The formatter uses java.text.MessageFormat - * style formatting to format parameters, so for example a format - * string "{0} {1}" would format two inserts into the message. - * @param inserts - * Array of parameters to the message. - * @return The formatted message for the current locale. - */ - public String formatMessage(String msg, Object[] inserts); - - public void dumpTrace(); -} \ No newline at end of file diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/logging/LoggerFactory.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/logging/LoggerFactory.java deleted file mode 100644 index 348b185..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/logging/LoggerFactory.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.logging; - -import java.lang.reflect.Method; - -/** - * LoggerFactory will create a logger instance ready for use by the caller. - * - * The default is to create a logger that utilises the Java's built in - * logging facility java.util.logging (JSR47). It is possible to override - * this for systems where JSR47 is not available or an alternative logging - * facility is needed by using setLogger and passing the the class name of - * a logger that implements {@link Logger} - */ -import java.util.MissingResourceException; -import java.util.ResourceBundle; -/** - * A factory that returns a logger for use by the MQTT client. - * - * The default log and trace facility uses Java's build in log facility:- - * java.util.logging. For systems where this is not available or where - * an alternative logging framework is required the logging facility can be - * replaced using {@link org.eclipse.paho.client.mqttv3.logging.LoggerFactory#setLogger(String)} - * which takes an implementation of the {@link org.eclipse.paho.client.mqttv3.logging.Logger} - * interface. - */ -public class LoggerFactory { - /** - * Default message catalog. - */ - public final static String MQTT_CLIENT_MSG_CAT = "org.eclipse.paho.client.mqttv3.internal.nls.logcat"; - private final static String className = LoggerFactory.class.getName(); - - private static String overrideloggerClassName = null; - /** - * Default logger that uses java.util.logging. - */ - private static String jsr47LoggerClassName = "org.eclipse.paho.client.mqttv3.logging.JSR47Logger"; - - /** - * Find or create a logger for a named package/class. - * If a logger has already been created with the given name - * it is returned. Otherwise a new logger is created. By default a logger - * that uses java.util.logging will be returned. - * - * @param messageCatalogName the resource bundle containing the logging messages. - * @param loggerID unique name to identify this logger. - * @return a suitable Logger. - * @throws Exception - */ - public static Logger getLogger(String messageCatalogName, String loggerID) { - String loggerClassName = overrideloggerClassName; - Logger logger = null; - - if (loggerClassName == null) { - loggerClassName = jsr47LoggerClassName; - } -// logger = getJSR47Logger(ResourceBundle.getBundle(messageCatalogName), loggerID, null) ; - logger = getLogger(loggerClassName, ResourceBundle.getBundle(messageCatalogName), loggerID, null) ; -// } - - if (null == logger) { - throw new MissingResourceException("Error locating the logging class", className, loggerID); - } - - return logger; - } - - - /** - * Return an instance of a logger - * - * @param the class name of the load to load - * @param messageCatalog the resource bundle containing messages - * @param loggerID an identifier for the logger - * @param resourceName a name or context to associate with this logger instance. - * @return a ready for use logger - */ - private static Logger getLogger(String loggerClassName, ResourceBundle messageCatalog, String loggerID, String resourceName) { //, FFDC ffdc) { - Logger logger = null; - Class logClass = null; - - try { - logClass = Class.forName(loggerClassName); - } catch (NoClassDefFoundError ncdfe) { - return null; - } catch (ClassNotFoundException cnfe) { - return null; - } - if (null != logClass) { - // Now instantiate the log - try { - logger = (Logger)logClass.newInstance(); - } catch (IllegalAccessException e) { - return null; - } catch (InstantiationException e) { - return null; - } catch (ExceptionInInitializerError e) { - return null; - } catch (SecurityException e) { - return null; - } - logger.initialise(messageCatalog, loggerID, resourceName); - } - - return logger; - } - - /** - * When run in JSR47, this allows access to the properties in the logging.properties - * file. - * If not run in JSR47, or the property isn't set, returns null. - * @param name the property to return - * @return the property value, or null if it isn't set or JSR47 isn't being used - */ - public static String getLoggingProperty(String name) { - String result = null; - try { - // Hide behind reflection as java.util.logging is guaranteed to be - // available. - Class logManagerClass = Class.forName("java.util.logging.LogManager"); - Method m1 = logManagerClass.getMethod("getLogManager", new Class[]{}); - Object logManagerInstance = m1.invoke(null, null); - Method m2 = logManagerClass.getMethod("getProperty", new Class[]{String.class}); - result = (String)m2.invoke(logManagerInstance,new Object[]{name}); - } catch(Exception e) { - // Any error, assume JSR47 isn't available and return null - result = null; - } - return result; - } - - /** - * Set the class name of the logger that the LoggerFactory will load - * If not set getLogger will attempt to create a logger - * appropriate for the platform. - * @param loggerClassName - Logger implementation class name to use. - */ - public static void setLogger(String loggerClassName) { - LoggerFactory.overrideloggerClassName = loggerClassName; - } -} \ No newline at end of file diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/logging/SimpleLogFormatter.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/logging/SimpleLogFormatter.java deleted file mode 100644 index 893db7f..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/logging/SimpleLogFormatter.java +++ /dev/null @@ -1,91 +0,0 @@ -package org.eclipse.paho.client.mqttv3.logging; - -import java.io.PrintWriter; -import java.io.StringWriter; -import java.text.MessageFormat; -import java.util.Date; -import java.util.logging.Formatter; -import java.util.logging.LogRecord; - -/** - * SimpleLogFormatter prints a single line - * log record in human readable form. - */ -public class SimpleLogFormatter extends Formatter { - - final String ls = System.getProperty("line.separator"); - /** - * Constructs a SimpleFormatter object. - */ - public SimpleLogFormatter() { - super(); - } - - /** - * Format the logrecord as a single line with well defined columns. - */ - public String format(LogRecord r) { - StringBuffer sb = new StringBuffer(); - sb.append(r.getLevel().getName()+"\t"); - sb.append(MessageFormat.format("{0, date, yy-MM-dd} {0, time, kk:mm:ss.SSSS} ", - new Object[] { new Date(r.getMillis()) })+"\t"); - String cnm = r.getSourceClassName(); - String cn=""; - if (cnm != null) { - int cnl = cnm.length(); - if (cnl>20) { - cn = r.getSourceClassName().substring(cnl-19); - } else { - char sp[] = {' '}; - StringBuffer sb1= new StringBuffer().append(cnm); - cn = sb1.append(sp,0, 1).toString(); - } - } - sb.append(cn+"\t").append(" "); - sb.append(left(r.getSourceMethodName(),23,' ')+"\t"); - sb.append(r.getThreadID()+"\t"); - sb.append(formatMessage(r)).append(ls); - if (null != r.getThrown()) { - sb.append("Throwable occurred: "); - Throwable t = r.getThrown(); - PrintWriter pw = null; - try { - StringWriter sw = new StringWriter(); - pw = new PrintWriter(sw); - t.printStackTrace(pw); - sb.append(sw.toString()); - } finally { - if (pw != null) { - try { - pw.close(); - } catch (Exception e) { - // ignore - } - } - } - } - return sb.toString(); - } - - /** - * Left justify a string. - * - * @param s the string to justify - * @param width the field width to justify within - * @param fillChar the character to fill with - * - * @return the justified string. - */ - public static String left(String s, int width, char fillChar) { - if (s.length() >= width) { - return s; - } - StringBuffer sb = new StringBuffer(width); - sb.append(s); - for (int i = width - s.length(); --i >= 0;) { - sb.append(fillChar); - } - return sb.toString(); - } - -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/logging/jsr47min.properties b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/logging/jsr47min.properties deleted file mode 100644 index 0626551..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/logging/jsr47min.properties +++ /dev/null @@ -1,83 +0,0 @@ -# Properties file which configures the operation of the JDK logging facility. -# -# The configuration in this file is the suggesgted configuration -# for collecting trace for helping debug problems related to the -# Paho MQTT client. It configures trace to be continuosly collected -# in memory with minimal impact on performance. -# -# When the push trigger (by default a Severe level message) or a -# specific request is made to "push" the in memory trace then it -# is "pushed" to the configured target handler. By default -# this is the standard java.util.logging.FileHandler. The Paho Debug -# class can be used to push the memory trace to its target -# -# To enable trace either: -# - use this properties file as is and set the logging facility up -# to use it by configuring the util logging system property e.g. -# -# >java -Djava.util.logging.config.file=\jsr47min.properties -# -# - This contents of this file can also be merged with another -# java.util.logging config file to ensure provide wider logging -# and trace including Paho trace - -# Global logging properties. -# ------------------------------------------ -# The set of handlers to be loaded upon startup. -# Comma-separated list of class names. -# - Root handlers are not enabled by default - just handlers on the Paho packages. -#handlers=java.util.logging.MemoryHandler,java.util.logging.FileHandler, java.util.logging.ConsoleHandler - -# Default global logging level. -# Loggers and Handlers may override this level -#.level=INFO - -# Loggers -# ------------------------------------------ -# A memoryhandler is attached to the paho packages -# and the level specified to collected all trace related -# to paho packages. This will override any root/global -# level handlers if set. -org.eclipse.paho.client.mqttv3.handlers=java.util.logging.MemoryHandler -org.eclipse.paho.client.mqttv3.level=ALL -# It is possible to set more granular trace on a per class basis e.g. -#org.eclipse.paho.client.mqttv3.internal.ClientComms.level=ALL - -# Handlers -# ----------------------------------------- -# Note: the target handler that is associated with the MemoryHandler is not a root handler -# and hence not returned when getting the handlers from root. It appears accessing -# target handler programatically is not possible as target is a private variable in -# class MemoryHandler -java.util.logging.MemoryHandler.level=FINEST -java.util.logging.MemoryHandler.size=10000 -java.util.logging.MemoryHandler.push=SEVERE -java.util.logging.MemoryHandler.target=java.util.logging.FileHandler -#java.util.logging.MemoryHandler.target=java.util.logging.ConsoleHandler - - -# --- FileHandler --- -# Override of global logging level -java.util.logging.FileHandler.level=ALL - -# Naming style for the output file: -# (The output file is placed in the directory -# defined by the "user.home" System property.) -# See java.util.logging for more options -java.util.logging.FileHandler.pattern=%h/paho%u.log - -# Limiting size of output file in bytes: -java.util.logging.FileHandler.limit=200000 - -# Number of output files to cycle through, by appending an -# integer to the base file name: -java.util.logging.FileHandler.count=3 - -# Style of output (Simple or XML): -java.util.logging.FileHandler.formatter=org.eclipse.paho.client.mqttv3.logging.SimpleLogFormatter - -# --- ConsoleHandler --- -# Override of global logging level -#java.util.logging.ConsoleHandler.level=INFO -#java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter -#java.util.logging.ConsoleHandler.formatter=org.eclipse.paho.client.mqttv3.logging.SimpleLogFormatter diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/logging/package.html b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/logging/package.html deleted file mode 100644 index a679cf1..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/logging/package.html +++ /dev/null @@ -1,18 +0,0 @@ - -Provides facilities to write and format log and trace to help debug problems. - -

                          The default log and trace facility uses Java's build in log facility:- -java.util.logging. For systems where this is not available or where -an alternative logging framework is required the logging facility can be -replaced using {@link org.eclipse.paho.client.mqttv3.logging.LoggerFactory#setLogger(String)} -which takes an implementation of the {@link org.eclipse.paho.client.mqttv3.logging.Logger} -interface. - -

                          A sample java.util.logging properties file - jsr47min.properties is provided that demonstrates -how to run with a memory based trace facility that runs with minimal performance -overhead. The memory buffer can be dumped when a log/trace record is written matching -the MemoryHandlers trigger level or when the push method is invoked on the MemoryHandler. -{@link org.eclipse.paho.client.mqttv3.util.Debug Debug} provides method to make it easy -to dump the memory buffer as well as other useful debug info. - - \ No newline at end of file diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/package.html b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/package.html deleted file mode 100644 index 1adc965..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/package.html +++ /dev/null @@ -1,127 +0,0 @@ - -Contains a programming interface enabling applications to communicate with an MQTT server - -

                          -The MQ Telemetry Transport (MQTT) is a lightweight broker-based publish/subscribe -messaging protocol designed to be open, simple, lightweight and easy to implement. -These characteristics make it ideal for use in constrained environments, for example, -but not limited to: -

                            -
                          • Where the network is expensive, has low bandwidth or is unreliable such as mobile and vsat networks -
                          • When run on an embedded or mobile device with limited processor, memory or battery -
                          -

                          Features of the protocol include: -

                            -
                          • The publish/subscribe message pattern to provide one-to-many message - distribution and decoupling of applications -
                          • A messaging transport that is agnostic to the content of the payload -
                          • The use of TCP/IP to provide network connectivity -
                          • The use of SSL/TLS to provide network security and trust -
                          • Three qualities of service for message delivery which are maintained across - network, client and server breaks. -
                              -
                            • "At most once", where messages are delivered according to the best efforts - of the underlying TCP/IP network. Message loss or duplication can occur. - This level could be used, for example, with ambient sensor data where it - does not matter if an individual reading is lost as the next one will be published soon after. -
                            • "At least once", where messages are assured to arrive but duplicates may occur. -
                            • "Exactly once", where message are assured to arrive exactly once. This - level could be used, for example, with billing systems where duplicate or - lost messages could lead to incorrect charges being applied. -
                            - The quality of service for message delivery is met even if the network connection - breaks, or the client or the server stop while a message is being delivered -
                          • A small transport overhead (the fixed-length header is just 2 bytes), and - protocol exchanges minimised to reduce network traffic -
                          • A mechanism to notify interested parties to an abnormal disconnection of - a client using the Last Will and Testament feature -
                          - -

                          The basic means of operating the client is:

                          -
                            -
                          1. Create an instance of {@link org.eclipse.paho.client.mqttv3.MqttClient} or - {@link org.eclipse.paho.client.mqttv3.MqttAsyncClient}, providing - the address of an MQTT server and a unique client identifier.
                          2. -
                          3. connect to the server
                          4. -
                          5. Exchange messages with the server: -
                              -
                            • publish messages to the server, - via a topic.
                            • -
                            • subscribe to one more topics. The server will send any messages - it receives on those topics to the client. The client will be informed when a message - arrives via a callback -
                            -
                          6. disconnect from the server.
                          7. -
                          - -

                          The programming model and concepts like the protocol are small and easy to use. Key concepts -to use when creating MQTT application include: -

                            -
                          • Every client instance that connects to an MQTT server must have a unique client identifier. - If a second instance of a client with the same ID connects to a server the first instance will be - disconnected. -
                          • For message delivery to be reliable and withstand both abnormal network breaks and clients/server - outages the client must use a persistent store to hold messages while they are being delivered. This is - the default case where a file based persistent store - {@link org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence MqttDefaultFilePersistence} is used. -
                          • When connecting the {@link org.eclipse.paho.client.mqttv3.MqttConnectOptions#setCleanSession(boolean) cleansession} - option has a big impact on the operation of the client. If set to false: -
                              -
                            • Message delivery will match the quality of service specified when the message was published even across - failures of the network, client or server -
                            • The server will store messages for active subscriptions on behalf of the client when the client is not connected. - The server will deliver these messages to the client the next time it connects. -
                            - If set to true: -
                              -
                            • Any state stored on the client and server related to the client will be cleansed - before the connection is fully started. Subscriptions from earlier sessions will be unsubscribed - and any messages still in-flight from previous sessions will be deleted. -
                            • When the client disconnects either as the result of the application requesting a disconnect - or a network failure, state related to the client will be cleansed just as at connect time. -
                            • Messages will only be delivered to the quality of service requested at publish time if - the connection is maintained while the message is being delivered -
                            -
                          • When subscribing for messages the subscription can be for an absolute topic or a wildcarded topic. -
                          • When unsubscribing the topic to be unsubscribed must match one specified on an earlier subscribe. -
                          • There are two MQTT client libraries to choose from: -
                              -
                            1. {@link org.eclipse.paho.client.mqttv3.IMqttAsyncClient MqttAsyncClient} which provides a non-blocking interface where - methods return before the requested operation has completed. The completion of the operation - can be monitored by in several ways: -
                                -
                              • Use the {@link org.eclipse.paho.client.mqttv3.IMqttToken#waitForCompletion waitForCompletion} - call on the token returned from the operation. This will block - until the operation completes. -
                              • Pass a {@link org.eclipse.paho.client.mqttv3.IMqttActionListener IMqttActionListener} - to the operation. The listener will then be called back when the operation completes. -
                              • Set a {@link org.eclipse.paho.client.mqttv3.MqttCallback MqttCallback} on the client. It - will be notified when a message arrive, a message have been delivered to the server and when the - connection to the server is lost. -
                              -
                            2. {@link org.eclipse.paho.client.mqttv3.IMqttClient MqttClient} where methods block until - the operation has completed. -
                            -
                          • For both the blocking and non-blocking clients some operations are asynchronous. This includes: -
                              -
                            • Notification that a new message has arrived: - {@link org.eclipse.paho.client.mqttv3.MqttCallback#messageArrived messageArrived}. -
                            • Notification that the connection to the server has broken: - {@link org.eclipse.paho.client.mqttv3.MqttCallback#connectionLost connectionLost}. -
                            • Notification that a message has been delivered to the server: - {@link org.eclipse.paho.client.mqttv3.MqttCallback#deliveryComplete deliveryComplete}. -
                            - A client registers interest in these notifications by registering a - {@link org.eclipse.paho.client.mqttv3.MqttCallback MqttCallback} on the client -
                          • There are a number of programs that demonstrate the different modes of - writing MQTT applications -
                              -
                            • {@link org.eclipse.paho.sample.mqttv3app.Sample} uses the blocking client interface -
                            • {@link org.eclipse.paho.sample.mqttv3app.SampleAsyncCallBack} uses the asynchronous client with - callbacks which are notified when an operation completes -
                            • {@link org.eclipse.paho.sample.mqttv3app.SampleAsyncWait} uses the asynchronous client and - shows how to use the token returned from each operation to block until the operation completes. -
                            -
                          - - \ No newline at end of file diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/persist/MemoryPersistence.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/persist/MemoryPersistence.java deleted file mode 100644 index de1a209..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/persist/MemoryPersistence.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.persist; - -import java.util.Enumeration; -import java.util.Hashtable; - -import org.eclipse.paho.client.mqttv3.MqttClientPersistence; -import org.eclipse.paho.client.mqttv3.MqttPersistable; -import org.eclipse.paho.client.mqttv3.MqttPersistenceException; - -/** - * Persistence that uses memory - * - * In cases where reliability is not required across client or device - * restarts memory this memory peristence can be used. In cases where - * reliability is required like when clean session is set to false - * then a non-volatile form of persistence should be used. - * - */ -public class MemoryPersistence implements MqttClientPersistence { - - private Hashtable data; - - /* (non-Javadoc) - * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#close() - */ - public void close() throws MqttPersistenceException { - data.clear(); - } - - /* (non-Javadoc) - * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#keys() - */ - public Enumeration keys() throws MqttPersistenceException { - return data.keys(); - } - - /* (non-Javadoc) - * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#get(java.lang.String) - */ - public MqttPersistable get(String key) throws MqttPersistenceException { - return (MqttPersistable)data.get(key); - } - - /* (non-Javadoc) - * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#open(java.lang.String, java.lang.String) - */ - public void open(String clientId, String serverURI) throws MqttPersistenceException { - this.data = new Hashtable(); - } - - /* (non-Javadoc) - * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#put(java.lang.String, org.eclipse.paho.client.mqttv3.MqttPersistable) - */ - public void put(String key, MqttPersistable persistable) throws MqttPersistenceException { - data.put(key, persistable); - } - - /* (non-Javadoc) - * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#remove(java.lang.String) - */ - public void remove(String key) throws MqttPersistenceException { - data.remove(key); - } - - /* (non-Javadoc) - * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#clear() - */ - public void clear() throws MqttPersistenceException { - data.clear(); - } - - /* (non-Javadoc) - * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#containsKey(java.lang.String) - */ - public boolean containsKey(String key) throws MqttPersistenceException { - return data.containsKey(key); - } -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/persist/MqttDefaultFilePersistence.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/persist/MqttDefaultFilePersistence.java deleted file mode 100644 index 2e4d44a..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/persist/MqttDefaultFilePersistence.java +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.persist; - -import java.io.File; -import java.io.FileFilter; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.FilenameFilter; -import java.io.IOException; -import java.util.Enumeration; -import java.util.Vector; - -import org.eclipse.paho.client.mqttv3.MqttClientPersistence; -import org.eclipse.paho.client.mqttv3.MqttPersistable; -import org.eclipse.paho.client.mqttv3.MqttPersistenceException; -import org.eclipse.paho.client.mqttv3.internal.FileLock; -import org.eclipse.paho.client.mqttv3.internal.MqttPersistentData; - -/** - * An implementation of the {@link MqttClientPersistence} interface that provides - * file based persistence. - * - * A directory is specified when the Persistence object is created. When the persistence - * is then opened (see {@link #open(String, String)}), a sub-directory is made beneath the base - * for this client ID and connection key. This allows one persistence base directory - * to be shared by multiple clients. - * - * The sub-directory's name is created from a concatenation of the client ID and connection key - * with any instance of '/', '\\', ':' or ' ' removed. - */ -public class MqttDefaultFilePersistence implements MqttClientPersistence { - - private File dataDir; - private File clientDir = null; - private FileLock fileLock = null; - private static final String MESSAGE_FILE_EXTENSION = ".msg"; - private static final String MESSAGE_BACKUP_FILE_EXTENSION = ".bup"; - private static final String LOCK_FILENAME = ".lck"; - - private static final FilenameFilter FILE_FILTER = new FilenameFilter() { - public boolean accept(File dir, String name) { return name.endsWith(MESSAGE_FILE_EXTENSION); } - }; - - public MqttDefaultFilePersistence() { //throws MqttPersistenceException { - this(System.getProperty("user.dir")); - } - - /** - * Create an file-based persistent data store within the specified directory. - * @param directory the directory to use. - */ - public MqttDefaultFilePersistence(String directory) { //throws MqttPersistenceException { - dataDir = new File(directory); - } - - public void open(String clientId, String theConnection) throws MqttPersistenceException { - - if (dataDir.exists() && !dataDir.isDirectory()) { - throw new MqttPersistenceException(); - } else if (!dataDir.exists() ) { - if (!dataDir.mkdirs()) { - throw new MqttPersistenceException(); - } - } - if (!dataDir.canWrite()) { - throw new MqttPersistenceException(); - } - - - StringBuffer keyBuffer = new StringBuffer(); - for (int i=0;i -Contains implementations of the MqttClientPersistence interface. - -

                          -An MQTT client needs a persistence mechanism to store messages while they -are in the process of being delivered. This package contains several -implementations of the interface. If a persistence class is not -specified on the constructor to an MQTT client, -{@link org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence MqttDefaultFilePersistence} -is used by default. - - \ No newline at end of file diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/util/Debug.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/util/Debug.java deleted file mode 100644 index 49e93c5..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/util/Debug.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ -package org.eclipse.paho.client.mqttv3.util; - -import java.util.Enumeration; -import java.util.Properties; - -import org.eclipse.paho.client.mqttv3.internal.ClientComms; -import org.eclipse.paho.client.mqttv3.logging.Logger; -import org.eclipse.paho.client.mqttv3.logging.LoggerFactory; - -/** - * Utility to help debug problems with the Paho MQTT client - * Once initialised a call to dumpClientDebug will force any memory trace - * together with pertinent client and system state to the main log facility. - * - * No client wide lock is taken when the dump is progress. This means the - * set of client state may not be consistent as the client can still be - * processing work while the dump is in progress. - */ -public class Debug { - - final static String className = ClientComms.class.getName(); - Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className); - - static String separator = "=============="; - static String lineSep = System.getProperty("line.separator","\n"); - - String clientID; - ClientComms comms; - - /** - * Set the debug facility up for a specific client - * @param clientID the ID of the client being debugged - * @param comms the ClientComms object of the client being debugged - */ - public Debug(String clientID, ClientComms comms) { - this.clientID = clientID; - this.comms = comms; - log.setResourceName(clientID); - } - - /** - * Dump maximum debug info. - * This includes state specific to a client as well - * as debug that is JVM wide like trace and system properties. - * All state is written as debug log entries. - */ - public void dumpClientDebug() { - dumpClientComms(); - dumpConOptions(); - dumpClientState(); - dumpBaseDebug(); - } - - /** - * Dump of JVM wide debug info. - * This includes trace and system properties. - * Includes trace and system properties - */ - public void dumpBaseDebug() { - dumpVersion(); - dumpSystemProperties(); - dumpMemoryTrace(); - } - - /** - * If memory trace is being used a request is made to push it - * to the target handler. - */ - protected void dumpMemoryTrace() { - log.dumpTrace(); - } - - /** - * Dump information that show the version of the MQTT client being used. - */ - protected void dumpVersion() { - StringBuffer vInfo = new StringBuffer(); - vInfo.append(lineSep+separator+" Version Info "+ separator+lineSep); - vInfo.append(left("Version",20,' ') + ": "+ ClientComms.VERSION + lineSep); - vInfo.append(left("Build Level",20,' ') + ": "+ ClientComms.BUILD_LEVEL + lineSep); - vInfo.append(separator+separator+separator+lineSep); - log.fine(className,"dumpVersion", vInfo.toString()); - } - - /** - * Dump the current set of system.properties to a log record - */ - public void dumpSystemProperties() { - - Properties sysProps = System.getProperties(); - log.fine(className,"dumpSystemProperties", dumpProperties(sysProps, "SystemProperties").toString()); - } - - /** - * Dump interesting variables from ClientState - */ - public void dumpClientState() { - Properties props = null; - if (comms != null && comms.getClientState() != null ) { - props = comms.getClientState().getDebug(); - log.fine(className,"dumpClientState", dumpProperties(props, clientID + " : ClientState").toString()); - } - } - - /** - * Dump interesting variables from ClientComms - */ - public void dumpClientComms() { - Properties props = null; - if (comms != null) { - props = comms.getDebug(); - log.fine(className,"dumpClientComms", dumpProperties(props, clientID + " : ClientComms").toString()); - } - } - - /** - * Dump Connection options - */ - public void dumpConOptions() { - Properties props = null; - if (comms != null) { - props = comms.getConOptions().getDebug(); - log.fine(className,"dumpConOptions", dumpProperties(props, clientID + " : Connect Options").toString()); - } - } - - - /** - * Return a set of properties as a formatted string - */ - public static String dumpProperties(Properties props, String name) { - - StringBuffer propStr = new StringBuffer(); - Enumeration propsE = props.propertyNames(); - propStr.append(lineSep+separator+" "+name+" "+ separator+lineSep); - while (propsE.hasMoreElements()) { - String key = (String)propsE.nextElement(); - propStr.append(left(key,28,' ') + ": "+ props.get(key)+lineSep); - } - propStr.append(separator+separator+separator+lineSep); - - return propStr.toString(); - } - - /** - * Left justify a string. - * - * @param s the string to justify - * @param width the field width to justify within - * @param fillChar the character to fill with - * - * @return the justified string. - */ - public static String left(String s, int width, char fillChar) { - if (s.length() >= width) { - return s; - } - StringBuffer sb = new StringBuffer(width); - sb.append(s); - for (int i = width - s.length(); --i >= 0;) { - sb.append(fillChar); - } - return sb.toString(); - } - -} diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/util/package.html b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/util/package.html deleted file mode 100644 index 19c41d8..0000000 --- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/util/package.html +++ /dev/null @@ -1,5 +0,0 @@ - -Provides helpers and utilities. - - - \ No newline at end of file diff --git a/org.eclipse.paho.sample.mqttv3app/pom.xml b/org.eclipse.paho.sample.mqttv3app/pom.xml new file mode 100644 index 0000000..b754633 --- /dev/null +++ b/org.eclipse.paho.sample.mqttv3app/pom.xml @@ -0,0 +1,26 @@ + + + + org.eclipse.paho + paho-parent + 0.9.0 + + + 4.0.0 + paho-mqtt-client-sample + 0.9.0 + jar + Paho :: A sample MQTT Client + A sample MQTT Client. + ${paho.url} + + + + org.eclipse.paho + paho-mqtt-client + ${project.version} + compile + + + \ No newline at end of file diff --git a/org.eclipse.paho.sample.mqttv3app/src/main/java/org/eclipse/paho/sample/mqttv3app/Sample.java b/org.eclipse.paho.sample.mqttv3app/src/main/java/org/eclipse/paho/sample/mqttv3app/Sample.java new file mode 100644 index 0000000..229e76e --- /dev/null +++ b/org.eclipse.paho.sample.mqttv3app/src/main/java/org/eclipse/paho/sample/mqttv3app/Sample.java @@ -0,0 +1,392 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ + +package org.eclipse.paho.sample.mqttv3app; + +import java.io.IOException; +import java.sql.Timestamp; + +import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; +import org.eclipse.paho.client.mqttv3.MqttCallback; +import org.eclipse.paho.client.mqttv3.MqttClient; +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.MqttMessage; +import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence; + +/** + * A sample application that demonstrates how to use the MQTT v3 Client blocking api. + * + * It can be run from the command line in one of two modes: + * - as a publisher, sending a single message to a topic on the server + * - as a subscriber, listening for messages from the server + * + * There are three versions of the sample that implement the same features + * but do so using using different programming styles: + *

                            + *
                          1. Sample (this one) which uses the API which blocks until the operation completes
                          2. + *
                          3. SampleAsyncWait shows how to use the asynchronous API with waiters that block until + * an action completes
                          4. + *
                          5. SampleAsyncCallBack shows how to use the asynchronous API where events are + * used to notify the application when an action completes
                          6. + *
                          + * + * If the application is run with the -h parameter then info is displayed that + * describes all of the options / parameters. + */ +public class Sample implements MqttCallback { + + /** + * The main entry point of the sample. + * + * This method handles parsing of the arguments specified on the + * command-line before performing the specified action. + */ + public static void main(String[] args) { + + // Default settings: + boolean quietMode = false; + String action = "publish"; + String topic = ""; + String message = "Message from blocking MQTTv3 Java client sample"; + int qos = 2; + String broker = "m2m.eclipse.org"; + int port = 1883; + String clientId = null; + String subTopic = "Sample/#"; + String pubTopic = "Sample/Java/v3"; + boolean cleanSession = true; // Non durable subscriptions + boolean ssl = false; + String password = null; + String userName = null; + // Parse the arguments - + for (int i=0; i 2) { + System.out.println("Invalid QoS: "+qos); + printHelp(); + return; + } + if (topic.equals("")) { + // Set the default topic according to the specified action + if (action.equals("publish")) { + topic = pubTopic; + } else { + topic = subTopic; + } + } + + String protocol = "tcp://"; + + if (ssl) { + protocol = "ssl://"; + } + + String url = protocol + broker + ":" + port; + + if (clientId == null || clientId.equals("")) { + clientId = "SampleJavaV3_"+action; + } + + // With a valid set of arguments, the real work of + // driving the client API can begin + try { + // Create an instance of this class + Sample sampleClient = new Sample(url, clientId, cleanSession, quietMode,userName,password); + + // Perform the requested action + if (action.equals("publish")) { + sampleClient.publish(topic,qos,message.getBytes()); + } else if (action.equals("subscribe")) { + sampleClient.subscribe(topic,qos); + } + } catch(MqttException me) { + // Display full details of any exception that occurs + System.out.println("reason "+me.getReasonCode()); + System.out.println("msg "+me.getMessage()); + System.out.println("loc "+me.getLocalizedMessage()); + System.out.println("cause "+me.getCause()); + System.out.println("excep "+me); + me.printStackTrace(); + } + } + + // Private instance variables + private MqttClient client; + private String brokerUrl; + private boolean quietMode; + private MqttConnectOptions conOpt; + private boolean clean; + private String password; + private String userName; + + /** + * Constructs an instance of the sample client wrapper + * @param brokerUrl the url of the server to connect to + * @param clientId the client id to connect with + * @param cleanSession clear state at end of connection or not (durable or non-durable subscriptions) + * @param quietMode whether debug should be printed to standard out + * @param userName the username to connect with + * @param password the password for the user + * @throws MqttException + */ + public Sample(String brokerUrl, String clientId, boolean cleanSession, boolean quietMode, String userName, String password) throws MqttException { + this.brokerUrl = brokerUrl; + this.quietMode = quietMode; + this.clean = cleanSession; + this.password = password; + this.userName = userName; + //This sample stores in a temporary directory... where messages temporarily + // stored until the message has been delivered to the server. + //..a real application ought to store them somewhere + // where they are not likely to get deleted or tampered with + String tmpDir = System.getProperty("java.io.tmpdir"); + MqttDefaultFilePersistence dataStore = new MqttDefaultFilePersistence(tmpDir); + + try { + // Construct the connection options object that contains connection parameters + // such as cleansession and LWAT + conOpt = new MqttConnectOptions(); + conOpt.setCleanSession(clean); + if(password != null ) { + conOpt.setPassword(this.password.toCharArray()); + } + if(userName != null) { + conOpt.setUserName(this.userName); + } + + // Construct an MQTT blocking mode client + client = new MqttClient(this.brokerUrl,clientId, dataStore); + + // Set this wrapper as the callback handler + client.setCallback(this); + + } catch (MqttException e) { + e.printStackTrace(); + log("Unable to set up client: "+e.toString()); + System.exit(1); + } + } + + /** + * Publish / send a message to an MQTT server + * @param topicName the name of the topic to publish to + * @param qos the quality of service to delivery the message at (0,1,2) + * @param payload the set of bytes to send to the MQTT server + * @throws MqttException + */ + public void publish(String topicName, int qos, byte[] payload) throws MqttException { + + // Connect to the MQTT server + log("Connecting to "+brokerUrl + " with client ID "+client.getClientId()); + client.connect(conOpt); + log("Connected"); + + String time = new Timestamp(System.currentTimeMillis()).toString(); + log("Publishing at: "+time+ " to topic \""+topicName+"\" qos "+qos); + + // Create and configure a message + MqttMessage message = new MqttMessage(payload); + message.setQos(qos); + + // Send the message to the server, control is not returned until + // it has been delivered to the server meeting the specified + // quality of service. + client.publish(topicName, message); + + // Disconnect the client + client.disconnect(); + log("Disconnected"); + } + + /** + * Subscribe to a topic on an MQTT server + * Once subscribed this method waits for the messages to arrive from the server + * that match the subscription. It continues listening for messages until the enter key is + * pressed. + * @param topicName to subscribe to (can be wild carded) + * @param qos the maximum quality of service to receive messages at for this subscription + * @throws MqttException + */ + public void subscribe(String topicName, int qos) throws MqttException { + + // Connect to the MQTT server + client.connect(conOpt); + log("Connected to "+brokerUrl+" with client ID "+client.getClientId()); + + // Subscribe to the requested topic + // The QOS specified is the maximum level that messages will be sent to the client at. + // For instance if QOS 1 is specified, any messages originally published at QOS 2 will + // be downgraded to 1 when delivering to the client but messages published at 1 and 0 + // will be received at the same level they were published at. + log("Subscribing to topic \""+topicName+"\" qos "+qos); + client.subscribe(topicName, qos); + + // Continue waiting for messages until the Enter is pressed + log("Press to exit"); + try { + System.in.read(); + } catch (IOException e) { + //If we can't read we'll just exit + } + + // Disconnect the client from the server + client.disconnect(); + log("Disconnected"); + } + + /** + * Utility method to handle logging. If 'quietMode' is set, this method does nothing + * @param message the message to log + */ + private void log(String message) { + if (!quietMode) { + System.out.println(message); + } + } + + /****************************************************************/ + /* Methods to implement the MqttCallback interface */ + /****************************************************************/ + + /** + * @see MqttCallback#connectionLost(Throwable) + */ + public void connectionLost(Throwable cause) { + // Called when the connection to the server has been lost. + // An application may choose to implement reconnection + // logic at this point. This sample simply exits. + log("Connection to " + brokerUrl + " lost!" + cause); + System.exit(1); + } + + /** + * @see MqttCallback#deliveryComplete(IMqttDeliveryToken) + */ + public void deliveryComplete(IMqttDeliveryToken token) { + // Called when a message has been delivered to the + // server. The token passed in here is the same one + // that was passed to or returned from the original call to publish. + // This allows applications to perform asynchronous + // delivery without blocking until delivery completes. + // + // This sample demonstrates asynchronous deliver and + // uses the token.waitForCompletion() call in the main thread which + // blocks until the delivery has completed. + // Additionally the deliveryComplete method will be called if + // the callback is set on the client + // + // If the connection to the server breaks before delivery has completed + // delivery of a message will complete after the client has re-connected. + // The getPendinTokens method will provide tokens for any messages + // that are still to be delivered. + } + + /** + * @see MqttCallback#messageArrived(String, MqttMessage) + */ + public void messageArrived(String topic, MqttMessage message) throws MqttException { + // Called when a message arrives from the server that matches any + // subscription made by the client + String time = new Timestamp(System.currentTimeMillis()).toString(); + System.out.println("Time:\t" +time + + " Topic:\t" + topic + + " Message:\t" + new String(message.getPayload()) + + " QoS:\t" + message.getQos()); + } + + /****************************************************************/ + /* End of MqttCallback methods */ + /****************************************************************/ + + static void printHelp() { + System.out.println( + "Syntax:\n\n" + + " Sample [-h] [-a publish|subscribe] [-t ] [-m ]\n" + + " [-s 0|1|2] -b ] [-p ] [-i ]\n\n" + + " -h Print this help text and quit\n" + + " -q Quiet mode (default is false)\n" + + " -a Perform the relevant action (default is publish)\n" + + " -t Publish/subscribe to instead of the default\n" + + " (publish: \"Sample/Java/v3\", subscribe: \"Sample/#\")\n" + + " -m Use instead of the default\n" + + " (\"Message from MQTTv3 Java client\")\n" + + " -s Use this QoS instead of the default (2)\n" + + " -b Use this name/IP address instead of the default (localhost)\n" + + " -p Use this port instead of the default (1883)\n\n" + + " -i Use this client ID instead of SampleJavaV3_\n" + + " -c Connect to the server with a clean session (default is false)\n" + + " \n\n Security Options \n" + + " -u Username \n" + + " -z Password \n" + + " \n\n SSL Options \n" + + " -v SSL enabled; true - (default is false) " + + " -k Use this JKS format key store to verify the client\n" + + " -w Passpharse to verify certificates in the keys store\n" + + " -r Use this JKS format keystore to verify the server\n" + + " If javax.net.ssl properties have been set only the -v flag needs to be set\n" + + "Delimit strings containing spaces with \"\"\n\n" + + "Publishers transmit a single message then disconnect from the server.\n" + + "Subscribers remain connected to the server and receive appropriate\n" + + "messages until is pressed.\n\n" + ); + } + +} \ No newline at end of file diff --git a/org.eclipse.paho.sample.mqttv3app/src/main/java/org/eclipse/paho/sample/mqttv3app/SampleAsyncCallBack.java b/org.eclipse.paho.sample.mqttv3app/src/main/java/org/eclipse/paho/sample/mqttv3app/SampleAsyncCallBack.java new file mode 100644 index 0000000..3b55700 --- /dev/null +++ b/org.eclipse.paho.sample.mqttv3app/src/main/java/org/eclipse/paho/sample/mqttv3app/SampleAsyncCallBack.java @@ -0,0 +1,648 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ + +package org.eclipse.paho.sample.mqttv3app; + +import java.io.IOException; +import java.sql.Timestamp; + +import org.eclipse.paho.client.mqttv3.IMqttActionListener; +import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; +import org.eclipse.paho.client.mqttv3.IMqttToken; +import org.eclipse.paho.client.mqttv3.MqttAsyncClient; +import org.eclipse.paho.client.mqttv3.MqttCallback; +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.MqttMessage; +import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence; + +/** + * A sample application that demonstrates how to use the MQTT v3 Client api in + * non-blocking callback/notification mode. + * + * It can be run from the command line in one of two modes: + * - as a publisher, sending a single message to a topic on the server + * - as a subscriber, listening for messages from the server + * + * There are three versions of the sample that implement the same features + * but do so using using different programming styles: + *
                            + *
                          1. Sample (this one) which uses the API which blocks until the operation completes
                          2. + *
                          3. SampleAsyncWait shows how to use the asynchronous API with waiters that block until + * an action completes
                          4. + *
                          5. SampleAsyncCallBack shows how to use the asynchronous API where events are + * used to notify the application when an action completes
                          6. + *
                          + * + * If the application is run with the -h parameter then info is displayed that + * describes all of the options / parameters. + */ +public class SampleAsyncCallBack implements MqttCallback { + + int state = BEGIN; + + static final int BEGIN = 0; + static final int CONNECTED = 1; + static final int PUBLISHED = 2; + static final int SUBSCRIBED = 3; + static final int DISCONNECTED = 4; + static final int FINISH = 5; + static final int ERROR = 6; + static final int DISCONNECT = 7; + + /** + * The main entry point of the sample. + * + * This method handles parsing the arguments specified on the + * command-line before performing the specified action. + */ + public static void main(String[] args) { + + // Default settings: + boolean quietMode = false; + String action = "publish"; + String topic = ""; + String message = "Message from async calback MQTTv3 Java client sample"; + int qos = 2; + String broker = "m2m.eclipse.org"; + int port = 1883; + String clientId = null; + String subTopic = "Sample/#"; + String pubTopic = "Sample/Java/v3"; + boolean cleanSession = true; // Non durable subscriptions + boolean ssl = false; + String password = null; + String userName = null; + + // Parse the arguments - + for (int i=0; i 2) { + System.out.println("Invalid QoS: "+qos); + printHelp(); + return; + } + if (topic.equals("")) { + // Set the default topic according to the specified action + if (action.equals("publish")) { + topic = pubTopic; + } else { + topic = subTopic; + } + } + + String protocol = "tcp://"; + + if (ssl) { + protocol = "ssl://"; + } + + String url = protocol + broker + ":" + port; + + if (clientId == null || clientId.equals("")) { + clientId = "SampleJavaV3_"+action; + } + + // With a valid set of arguments, the real work of + // driving the client API can begin + try { + // Create an instance of the Sample client wrapper + SampleAsyncCallBack sampleClient = new SampleAsyncCallBack(url,clientId,cleanSession, quietMode,userName,password); + + // Perform the specified action + if (action.equals("publish")) { + sampleClient.publish(topic,qos,message.getBytes()); + } else if (action.equals("subscribe")) { + sampleClient.subscribe(topic,qos); + } + } catch(MqttException me) { + // Display full details of any exception that occurs + System.out.println("reason "+me.getReasonCode()); + System.out.println("msg "+me.getMessage()); + System.out.println("loc "+me.getLocalizedMessage()); + System.out.println("cause "+me.getCause()); + System.out.println("excep "+me); + me.printStackTrace(); + } catch (Throwable th) { + System.out.println("Throwable caught "+th); + th.printStackTrace(); + } + } + + // Private instance variables + MqttAsyncClient client; + String brokerUrl; + private boolean quietMode; + private MqttConnectOptions conOpt; + private boolean clean; + Throwable ex = null; + Object waiter = new Object(); + boolean donext = false; + private String password; + private String userName; + + /** + * Constructs an instance of the sample client wrapper + * @param brokerUrl the url to connect to + * @param clientId the client id to connect with + * @param cleanSession clear state at end of connection or not (durable or non-durable subscriptions) + * @param quietMode whether debug should be printed to standard out + * @param userName the username to connect with + * @param password the password for the user + * @throws MqttException + */ + public SampleAsyncCallBack(String brokerUrl, String clientId, boolean cleanSession, + boolean quietMode, String userName, String password) throws MqttException { + this.brokerUrl = brokerUrl; + this.quietMode = quietMode; + this.clean = cleanSession; + this.password = password; + this.userName = userName; + //This sample stores in a temporary directory... where messages temporarily + // stored until the message has been delivered to the server. + //..a real application ought to store them somewhere + // where they are not likely to get deleted or tampered with + String tmpDir = System.getProperty("java.io.tmpdir"); + MqttDefaultFilePersistence dataStore = new MqttDefaultFilePersistence(tmpDir); + + try { + // Construct the object that contains connection parameters + // such as cleansession and LWAT + conOpt = new MqttConnectOptions(); + conOpt.setCleanSession(clean); + if(password != null ) { + conOpt.setPassword(this.password.toCharArray()); + } + if(userName != null) { + conOpt.setUserName(this.userName); + } + + // Construct the MqttClient instance + client = new MqttAsyncClient(this.brokerUrl,clientId, dataStore); + + // Set this wrapper as the callback handler + client.setCallback(this); + + } catch (MqttException e) { + e.printStackTrace(); + log("Unable to set up client: "+e.toString()); + System.exit(1); + } + } + + /** + * Publish / send a message to an MQTT server + * @param topicName the name of the topic to publish to + * @param qos the quality of service to delivery the message at (0,1,2) + * @param payload the set of bytes to send to the MQTT server + * @throws MqttException + */ + public void publish(String topicName, int qos, byte[] payload) throws Throwable { + // Use a state machine to decide which step to do next. State change occurs + // when a notification is received that an MQTT action has completed + while (state != FINISH) { + switch (state) { + case BEGIN: + // Connect using a non blocking connect + MqttConnector con = new MqttConnector(); + con.doConnect(); + break; + case CONNECTED: + // Publish using a non blocking publisher + Publisher pub = new Publisher(); + pub.doPublish(topicName, qos, payload); + break; + case PUBLISHED: + state = DISCONNECT; + donext = true; + break; + case DISCONNECT: + Disconnector disc = new Disconnector(); + disc.doDisconnect(); + break; + case ERROR: + throw ex; + case DISCONNECTED: + state = FINISH; + donext = true; + break; + } + +// if (state != FINISH) { + // Wait until notified about a state change and then perform next action + waitForStateChange(10000); +// } + } + } + + /** + * Wait for a maximum amount of time for a state change event to occur + * @param maxTTW maximum time to wait in milliseconds + * @throws MqttException + */ + private void waitForStateChange(int maxTTW ) throws MqttException { + synchronized (waiter) { + if (!donext ) { + try { + waiter.wait(maxTTW); + } catch (InterruptedException e) { + log("timed out"); + e.printStackTrace(); + } + + if (ex != null) { + throw (MqttException)ex; + } + } + donext = false; + } + } + + /** + * Subscribe to a topic on an MQTT server + * Once subscribed this method waits for the messages to arrive from the server + * that match the subscription. It continues listening for messages until the enter key is + * pressed. + * @param topicName to subscribe to (can be wild carded) + * @param qos the maximum quality of service to receive messages at for this subscription + * @throws MqttException + */ + public void subscribe(String topicName, int qos) throws Throwable { + // Use a state machine to decide which step to do next. State change occurs + // when a notification is received that an MQTT action has completed + while (state != FINISH) { + switch (state) { + case BEGIN: + // Connect using a non blocking connect + MqttConnector con = new MqttConnector(); + con.doConnect(); + break; + case CONNECTED: + // Subscribe using a non blocking subscribe + Subscriber sub = new Subscriber(); + sub.doSubscribe(topicName, qos); + break; + case SUBSCRIBED: + // Block until Enter is pressed allowing messages to arrive + log("Press to exit"); + try { + System.in.read(); + } catch (IOException e) { + //If we can't read we'll just exit + } + state = DISCONNECT; + donext = true; + break; + case DISCONNECT: + Disconnector disc = new Disconnector(); + disc.doDisconnect(); + break; + case ERROR: + throw ex; + case DISCONNECTED: + state = FINISH; + donext = true; + break; + } + +// if (state != FINISH && state != DISCONNECT) { + waitForStateChange(10000); + } +// } + } + + /** + * Utility method to handle logging. If 'quietMode' is set, this method does nothing + * @param message the message to log + */ + void log(String message) { + if (!quietMode) { + System.out.println(message); + } + } + + /****************************************************************/ + /* Methods to implement the MqttCallback interface */ + /****************************************************************/ + + /** + * @see MqttCallback#connectionLost(Throwable) + */ + public void connectionLost(Throwable cause) { + // Called when the connection to the server has been lost. + // An application may choose to implement reconnection + // logic at this point. This sample simply exits. + log("Connection to " + brokerUrl + " lost!" + cause); + System.exit(1); + } + + /** + * @see MqttCallback#deliveryComplete(IMqttDeliveryToken) + */ + public void deliveryComplete(IMqttDeliveryToken token) { + // Called when a message has been delivered to the + // server. The token passed in here is the same one + // that was returned from the original call to publish. + // This allows applications to perform asynchronous + // delivery without blocking until delivery completes. + // + // This sample demonstrates asynchronous deliver, registering + // a callback to be notified on each call to publish. + // + // The deliveryComplete method will also be called if + // the callback is set on the client + // + log("Delivery complete callback: Publish Completed "+token.getTopics()); + } + + /** + * @see MqttCallback#messageArrived(String, MqttMessage) + */ + public void messageArrived(String topic, MqttMessage message) throws MqttException { + // Called when a message arrives from the server that matches any + // subscription made by the client + String time = new Timestamp(System.currentTimeMillis()).toString(); + System.out.println("Time:\t" +time + + " Topic:\t" + topic + + " Message:\t" + new String(message.getPayload()) + + " QoS:\t" + message.getQos()); + } + + /****************************************************************/ + /* End of MqttCallback methods */ + /****************************************************************/ + static void printHelp() { + System.out.println( + "Syntax:\n\n" + + " Sample [-h] [-a publish|subscribe] [-t ] [-m ]\n" + + " [-s 0|1|2] -b ] [-p ] [-i ]\n\n" + + " -h Print this help text and quit\n" + + " -q Quiet mode (default is false)\n" + + " -a Perform the relevant action (default is publish)\n" + + " -t Publish/subscribe to instead of the default\n" + + " (publish: \"Sample/Java/v3\", subscribe: \"Sample/#\")\n" + + " -m Use instead of the default\n" + + " (\"Message from MQTTv3 Java client\")\n" + + " -s Use this QoS instead of the default (2)\n" + + " -b Use this name/IP address instead of the default (localhost)\n" + + " -p Use this port instead of the default (1883)\n\n" + + " -i Use this client ID instead of SampleJavaV3_\n" + + " -c Connect to the server with a clean session (default is false)\n" + + " \n\n Security Options \n" + + " -u Username \n" + + " -z Password \n" + + " \n\n SSL Options \n" + + " -v SSL enabled; true - (default is false) " + + " -k Use this JKS format key store to verify the client\n" + + " -w Passpharse to verify certificates in the keys store\n" + + " -r Use this JKS format keystore to verify the server\n" + + " If javax.net.ssl properties have been set only the -v flag needs to be set\n" + + "Delimit strings containing spaces with \"\"\n\n" + + "Publishers transmit a single message then disconnect from the server.\n" + + "Subscribers remain connected to the server and receive appropriate\n" + + "messages until is pressed.\n\n" + ); + } + + /** + * Connect in a non blocking way and then sit back and wait to be + * notified that the action has completed. + */ + public class MqttConnector { + + public MqttConnector() { + } + + public void doConnect() { + // Connect to the server + // Get a token and setup an asynchronous listener on the token which + // will be notified once the connect completes + log("Connecting to "+brokerUrl + " with client ID "+client.getClientId()); + + IMqttActionListener conListener = new IMqttActionListener() { + public void onSuccess(IMqttToken asyncActionToken) { + log("Connected"); + state = CONNECTED; + carryOn(); + } + + public void onFailure(IMqttToken asyncActionToken, Throwable exception) { + ex = exception; + state = ERROR; + log ("connect failed" +exception); + carryOn(); + } + + public void carryOn() { + synchronized (waiter) { + donext=true; + waiter.notifyAll(); + } + } + }; + + try { + // Connect using a non blocking connect + client.connect(conOpt,"Connect sample context", conListener); + } catch (MqttException e) { + // If though it is a non blocking connect an exception can be + // thrown if validation of parms fails or other checks such + // as already connected fail. + state = ERROR; + donext = true; + ex = e; + } + } + } + + /** + * Publish in a non blocking way and then sit back and wait to be + * notified that the action has completed. + */ + public class Publisher { + public void doPublish(String topicName, int qos, byte[] payload) { + // Send / publish a message to the server + // Get a token and setup an asynchronous listener on the token which + // will be notified once the message has been delivered + MqttMessage message = new MqttMessage(payload); + message.setQos(qos); + + + String time = new Timestamp(System.currentTimeMillis()).toString(); + log("Publishing at: "+time+ " to topic \""+topicName+"\" qos "+qos); + + // Setup a listener object to be notified when the publish completes. + // + IMqttActionListener pubListener = new IMqttActionListener() { + public void onSuccess(IMqttToken asyncActionToken) { + log("Publish Completed"); + state = PUBLISHED; + carryOn(); + } + + public void onFailure(IMqttToken asyncActionToken, Throwable exception) { + ex = exception; + state = ERROR; + log ("Publish failed" +exception); + carryOn(); + } + + public void carryOn() { + synchronized (waiter) { + donext=true; + waiter.notifyAll(); + } + } + }; + + try { + // Publish the message + client.publish(topicName, message, "Pub sample context", pubListener); + } catch (MqttException e) { + state = ERROR; + donext = true; + ex = e; + } + } + } + + /** + * Subscribe in a non blocking way and then sit back and wait to be + * notified that the action has completed. + */ + public class Subscriber { + public void doSubscribe(String topicName, int qos) { + // Make a subscription + // Get a token and setup an asynchronous listener on the token which + // will be notified once the subscription is in place. + log("Subscribing to topic \""+topicName+"\" qos "+qos); + + IMqttActionListener subListener = new IMqttActionListener() { + public void onSuccess(IMqttToken asyncActionToken) { + log("Subscribe Completed"); + state = SUBSCRIBED; + carryOn(); + } + + public void onFailure(IMqttToken asyncActionToken, Throwable exception) { + ex = exception; + state = ERROR; + log ("Subscribe failed" +exception); + carryOn(); + } + + public void carryOn() { + synchronized (waiter) { + donext=true; + waiter.notifyAll(); + } + } + }; + + try { + client.subscribe(topicName, qos, "Subscribe sample context", subListener); + } catch (MqttException e) { + state = ERROR; + donext = true; + ex = e; + } + } + } + + /** + * Disconnect in a non blocking way and then sit back and wait to be + * notified that the action has completed. + */ + public class Disconnector { + public void doDisconnect() { + // Disconnect the client + log("Disconnecting"); + + IMqttActionListener discListener = new IMqttActionListener() { + public void onSuccess(IMqttToken asyncActionToken) { + log("Disconnect Completed"); + state = DISCONNECTED; + carryOn(); + } + + public void onFailure(IMqttToken asyncActionToken, Throwable exception) { + ex = exception; + state = ERROR; + log ("Disconnect failed" +exception); + carryOn(); + } + public void carryOn() { + synchronized (waiter) { + donext=true; + waiter.notifyAll(); + } + } + }; + + try { + client.disconnect("Disconnect sample context", discListener); + } catch (MqttException e) { + state = ERROR; + donext = true; + ex = e; + } + } + } +} \ No newline at end of file diff --git a/org.eclipse.paho.sample.mqttv3app/src/main/java/org/eclipse/paho/sample/mqttv3app/SampleAsyncWait.java b/org.eclipse.paho.sample.mqttv3app/src/main/java/org/eclipse/paho/sample/mqttv3app/SampleAsyncWait.java new file mode 100644 index 0000000..96da936 --- /dev/null +++ b/org.eclipse.paho.sample.mqttv3app/src/main/java/org/eclipse/paho/sample/mqttv3app/SampleAsyncWait.java @@ -0,0 +1,421 @@ +/* + * Copyright (c) 2009, 2012 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ + +package org.eclipse.paho.sample.mqttv3app; + +import java.io.IOException; +import java.sql.Timestamp; + +import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; +import org.eclipse.paho.client.mqttv3.IMqttToken; +import org.eclipse.paho.client.mqttv3.MqttAsyncClient; +import org.eclipse.paho.client.mqttv3.MqttCallback; +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.MqttMessage; +import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence; + +/** + * A sample application that demonstrates how to use the MQTT v3 Client api in + * non-blocking waiter mode. + * + * It can be run from the command line in one of two modes: + * - as a publisher, sending a single message to a topic on the server + * - as a subscriber, listening for messages from the server + * + * There are three versions of the sample that implement the same features + * but do so using using different programming styles: + *
                            + *
                          1. Sample (this one) which uses the API which blocks until the operation completes
                          2. + *
                          3. SampleAsyncWait shows how to use the asynchronous API with waiters that block until + * an action completes
                          4. + *
                          5. SampleAsyncCallBack shows how to use the asynchronous API where events are + * used to notify the application when an action completes
                          6. + *
                          + * + * If the application is run with the -h parameter then info is displayed that + * describes all of the options / parameters. + */ + +public class SampleAsyncWait implements MqttCallback { + + /** + * The main entry point of the sample. + * + * This method handles parsing the arguments specified on the + * command-line before performing the specified action. + */ + public static void main(String[] args) { + + // Default settings: + boolean quietMode = false; + String action = "publish"; + String topic = ""; + String message = "Message from async waiter MQTTv3 Java client sample"; + int qos = 2; + String broker = "m2m.eclipse.org"; + int port = 1883; + String clientId = null; + String subTopic = "Sample/#"; + String pubTopic = "Sample/Java/v3"; + boolean cleanSession = true; // Non durable subscriptions + boolean ssl = false; + String userName = null; + String password = null; + + // Parse the arguments - + for (int i=0; i 2) { + System.out.println("Invalid QoS: "+qos); + printHelp(); + return; + } + if (topic.equals("")) { + // Set the default topic according to the specified action + if (action.equals("publish")) { + topic = pubTopic; + } else { + topic = subTopic; + } + } + + String protocol = "tcp://"; + + if (ssl) { + protocol = "ssl://"; + } + + String url = protocol + broker + ":" + port; + + if (clientId == null || clientId.equals("")) { + clientId = "SampleJavaV3_"+action; + } + + // With a valid set of arguments, the real work of + // driving the client API can begin + try { + // Create an instance of this class + SampleAsyncWait sampleClient = new SampleAsyncWait(url,clientId,cleanSession, quietMode,userName,password); + + // Perform the specified action + if (action.equals("publish")) { + sampleClient.publish(topic,qos,message.getBytes()); + } else if (action.equals("subscribe")) { + sampleClient.subscribe(topic,qos); + } + } catch(MqttException me) { + // Display full details of any exception that occurs + System.out.println("reason "+me.getReasonCode()); + System.out.println("msg "+me.getMessage()); + System.out.println("loc "+me.getLocalizedMessage()); + System.out.println("cause "+me.getCause()); + System.out.println("excep "+me); + me.printStackTrace(); + } + } + + // Private instance variables + private MqttAsyncClient client; + private String brokerUrl; + private boolean quietMode; + private MqttConnectOptions conOpt; + private boolean clean; + private String password; + private String userName; + + /** + * Constructs an instance of the sample client wrapper + * @param brokerUrl the url to connect to + * @param clientId the client id to connect with + * @param cleanSession clear state at end of connection or not (durable or non-durable subscriptions) + * @param quietMode whether debug should be printed to standard out + * @param userName the username to connect with + * @param password the password for the user + * @throws MqttException + */ + public SampleAsyncWait(String brokerUrl, String clientId, boolean cleanSession, + boolean quietMode, String userName, String password) throws MqttException { + this.brokerUrl = brokerUrl; + this.quietMode = quietMode; + this.clean = cleanSession; + this.userName = userName; + this.password = password; + //This sample stores in a temporary directory... where messages temporarily + // stored until the message has been delivered to the server. + //..a real application ought to store them somewhere + // where they are not likely to get deleted or tampered with + String tmpDir = System.getProperty("java.io.tmpdir"); + MqttDefaultFilePersistence dataStore = new MqttDefaultFilePersistence(tmpDir); + + try { + // Construct the connection options object that contains connection parameters + // such as cleansession and LWAT + conOpt = new MqttConnectOptions(); + conOpt.setCleanSession(clean); + if(password != null ) { + conOpt.setPassword(this.password.toCharArray()); + } + if(userName != null) { + conOpt.setUserName(this.userName); + } + + // Construct a non blocking MQTT client instance + client = new MqttAsyncClient(this.brokerUrl,clientId, dataStore); + + // Set this wrapper as the callback handler + client.setCallback(this); + + } catch (MqttException e) { + e.printStackTrace(); + log("Unable to set up client: "+e.toString()); + System.exit(1); + } + } + + /** + * Publish / send a message to an MQTT server + * @param topicName the name of the topic to publish to + * @param qos the quality of service to delivery the message at (0,1,2) + * @param payload the set of bytes to send to the MQTT server + * @throws MqttException + */ + public void publish(String topicName, int qos, byte[] payload) throws MqttException { + + // Connect to the MQTT server + // issue a non-blocking connect and then use the token to wait until the + // connect completes. An exception is thrown if connect fails. + log("Connecting to "+brokerUrl + " with client ID "+client.getClientId()); + IMqttToken conToken = client.connect(conOpt,null,null); + conToken.waitForCompletion(); + log("Connected"); + + String time = new Timestamp(System.currentTimeMillis()).toString(); + log("Publishing at: "+time+ " to topic \""+topicName+"\" qos "+qos); + + // Construct the message to send + MqttMessage message = new MqttMessage(payload); + message.setQos(qos); + + // Send the message to the server, control is returned as soon + // as the MQTT client has accepted to deliver the message. + // Use the delivery token to wait until the message has been + // delivered + IMqttDeliveryToken pubToken = client.publish(topicName, message, null, null); + pubToken.waitForCompletion(); + log("Published"); + + // Disconnect the client + // Issue the disconnect and then use a token to wait until + // the disconnect completes. + log("Disconnecting"); + IMqttToken discToken = client.disconnect(null, null); + discToken.waitForCompletion(); + log("Disconnected"); + } + + /** + * Subscribe to a topic on an MQTT server + * Once subscribed this method waits for the messages to arrive from the server + * that match the subscription. It continues listening for messages until the enter key is + * pressed. + * @param topicName to subscribe to (can be wild carded) + * @param qos the maximum quality of service to receive messages at for this subscription + * @throws MqttException + */ + public void subscribe(String topicName, int qos) throws MqttException { + + // Connect to the MQTT server + // issue a non-blocking connect and then use the token to wait until the + // connect completes. An exception is thrown if connect fails. + log("Connecting to "+brokerUrl + " with client ID "+client.getClientId()); + IMqttToken conToken = client.connect(conOpt,null, null); + conToken.waitForCompletion(); + log("Connected"); + + // Subscribe to the requested topic. + // Control is returned as soon client has accepted to deliver the subscription. + // Use a token to wait until the subscription is in place. + log("Subscribing to topic \""+topicName+"\" qos "+qos); + + IMqttToken subToken = client.subscribe(topicName, qos, null, null); + subToken.waitForCompletion(); + log("Subscribed to topic \""+topicName); + + // Continue waiting for messages until the Enter is pressed + log("Press to exit"); + try { + System.in.read(); + } catch (IOException e) { + //If we can't read we'll just exit + } + + // Disconnect the client + // Issue the disconnect and then use the token to wait until + // the disconnect completes. + log("Disconnecting"); + IMqttToken discToken = client.disconnect(null, null); + discToken.waitForCompletion(); + log("Disconnected"); + } + + /** + * Utility method to handle logging. If 'quietMode' is set, this method does nothing + * @param message the message to log + */ + private void log(String message) { + if (!quietMode) { + System.out.println(message); + } + } + + /****************************************************************/ + /* Methods to implement the MqttCallback interface */ + /****************************************************************/ + + /** + * @see MqttCallback#connectionLost(Throwable) + */ + public void connectionLost(Throwable cause) { + // Called when the connection to the server has been lost. + // An application may choose to implement reconnection + // logic at this point. This sample simply exits. + log("Connection to " + brokerUrl + " lost!" + cause); + System.exit(1); + } + + /** + * @see MqttCallback#deliveryComplete(IMqttDeliveryToken) + */ + public void deliveryComplete(IMqttDeliveryToken token) { + // Called when a message has been delivered to the + // server. The token passed in here is the same one + // that was passed to or returned from the original call to publish. + // This allows applications to perform asynchronous + // delivery without blocking until delivery completes. + // + // This sample demonstrates asynchronous deliver and + // uses the token.waitForCompletion() call in the main thread which + // blocks until the delivery has completed. + // Additionally the deliveryComplete method will be called if + // the callback is set on the client + // + // If the connection to the server breaks before delivery has completed + // delivery of a message will complete after the client has re-connected. + // The getPendinTokens method will provide tokens for any messages + // that are still to be delivered. + try { + log("Delivery complete callback: Publish Completed "+token.getMessage()); + } catch (Exception ex) { + log("Exception in delivery complete callback"+ex); + } + } + + /** + * @see MqttCallback#messageArrived(String, MqttMessage) + */ + public void messageArrived(String topic, MqttMessage message) throws MqttException { + // Called when a message arrives from the server that matches any + // subscription made by the client + String time = new Timestamp(System.currentTimeMillis()).toString(); + System.out.println("Time:\t" +time + + " Topic:\t" + topic + + " Message:\t" + new String(message.getPayload()) + + " QoS:\t" + message.getQos()); + } + + /****************************************************************/ + /* End of MqttCallback methods */ + /****************************************************************/ + + static void printHelp() { + System.out.println( + "Syntax:\n\n" + + " Sample [-h] [-a publish|subscribe] [-t ] [-m ]\n" + + " [-s 0|1|2] -b ] [-p ] [-i ]\n\n" + + " -h Print this help text and quit\n" + + " -q Quiet mode (default is false)\n" + + " -a Perform the relevant action (default is publish)\n" + + " -t Publish/subscribe to instead of the default\n" + + " (publish: \"Sample/Java/v3\", subscribe: \"Sample/#\")\n" + + " -m Use instead of the default\n" + + " (\"Message from MQTTv3 Java client\")\n" + + " -s Use this QoS instead of the default (2)\n" + + " -b Use this name/IP address instead of the default (localhost)\n" + + " -p Use this port instead of the default (1883)\n\n" + + " -i Use this client ID instead of SampleJavaV3_\n" + + " -c Connect to the server with a clean session (default is false)\n" + + " \n\n Security Options \n" + + " -u Username \n" + + " -z Password \n" + + " \n\n SSL Options \n" + + " -v SSL enabled; true - (default is false) " + + " -k Use this JKS format key store to verify the client\n" + + " -w Passpharse to verify certificates in the keys store\n" + + " -r Use this JKS format keystore to verify the server\n" + + " If javax.net.ssl properties have been set only the -v flag needs to be set\n" + + "Delimit strings containing spaces with \"\"\n\n" + + "Publishers transmit a single message then disconnect from the server.\n" + + "Subscribers remain connected to the server and receive appropriate\n" + + "messages until is pressed.\n\n" + ); + } + +} \ No newline at end of file diff --git a/org.eclipse.paho.sample.mqttv3app/src/main/java/org/eclipse/paho/sample/mqttv3app/package.html b/org.eclipse.paho.sample.mqttv3app/src/main/java/org/eclipse/paho/sample/mqttv3app/package.html new file mode 100644 index 0000000..7294082 --- /dev/null +++ b/org.eclipse.paho.sample.mqttv3app/src/main/java/org/eclipse/paho/sample/mqttv3app/package.html @@ -0,0 +1,3 @@ + +Contains a set of sample applications that show how to use the MQTT programming interface. + diff --git a/org.eclipse.paho.sample.mqttv3app/src/org/eclipse/paho/sample/mqttv3app/Sample.java b/org.eclipse.paho.sample.mqttv3app/src/org/eclipse/paho/sample/mqttv3app/Sample.java deleted file mode 100644 index 229e76e..0000000 --- a/org.eclipse.paho.sample.mqttv3app/src/org/eclipse/paho/sample/mqttv3app/Sample.java +++ /dev/null @@ -1,392 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ - -package org.eclipse.paho.sample.mqttv3app; - -import java.io.IOException; -import java.sql.Timestamp; - -import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; -import org.eclipse.paho.client.mqttv3.MqttCallback; -import org.eclipse.paho.client.mqttv3.MqttClient; -import org.eclipse.paho.client.mqttv3.MqttConnectOptions; -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.MqttMessage; -import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence; - -/** - * A sample application that demonstrates how to use the MQTT v3 Client blocking api. - * - * It can be run from the command line in one of two modes: - * - as a publisher, sending a single message to a topic on the server - * - as a subscriber, listening for messages from the server - * - * There are three versions of the sample that implement the same features - * but do so using using different programming styles: - *
                            - *
                          1. Sample (this one) which uses the API which blocks until the operation completes
                          2. - *
                          3. SampleAsyncWait shows how to use the asynchronous API with waiters that block until - * an action completes
                          4. - *
                          5. SampleAsyncCallBack shows how to use the asynchronous API where events are - * used to notify the application when an action completes
                          6. - *
                          - * - * If the application is run with the -h parameter then info is displayed that - * describes all of the options / parameters. - */ -public class Sample implements MqttCallback { - - /** - * The main entry point of the sample. - * - * This method handles parsing of the arguments specified on the - * command-line before performing the specified action. - */ - public static void main(String[] args) { - - // Default settings: - boolean quietMode = false; - String action = "publish"; - String topic = ""; - String message = "Message from blocking MQTTv3 Java client sample"; - int qos = 2; - String broker = "m2m.eclipse.org"; - int port = 1883; - String clientId = null; - String subTopic = "Sample/#"; - String pubTopic = "Sample/Java/v3"; - boolean cleanSession = true; // Non durable subscriptions - boolean ssl = false; - String password = null; - String userName = null; - // Parse the arguments - - for (int i=0; i 2) { - System.out.println("Invalid QoS: "+qos); - printHelp(); - return; - } - if (topic.equals("")) { - // Set the default topic according to the specified action - if (action.equals("publish")) { - topic = pubTopic; - } else { - topic = subTopic; - } - } - - String protocol = "tcp://"; - - if (ssl) { - protocol = "ssl://"; - } - - String url = protocol + broker + ":" + port; - - if (clientId == null || clientId.equals("")) { - clientId = "SampleJavaV3_"+action; - } - - // With a valid set of arguments, the real work of - // driving the client API can begin - try { - // Create an instance of this class - Sample sampleClient = new Sample(url, clientId, cleanSession, quietMode,userName,password); - - // Perform the requested action - if (action.equals("publish")) { - sampleClient.publish(topic,qos,message.getBytes()); - } else if (action.equals("subscribe")) { - sampleClient.subscribe(topic,qos); - } - } catch(MqttException me) { - // Display full details of any exception that occurs - System.out.println("reason "+me.getReasonCode()); - System.out.println("msg "+me.getMessage()); - System.out.println("loc "+me.getLocalizedMessage()); - System.out.println("cause "+me.getCause()); - System.out.println("excep "+me); - me.printStackTrace(); - } - } - - // Private instance variables - private MqttClient client; - private String brokerUrl; - private boolean quietMode; - private MqttConnectOptions conOpt; - private boolean clean; - private String password; - private String userName; - - /** - * Constructs an instance of the sample client wrapper - * @param brokerUrl the url of the server to connect to - * @param clientId the client id to connect with - * @param cleanSession clear state at end of connection or not (durable or non-durable subscriptions) - * @param quietMode whether debug should be printed to standard out - * @param userName the username to connect with - * @param password the password for the user - * @throws MqttException - */ - public Sample(String brokerUrl, String clientId, boolean cleanSession, boolean quietMode, String userName, String password) throws MqttException { - this.brokerUrl = brokerUrl; - this.quietMode = quietMode; - this.clean = cleanSession; - this.password = password; - this.userName = userName; - //This sample stores in a temporary directory... where messages temporarily - // stored until the message has been delivered to the server. - //..a real application ought to store them somewhere - // where they are not likely to get deleted or tampered with - String tmpDir = System.getProperty("java.io.tmpdir"); - MqttDefaultFilePersistence dataStore = new MqttDefaultFilePersistence(tmpDir); - - try { - // Construct the connection options object that contains connection parameters - // such as cleansession and LWAT - conOpt = new MqttConnectOptions(); - conOpt.setCleanSession(clean); - if(password != null ) { - conOpt.setPassword(this.password.toCharArray()); - } - if(userName != null) { - conOpt.setUserName(this.userName); - } - - // Construct an MQTT blocking mode client - client = new MqttClient(this.brokerUrl,clientId, dataStore); - - // Set this wrapper as the callback handler - client.setCallback(this); - - } catch (MqttException e) { - e.printStackTrace(); - log("Unable to set up client: "+e.toString()); - System.exit(1); - } - } - - /** - * Publish / send a message to an MQTT server - * @param topicName the name of the topic to publish to - * @param qos the quality of service to delivery the message at (0,1,2) - * @param payload the set of bytes to send to the MQTT server - * @throws MqttException - */ - public void publish(String topicName, int qos, byte[] payload) throws MqttException { - - // Connect to the MQTT server - log("Connecting to "+brokerUrl + " with client ID "+client.getClientId()); - client.connect(conOpt); - log("Connected"); - - String time = new Timestamp(System.currentTimeMillis()).toString(); - log("Publishing at: "+time+ " to topic \""+topicName+"\" qos "+qos); - - // Create and configure a message - MqttMessage message = new MqttMessage(payload); - message.setQos(qos); - - // Send the message to the server, control is not returned until - // it has been delivered to the server meeting the specified - // quality of service. - client.publish(topicName, message); - - // Disconnect the client - client.disconnect(); - log("Disconnected"); - } - - /** - * Subscribe to a topic on an MQTT server - * Once subscribed this method waits for the messages to arrive from the server - * that match the subscription. It continues listening for messages until the enter key is - * pressed. - * @param topicName to subscribe to (can be wild carded) - * @param qos the maximum quality of service to receive messages at for this subscription - * @throws MqttException - */ - public void subscribe(String topicName, int qos) throws MqttException { - - // Connect to the MQTT server - client.connect(conOpt); - log("Connected to "+brokerUrl+" with client ID "+client.getClientId()); - - // Subscribe to the requested topic - // The QOS specified is the maximum level that messages will be sent to the client at. - // For instance if QOS 1 is specified, any messages originally published at QOS 2 will - // be downgraded to 1 when delivering to the client but messages published at 1 and 0 - // will be received at the same level they were published at. - log("Subscribing to topic \""+topicName+"\" qos "+qos); - client.subscribe(topicName, qos); - - // Continue waiting for messages until the Enter is pressed - log("Press to exit"); - try { - System.in.read(); - } catch (IOException e) { - //If we can't read we'll just exit - } - - // Disconnect the client from the server - client.disconnect(); - log("Disconnected"); - } - - /** - * Utility method to handle logging. If 'quietMode' is set, this method does nothing - * @param message the message to log - */ - private void log(String message) { - if (!quietMode) { - System.out.println(message); - } - } - - /****************************************************************/ - /* Methods to implement the MqttCallback interface */ - /****************************************************************/ - - /** - * @see MqttCallback#connectionLost(Throwable) - */ - public void connectionLost(Throwable cause) { - // Called when the connection to the server has been lost. - // An application may choose to implement reconnection - // logic at this point. This sample simply exits. - log("Connection to " + brokerUrl + " lost!" + cause); - System.exit(1); - } - - /** - * @see MqttCallback#deliveryComplete(IMqttDeliveryToken) - */ - public void deliveryComplete(IMqttDeliveryToken token) { - // Called when a message has been delivered to the - // server. The token passed in here is the same one - // that was passed to or returned from the original call to publish. - // This allows applications to perform asynchronous - // delivery without blocking until delivery completes. - // - // This sample demonstrates asynchronous deliver and - // uses the token.waitForCompletion() call in the main thread which - // blocks until the delivery has completed. - // Additionally the deliveryComplete method will be called if - // the callback is set on the client - // - // If the connection to the server breaks before delivery has completed - // delivery of a message will complete after the client has re-connected. - // The getPendinTokens method will provide tokens for any messages - // that are still to be delivered. - } - - /** - * @see MqttCallback#messageArrived(String, MqttMessage) - */ - public void messageArrived(String topic, MqttMessage message) throws MqttException { - // Called when a message arrives from the server that matches any - // subscription made by the client - String time = new Timestamp(System.currentTimeMillis()).toString(); - System.out.println("Time:\t" +time + - " Topic:\t" + topic + - " Message:\t" + new String(message.getPayload()) + - " QoS:\t" + message.getQos()); - } - - /****************************************************************/ - /* End of MqttCallback methods */ - /****************************************************************/ - - static void printHelp() { - System.out.println( - "Syntax:\n\n" + - " Sample [-h] [-a publish|subscribe] [-t ] [-m ]\n" + - " [-s 0|1|2] -b ] [-p ] [-i ]\n\n" + - " -h Print this help text and quit\n" + - " -q Quiet mode (default is false)\n" + - " -a Perform the relevant action (default is publish)\n" + - " -t Publish/subscribe to instead of the default\n" + - " (publish: \"Sample/Java/v3\", subscribe: \"Sample/#\")\n" + - " -m Use instead of the default\n" + - " (\"Message from MQTTv3 Java client\")\n" + - " -s Use this QoS instead of the default (2)\n" + - " -b Use this name/IP address instead of the default (localhost)\n" + - " -p Use this port instead of the default (1883)\n\n" + - " -i Use this client ID instead of SampleJavaV3_\n" + - " -c Connect to the server with a clean session (default is false)\n" + - " \n\n Security Options \n" + - " -u Username \n" + - " -z Password \n" + - " \n\n SSL Options \n" + - " -v SSL enabled; true - (default is false) " + - " -k Use this JKS format key store to verify the client\n" + - " -w Passpharse to verify certificates in the keys store\n" + - " -r Use this JKS format keystore to verify the server\n" + - " If javax.net.ssl properties have been set only the -v flag needs to be set\n" + - "Delimit strings containing spaces with \"\"\n\n" + - "Publishers transmit a single message then disconnect from the server.\n" + - "Subscribers remain connected to the server and receive appropriate\n" + - "messages until is pressed.\n\n" - ); - } - -} \ No newline at end of file diff --git a/org.eclipse.paho.sample.mqttv3app/src/org/eclipse/paho/sample/mqttv3app/SampleAsyncCallBack.java b/org.eclipse.paho.sample.mqttv3app/src/org/eclipse/paho/sample/mqttv3app/SampleAsyncCallBack.java deleted file mode 100644 index 3b55700..0000000 --- a/org.eclipse.paho.sample.mqttv3app/src/org/eclipse/paho/sample/mqttv3app/SampleAsyncCallBack.java +++ /dev/null @@ -1,648 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ - -package org.eclipse.paho.sample.mqttv3app; - -import java.io.IOException; -import java.sql.Timestamp; - -import org.eclipse.paho.client.mqttv3.IMqttActionListener; -import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; -import org.eclipse.paho.client.mqttv3.IMqttToken; -import org.eclipse.paho.client.mqttv3.MqttAsyncClient; -import org.eclipse.paho.client.mqttv3.MqttCallback; -import org.eclipse.paho.client.mqttv3.MqttConnectOptions; -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.MqttMessage; -import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence; - -/** - * A sample application that demonstrates how to use the MQTT v3 Client api in - * non-blocking callback/notification mode. - * - * It can be run from the command line in one of two modes: - * - as a publisher, sending a single message to a topic on the server - * - as a subscriber, listening for messages from the server - * - * There are three versions of the sample that implement the same features - * but do so using using different programming styles: - *
                            - *
                          1. Sample (this one) which uses the API which blocks until the operation completes
                          2. - *
                          3. SampleAsyncWait shows how to use the asynchronous API with waiters that block until - * an action completes
                          4. - *
                          5. SampleAsyncCallBack shows how to use the asynchronous API where events are - * used to notify the application when an action completes
                          6. - *
                          - * - * If the application is run with the -h parameter then info is displayed that - * describes all of the options / parameters. - */ -public class SampleAsyncCallBack implements MqttCallback { - - int state = BEGIN; - - static final int BEGIN = 0; - static final int CONNECTED = 1; - static final int PUBLISHED = 2; - static final int SUBSCRIBED = 3; - static final int DISCONNECTED = 4; - static final int FINISH = 5; - static final int ERROR = 6; - static final int DISCONNECT = 7; - - /** - * The main entry point of the sample. - * - * This method handles parsing the arguments specified on the - * command-line before performing the specified action. - */ - public static void main(String[] args) { - - // Default settings: - boolean quietMode = false; - String action = "publish"; - String topic = ""; - String message = "Message from async calback MQTTv3 Java client sample"; - int qos = 2; - String broker = "m2m.eclipse.org"; - int port = 1883; - String clientId = null; - String subTopic = "Sample/#"; - String pubTopic = "Sample/Java/v3"; - boolean cleanSession = true; // Non durable subscriptions - boolean ssl = false; - String password = null; - String userName = null; - - // Parse the arguments - - for (int i=0; i 2) { - System.out.println("Invalid QoS: "+qos); - printHelp(); - return; - } - if (topic.equals("")) { - // Set the default topic according to the specified action - if (action.equals("publish")) { - topic = pubTopic; - } else { - topic = subTopic; - } - } - - String protocol = "tcp://"; - - if (ssl) { - protocol = "ssl://"; - } - - String url = protocol + broker + ":" + port; - - if (clientId == null || clientId.equals("")) { - clientId = "SampleJavaV3_"+action; - } - - // With a valid set of arguments, the real work of - // driving the client API can begin - try { - // Create an instance of the Sample client wrapper - SampleAsyncCallBack sampleClient = new SampleAsyncCallBack(url,clientId,cleanSession, quietMode,userName,password); - - // Perform the specified action - if (action.equals("publish")) { - sampleClient.publish(topic,qos,message.getBytes()); - } else if (action.equals("subscribe")) { - sampleClient.subscribe(topic,qos); - } - } catch(MqttException me) { - // Display full details of any exception that occurs - System.out.println("reason "+me.getReasonCode()); - System.out.println("msg "+me.getMessage()); - System.out.println("loc "+me.getLocalizedMessage()); - System.out.println("cause "+me.getCause()); - System.out.println("excep "+me); - me.printStackTrace(); - } catch (Throwable th) { - System.out.println("Throwable caught "+th); - th.printStackTrace(); - } - } - - // Private instance variables - MqttAsyncClient client; - String brokerUrl; - private boolean quietMode; - private MqttConnectOptions conOpt; - private boolean clean; - Throwable ex = null; - Object waiter = new Object(); - boolean donext = false; - private String password; - private String userName; - - /** - * Constructs an instance of the sample client wrapper - * @param brokerUrl the url to connect to - * @param clientId the client id to connect with - * @param cleanSession clear state at end of connection or not (durable or non-durable subscriptions) - * @param quietMode whether debug should be printed to standard out - * @param userName the username to connect with - * @param password the password for the user - * @throws MqttException - */ - public SampleAsyncCallBack(String brokerUrl, String clientId, boolean cleanSession, - boolean quietMode, String userName, String password) throws MqttException { - this.brokerUrl = brokerUrl; - this.quietMode = quietMode; - this.clean = cleanSession; - this.password = password; - this.userName = userName; - //This sample stores in a temporary directory... where messages temporarily - // stored until the message has been delivered to the server. - //..a real application ought to store them somewhere - // where they are not likely to get deleted or tampered with - String tmpDir = System.getProperty("java.io.tmpdir"); - MqttDefaultFilePersistence dataStore = new MqttDefaultFilePersistence(tmpDir); - - try { - // Construct the object that contains connection parameters - // such as cleansession and LWAT - conOpt = new MqttConnectOptions(); - conOpt.setCleanSession(clean); - if(password != null ) { - conOpt.setPassword(this.password.toCharArray()); - } - if(userName != null) { - conOpt.setUserName(this.userName); - } - - // Construct the MqttClient instance - client = new MqttAsyncClient(this.brokerUrl,clientId, dataStore); - - // Set this wrapper as the callback handler - client.setCallback(this); - - } catch (MqttException e) { - e.printStackTrace(); - log("Unable to set up client: "+e.toString()); - System.exit(1); - } - } - - /** - * Publish / send a message to an MQTT server - * @param topicName the name of the topic to publish to - * @param qos the quality of service to delivery the message at (0,1,2) - * @param payload the set of bytes to send to the MQTT server - * @throws MqttException - */ - public void publish(String topicName, int qos, byte[] payload) throws Throwable { - // Use a state machine to decide which step to do next. State change occurs - // when a notification is received that an MQTT action has completed - while (state != FINISH) { - switch (state) { - case BEGIN: - // Connect using a non blocking connect - MqttConnector con = new MqttConnector(); - con.doConnect(); - break; - case CONNECTED: - // Publish using a non blocking publisher - Publisher pub = new Publisher(); - pub.doPublish(topicName, qos, payload); - break; - case PUBLISHED: - state = DISCONNECT; - donext = true; - break; - case DISCONNECT: - Disconnector disc = new Disconnector(); - disc.doDisconnect(); - break; - case ERROR: - throw ex; - case DISCONNECTED: - state = FINISH; - donext = true; - break; - } - -// if (state != FINISH) { - // Wait until notified about a state change and then perform next action - waitForStateChange(10000); -// } - } - } - - /** - * Wait for a maximum amount of time for a state change event to occur - * @param maxTTW maximum time to wait in milliseconds - * @throws MqttException - */ - private void waitForStateChange(int maxTTW ) throws MqttException { - synchronized (waiter) { - if (!donext ) { - try { - waiter.wait(maxTTW); - } catch (InterruptedException e) { - log("timed out"); - e.printStackTrace(); - } - - if (ex != null) { - throw (MqttException)ex; - } - } - donext = false; - } - } - - /** - * Subscribe to a topic on an MQTT server - * Once subscribed this method waits for the messages to arrive from the server - * that match the subscription. It continues listening for messages until the enter key is - * pressed. - * @param topicName to subscribe to (can be wild carded) - * @param qos the maximum quality of service to receive messages at for this subscription - * @throws MqttException - */ - public void subscribe(String topicName, int qos) throws Throwable { - // Use a state machine to decide which step to do next. State change occurs - // when a notification is received that an MQTT action has completed - while (state != FINISH) { - switch (state) { - case BEGIN: - // Connect using a non blocking connect - MqttConnector con = new MqttConnector(); - con.doConnect(); - break; - case CONNECTED: - // Subscribe using a non blocking subscribe - Subscriber sub = new Subscriber(); - sub.doSubscribe(topicName, qos); - break; - case SUBSCRIBED: - // Block until Enter is pressed allowing messages to arrive - log("Press to exit"); - try { - System.in.read(); - } catch (IOException e) { - //If we can't read we'll just exit - } - state = DISCONNECT; - donext = true; - break; - case DISCONNECT: - Disconnector disc = new Disconnector(); - disc.doDisconnect(); - break; - case ERROR: - throw ex; - case DISCONNECTED: - state = FINISH; - donext = true; - break; - } - -// if (state != FINISH && state != DISCONNECT) { - waitForStateChange(10000); - } -// } - } - - /** - * Utility method to handle logging. If 'quietMode' is set, this method does nothing - * @param message the message to log - */ - void log(String message) { - if (!quietMode) { - System.out.println(message); - } - } - - /****************************************************************/ - /* Methods to implement the MqttCallback interface */ - /****************************************************************/ - - /** - * @see MqttCallback#connectionLost(Throwable) - */ - public void connectionLost(Throwable cause) { - // Called when the connection to the server has been lost. - // An application may choose to implement reconnection - // logic at this point. This sample simply exits. - log("Connection to " + brokerUrl + " lost!" + cause); - System.exit(1); - } - - /** - * @see MqttCallback#deliveryComplete(IMqttDeliveryToken) - */ - public void deliveryComplete(IMqttDeliveryToken token) { - // Called when a message has been delivered to the - // server. The token passed in here is the same one - // that was returned from the original call to publish. - // This allows applications to perform asynchronous - // delivery without blocking until delivery completes. - // - // This sample demonstrates asynchronous deliver, registering - // a callback to be notified on each call to publish. - // - // The deliveryComplete method will also be called if - // the callback is set on the client - // - log("Delivery complete callback: Publish Completed "+token.getTopics()); - } - - /** - * @see MqttCallback#messageArrived(String, MqttMessage) - */ - public void messageArrived(String topic, MqttMessage message) throws MqttException { - // Called when a message arrives from the server that matches any - // subscription made by the client - String time = new Timestamp(System.currentTimeMillis()).toString(); - System.out.println("Time:\t" +time + - " Topic:\t" + topic + - " Message:\t" + new String(message.getPayload()) + - " QoS:\t" + message.getQos()); - } - - /****************************************************************/ - /* End of MqttCallback methods */ - /****************************************************************/ - static void printHelp() { - System.out.println( - "Syntax:\n\n" + - " Sample [-h] [-a publish|subscribe] [-t ] [-m ]\n" + - " [-s 0|1|2] -b ] [-p ] [-i ]\n\n" + - " -h Print this help text and quit\n" + - " -q Quiet mode (default is false)\n" + - " -a Perform the relevant action (default is publish)\n" + - " -t Publish/subscribe to instead of the default\n" + - " (publish: \"Sample/Java/v3\", subscribe: \"Sample/#\")\n" + - " -m Use instead of the default\n" + - " (\"Message from MQTTv3 Java client\")\n" + - " -s Use this QoS instead of the default (2)\n" + - " -b Use this name/IP address instead of the default (localhost)\n" + - " -p Use this port instead of the default (1883)\n\n" + - " -i Use this client ID instead of SampleJavaV3_\n" + - " -c Connect to the server with a clean session (default is false)\n" + - " \n\n Security Options \n" + - " -u Username \n" + - " -z Password \n" + - " \n\n SSL Options \n" + - " -v SSL enabled; true - (default is false) " + - " -k Use this JKS format key store to verify the client\n" + - " -w Passpharse to verify certificates in the keys store\n" + - " -r Use this JKS format keystore to verify the server\n" + - " If javax.net.ssl properties have been set only the -v flag needs to be set\n" + - "Delimit strings containing spaces with \"\"\n\n" + - "Publishers transmit a single message then disconnect from the server.\n" + - "Subscribers remain connected to the server and receive appropriate\n" + - "messages until is pressed.\n\n" - ); - } - - /** - * Connect in a non blocking way and then sit back and wait to be - * notified that the action has completed. - */ - public class MqttConnector { - - public MqttConnector() { - } - - public void doConnect() { - // Connect to the server - // Get a token and setup an asynchronous listener on the token which - // will be notified once the connect completes - log("Connecting to "+brokerUrl + " with client ID "+client.getClientId()); - - IMqttActionListener conListener = new IMqttActionListener() { - public void onSuccess(IMqttToken asyncActionToken) { - log("Connected"); - state = CONNECTED; - carryOn(); - } - - public void onFailure(IMqttToken asyncActionToken, Throwable exception) { - ex = exception; - state = ERROR; - log ("connect failed" +exception); - carryOn(); - } - - public void carryOn() { - synchronized (waiter) { - donext=true; - waiter.notifyAll(); - } - } - }; - - try { - // Connect using a non blocking connect - client.connect(conOpt,"Connect sample context", conListener); - } catch (MqttException e) { - // If though it is a non blocking connect an exception can be - // thrown if validation of parms fails or other checks such - // as already connected fail. - state = ERROR; - donext = true; - ex = e; - } - } - } - - /** - * Publish in a non blocking way and then sit back and wait to be - * notified that the action has completed. - */ - public class Publisher { - public void doPublish(String topicName, int qos, byte[] payload) { - // Send / publish a message to the server - // Get a token and setup an asynchronous listener on the token which - // will be notified once the message has been delivered - MqttMessage message = new MqttMessage(payload); - message.setQos(qos); - - - String time = new Timestamp(System.currentTimeMillis()).toString(); - log("Publishing at: "+time+ " to topic \""+topicName+"\" qos "+qos); - - // Setup a listener object to be notified when the publish completes. - // - IMqttActionListener pubListener = new IMqttActionListener() { - public void onSuccess(IMqttToken asyncActionToken) { - log("Publish Completed"); - state = PUBLISHED; - carryOn(); - } - - public void onFailure(IMqttToken asyncActionToken, Throwable exception) { - ex = exception; - state = ERROR; - log ("Publish failed" +exception); - carryOn(); - } - - public void carryOn() { - synchronized (waiter) { - donext=true; - waiter.notifyAll(); - } - } - }; - - try { - // Publish the message - client.publish(topicName, message, "Pub sample context", pubListener); - } catch (MqttException e) { - state = ERROR; - donext = true; - ex = e; - } - } - } - - /** - * Subscribe in a non blocking way and then sit back and wait to be - * notified that the action has completed. - */ - public class Subscriber { - public void doSubscribe(String topicName, int qos) { - // Make a subscription - // Get a token and setup an asynchronous listener on the token which - // will be notified once the subscription is in place. - log("Subscribing to topic \""+topicName+"\" qos "+qos); - - IMqttActionListener subListener = new IMqttActionListener() { - public void onSuccess(IMqttToken asyncActionToken) { - log("Subscribe Completed"); - state = SUBSCRIBED; - carryOn(); - } - - public void onFailure(IMqttToken asyncActionToken, Throwable exception) { - ex = exception; - state = ERROR; - log ("Subscribe failed" +exception); - carryOn(); - } - - public void carryOn() { - synchronized (waiter) { - donext=true; - waiter.notifyAll(); - } - } - }; - - try { - client.subscribe(topicName, qos, "Subscribe sample context", subListener); - } catch (MqttException e) { - state = ERROR; - donext = true; - ex = e; - } - } - } - - /** - * Disconnect in a non blocking way and then sit back and wait to be - * notified that the action has completed. - */ - public class Disconnector { - public void doDisconnect() { - // Disconnect the client - log("Disconnecting"); - - IMqttActionListener discListener = new IMqttActionListener() { - public void onSuccess(IMqttToken asyncActionToken) { - log("Disconnect Completed"); - state = DISCONNECTED; - carryOn(); - } - - public void onFailure(IMqttToken asyncActionToken, Throwable exception) { - ex = exception; - state = ERROR; - log ("Disconnect failed" +exception); - carryOn(); - } - public void carryOn() { - synchronized (waiter) { - donext=true; - waiter.notifyAll(); - } - } - }; - - try { - client.disconnect("Disconnect sample context", discListener); - } catch (MqttException e) { - state = ERROR; - donext = true; - ex = e; - } - } - } -} \ No newline at end of file diff --git a/org.eclipse.paho.sample.mqttv3app/src/org/eclipse/paho/sample/mqttv3app/SampleAsyncWait.java b/org.eclipse.paho.sample.mqttv3app/src/org/eclipse/paho/sample/mqttv3app/SampleAsyncWait.java deleted file mode 100644 index 96da936..0000000 --- a/org.eclipse.paho.sample.mqttv3app/src/org/eclipse/paho/sample/mqttv3app/SampleAsyncWait.java +++ /dev/null @@ -1,421 +0,0 @@ -/* - * Copyright (c) 2009, 2012 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Dave Locke - initial API and implementation and/or initial documentation - */ - -package org.eclipse.paho.sample.mqttv3app; - -import java.io.IOException; -import java.sql.Timestamp; - -import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; -import org.eclipse.paho.client.mqttv3.IMqttToken; -import org.eclipse.paho.client.mqttv3.MqttAsyncClient; -import org.eclipse.paho.client.mqttv3.MqttCallback; -import org.eclipse.paho.client.mqttv3.MqttConnectOptions; -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.MqttMessage; -import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence; - -/** - * A sample application that demonstrates how to use the MQTT v3 Client api in - * non-blocking waiter mode. - * - * It can be run from the command line in one of two modes: - * - as a publisher, sending a single message to a topic on the server - * - as a subscriber, listening for messages from the server - * - * There are three versions of the sample that implement the same features - * but do so using using different programming styles: - *
                            - *
                          1. Sample (this one) which uses the API which blocks until the operation completes
                          2. - *
                          3. SampleAsyncWait shows how to use the asynchronous API with waiters that block until - * an action completes
                          4. - *
                          5. SampleAsyncCallBack shows how to use the asynchronous API where events are - * used to notify the application when an action completes
                          6. - *
                          - * - * If the application is run with the -h parameter then info is displayed that - * describes all of the options / parameters. - */ - -public class SampleAsyncWait implements MqttCallback { - - /** - * The main entry point of the sample. - * - * This method handles parsing the arguments specified on the - * command-line before performing the specified action. - */ - public static void main(String[] args) { - - // Default settings: - boolean quietMode = false; - String action = "publish"; - String topic = ""; - String message = "Message from async waiter MQTTv3 Java client sample"; - int qos = 2; - String broker = "m2m.eclipse.org"; - int port = 1883; - String clientId = null; - String subTopic = "Sample/#"; - String pubTopic = "Sample/Java/v3"; - boolean cleanSession = true; // Non durable subscriptions - boolean ssl = false; - String userName = null; - String password = null; - - // Parse the arguments - - for (int i=0; i 2) { - System.out.println("Invalid QoS: "+qos); - printHelp(); - return; - } - if (topic.equals("")) { - // Set the default topic according to the specified action - if (action.equals("publish")) { - topic = pubTopic; - } else { - topic = subTopic; - } - } - - String protocol = "tcp://"; - - if (ssl) { - protocol = "ssl://"; - } - - String url = protocol + broker + ":" + port; - - if (clientId == null || clientId.equals("")) { - clientId = "SampleJavaV3_"+action; - } - - // With a valid set of arguments, the real work of - // driving the client API can begin - try { - // Create an instance of this class - SampleAsyncWait sampleClient = new SampleAsyncWait(url,clientId,cleanSession, quietMode,userName,password); - - // Perform the specified action - if (action.equals("publish")) { - sampleClient.publish(topic,qos,message.getBytes()); - } else if (action.equals("subscribe")) { - sampleClient.subscribe(topic,qos); - } - } catch(MqttException me) { - // Display full details of any exception that occurs - System.out.println("reason "+me.getReasonCode()); - System.out.println("msg "+me.getMessage()); - System.out.println("loc "+me.getLocalizedMessage()); - System.out.println("cause "+me.getCause()); - System.out.println("excep "+me); - me.printStackTrace(); - } - } - - // Private instance variables - private MqttAsyncClient client; - private String brokerUrl; - private boolean quietMode; - private MqttConnectOptions conOpt; - private boolean clean; - private String password; - private String userName; - - /** - * Constructs an instance of the sample client wrapper - * @param brokerUrl the url to connect to - * @param clientId the client id to connect with - * @param cleanSession clear state at end of connection or not (durable or non-durable subscriptions) - * @param quietMode whether debug should be printed to standard out - * @param userName the username to connect with - * @param password the password for the user - * @throws MqttException - */ - public SampleAsyncWait(String brokerUrl, String clientId, boolean cleanSession, - boolean quietMode, String userName, String password) throws MqttException { - this.brokerUrl = brokerUrl; - this.quietMode = quietMode; - this.clean = cleanSession; - this.userName = userName; - this.password = password; - //This sample stores in a temporary directory... where messages temporarily - // stored until the message has been delivered to the server. - //..a real application ought to store them somewhere - // where they are not likely to get deleted or tampered with - String tmpDir = System.getProperty("java.io.tmpdir"); - MqttDefaultFilePersistence dataStore = new MqttDefaultFilePersistence(tmpDir); - - try { - // Construct the connection options object that contains connection parameters - // such as cleansession and LWAT - conOpt = new MqttConnectOptions(); - conOpt.setCleanSession(clean); - if(password != null ) { - conOpt.setPassword(this.password.toCharArray()); - } - if(userName != null) { - conOpt.setUserName(this.userName); - } - - // Construct a non blocking MQTT client instance - client = new MqttAsyncClient(this.brokerUrl,clientId, dataStore); - - // Set this wrapper as the callback handler - client.setCallback(this); - - } catch (MqttException e) { - e.printStackTrace(); - log("Unable to set up client: "+e.toString()); - System.exit(1); - } - } - - /** - * Publish / send a message to an MQTT server - * @param topicName the name of the topic to publish to - * @param qos the quality of service to delivery the message at (0,1,2) - * @param payload the set of bytes to send to the MQTT server - * @throws MqttException - */ - public void publish(String topicName, int qos, byte[] payload) throws MqttException { - - // Connect to the MQTT server - // issue a non-blocking connect and then use the token to wait until the - // connect completes. An exception is thrown if connect fails. - log("Connecting to "+brokerUrl + " with client ID "+client.getClientId()); - IMqttToken conToken = client.connect(conOpt,null,null); - conToken.waitForCompletion(); - log("Connected"); - - String time = new Timestamp(System.currentTimeMillis()).toString(); - log("Publishing at: "+time+ " to topic \""+topicName+"\" qos "+qos); - - // Construct the message to send - MqttMessage message = new MqttMessage(payload); - message.setQos(qos); - - // Send the message to the server, control is returned as soon - // as the MQTT client has accepted to deliver the message. - // Use the delivery token to wait until the message has been - // delivered - IMqttDeliveryToken pubToken = client.publish(topicName, message, null, null); - pubToken.waitForCompletion(); - log("Published"); - - // Disconnect the client - // Issue the disconnect and then use a token to wait until - // the disconnect completes. - log("Disconnecting"); - IMqttToken discToken = client.disconnect(null, null); - discToken.waitForCompletion(); - log("Disconnected"); - } - - /** - * Subscribe to a topic on an MQTT server - * Once subscribed this method waits for the messages to arrive from the server - * that match the subscription. It continues listening for messages until the enter key is - * pressed. - * @param topicName to subscribe to (can be wild carded) - * @param qos the maximum quality of service to receive messages at for this subscription - * @throws MqttException - */ - public void subscribe(String topicName, int qos) throws MqttException { - - // Connect to the MQTT server - // issue a non-blocking connect and then use the token to wait until the - // connect completes. An exception is thrown if connect fails. - log("Connecting to "+brokerUrl + " with client ID "+client.getClientId()); - IMqttToken conToken = client.connect(conOpt,null, null); - conToken.waitForCompletion(); - log("Connected"); - - // Subscribe to the requested topic. - // Control is returned as soon client has accepted to deliver the subscription. - // Use a token to wait until the subscription is in place. - log("Subscribing to topic \""+topicName+"\" qos "+qos); - - IMqttToken subToken = client.subscribe(topicName, qos, null, null); - subToken.waitForCompletion(); - log("Subscribed to topic \""+topicName); - - // Continue waiting for messages until the Enter is pressed - log("Press to exit"); - try { - System.in.read(); - } catch (IOException e) { - //If we can't read we'll just exit - } - - // Disconnect the client - // Issue the disconnect and then use the token to wait until - // the disconnect completes. - log("Disconnecting"); - IMqttToken discToken = client.disconnect(null, null); - discToken.waitForCompletion(); - log("Disconnected"); - } - - /** - * Utility method to handle logging. If 'quietMode' is set, this method does nothing - * @param message the message to log - */ - private void log(String message) { - if (!quietMode) { - System.out.println(message); - } - } - - /****************************************************************/ - /* Methods to implement the MqttCallback interface */ - /****************************************************************/ - - /** - * @see MqttCallback#connectionLost(Throwable) - */ - public void connectionLost(Throwable cause) { - // Called when the connection to the server has been lost. - // An application may choose to implement reconnection - // logic at this point. This sample simply exits. - log("Connection to " + brokerUrl + " lost!" + cause); - System.exit(1); - } - - /** - * @see MqttCallback#deliveryComplete(IMqttDeliveryToken) - */ - public void deliveryComplete(IMqttDeliveryToken token) { - // Called when a message has been delivered to the - // server. The token passed in here is the same one - // that was passed to or returned from the original call to publish. - // This allows applications to perform asynchronous - // delivery without blocking until delivery completes. - // - // This sample demonstrates asynchronous deliver and - // uses the token.waitForCompletion() call in the main thread which - // blocks until the delivery has completed. - // Additionally the deliveryComplete method will be called if - // the callback is set on the client - // - // If the connection to the server breaks before delivery has completed - // delivery of a message will complete after the client has re-connected. - // The getPendinTokens method will provide tokens for any messages - // that are still to be delivered. - try { - log("Delivery complete callback: Publish Completed "+token.getMessage()); - } catch (Exception ex) { - log("Exception in delivery complete callback"+ex); - } - } - - /** - * @see MqttCallback#messageArrived(String, MqttMessage) - */ - public void messageArrived(String topic, MqttMessage message) throws MqttException { - // Called when a message arrives from the server that matches any - // subscription made by the client - String time = new Timestamp(System.currentTimeMillis()).toString(); - System.out.println("Time:\t" +time + - " Topic:\t" + topic + - " Message:\t" + new String(message.getPayload()) + - " QoS:\t" + message.getQos()); - } - - /****************************************************************/ - /* End of MqttCallback methods */ - /****************************************************************/ - - static void printHelp() { - System.out.println( - "Syntax:\n\n" + - " Sample [-h] [-a publish|subscribe] [-t ] [-m ]\n" + - " [-s 0|1|2] -b ] [-p ] [-i ]\n\n" + - " -h Print this help text and quit\n" + - " -q Quiet mode (default is false)\n" + - " -a Perform the relevant action (default is publish)\n" + - " -t Publish/subscribe to instead of the default\n" + - " (publish: \"Sample/Java/v3\", subscribe: \"Sample/#\")\n" + - " -m Use instead of the default\n" + - " (\"Message from MQTTv3 Java client\")\n" + - " -s Use this QoS instead of the default (2)\n" + - " -b Use this name/IP address instead of the default (localhost)\n" + - " -p Use this port instead of the default (1883)\n\n" + - " -i Use this client ID instead of SampleJavaV3_\n" + - " -c Connect to the server with a clean session (default is false)\n" + - " \n\n Security Options \n" + - " -u Username \n" + - " -z Password \n" + - " \n\n SSL Options \n" + - " -v SSL enabled; true - (default is false) " + - " -k Use this JKS format key store to verify the client\n" + - " -w Passpharse to verify certificates in the keys store\n" + - " -r Use this JKS format keystore to verify the server\n" + - " If javax.net.ssl properties have been set only the -v flag needs to be set\n" + - "Delimit strings containing spaces with \"\"\n\n" + - "Publishers transmit a single message then disconnect from the server.\n" + - "Subscribers remain connected to the server and receive appropriate\n" + - "messages until is pressed.\n\n" - ); - } - -} \ No newline at end of file diff --git a/org.eclipse.paho.sample.mqttv3app/src/org/eclipse/paho/sample/mqttv3app/package.html b/org.eclipse.paho.sample.mqttv3app/src/org/eclipse/paho/sample/mqttv3app/package.html deleted file mode 100644 index 7294082..0000000 --- a/org.eclipse.paho.sample.mqttv3app/src/org/eclipse/paho/sample/mqttv3app/package.html +++ /dev/null @@ -1,3 +0,0 @@ - -Contains a set of sample applications that show how to use the MQTT programming interface. - diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..f3d26c8 --- /dev/null +++ b/pom.xml @@ -0,0 +1,99 @@ + + 4.0.0 + org.eclipse.paho + paho-parent + 0.9.0 + pom + Paho :: Administrative Parent + Administrative parent pom for Paho modules + ${paho.url} + 2011 + + + + bugzilla + https://bugs.eclipse.org/bugs/enter_bug.cgi?product=Paho + + + + Paho Developer Mailing List + http://dev.eclipse.org/mhonarc/lists/paho-dev + https://dev.eclipse.org/mailman/listinfo/paho-dev + https://dev.eclipse.org/mailman/listinfo/paho-dev + + + + + + Nick O'Leary + http://knolleary.net/ + IBM + http://www.ibm.com + + + Dave Locke + IBM + http://www.ibm.com + + + + + + Nicolas Deverge + ndeverge@ekito.fr + ekito + http://www.ekito.fr + + + + + + + Eclipse Public License - Version 1.0 + http://www.eclipse.org/org/documents/epl-v10.php + + + + + scm:git://git.eclipse.org/gitroot/paho/org.eclipse.paho.mqtt.java.git + scm:git://git.eclipse.org/gitroot/paho/org.eclipse.paho.mqtt.java.git + http://git.eclipse.org/c/paho/org.eclipse.paho.mqtt.java.git/tree/ + + + + UTF-8 + http://www.eclipse.org/paho + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + 1.2 + 1.2 + 1.5 + + + + + + + org.eclipse.paho.client.mqttv3 + org.eclipse.paho.client.mqttv3.internal.traceformat + org.eclipse.paho.sample.mqttv3app + + + + + + org.eclipse.paho + jpaho-mqtt-client + ${project.version} + + + + \ No newline at end of file