Download
Getting Started
Members
Projects
Community
Marketplace
Events
Planet Eclipse
Newsletter
Videos
Participate
Report a Bug
Forums
Mailing Lists
Wiki
IRC
How to Contribute
Working Groups
Automotive
Internet of Things
LocationTech
Long-Term Support
PolarSys
Science
OpenMDM
More
Community
Marketplace
Events
Planet Eclipse
Newsletter
Videos
Participate
Report a Bug
Forums
Mailing Lists
Wiki
IRC
How to Contribute
Working Groups
Automotive
Internet of Things
LocationTech
Long-Term Support
PolarSys
Science
OpenMDM
Toggle navigation
Bugzilla – Attachment 229674 Details for
Bug 382471
Paho Java client Mavenization
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
Log In
[x]
|
Terms of Use
|
Copyright Agent
[patch]
Patch to create Maven artifacts
mavenize.patch (text/plain), 1.11 MB, created by
Andy Piper
on 2013-04-12 10:19:53 EDT
(
hide
)
Description:
Patch to create Maven artifacts
Filename:
MIME Type:
Creator:
Andy Piper
Created:
2013-04-12 10:19:53 EDT
Size:
1.11 MB
patch
obsolete
>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 @@ >+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> >+ >+ <parent> >+ <groupId>org.eclipse.paho</groupId> >+ <artifactId>paho-parent</artifactId> >+ <version>0.9.0</version> >+ </parent> >+ >+ <modelVersion>4.0.0</modelVersion> >+ <artifactId>paho-mqtt-client-traceformatter</artifactId> >+ <version>0.9.0</version> >+ <packaging>jar</packaging> >+ <name>Paho :: MQTT Client trace formatter</name> >+ <description>A trace formatter for the MQTT Client library. It currently outputs HTML files.</description> >+ <url>${paho.url}</url> >+ >+ <dependencies> >+ <dependency> >+ <groupId>org.eclipse.paho</groupId> >+ <artifactId>paho-mqtt-client</artifactId> >+ <version>${project.version}</version> >+ <scope>compile</scope> >+ </dependency> >+ </dependencies> >+</project> >\ 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;i<args.length; i+=2) { >+ if (args[i].equals("-d")) { >+ dir = args[i+1]; >+ } else if (args[i].equals("-o")) { >+ file = args[i+1]; >+ } else { >+ System.out.println("Unknown arg: "+args[i]); >+ usageAndExit(); >+ } >+ } >+ >+ try { >+ LogMessageExtractor tpe = new LogMessageExtractor(dir, file); >+ tpe.parse(); >+ } catch (Exception e) { >+ e.printStackTrace(); >+ System.exit(1); >+ } >+ } >+ >+ private static void usageAndExit() { >+ System.out.println("usage:\n org.eclipse.paho.client.mqttv3.internal.trace.TracePointExtractor [-d baseDir] [-o outputFile]"); >+ System.out.println(" -d baseDir the source base directory [.]"); >+ System.out.println(" -o outputFile the output file. [./trace.properties]"); >+ System.exit(1); >+ } >+ >+ >+ private String basedir; >+ private String outputfile; >+ private Pattern pattern; >+ private PrintStream out; >+ private HashMap points; >+ >+ public LogMessageExtractor(String basedir, String outputfile) { >+ this.basedir = (new File(basedir)).getAbsolutePath(); >+ this.outputfile = outputfile; >+ this.pattern = Pattern.compile("^\\s*//\\s*@TRACE\\s*(\\d+)=(.*?)\\s*$"); >+ this.points = new HashMap(); >+ } >+ public void parse() throws Exception { >+ System.out.println("Scanning source directories: "+this.basedir); >+ System.out.println("Outputing results to: "+this.outputfile); >+ this.out = new PrintStream(new FileOutputStream(this.outputfile)); >+ out.println("0=MQTT Catalog"); >+ short rc = scanDirectory(new File(this.basedir)); >+ this.out.close(); >+ if (rc == 0 ) { >+ System.out.println("Finished"); >+ } else { >+ System.out.println("Problems found"); >+ throw new Exception(); >+ } >+ } >+ >+ public short scanDirectory(File f) throws Exception { >+ short rc = 0; >+ if (f.isFile() && f.getName().endsWith(".java")) { >+ short rc1 = parseFile(f); >+ if (rc1>0) { >+ rc = rc1;; >+ } >+ } else { >+ if (f.isDirectory()) { >+ File[] files = f.listFiles(); >+ for (int i=0;i<files.length;i++) { >+ short rc1 = scanDirectory(files[i]); >+ if (rc1>0) { >+ 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;i<args.length; i+=2) { >- if (args[i].equals("-d")) { >- dir = args[i+1]; >- } else if (args[i].equals("-o")) { >- file = args[i+1]; >- } else { >- System.out.println("Unknown arg: "+args[i]); >- usageAndExit(); >- } >- } >- >- try { >- LogMessageExtractor tpe = new LogMessageExtractor(dir, file); >- tpe.parse(); >- } catch (Exception e) { >- e.printStackTrace(); >- System.exit(1); >- } >- } >- >- private static void usageAndExit() { >- System.out.println("usage:\n org.eclipse.paho.client.mqttv3.internal.trace.TracePointExtractor [-d baseDir] [-o outputFile]"); >- System.out.println(" -d baseDir the source base directory [.]"); >- System.out.println(" -o outputFile the output file. [./trace.properties]"); >- System.exit(1); >- } >- >- >- private String basedir; >- private String outputfile; >- private Pattern pattern; >- private PrintStream out; >- private HashMap points; >- >- public LogMessageExtractor(String basedir, String outputfile) { >- this.basedir = (new File(basedir)).getAbsolutePath(); >- this.outputfile = outputfile; >- this.pattern = Pattern.compile("^\\s*//\\s*@TRACE\\s*(\\d+)=(.*?)\\s*$"); >- this.points = new HashMap(); >- } >- public void parse() throws Exception { >- System.out.println("Scanning source directories: "+this.basedir); >- System.out.println("Outputing results to: "+this.outputfile); >- this.out = new PrintStream(new FileOutputStream(this.outputfile)); >- out.println("0=MQTT Catalog"); >- short rc = scanDirectory(new File(this.basedir)); >- this.out.close(); >- if (rc == 0 ) { >- System.out.println("Finished"); >- } else { >- System.out.println("Problems found"); >- throw new Exception(); >- } >- } >- >- public short scanDirectory(File f) throws Exception { >- short rc = 0; >- if (f.isFile() && f.getName().endsWith(".java")) { >- short rc1 = parseFile(f); >- if (rc1>0) { >- rc = rc1;; >- } >- } else { >- if (f.isDirectory()) { >- File[] files = f.listFiles(); >- for (int i=0;i<files.length;i++) { >- short rc1 = scanDirectory(files[i]); >- if (rc1>0) { >- 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 @@ >+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> >+ >+ <parent> >+ <groupId>org.eclipse.paho</groupId> >+ <artifactId>paho-parent</artifactId> >+ <version>0.9.0</version> >+ </parent> >+ >+ <modelVersion>4.0.0</modelVersion> >+ <artifactId>paho-mqtt-client</artifactId> >+ <version>0.9.0</version> >+ <packaging>jar</packaging> >+ <name>Paho :: MQTT Client library</name> >+ <description>MQTT Client library</description> >+ <url>${paho.url}</url> >+ >+ <properties> >+ <build.level>${maven.build.timestamp}</build.level> >+ <maven.build.timestamp.format>yyMMdd</maven.build.timestamp.format> >+ </properties> >+ >+ <build> >+ <resources> >+ <resource> >+ <directory>src/main/java</directory> >+ <filtering>true</filtering> >+ </resource> >+ </resources> >+ <plugins> >+ <plugin> >+ <groupId>org.apache.felix</groupId> >+ <artifactId>maven-bundle-plugin</artifactId> >+ <version>2.3.7</version> >+ <extensions>true</extensions> >+ <executions> >+ <execution> >+ <id>generate-manifest</id> >+ <goals> >+ <goal>manifest</goal> >+ </goals> >+ <configuration> >+ <instructions> >+ <Build-Level>L${build.level}</Build-Level> >+ <Bundle-ClassPath>.</Bundle-ClassPath> >+ <Bundle-SymbolicName>$(maven-symbolicname);singleton:=true</Bundle-SymbolicName> >+ <Import-Package>javax.net;resolution:=optional, javax.net.ssl;resolution:=optional</Import-Package> >+ </instructions> >+ </configuration> >+ </execution> >+ </executions> >+ </plugin> >+ <plugin> >+ <groupId>org.apache.maven.plugins</groupId> >+ <artifactId>maven-jar-plugin</artifactId> >+ <version>2.3.2</version> >+ <executions> >+ <execution> >+ <id>artifact-jar</id> >+ <goals> >+ <goal>jar</goal> >+ </goals> >+ </execution> >+ <execution> >+ <id>test-jar</id> >+ <goals> >+ <goal>test-jar</goal> >+ </goals> >+ </execution> >+ </executions> >+ <configuration> >+ <archive> >+ <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile> >+ </archive> >+ </configuration> >+ </plugin> >+ </plugins> >+ </build> >+ >+</project> >\ 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. >+ * >+ * <p>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. >+ * </p> >+ */ >+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. >+ * <p> >+ * It provides applications a simple programming interface to all features of the MQTT version 3.1 >+ * specification including: >+ * <ul> >+ * <li>connect >+ * <li>publish >+ * <li>subscribe >+ * <li>unsubscribe >+ * <li>disconnect >+ * </ul> >+ * </p> >+ * <p> >+ * There are two styles of MQTT client, this one and {@link IMqttClient}. >+ * <ul> >+ * <li>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.</li> >+ * <li>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. </li> >+ * </ul> >+ * </p> >+ * <p> >+ * 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}</p> >+ * >+ * <p>There are two forms of non-blocking method: >+ * <ol> >+ * <li> >+ * <code><pre> >+ * IMqttToken token = asyncClient.method(parms) >+ * </pre></code> >+ * <p>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: >+ * <code><pre> >+ * IMqttToken conToken; >+ * conToken = asyncClient.client.connect(conToken); >+ * ... do some work... >+ * conToken.waitForCompletion(); >+ * </pre></code> >+ * /p> >+ * <p>To turn a method into a blocking invocation the following form can be used: >+ * <code><pre> >+ * IMqttToken token; >+ * token = asyncClient.method(parms).waitForCompletion(); >+ * </pre></code> >+ >+ * </li> >+ * >+ * <li> >+ * <code><pre> >+ * IMqttToken token method(parms, Object userContext, IMqttActionListener callback) >+ * </pre></code> >+ * <p>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: >+ * <code><pre> >+ * 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. >+ * </pre></code> >+ * </p> >+ * </li> >+ * </ol> >+ * <p>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.</p> >+ * >+ */ >+public interface IMqttAsyncClient { >+ /** >+ * Connects to an MQTT server using the default options. >+ * <p>The default options are specified in {@link MqttConnectOptions} class. >+ * </p> >+ * >+ * @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. >+ * <p>The connection will be established using the options specified in the >+ * {@link MqttConnectOptions} parameter. >+ * </p> >+ * >+ * @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. >+ * <p>The default options are specified in {@link MqttConnectOptions} class. >+ * </p> >+ * >+ * @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. >+ * <p>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. >+ * </p> >+ * <p>The method returns control before the connect completes. Completion can >+ * be tracked by: >+ * <ul> >+ * <li>Waiting on the returned token {@link IMqttToken#waitForCompletion()} or</li> >+ * <li>Passing in a callback {@link IMqttActionListener}</li> >+ * </ul> >+ * </p> >+ * >+ * @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. >+ * <p>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. >+ * </p> >+ * >+ * @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. >+ * <p>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. >+ * </p> >+ * @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. >+ * <p>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. >+ * </p> >+ * >+ * @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. >+ * <p> >+ * 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.</p> >+ * <p>This method must not be called from inside {@link MqttCallback} methods.</p> >+ * <p>The method returns control before the disconnect completes. Completion can >+ * be tracked by: >+ * <ul> >+ * <li>Waiting on the returned token {@link IMqttToken#waitForCompletion()} or</li> >+ * <li>Passing in a callback {@link IMqttActionListener}</li> >+ * </ul> >+ * </p> >+ * >+ * @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 <code>true</code> if connected, <code>false</code> otherwise. >+ */ >+ public boolean isConnected(); >+ >+ /** >+ * Returns the client ID used by this client. >+ * <p>All clients connected to the >+ * same server or server farm must have a unique ID. >+ * </p> >+ * >+ * @return the client ID used by this client. >+ */ >+ public String getClientId(); >+ >+ /** >+ * Returns the address of the server used by this client. >+ * <p>The format of the returned String is the same as that used on the constructor. >+ * </p> >+ * >+ * @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 >+ * <p>A convenience method, which will >+ * create a new {@link MqttMessage} object with a byte array payload and the >+ * specified QoS, and then publish it. >+ * </p> >+ * >+ * @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 >+ * <p>A convenience method, which will >+ * create a new {@link MqttMessage} object with a byte array payload and the >+ * specified QoS, and then publish it. >+ * </p> >+ * >+ * @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. >+ * <p> >+ * 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: >+ * <ul> >+ * <li>The connection is re-established with the same clientID >+ * <li>The original connection was made with (@link MqttConnectOptions#setCleanSession(boolean)} >+ * set to false >+ * <li>The connection is re-established with (@link MqttConnectOptions#setCleanSession(boolean)} >+ * set to false >+ * <liDepending when the failure occurs QOS 0 messages may not be delivered. >+ * </ul> >+ * </p> >+ * >+ * <p>When building an application, >+ * the design of the topic tree should take into account the following principles >+ * of topic name syntax and semantics:</p> >+ * >+ * <ul> >+ * <li>A topic must be at least one character long.</li> >+ * <li>Topic names are case sensitive. For example, <em>ACCOUNTS</em> and <em>Accounts</em> are >+ * two different topics.</li> >+ * <li>Topic names can include the space character. For example, <em>Accounts >+ * payable</em> is a valid topic.</li> >+ * <li>A leading "/" creates a distinct topic. For example, <em>/finance</em> is >+ * different from <em>finance</em>. <em>/finance</em> matches "+/+" and "/+", but >+ * not "+".</li> >+ * <li>Do not include the null character (Unicode<samp class="codeph"> \x0000</samp>) in >+ * any topic.</li> >+ * </ul> >+ * >+ * <p>The following principles apply to the construction and content of a topic >+ * tree:</p> >+ * >+ * <ul> >+ * <li>The length is limited to 64k but within that there are no limits to the >+ * number of levels in a topic tree.</li> >+ * <li>There can be any number of root nodes; that is, there can be any number >+ * of topic trees.</li> >+ * </ul> >+ * </p> >+ * <p>The method returns control before the publish completes. Completion can >+ * be tracked by: >+ * <ul> >+ * <li>Setting an {@link IMqttAsyncClient#setCallback(MqttCallback)} where the >+ * {@link MqttCallback#deliveryComplete(IMqttDeliveryToken)} >+ * method will be called.</li> >+ * <li>Waiting on the returned token {@link MqttToken#waitForCompletion()} or</li> >+ * <li>Passing in a callback {@link IMqttActionListener} to this method</li> >+ * </ul> >+ * </p> >+ * >+ * @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. >+ * >+ * <p>Provides an optimised way to subscribe to multiple topics compared to >+ * subscribing to each one individually.</p> >+ * >+ * @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. >+ * <p>Provides an optimised way to subscribe to multiple topics compared to >+ * subscribing to each one individually.</p> >+ * <p>The {@link #setCallback(MqttCallback)} method >+ * should be called before this method, otherwise any received messages >+ * will be discarded. >+ * </p> >+ * <p> >+ * If (@link MqttConnectOptions#setCleanSession(boolean)} was set to true >+ * when when connecting to the server then the subscription remains in place >+ * until either: >+ * <ul> >+ * <li>The client disconnects</li> >+ * <li>An unsubscribe method is called to un-subscribe the topic</li> >+ * </li> >+ * </p> >+ * <p> >+ * If (@link MqttConnectOptions#setCleanSession(boolean)} was set to false >+ * when connecting to the server then the subscription remains in place >+ * until either: >+ * <ul> >+ * <li>An unsubscribe method is called to un-subscribe the topic</li> >+ * <li>The next time the client connects with CleanSession set to true</ul> >+ * </li> >+ * 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 <bold>same client ID</bold> the server will >+ * deliver the stored messages to the client. >+ * </p> >+ * >+ * <p>The "topic filter" string used when subscribing >+ * may contain special characters, which allow you to subscribe to multiple topics >+ * at once.</p> >+ * <p>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. >+ * <dl> >+ * <dt>Topic level separator</dt> >+ * <dd>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.</dd> >+ * >+ * <dt>Multi-level wildcard</dt> >+ * <dd><p>The number sign (#) is a wildcard character that matches >+ * any number of levels within a topic. For example, if you subscribe to >+ * <span><span class="filepath">finance/stock/ibm/#</span></span>, you receive >+ * messages on these topics: >+ * <pre> finance/stock/ibm<br /> finance/stock/ibm/closingprice<br /> finance/stock/ibm/currentprice</pre> >+ * </p> >+ * <p>The multi-level wildcard >+ * can represent zero or more levels. Therefore, <em>finance/#</em> can also match >+ * the singular <em>finance</em>, where <em>#</em> represents zero levels. The topic >+ * level separator is meaningless in this context, because there are no levels >+ * to separate.</p> >+ * >+ * <p>The <span>multi-level</span> wildcard can >+ * be specified only on its own or next to the topic level separator character. >+ * Therefore, <em>#</em> and <em>finance/#</em> are both valid, but <em>finance#</em> is >+ * not valid. <span>The multi-level wildcard must be the last character >+ * used within the topic tree. For example, <em>finance/#</em> is valid but >+ * <em>finance/#/closingprice</em> is not valid.</span></p></dd> >+ * >+ * <dt>Single-level wildcard</dt> >+ * <dd><p>The plus sign (+) is a wildcard character that matches only one topic >+ * level. For example, <em>finance/stock/+</em> matches >+ * <em>finance/stock/ibm</em> and <em>finance/stock/xyz</em>, >+ * but not <em>finance/stock/ibm/closingprice</em>. Also, because the single-level >+ * wildcard matches only a single level, <em>finance/+</em> does not match <em>finance</em>.</p> >+ * >+ * <p>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, >+ * <em>+</em> and <em>finance/+</em> are both valid, but <em>finance+</em> is >+ * not valid. <span>The single-level wildcard can be used at the end of the >+ * topic tree or within the topic tree. >+ * For example, <em>finance/+</em> and <em>finance/+/ibm</em> are both valid.</span></p> >+ * </dd> >+ * </dl> >+ * </p> >+ * <p>The method returns control before the subscribe completes. Completion can >+ * be tracked by: >+ * <ul> >+ * <li>Waiting on the supplied token {@link MqttToken#waitForCompletion()} or</li> >+ * <li>Passing in a callback {@link IMqttActionListener} to this method</li> >+ * </ul> >+ * </p> >+ * >+ * @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 >+ * <p> >+ * 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. >+ * </p> >+ * <p>The topic(s) specified on the unsubscribe must match the topic(s) >+ * specified in the original subscribe request for the unsubscribe to succeed >+ * </p> >+ * <p>The method returns control before the unsubscribe completes. Completion can >+ * be tracked by: >+ * <ul> >+ * <li>Waiting on the returned token {@link MqttToken#waitForCompletion()} or</li> >+ * <li>Passing in a callback {@link IMqttActionListener} to this method</li> >+ * </ul> >+ * </p> >+ * >+ * @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. >+ * <p>There are a number of events that the listener will be notified about. >+ * These include: >+ * <ul> >+ * <li>A new message has arrived and is ready to be processed</li> >+ * <li>The connection to the server has been lost</li> >+ * <li>Delivery of a message to the server has completed</li> >+ * </ul> >+ * </p> >+ * <p>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.<p> >+ * @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. >+ * <p>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. >+ * </p> >+ * <p>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</P> >+ * @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. >+ * <p> >+ * This interface allows applications to utilise all features of the MQTT version 3.1 >+ * specification including: >+ * <ul> >+ * <li>connect >+ * <li>publish >+ * <li>subscribe >+ * <li>unsubscribe >+ * <li>disconnect >+ * </ul> >+ * </p> >+ * <p> >+ * There are two styles of MQTT client, this one and {@link IMqttAsyncClient}. >+ * <ul> >+ * <li>IMqttClient provides a set of methods that block and return control to the application >+ * program once the MQTT action has completed.</li> >+ * <li>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.</li> >+ * </ul> >+ * </p> >+ * <p> >+ * 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: >+ * <code><pre> >+ * IMqttToken token; >+ * token = asyncClient.method(parms).waitForCompletion(); >+ * </pre></code> >+ * 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.</p> >+ */ >+public interface IMqttClient { //extends IMqttAsyncClient { >+ /** >+ * Connects to an MQTT server using the default options. >+ * <p>The default options are specified in {@link MqttConnectOptions} class. >+ * </p> >+ * >+ * @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. >+ * <p>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. >+ * </p> >+ * <p>This is a blocking method that returns once connect completes</p> >+ * >+ * @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. >+ * <p>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. >+ * </p> >+ * >+ * @see #disconnect(long) >+ */ >+ public void disconnect() throws MqttException; >+ >+ /** >+ * Disconnects from the server. >+ * <p> >+ * 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.</p> >+ * >+ * <p>This is a blocking method that returns once disconnect completes</p> >+ * >+ * @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. >+ * <p>The {@link #setCallback(MqttCallback)} method >+ * should be called before this method, otherwise any received messages >+ * will be discarded. >+ * </p> >+ * <p> >+ * If (@link MqttConnectOptions#setCleanSession(boolean)} was set to true >+ * when when connecting to the server then the subscription remains in place >+ * until either: >+ * <ul> >+ * <li>The client disconnects</li> >+ * <li>An unsubscribe method is called to un-subscribe the topic</li> >+ * </li> >+ * </p> >+ * <p> >+ * If (@link MqttConnectOptions#setCleanSession(boolean)} was set to false >+ * when when connecting to the server then the subscription remains in place >+ * until either: >+ * <ul> >+ * <li>An unsubscribe method is called to un-subscribe the topic</li> >+ * <li>The client connects with CleanSession set to true</ul> >+ * </li> >+ * 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 <bold>same client ID</bold> the server will >+ * deliver the stored messages to the client. >+ * </p> >+ * >+ * <p>The "topic filter" string used when subscribing >+ * may contain special characters, which allow you to subscribe to multiple topics >+ * at once.</p> >+ * <p>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. >+ * <dl> >+ * <dt>Topic level separator</dt> >+ * <dd>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.</dd> >+ * >+ * <dt>Multi-level wildcard</dt> >+ * <dd><p>The number sign (#) is a wildcard character that matches >+ * any number of levels within a topic. For example, if you subscribe to >+ * <span><span class="filepath">finance/stock/ibm/#</span></span>, you receive >+ * messages on these topics: >+ * <pre> finance/stock/ibm<br /> finance/stock/ibm/closingprice<br /> finance/stock/ibm/currentprice</pre> >+ * </p> >+ * <p>The multi-level wildcard >+ * can represent zero or more levels. Therefore, <em>finance/#</em> can also match >+ * the singular <em>finance</em>, where <em>#</em> represents zero levels. The topic >+ * level separator is meaningless in this context, because there are no levels >+ * to separate.</p> >+ * >+ * <p>The <span>multi-level</span> wildcard can >+ * be specified only on its own or next to the topic level separator character. >+ * Therefore, <em>#</em> and <em>finance/#</em> are both valid, but <em>finance#</em> is >+ * not valid. <span>The multi-level wildcard must be the last character >+ * used within the topic tree. For example, <em>finance/#</em> is valid but >+ * <em>finance/#/closingprice</em> is not valid.</span></p></dd> >+ * >+ * <dt>Single-level wildcard</dt> >+ * <dd><p>The plus sign (+) is a wildcard character that matches only one topic >+ * level. For example, <em>finance/stock/+</em> matches >+ * <em>finance/stock/ibm</em> and <em>finance/stock/xyz</em>, >+ * but not <em>finance/stock/ibm/closingprice</em>. Also, because the single-level >+ * wildcard matches only a single level, <em>finance/+</em> does not match <em>finance</em>.</p> >+ * >+ * <p>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, >+ * <em>+</em> and <em>finance/+</em> are both valid, but <em>finance+</em> is >+ * not valid. <span>The single-level wildcard can be used at the end of the >+ * topic tree or within the topic tree. >+ * For example, <em>finance/+</em> and <em>finance/+/ibm</em> are both valid.</span></p> >+ * </dd> >+ * </dl> >+ * </p> >+ * >+ * <p>This is a blocking method that returns once subscribe completes</p> >+ * >+ * @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 >+ * <p> >+ * 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. >+ * </p> >+ * <p>The topic(s) specified on the unsubscribe must match the topic(s) >+ * specified in the original subscribe request for the subscribe to succeed >+ * </p> >+ * >+ * <p>This is a blocking method that returns once unsubscribe completes</p> >+ * >+ * @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 >+ * <p>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. >+ * </p> >+ * >+ * @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. >+ * <p> >+ * 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: >+ * <ul> >+ * <li>The connection is re-established with the same clientID >+ * <li>The original connection was made with (@link MqttConnectOptions#setCleanSession(boolean)} >+ * set to false >+ * <li>The connection is re-established with (@link MqttConnectOptions#setCleanSession(boolean)} >+ * set to false >+ * </ul> >+ * </p> >+ * <p>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: >+ * <ul> >+ * <li>Register a {@link #setCallback(MqttCallback)} callback on the client and the delivery complete >+ * callback will be notified once a delivery of a message completes >+ * <li>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. >+ * </ul> >+ * </p> >+ * >+ * <p>When building an application, >+ * the design of the topic tree should take into account the following principles >+ * of topic name syntax and semantics:</p> >+ * >+ * <ul> >+ * <li>A topic must be at least one character long.</li> >+ * <li>Topic names are case sensitive. For example, <em>ACCOUNTS</em> and <em>Accounts</em> are >+ * two different topics.</li> >+ * <li>Topic names can include the space character. For example, <em>Accounts >+ * payable</em> is a valid topic.</li> >+ * <li>A leading "/" creates a distinct topic. For example, <em>/finance</em> is >+ * different from <em>finance</em>. <em>/finance</em> matches "+/+" and "/+", but >+ * not "+".</li> >+ * <li>Do not include the null character (Unicode<samp class="codeph"> \x0000</samp>) in >+ * any topic.</li> >+ * </ul> >+ * >+ * <p>The following principles apply to the construction and content of a topic >+ * tree:</p> >+ * >+ * <ul> >+ * <li>The length is limited to 64k but within that there are no limits to the >+ * number of levels in a topic tree.</li> >+ * <li>There can be any number of root nodes; that is, there can be any number >+ * of topic trees.</li> >+ * </ul> >+ * </p> >+ * >+ * <p>This is a blocking method that returns once publish completes</p> * >+ * >+ * @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. >+ * <p>There are a number of events that listener will be notified about. These include >+ * <ul> >+ * <li>A new message has arrived and is ready to be processed</li> >+ * <li>The connection to the server has been lost</li> >+ * <li>Delivery of a message to the server has completed.</li> >+ * </ul> >+ * </p> >+ * <p>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<p> >+ * @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. >+ * <p>An alternative method that should be used in preference to this one when publishing a message is: >+ * <ul> >+ * <li>{@link MqttClient#publish(String, MqttMessage)} to publish a message in a blocking manner >+ * <li>or use publish methods on the non blocking client like {@link IMqttAsyncClient#publish(String, MqttMessage, Object, IMqttActionListener)} >+ * </ul> >+ * </p> >+ * <p>When building an application, >+ * the design of the topic tree should take into account the following principles >+ * of topic name syntax and semantics:</p> >+ * >+ * <ul> >+ * <li>A topic must be at least one character long.</li> >+ * <li>Topic names are case sensitive. For example, <em>ACCOUNTS</em> and <em>Accounts</em> are >+ * two different topics.</li> >+ * <li>Topic names can include the space character. For example, <em>Accounts >+ * payable</em> is a valid topic.</li> >+ * <li>A leading "/" creates a distinct topic. For example, <em>/finance</em> is >+ * different from <em>finance</em>. <em>/finance</em> matches "+/+" and "/+", but >+ * not "+".</li> >+ * <li>Do not include the null character (Unicode<samp class="codeph"> \x0000</samp>) in >+ * any topic.</li> >+ * </ul> >+ * >+ * <p>The following principles apply to the construction and content of a topic >+ * tree:</p> >+ * >+ * <ul> >+ * <li>The length is limited to 64k but within that there are no limits to the >+ * number of levels in a topic tree.</li> >+ * <li>There can be any number of root nodes; that is, there can be any number >+ * of topic trees.</li> >+ * </ul> >+ * </p> >+ * >+ * @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 <code>true</code> if connected, <code>false</code> otherwise. >+ */ >+ public boolean isConnected(); >+ >+ /** >+ * Returns the client ID used by this client. >+ * <p>All clients connected to the >+ * same server or server farm must have a unique ID. >+ * </p> >+ * >+ * @return the client ID used by this client. >+ */ >+ public String getClientId(); >+ >+ /** >+ * Returns the address of the server used by this client, as a URI. >+ * <p>The format is the same as specified on the constructor. >+ * </p> >+ * >+ * @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. >+ * <p>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. >+ * </p> >+ * <p>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</P> >+ * @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 >+ * >+ * <p>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 >+ * <ul> >+ * <li>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. >+ * <li>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. >+ * </ul> >+ * <p> >+ * An action is in progress until either: >+ * <ul> >+ * <li>isComplete() returns true or >+ * <li>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. >+ * </ul> >+ * </p> >+ * >+ */ >+ >+public interface IMqttDeliveryToken extends IMqttToken { >+ /** >+ * Returns the message associated with this token. >+ * <p>Until the message has been delivered, the message being delivered will >+ * be returned. Once the message has been delivered <code>null</code> 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. >+ * >+ * <p>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.<p> >+ * <p> >+ * An action is in progress until either: >+ * <ul> >+ * <li>isComplete() returns true or >+ * <li>getException() is not null. >+ * </ul> >+ * </p> >+ * >+ */ >+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. >+ * <p>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. </p> >+ * <p>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. >+ * </p> >+ * >+ * @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. >+ * <p>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. >+ * </p> >+ */ >+ public boolean isComplete(); >+ >+ /** >+ * Returns an exception providing more detail if an operation failed >+ * <p>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 >+ * </p> >+ * @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. >+ * <p>Once a listener is registered it will be invoked when the action the token >+ * is associated with either succeeds or fails. >+ * </p> >+ * @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. >+ * <p>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</p> >+ * @param userContext to associate with an action >+ */ >+ public void setUserContext(Object userContext); >+ >+ /** >+ * Retrieve the context associated with an action. >+ * <p>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. </p> >+ >+ * @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. >+ * >+ * <p>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. >+ * </p> >+ * <p>An application can connect to an MQTT server using: >+ * <ul> >+ * <li>A plain TCP socket >+ * <li>An secure SSL/TLS socket >+ * </ul> >+ * </p> >+ * <p>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. >+ * </p> >+ * <p>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. >+ * </p> >+ * <p>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. >+ * </p> >+ * <p>The message store interface is pluggable. Different stores can be used by implementing >+ * the {@link MqttClientPersistence} interface and passing it to the clients constructor. >+ * </p> >+ * >+ * @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. >+ * <p> >+ * 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: >+ * <ul> >+ * <li><code>tcp://localhost:1883</code></li> >+ * <li><code>ssl://localhost:8883</code></li> >+ * </ul> >+ * If the port is not specified, it will >+ * default to 1883 for "tcp://" URIs, and 8883 for "ssl://" URIs. >+ * </p> >+ * <p> >+ * 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. >+ * </p> >+ * <p> >+ * In Java SE, SSL can be configured in one of several ways, which the >+ * client will use in the following order: >+ * </p> >+ * <ul> >+ * <li><strong>Supplying an <code>SSLSocketFactory</code></strong> - applications can >+ * use {@link MqttConnectOptions#setSocketFactory(SocketFactory)} to supply >+ * a factory with the appropriate SSL settings.</li> >+ * <li><strong>SSL Properties</strong> - applications can supply SSL settings as a >+ * simple Java Properties using {@link MqttConnectOptions#setSSLProperties(Properties)}.</li> >+ * <li><strong>Use JVM settings</strong> - There are a number of standard >+ * Java system properties that can be used to configure key and trust stores.</li> >+ * </ul> >+ * >+ * <p>In Java ME, the platform settings are used for SSL connections.</p> >+ * >+ * <p>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. >+ * <p> >+ * 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: >+ * <ul> >+ * <li><code>tcp://localhost:1883</code></li> >+ * <li><code>ssl://localhost:8883</code></li> >+ * </ul> >+ * If the port is not specified, it will >+ * default to 1883 for "tcp://" URIs, and 8883 for "ssl://" URIs. >+ * </p> >+ * <p> >+ * 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. >+ * </p> >+ * <p> >+ * In Java SE, SSL can be configured in one of several ways, which the >+ * client will use in the following order: >+ * </p> >+ * <ul> >+ * <li><strong>Supplying an <code>SSLSocketFactory</code></strong> - applications can >+ * use {@link MqttConnectOptions#setSocketFactory(SocketFactory)} to supply >+ * a factory with the appropriate SSL settings.</li> >+ * <li><strong>SSL Properties</strong> - applications can supply SSL settings as a >+ * simple Java Properties using {@link MqttConnectOptions#setSSLProperties(Properties)}.</li> >+ * <li><strong>Use JVM settings</strong> - There are a number of standard >+ * Java system properties that can be used to configure key and trust stores.</li> >+ * </ul> >+ * >+ * <p>In Java ME, the platform settings are used for SSL connections.</p> >+ * <p> >+ * 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. >+ * </p> >+ * <p>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 <code>null</code>.</p> >+ * >+ * @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=<init> 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. >+ * <p>There are two alternative methods that should be used in preference to this one when publishing a message: >+ * <ul> >+ * <li>{@link MqttAsyncClient#publish(String, MqttMessage, MqttDeliveryToken)} to publish a message in a non-blocking manner or >+ * <li>{@link MqttClient#publishBlock(String, MqttMessage, MqttDeliveryToken)} to publish a message in a blocking manner >+ * </ul> >+ * </p> >+ * <p>When you build an application, >+ * the design of the topic tree should take into account the following principles >+ * of topic name syntax and semantics:</p> >+ * >+ * <ul> >+ * <li>A topic must be at least one character long.</li> >+ * <li>Topic names are case sensitive. For example, <em>ACCOUNTS</em> and <em>Accounts</em> are >+ * two different topics.</li> >+ * <li>Topic names can include the space character. For example, <em>Accounts >+ * payable</em> is a valid topic.</li> >+ * <li>A leading "/" creates a distinct topic. For example, <em>/finance</em> is >+ * different from <em>finance</em>. <em>/finance</em> matches "+/+" and "/+", but >+ * not "+".</li> >+ * <li>Do not include the null character (Unicode<samp class="codeph"> \x0000</samp>) in >+ * any topic.</li> >+ * </ul> >+ * >+ * <p>The following principles apply to the construction and content of a topic >+ * tree:</p> >+ * >+ * <ul> >+ * <li>The length is limited to 64k but within that there are no limits to the >+ * number of levels in a topic tree.</li> >+ * <li>There can be any number of root nodes; that is, there can be any number >+ * of topic trees.</li> >+ * </ul> >+ * </p> >+ * >+ * @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;i<topicFilters.length;i++) { >+ if (i>0) { >+ 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;i<topicFilters.length;i++) { >+ if (i>0) { >+ 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. >+ * <p>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.</p> >+ * @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 >+ * <p>Checks the topic does not contain a wild card character.</p> >+ * @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. >+ * >+ * <p> >+ * This method is invoked synchronously by the MQTT client. An >+ * acknowledgement is not sent back to the server until this >+ * method returns cleanly.</p> >+ * <p> >+ * If an implementation of this method throws an <code>Exception</code>, 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.</p> >+ * <p> >+ * 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.</p> >+ * <p> >+ * 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.</p> >+ * <p> >+ * 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.</p> >+ * >+ * @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. >+ * >+ * <p>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. >+ * </p> >+ * <p>An application can connect to an MQTT server using: >+ * <ul> >+ * <li>A plain TCP socket >+ * <li>An secure SSL/TLS socket >+ * </ul> >+ * </p> >+ * <p>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. >+ * </p> >+ * <p>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. >+ * </p> >+ * <p>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. </p> >+ * <p>The message store interface is pluggable. Different stores can be used by implementing >+ * the {@link MqttClientPersistence} interface and passing it to the clients constructor. >+ * </p> >+ * >+ * @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. >+ * <p> >+ * 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: >+ * <ul> >+ * <li><code>tcp://localhost:1883</code></li> >+ * <li><code>ssl://localhost:8883</code></li> >+ * </ul> >+ * If the port is not specified, it will >+ * default to 1883 for "tcp://" URIs, and 8883 for "ssl://" URIs. >+ * </p> >+ * <p> >+ * 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. >+ * </p> >+ * <p> >+ * In Java SE, SSL can be configured in one of several ways, which the >+ * client will use in the following order: >+ * </p> >+ * <ul> >+ * <li><strong>Supplying an <code>SSLSocketFactory</code></strong> - applications can >+ * use {@link MqttConnectOptions#setSocketFactory(SocketFactory)} to supply >+ * a factory with the appropriate SSL settings.</li> >+ * <li><strong>SSL Properties</strong> - applications can supply SSL settings as a >+ * simple Java Properties using {@link MqttConnectOptions#setSSLProperties(Properties)}.</li> >+ * <li><strong>Use JVM settings</strong> - There are a number of standard >+ * Java system properties that can be used to configure key and trust stores.</li> >+ * </ul> >+ * >+ * <p>In Java ME, the platform settings are used for SSL connections.</p> >+ * >+ * <p>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. >+ * <p> >+ * 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: >+ * <ul> >+ * <li><code>tcp://localhost:1883</code></li> >+ * <li><code>ssl://localhost:8883</code></li> >+ * </ul> >+ * If the port is not specified, it will >+ * default to 1883 for "tcp://" URIs, and 8883 for "ssl://" URIs. >+ * </p> >+ * <p> >+ * 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. >+ * </p> >+ * <p> >+ * In Java SE, SSL can be configured in one of several ways, which the >+ * client will use in the following order: >+ * </p> >+ * <ul> >+ * <li><strong>Supplying an <code>SSLSocketFactory</code></strong> - applications can >+ * use {@link MqttConnectOptions#setSocketFactory(SocketFactory)} to supply >+ * a factory with the appropriate SSL settings.</li> >+ * <li><strong>SSL Properties</strong> - applications can supply SSL settings as a >+ * simple Java Properties using {@link MqttConnectOptions#setSSLProperties(Properties)}.</li> >+ * <li><strong>Use JVM settings</strong> - There are a number of standard >+ * Java system properties that can be used to configure key and trust stores.</li> >+ * </ul> >+ * >+ * <p>In Java ME, the platform settings are used for SSL connections.</p> >+ * <p> >+ * 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. >+ * </p> >+ * <p>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 <code>null</code>.</p> >+ * >+ * @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; i<qos.length; i++) { >+ qos[i] = 1; >+ } >+ this.subscribe(topicFilters, qos); >+ } >+ >+ /* >+ * @see IMqttClient#subscribe(String, int) >+ */ >+ public void subscribe(String topicFilter, int qos) throws MqttException { >+ this.subscribe(new String[] {topicFilter}, new int[] {qos}); >+ } >+ >+ /* >+ * @see IMqttClient#subscribe(String[], int[]) >+ */ >+ public void subscribe(String[] topicFilters, int[] qos) throws MqttException { >+ aClient.subscribe(topicFilters, qos, null,null).waitForCompletion(getTimeToWait()); >+ } >+ >+ /* >+ * @see IMqttClient#unsubscribe(String) >+ */ >+ public void unsubscribe(String topicFilter) throws MqttException { >+ unsubscribe(new String[] {topicFilter}); >+ } >+ >+ /* >+ * @see IMqttClient#unsubscribe(String[]) >+ */ >+ public void unsubscribe(String[] topicFilters) throws MqttException { >+ aClient.unsubscribe(topicFilters, null,null).waitForCompletion(getTimeToWait()); >+ } >+ >+ /* >+ * @see IMqttClient#publishBlock(String, byte[], int, boolean) >+ */ >+ public void publish(String topic, byte[] payload,int qos, boolean retained) throws MqttException, >+ MqttPersistenceException { >+ MqttMessage message = new MqttMessage(payload); >+ message.setQos(qos); >+ message.setRetained(retained); >+ this.publish(topic, message); >+ } >+ >+ /* >+ * @see IMqttClient#publishBlock(String, MqttMessage) >+ */ >+ public void publish(String topic, MqttMessage message) throws MqttException, >+ MqttPersistenceException { >+ aClient.publish(topic, message, null, null).waitForCompletion(getTimeToWait()); >+ } >+ >+ /** >+ * Set the maximum time to wait for an action to complete >+ * <p>Set the maximum time to wait for an action to complete before >+ * returning control to the invoking application. Control is returned >+ * when: >+ * <ul> >+ * <li>the action completes >+ * <li>or when the timeout if exceeded >+ * <li>or when the client is disconnect/shutdown >+ * <ul> >+ * 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. >+ * </p> >+ * @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. >+ * <p>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.</p> >+ * @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. >+ * <p> >+ * 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.</p> >+ * <p> >+ * It is up to the persistence interface to log any exceptions or error information >+ * which may be required when diagnosing a persistence failure.</p> >+ */ >+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 <code>MqttConnectOptions</code> object using the >+ * default values. >+ * >+ * The defaults are: >+ * <ul> >+ * <li>The keepalive interval is 60 seconds</li> >+ * <li>Clean Session is true</li> >+ * <li>The message delivery retry interval is 15 seconds</li> >+ * <li>The connection timeout period is 30 seconds</li> >+ * <li>No Will message is set</li> >+ * <li>A standard SocketFactory is used</li> >+ * </ul> >+ * 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. >+ * <p>The default value is 60 seconds</p> >+ * >+ * @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 >+ * <code>null</code> if one has not been set. >+ */ >+ public SocketFactory getSocketFactory() { >+ return socketFactory; >+ } >+ >+ /** >+ * Sets the <code>SocketFactory</code> to use. This allows an application >+ * to apply its own policies around the creation of network sockets. If >+ * using an SSL connection, an <code>SSLSocketFactory</code> 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 <code>null</code> 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 >+ * <code>IllegalStateException</code> being thrown. >+ * @return the message to use, or <code>null</code> 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 >+ * <em>not</em> used if a SocketFactory has been set using >+ * {@link #setSocketFactory(SocketFactory)}. >+ * The following properties can be used:</p> >+ * <dl> >+ * <dt>com.ibm.ssl.protocol</dt> >+ * <dd>One of: SSL, SSLv3, TLS, TLSv1, SSL_TLS.</dd> >+ * <dt>com.ibm.ssl.contextProvider >+ * <dd>Underlying JSSE provider. For example "IBMJSSE2" or "SunJSSE"</dd> >+ * >+ * <dt>com.ibm.ssl.keyStore</dt> >+ * <dd>The name of the file that contains the KeyStore object that you >+ * want the KeyManager to use. For example /mydir/etc/key.p12</dd> >+ * >+ * <dt>com.ibm.ssl.keyStorePassword</dt> >+ * <dd>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: >+ * <code>com.ibm.micro.security.Password.obfuscate(char[] password)</code>. >+ * 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.</dd> >+ * >+ * <dt>com.ibm.ssl.keyStoreType</dt> >+ * <dd>Type of key store, for example "PKCS12", "JKS", or "JCEKS".</dd> >+ * >+ * <dt>com.ibm.ssl.keyStoreProvider</dt> >+ * <dd>Key store provider, for example "IBMJCE" or "IBMJCEFIPS".</dd> >+ * >+ * <dt>com.ibm.ssl.trustStore</dt> >+ * <dd>The name of the file that contains the KeyStore object that you >+ * want the TrustManager to use.</dd> >+ * >+ * <dt>com.ibm.ssl.trustStorePassword</dt> >+ * <dd>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: >+ * <code>com.ibm.micro.security.Password.obfuscate(char[] password)</code>. >+ * 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.</dd> >+ * >+ * <dt>com.ibm.ssl.trustStoreType</dt> >+ * <dd>The type of KeyStore object that you want the default TrustManager to use. >+ * Same possible values as "keyStoreType".</dd> >+ * >+ * <dt>com.ibm.ssl.trustStoreProvider</dt> >+ * <dd>Trust store provider, for example "IBMJCE" or "IBMJCEFIPS".</dd> >+ * >+ * <dt>com.ibm.ssl.enabledCipherSuites</dt> >+ * <dd>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.</dd> >+ * >+ * <dt>com.ibm.ssl.keyManager</dt> >+ * <dd>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". >+ * </dd> >+ * >+ * <dt>com.ibm.ssl.trustManager</dt> >+ * <dd>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". >+ * </dd> >+ * </dl> >+ */ >+ 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. >+ * >+ * <p> >+ * Used to track the the delivery progress of a message when a publish is >+ * executed in a non-blocking manner (run in the background)</p> >+ * >+ * @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. >+ * <p>Until the message has been delivered, the message being delivered will >+ * be returned. Once the message has been delivered <code>null</code> 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 <code>SocketFactory</code> do not match. >+ * URIs beginning <code>tcp://</code> must use a <code>javax.net.SocketFactory</code>, >+ * and URIs beginning <code>ssl://</code> must use a <code>javax.net.ssl.SSLSocketFactory</code>. >+ */ >+ 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 <code>MqttException</code> 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 <code>MqttException</code> with the specified >+ * <code>Throwable</code> 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 <code>MqttException</code> with the specified >+ * <code>Throwable</code> 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 <code>null</code>. >+ */ >+ public Throwable getCause() { >+ return cause; >+ } >+ >+ /** >+ * Returns the detail message for this exception. >+ * @return the detail message, which may be <code>null</code>. >+ */ >+ public String getMessage() { >+ return MessageCatalog.getMessage(reasonCode); >+ } >+ >+ /** >+ * Returns a <code>String</code> representation of this exception. >+ * @return a <code>String</code> 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: >+ * <ul> >+ * <li>Message QoS set to 1</li> >+ * <li>Message will not be "retained" by the server</li> >+ * </ul> >+ */ >+ 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 <code>true</code> 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 <code>false</code> will clear the >+ * retained message from the server. The default value is <code>false</code> >+ * >+ * @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. >+ * <ul> >+ * <li>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".</li> >+ * >+ * <li>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 <code>MqttConnectOptions</code>. >+ * 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.</li> >+ * >+ * <li>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 <code>MqttConnectOptions</code>. >+ * If a persistence mechanism is not specified, the message will not be >+ * delivered in the event of a client failure.</li> >+ * >+ * 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 <code>true</code> if the values can be changed, >+ * <code>false</code> 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 <code>true</code> 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. >+ * <p> >+ * 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.</p> >+ * <p> >+ * 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: >+ * <ul> >+ * <li>It could return the data as it was passed in >+ * originally, with the same byte arrays and offsets.</li> >+ * <li>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</li> >+ * <li>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.</li> >+ * </ul> >+ * </p> >+ */ >+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 <code>MqttPersistenceException</code> >+ */ >+ public MqttPersistenceException() { >+ super(REASON_CODE_CLIENT_EXCEPTION); >+ } >+ >+ /** >+ * Constructs a new <code>MqttPersistenceException</code> 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 <code>MqttPersistenceException</code> with the specified >+ * <code>Throwable</code> as the underlying reason. >+ * @param cause the underlying cause of the exception. >+ */ >+ public MqttPersistenceException(Throwable cause) { >+ super(cause); >+ } >+ /** >+ * Constructs a new <code>MqttPersistenceException</code> with the specified >+ * <code>Throwable</code> 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 <code>MqttSecurityException</code> 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 <code>MqttSecurityException</code> with the specified >+ * <code>Throwable</code> as the underlying reason. >+ * @param cause the underlying cause of the exception. >+ */ >+ public MqttSecurityException(Throwable cause) { >+ super(cause); >+ } >+ /** >+ * Constructs a new <code>MqttSecurityException</code> with the specified >+ * code and <code>Throwable</code> 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. >+ * <p> >+ * A token that implements the ImqttToken interface is returned from all non-blocking >+ * method with the exception of publish. >+ * </p> >+ * >+ * @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<toks.length; i++) { >+ toks[i].internalTok.setException(null); >+ } >+ >+ // Save the conncet token in tokenStore as failure can occur before send >+ tokenStore.saveToken(conToken,conPacket); >+ >+ // Connect to the server at the network level e.g. TCP socket and then >+ // start the background processing threads before sending the connect >+ // packet. >+ networkModule.start(); >+ receiver = new CommsReceiver(clientComms, clientState, tokenStore, networkModule.getInputStream()); >+ receiver.start("MQTT Rec: "+getClient().getClientId()); >+ sender = new CommsSender(clientComms, clientState, tokenStore, networkModule.getOutputStream()); >+ sender.start("MQTT Snd: "+getClient().getClientId()); >+ callback.start("MQTT Call: "+getClient().getClientId()); >+ >+ internalSend(conPacket, conToken); >+ } catch (MqttException ex) { >+ //@TRACE 212=connect failed: unexpected exception >+ log.fine(className, methodName, "212", null, ex); >+ mqttEx = ex; >+ } catch (Exception ex) { >+ //@TRACE 209=connect failed: unexpected exception >+ log.fine(className, methodName, "209", null, ex); >+ mqttEx = ExceptionHelper.createMqttException(ex); >+ } >+ >+ if (mqttEx != null) { >+ shutdownConnection(conToken, mqttEx); >+ } >+ } >+ } >+ >+ // Kick off the disconnect processing in the background so that it does not block. For instance >+ // the quiesce >+ private class DisconnectBG implements Runnable { >+ Thread dBg = null; >+ MqttDisconnect disconnect; >+ long quiesceTimeout; >+ MqttToken token; >+ >+ DisconnectBG(MqttDisconnect disconnect, long quiesceTimeout, MqttToken token ) { >+ this.disconnect = disconnect; >+ this.quiesceTimeout = quiesceTimeout; >+ this.token = token; >+ } >+ >+ void start() { >+ dBg = new Thread(this, "MQTT Disc: "+getClient().getClientId()); >+ dBg.start(); >+ } >+ public void run() { >+ final String methodName = "disconnectBG:run"; >+ //@TRACE 221=> >+ 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, "<Init>", "" ); >+ >+ 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 = "<Init>"; >+ >+ 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.<BR> >+ * 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.<BR> >+ * 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;i<enabledCiphers.length;i++) { >+ if (i>0) { >+ 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<getTopics().length; i++) { >+ tok.append(getTopics()[i]+", "); >+ } >+ } >+ tok.append(" ,usercontext="+getUserContext()); >+ tok.append(" ,isComplete="+isComplete()); >+ tok.append(" ,isNotified="+isNotified()); >+ tok.append(" ,exception="+getException()); >+ tok.append(" ,actioncallback="+getActionCallback()); >+ >+ return tok.toString(); >+ } >+ >+} >diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/logcat.properties b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/logcat.properties >new file mode 100644 >index 0000000..1063c9e >--- /dev/null >+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/logcat.properties >@@ -0,0 +1,132 @@ >+0=MQTT Catalog >+200=internalSend key={0} message={1} token={2} >+213=fail: token in use: key={0} message={1} token={2} >+208=failed: not connected >+224=failed: not disconnected >+214=state=CONNECTING >+207=connect failed: not disconnected {0} >+215=state=CONNECTED >+204=connect failed: rc={0} >+216=state=DISCONNECTING >+217=state=DISCONNECTED >+222=> >+223=failed: in closed state >+211=failed: already disconnected >+219=failed: already disconnecting >+210=failed: called on callback thread >+218=state=DISCONNECTING >+220=> >+212=connect failed: unexpected exception >+209=connect failed: unexpected exception >+221=> >+603=clearState >+602=key={0} exception >+601=key={0} message={1} >+600=> >+604=inbound QoS 2 publish key={0} message={1} >+605=outbound QoS 2 pubrel key={0} message={1} >+606=outbound QoS 2 completed key={0} message={1} >+607=outbound QoS 2 publish key={0} message={1} >+608=outbound QoS 1 publish key={0} message={1} >+609=removing orphaned pubrel key={0} >+610=QoS 2 publish key={0} >+611=QoS 2 pubrel key={0} >+612=QoS 1 publish key={0} >+613= sending {0} msgs at max inflight window >+628=pending publish key={0} qos={1} message={2} >+615=pending send key={0} message {1} >+618=key={0} QoS={1} >+620=ping needed. keepAlive={0} lastOutboundActivity={1} lastInboundActivity={2} >+619=Timed out as no activity, keepAlive={0} lastOutboundActivity={1} lastInboundActivity={2} >+644=nothing to send, wait for {0} ms >+621=no outstanding flows and not connected >+617=+1 inflightpubrels={0} >+623=+1 actualInFlight={0} >+622=inflight window full >+625=key={0} >+646=-1 actualInFlight={0} >+626=quiescing={0} actualInFlight={1} pendingFlows={2} inFlightPubRels={3} callbackQuiesce={4} tokens={5} >+627=received key={0} message={1} >+651=received key={0} message={1} >+629=received key={0} token={1} message={2} >+650=removed Qos 1 publish. key={0} >+645=removed QoS 2 publish/pubrel. key={0}, -1 inFlightPubRels={1} >+648=key{0}, msg={1}, excep={2} >+649=key={0},excep={1} >+631=connected >+632=reason {0} >+633=disconnected >+637=timeout={0} >+639=wait for outstanding: actualInFlight={0} pendingFlows={1} inFlightPubRels={2} tokens={3} >+640=finished >+638=notifying queueLock holders >+641=remove publish from persistence. key={0} >+700=stopping >+701=notify workAvailable and wait for run >+703=stopped >+704=wait for workAvailable >+706=notify spaceAvailable >+714=callback threw exception >+705=callback and notify for key={0} >+708=call connectionLost >+720=exception from connectionLost {0} >+716=call onSuccess key={0} >+717=call onFailure key {0} >+709=wait for spaceAvailable >+710=new msg avail, notify workAvailable >+711=quiesce notify spaceAvailable >+713=call messageArrived key={0} topic={1} >+715=new workAvailable. key={0} >+719=callback threw ex: >+855=starting >+850=stopping >+851=stopped >+852=network read message >+856=Stopping, MQttException >+853=Stopping due to IOException >+854=< >+800=stopping sender >+801=stopped >+802=network send key={0} msg={1} >+803=get message returned null, stopping} >+805=< >+804=exception >+308=<> >+306=key={0} >+302=existing key={0} message={1} token={2} >+303=creating new token key={0} message={1} token={2} >+300=key={0} message={1} >+307=key={0} token={1} >+309=resp={0} >+310=> >+311=> >+312=> >+305=> {0} tokens >+260=setEnabledCiphers ciphers={0} >+252=connect to host {0} port {1} timeout {2} >+250=Failed to create TCP socket >+407=key={0} wait max={1} token={2} >+406=key={0} timed out token={1} >+400=>key={0} timeout={1} sent={2} completed={3} hasException={4} response={5} token={6} >+408=key={0} wait max={1} >+401=failed with exception >+402=key={0} response={1} >+404=>key={0} response={1} excep={2} >+411=>key={0} response={1} excep={2} >+409=wait key={0} >+403=> key={0} >+410=> key={0} >+101=<init> ClientID={0} ServerURI={1} PersistenceType={2} >+115=URI={0} >+103=cleanSession={0} connectionTimeout={1} TimekeepAlive={2} userName={3} password={4} will={5} userContext={6} callback={7} >+104=> quiesceTimeout={0} userContext={1} callback={2} >+105=< exception >+108=< >+106=Subscribe topic={0} userContext={1} callback={2} >+109=< >+107=Unsubscribe topic={0} userContext={1} callback={2} >+110=< >+111=< topic={0} message={1}userContext={1} callback={2} >+112=< >+113=< >+114=> >diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages.properties b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages.properties >new file mode 100644 >index 0000000..d04e82d >--- /dev/null >+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages.properties >@@ -0,0 +1,35 @@ >+#/* >+# * 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 >+# */ >+# NLS_MESSAGEFORMAT_VAR >+# NLS_ENCODING=UNICODE >+1=Invalid protocol version >+2=Invalid client ID >+3=Broker unavailable >+4=Bad user name or password >+5=Not authorized to connect >+6=Unexpected error >+32000=Timed out waiting for a response from the server >+32100=Client is connected >+32101=Client is disconnected >+32102=Client is currently disconnecting >+32103=Unable to connect to server >+32104=Client is not connected >+32105=The specified SocketFactory type does not match the broker URI >+32106=SSL configuration error >+32107=Disconnecting is not allowed from a callback method >+32108=Unrecognized packet >+32109=Connection lost >+32110=Connect already in progress >+32111=Client is closed >+32200=Persistence already in use >+32201=Token already in use >+32202=Too many publishes in progress >diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_cs.properties b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_cs.properties >new file mode 100644 >index 0000000..baec64f >--- /dev/null >+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_cs.properties >@@ -0,0 +1,30 @@ >+#/* >+# * 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 >+# */ >+# NLS_MESSAGEFORMAT_VAR >+# NLS_ENCODING=UNICODE >+1=Neplatn\u00e1 verze protokolu >+2=Neplatn\u00e9 ID klienta >+3=Nedostupn\u00fd zprost\u0159edkovatel >+4=Chybn\u00e9 jm\u00e9no u\u017eivatele nebo heslo >+5=Chyb\u00ed autorizace pro p\u0159ipojen\u00ed >+32000=Vypr\u0161en\u00ed \u010dasov\u00e9ho limitu pro odpov\u011b\u010f ze serveru >+32100=Klient je ji\u017e p\u0159ipojen >+32101=Klient je ji\u017e odpojen >+32102=Klient se aktu\u00e1ln\u011b odpojuje >+32103=Nelze se p\u0159ipojit k serveru >+32104=Klient nen\u00ed p\u0159ipojen >+32105=Ur\u010den\u00fd typ polo\u017eky SocketFactory neodpov\u00edd\u00e1 identifik\u00e1toru URI zprost\u0159edkovatele. >+32106=Chyba konfigurace zabezpe\u010den\u00ed SSL >+32107=Z metody zp\u011btn\u00e9ho vol\u00e1n\u00ed nen\u00ed povoleno odpojen\u00ed >+32108=Nerozpoznan\u00fd paket >+32109=P\u0159ipojen\u00ed bylo ztraceno. >+32200=Perzistence je ji\u017e pou\u017e\u00edv\u00e1na. >diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_de.properties b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_de.properties >new file mode 100644 >index 0000000..d810e23 >--- /dev/null >+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_de.properties >@@ -0,0 +1,30 @@ >+#/* >+# * 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 >+# */ >+# NLS_MESSAGEFORMAT_VAR >+# NLS_ENCODING=UNICODE >+1=Protokollversion ung\u00fcltig >+2=Client-ID ung\u00fcltig >+3=Broker nicht verf\u00fcgbar >+4=Benutzername oder Kennwort falsch >+5=Keine Berechtigung f\u00fcr Verbindung >+32000=Zeitlimit\u00fcberschreitung beim Warten auf eine Antwort vom Server >+32100=Verbindung zu Client besteht bereits >+32101=Verbindung zu Client ist bereits getrennt >+32102=Verbindung zu Client wird derzeit getrennt >+32103=Verbindung zu Server kann nicht hergestellt werden >+32104=Keine Verbindung zu Client >+32105=Der angegebene Socket-Factorytyp entspricht nicht der Broker-URI >+32106=SSL-Konfigurationsfehler >+32107=Trennung einer Verbindung \u00fcber eine Callback-Methode ist nicht zul\u00e4ssig >+32108=Paket nicht erkannt >+32109=Verbindung wurde getrennt >+32200=Persistenz wird bereits verwendet >diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_es.properties b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_es.properties >new file mode 100644 >index 0000000..3dea170 >--- /dev/null >+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_es.properties >@@ -0,0 +1,30 @@ >+#/* >+# * 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 >+# */ >+# NLS_MESSAGEFORMAT_VAR >+# NLS_ENCODING=UNICODE >+1=Versi\u00f3n de protocolo incorrecta >+2=Identificador de cliente incorrecto >+3=Intermediario no disponible >+4=Nombre de usuario o contrase\u00f1a incorrecto >+5=No autorizado a conectarse >+32000=Tiempo de espera excedido al esperar una respuesta del servidor >+32100=Cliente ya conectado >+32101=Cliente ya desconectado >+32102=El cliente se est\u00e1 desconectando >+32103=No es posible conectarse al servidor >+32104=El cliente no est\u00e1 conectado >+32105=El tipo SocketFactory especificado no coincide con el URI del intermediario >+32106=Error de configuraci\u00f3n SSL >+32107=No se permite la desconexi\u00f3n desde un m\u00e9todo de devoluci\u00f3n de llamada >+32108=Paquete no reconocido >+32109=Se ha perdido la conexi\u00f3n >+32200=La persistencia ya se est\u00e1 utilizando >diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_fr.properties b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_fr.properties >new file mode 100644 >index 0000000..c3650d5 >--- /dev/null >+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_fr.properties >@@ -0,0 +1,30 @@ >+#/* >+# * 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 >+# */ >+# NLS_MESSAGEFORMAT_VAR >+# NLS_ENCODING=UNICODE >+1=Version de protocole incorrecte >+2=ID client incorrect >+3=Courtier indisponible >+4=Nom d'utilisateur ou mot de passe incorrect >+5=L'utilisateur n'est pas autoris\u00e9 \u00e0 se connecter >+32000=Expiration du d\u00e9lai d'attente d'une r\u00e9ponse du serveur >+32100=Client d\u00e9j\u00e0 connect\u00e9 >+32101=Client d\u00e9j\u00e0 d\u00e9connect\u00e9 >+32102=Client en cours de d\u00e9connexion >+32103=Impossible de se connecter au serveur >+32104=Client non connect\u00e9 >+32105=Le type SocketFactory sp\u00e9cifi\u00e9 ne correspond pas \u00e0 l'URI de courtier >+32106=Erreur de configuration SSL >+32107=D\u00e9connexion non autoris\u00e9e pour une m\u00e9thode de rappel >+32108=Paquet non reconnu >+32109=Connexion perdue >+32200=La persistance est d\u00e9j\u00e0 en cours d'utilisation >diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_hu.properties b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_hu.properties >new file mode 100644 >index 0000000..59a1b34 >--- /dev/null >+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_hu.properties >@@ -0,0 +1,30 @@ >+#/* >+# * 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 >+# */ >+# NLS_MESSAGEFORMAT_VAR >+# NLS_ENCODING=UNICODE >+1=\u00c9rv\u00e9nytelen protokoll v\u00e1ltozat >+2=\u00c9rv\u00e9nytelen \u00fcgyf\u00e9lazonos\u00edt\u00f3 >+3=K\u00f6zvet\u00edt\u0151 nem el\u00e9rhet\u0151 >+4=Rossz felhaszn\u00e1l\u00f3i n\u00e9v vagy jelsz\u00f3 >+5=Nem jogosult csatlakozni >+32000=T\u00fall\u00e9pte a megengedett id\u0151t a kiszolg\u00e1l\u00f3 v\u00e1lasz\u00e1ra v\u00e1rva >+32100=Az \u00fcgyf\u00e9l m\u00e1r csatlakozik >+32101=Az \u00fcgyf\u00e9l m\u00e1r sz\u00e9tkapcsolt >+32102=Az \u00fcgyf\u00e9l \u00e9pp megszak\u00edtja a kapcsolatot >+32103=Nem lehet kapcsol\u00f3dni a kiszolg\u00e1l\u00f3hoz >+32104=Az \u00fcgyf\u00e9l nincs csatlakoztatva >+32105=A megadott SocketFactory t\u00edpus nem illeszkedik a k\u00f6zvet\u00edt\u0151 URI azonos\u00edt\u00f3hoz >+32106=SSL konfigur\u00e1ci\u00f3s hiba >+32107=A megszak\u00edt\u00e1s visszah\u00edv\u00e1s met\u00f3dusb\u00f3l nem enged\u00e9lyezett >+32108=Ismeretlen csomag >+32109=Kapcsolat elveszett >+32200=A megmarad\u00f3 \u00e1llapot m\u00e1r haszn\u00e1latban van >diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_it.properties b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_it.properties >new file mode 100644 >index 0000000..7b68976 >--- /dev/null >+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_it.properties >@@ -0,0 +1,30 @@ >+#/* >+# * 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 >+# */ >+# NLS_MESSAGEFORMAT_VAR >+# NLS_ENCODING=UNICODE >+1=Versione di protocollo non valida >+2=ID client non valido >+3=Broker non disponibile >+4=Nome utente o password non validi >+5=Non autorizzato per la connessione >+32000=Scaduto in attesa di una risposta dal server >+32100=Client gi\u00e0 connesso >+32101=Client gi\u00e0 disconnesso >+32102=Client in fase di disconnessione >+32103=Impossibile effettuare la connessione al server >+32104=Client non connesso >+32105=Il tipo SocketFactory specificato non corrisponde all'URI del broker >+32106=Errore di configurazione SSL >+32107=Disconnessione non consentita da un metodo callback >+32108=Pacchetto non riconosciuto >+32109=Connessione persa >+32200=Persistenza gi\u00e0 in uso >diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_ja.properties b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_ja.properties >new file mode 100644 >index 0000000..a6ac091 >--- /dev/null >+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_ja.properties >@@ -0,0 +1,30 @@ >+#/* >+# * 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 >+# */ >+# NLS_MESSAGEFORMAT_VAR >+# NLS_ENCODING=UNICODE >+1=\u7121\u52b9\u306a\u30d7\u30ed\u30c8\u30b3\u30eb\u30fb\u30d0\u30fc\u30b8\u30e7\u30f3\u3067\u3059 >+2=\u7121\u52b9\u306a\u30af\u30e9\u30a4\u30a2\u30f3\u30c8 ID \u3067\u3059 >+3=\u30d6\u30ed\u30fc\u30ab\u30fc\u304c\u4f7f\u7528\u4e0d\u53ef\u3067\u3059 >+4=\u9593\u9055\u3063\u305f\u30e6\u30fc\u30b6\u30fc\u540d\u307e\u305f\u306f\u30d1\u30b9\u30ef\u30fc\u30c9\u3067\u3059 >+5=\u63a5\u7d9a\u3059\u308b\u6a29\u9650\u304c\u3042\u308a\u307e\u305b\u3093 >+32000=\u30b5\u30fc\u30d0\u30fc\u304b\u3089\u306e\u5fdc\u7b54\u5f85\u6a5f\u304c\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8\u306b\u306a\u308a\u307e\u3057\u305f >+32100=\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306f\u65e2\u306b\u63a5\u7d9a\u6e08\u307f\u3067\u3059 >+32101=\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306f\u65e2\u306b\u5207\u65ad\u6e08\u307f\u3067\u3059 >+32102=\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306f\u73fe\u5728\u5207\u65ad\u4e2d\u3067\u3059 >+32103=\u30b5\u30fc\u30d0\u30fc\u306b\u63a5\u7d9a\u3067\u304d\u307e\u305b\u3093 >+32104=\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306f\u63a5\u7d9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093 >+32105=\u6307\u5b9a\u3055\u308c\u305f SocketFactory \u30bf\u30a4\u30d7\u306f\u30d6\u30ed\u30fc\u30ab\u30fc URI \u3068\u4e00\u81f4\u3057\u307e\u305b\u3093 >+32106=SSL \u69cb\u6210\u30a8\u30e9\u30fc\u3067\u3059 >+32107=\u30b3\u30fc\u30eb\u30d0\u30c3\u30af\u30fb\u30e1\u30bd\u30c3\u30c9\u304b\u3089\u306e\u5207\u65ad\u306f\u8a31\u53ef\u3055\u308c\u307e\u305b\u3093 >+32108=\u8b58\u5225\u3055\u308c\u3066\u3044\u306a\u3044\u30d1\u30b1\u30c3\u30c8\u3067\u3059 >+32109=\u63a5\u7d9a\u55aa\u5931 >+32200=\u30d1\u30fc\u30b7\u30b9\u30bf\u30f3\u30b9\u306f\u3059\u3067\u306b\u4f7f\u7528\u4e2d\u3067\u3059\u3002 >diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_ko.properties b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_ko.properties >new file mode 100644 >index 0000000..c75b34d >--- /dev/null >+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_ko.properties >@@ -0,0 +1,30 @@ >+#/* >+# * 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 >+# */ >+# NLS_MESSAGEFORMAT_VAR >+# NLS_ENCODING=UNICODE >+1=\uc62c\ubc14\ub974\uc9c0 \uc54a\uc740 \ud504\ub85c\ud1a0\ucf5c \ubc84\uc804 >+2=\uc62c\ubc14\ub974\uc9c0 \uc54a\uc740 \ud074\ub77c\uc774\uc5b8\ud2b8 ID >+3=\ube0c\ub85c\ucee4 \uc0ac\uc6a9 \ubd88\uac00\ub2a5 >+4=\uc798\ubabb\ub41c \uc0ac\uc6a9\uc790 \uc774\ub984 \ub610\ub294 \ube44\ubc00\ubc88\ud638 >+5=\uc5f0\uacb0\ud560 \uc218 \uc788\ub294 \uad8c\ud55c\uc774 \ubd80\uc5ec\ub418\uc9c0 \uc54a\uc74c >+32000=\uc11c\ubc84\uc5d0\uc11c \uc751\ub2f5\uc744 \uae30\ub2e4\ub9ac\ub294 \uc911 \uc81c\ud55c\uc2dc\uac04 \ucd08\uacfc >+32100=\ud074\ub77c\uc774\uc5b8\ud2b8\uac00 \uc774\ubbf8 \uc5f0\uacb0\ub428 >+32101=\ud074\ub77c\uc774\uc5b8\ud2b8\uac00 \uc774\ubbf8 \uc5f0\uacb0\uc774 \ub04a\uae40 >+32102=\ud604\uc7ac \ud074\ub77c\uc774\uc5b8\ud2b8\uac00 \uc5f0\uacb0\uc744 \ub04a\ub294 \uc911 >+32103=\uc11c\ubc84\uc5d0 \uc5f0\uacb0\ud560 \uc218 \uc5c6\uc74c >+32104=\ud074\ub77c\uc774\uc5b8\ud2b8\uac00 \uc5f0\uacb0\ub418\uc9c0 \uc54a\uc74c >+32105=\uc9c0\uc815\ub41c SocketFactory \uc720\ud615\uc774 \ube0c\ub85c\ucee4 URI\uc640 \uc77c\uce58\ud558\uc9c0 \uc54a\uc74c >+32106=SSL \uad6c\uc131 \uc624\ub958 >+32107=\ucf5c\ubc31 \uba54\uc18c\ub4dc\ub85c\ubd80\ud130 \uc5f0\uacb0\uc744 \ub04a\ub294 \uac83\uc774 \ud5c8\uc6a9\ub418\uc9c0 \uc54a\uc74c >+32108=\uc778\uc2dd\ub418\uc9c0 \uc54a\uc740 \ud328\ud0b7 >+32109=\uc5f0\uacb0 \uc720\uc2e4 >+32200=\uc9c0\uc18d \ud30c\uc77c\uc744 \uc774\ubbf8 \uc0ac\uc6a9 \uc911 >diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_pl.properties b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_pl.properties >new file mode 100644 >index 0000000..6c1222a >--- /dev/null >+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_pl.properties >@@ -0,0 +1,30 @@ >+#/* >+# * 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 >+# */ >+# NLS_MESSAGEFORMAT_VAR >+# NLS_ENCODING=UNICODE >+1=Niepoprawna wersja protoko\u0142u >+2=Niepoprawny identyfikator klienta >+3=Broker niedost\u0119pny >+4=Niepoprawna nazwa u\u017cytkownika lub has\u0142o >+5=Brak autoryzacji do nawi\u0105zania po\u0142\u0105czenia >+32000=Przekroczono limit czasu oczekiwania na odpowied\u017a z serwera >+32100=Po\u0142\u0105czenie z klientem zosta\u0142o ju\u017c nawi\u0105zane >+32101=Klient ju\u017c si\u0119 roz\u0142\u0105czy\u0142 >+32102=Klient roz\u0142\u0105cza si\u0119 >+32103=Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia z serwerem >+32104=Po\u0142\u0105czenie z klientem nie jest nawi\u0105zane >+32105=Podany typ fabryki SocketFactory nie jest zgodny z identyfikatorem URI brokera >+32106=B\u0142\u0105d konfiguracji protoko\u0142u SSL >+32107=Roz\u0142\u0105czenie nie jest dozwolone w metodzie procedury zwrotnej >+32108=Nierozpoznany pakiet >+32109=Utracono po\u0142\u0105czenie >+32200=Trwa\u0142o\u015b\u0107 jest ju\u017c w u\u017cyciu >diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_pt_BR.properties b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_pt_BR.properties >new file mode 100644 >index 0000000..e4560de >--- /dev/null >+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_pt_BR.properties >@@ -0,0 +1,30 @@ >+#/* >+# * 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 >+# */ >+# NLS_MESSAGEFORMAT_VAR >+# NLS_ENCODING=UNICODE >+1=Vers\u00e3o de protocolo inv\u00e1lida >+2=ID de cliente inv\u00e1lido >+3=Broker indispon\u00edvel >+4=Nome de usu\u00e1rio ou senha inv\u00e1lidos >+5=N\u00e3o autorizado a conectar >+32000=Tempo limite atingido ao aguardar por uma resposta do servidor >+32100=Cliente j\u00e1 conectado >+32101=Cliente j\u00e1 desconectado >+32102=Cliente desconectando atualmente >+32103=N\u00e3o \u00e9 poss\u00edvel se conectar ao servidor >+32104=O cliente n\u00e3o est\u00e1 conectado >+32105=O tipo SocketFactory especificado n\u00e3o corresponde ao URI do broker >+32106=Erro de configura\u00e7\u00e3o de SSL >+32107=A desconex\u00e3o n\u00e3o \u00e9 permitida a partir de um m\u00e9todo de retorno de chamada >+32108=Pacote n\u00e3o reconhecido >+32109=Conex\u00e3o perdida >+32200=Persist\u00eancia j\u00e1 em uso >diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_ru.properties b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_ru.properties >new file mode 100644 >index 0000000..9ca99a0 >--- /dev/null >+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_ru.properties >@@ -0,0 +1,30 @@ >+#/* >+# * 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 >+# */ >+# NLS_MESSAGEFORMAT_VAR >+# NLS_ENCODING=UNICODE >+1=\u041d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0430 >+2=\u041d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u0418\u0414 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 >+3=\u041f\u043e\u0441\u0440\u0435\u0434\u043d\u0438\u043a \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d >+4=\u041e\u0448\u0438\u0431\u043e\u0447\u043d\u043e\u0435 \u0438\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438\u043b\u0438 \u043f\u0430\u0440\u043e\u043b\u044c >+5=\u041d\u0435\u0442 \u043f\u0440\u0430\u0432 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043d\u0430 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 >+32000=\u0422\u0430\u043c-\u0430\u0443\u0442 \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u044f \u043e\u0442\u0432\u0435\u0442\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 >+32100=\u041a\u043b\u0438\u0435\u043d\u0442 \u0443\u0436\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d >+32101=\u041a\u043b\u0438\u0435\u043d\u0442 \u0443\u0436\u0435 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d >+32102=\u041a\u043b\u0438\u0435\u043d\u0442 \u043e\u0442\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f >+32103=\u041d\u0435 \u0443\u0434\u0430\u0435\u0442\u0441\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443 >+32104=\u041a\u043b\u0438\u0435\u043d\u0442 \u043d\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d >+32105=\u0422\u0438\u043f SocketFactory \u043d\u0435 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 URI \u043f\u043e\u0441\u0440\u0435\u0434\u043d\u0438\u043a\u0430 >+32106=\u041e\u0448\u0438\u0431\u043a\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 SSL >+32107=\u041e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043d\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u043e \u0432 \u043c\u0435\u0442\u043e\u0434\u0435 \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430 >+32108=\u041d\u0435\u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u043d\u043d\u044b\u0439 \u043f\u0430\u043a\u0435\u0442 >+32109=\u0421\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043f\u043e\u0442\u0435\u0440\u044f\u043d\u043e >+32200=\u0425\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u0443\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f >diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_zh_CN.properties b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_zh_CN.properties >new file mode 100644 >index 0000000..cd3ae0d >--- /dev/null >+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_zh_CN.properties >@@ -0,0 +1,30 @@ >+#/* >+# * 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 >+# */ >+# NLS_MESSAGEFORMAT_VAR >+# NLS_ENCODING=UNICODE >+1=\u65e0\u6548\u534f\u8bae\u7248\u672c >+2=\u65e0\u6548\u5ba2\u6237\u673a\u6807\u8bc6 >+3=\u4ee3\u7406\u7a0b\u5e8f\u4e0d\u53ef\u7528 >+4=\u9519\u8bef\u7684\u7528\u6237\u540d\u6216\u5bc6\u7801 >+5=\u65e0\u6743\u8fde\u63a5 >+32000=\u7b49\u5f85\u6765\u81ea\u670d\u52a1\u5668\u7684\u54cd\u5e94\u65f6\u8d85\u65f6 >+32100=\u5ba2\u6237\u673a\u5df2\u8fde\u63a5 >+32101=\u5ba2\u6237\u673a\u5df2\u65ad\u5f00\u8fde\u63a5 >+32102=\u5ba2\u6237\u673a\u6b63\u5728\u65ad\u5f00\u8fde\u63a5 >+32103=\u65e0\u6cd5\u8fde\u63a5\u81f3\u670d\u52a1\u5668 >+32104=\u5ba2\u6237\u673a\u672a\u8fde\u63a5 >+32105=\u6307\u5b9a\u7684 SocketFactory \u7c7b\u578b\u4e0e\u4ee3\u7406\u7a0b\u5e8f URI \u4e0d\u5339\u914d >+32106=SSL \u914d\u7f6e\u9519\u8bef >+32107=\u4e0d\u5141\u8bb8\u901a\u8fc7\u56de\u8c03\u65b9\u6cd5\u65ad\u5f00\u8fde\u63a5 >+32108=\u4e0d\u53ef\u8bc6\u522b\u7684\u5305 >+32109=\u5df2\u65ad\u5f00\u8fde\u63a5 >+32200=\u6301\u4e45\u6027\u5df2\u5728\u4f7f\u7528\u4e2d >diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_zh_TW.properties b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_zh_TW.properties >new file mode 100644 >index 0000000..441b90e >--- /dev/null >+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_zh_TW.properties >@@ -0,0 +1,30 @@ >+#/* >+# * 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 >+# */ >+# NLS_MESSAGEFORMAT_VAR >+# NLS_ENCODING=UNICODE >+1=\u901a\u8a0a\u5354\u5b9a\u7248\u672c\u7121\u6548 >+2=\u7528\u6236\u7aef ID \u7121\u6548 >+3=\u5206\u914d\u7ba1\u7406\u7cfb\u7d71\u7121\u6cd5\u4f7f\u7528 >+4=\u4f7f\u7528\u8005\u540d\u7a31\u6216\u5bc6\u78bc\u4e0d\u7576 >+5=\u672a\u7372\u6388\u6b0a\u9023\u63a5 >+32000=\u7b49\u5f85\u4f3a\u670d\u5668\u7684\u56de\u61c9\u6642\u903e\u6642 >+32100=\u7528\u6236\u7aef\u5df2\u9023\u63a5 >+32101=\u7528\u6236\u7aef\u5df2\u4e2d\u65b7\u9023\u7dda >+32102=\u7528\u6236\u7aef\u76ee\u524d\u6b63\u5728\u4e2d\u65b7\u9023\u7dda >+32103=\u7121\u6cd5\u9023\u63a5\u5230\u4f3a\u670d\u5668 >+32104=\u7528\u6236\u7aef\u672a\u9023\u63a5 >+32105=\u6307\u5b9a\u7684 SocketFactory \u985e\u578b\u8207\u5206\u914d\u7ba1\u7406\u7cfb\u7d71 URI \u4e0d\u7b26 >+32106=SSL \u914d\u7f6e\u932f\u8aa4 >+32107=\u4e0d\u5bb9\u8a31\u8207\u56de\u547c\u65b9\u6cd5\u4e2d\u65b7\u9023\u7dda >+32108=\u5c01\u5305\u7121\u6cd5\u8fa8\u8b58 >+32109=\u9023\u7dda\u907a\u5931 >+32200=\u6301\u7e8c\u6027\u5df2\u5728\u4f7f\u7528\u4e2d >diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/security/SSLSocketFactoryFactory.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/security/SSLSocketFactoryFactory.java >new file mode 100644 >index 0000000..c7ba080 >--- /dev/null >+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/security/SSLSocketFactoryFactory.java >@@ -0,0 +1,1352 @@ >+/* >+ * 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.security; >+ >+import java.io.FileInputStream; >+import java.io.FileNotFoundException; >+import java.io.IOException; >+import java.security.KeyManagementException; >+import java.security.KeyStore; >+import java.security.KeyStoreException; >+import java.security.NoSuchAlgorithmException; >+import java.security.NoSuchProviderException; >+import java.security.UnrecoverableKeyException; >+import java.security.cert.CertificateException; >+import java.util.Hashtable; >+import java.util.Iterator; >+import java.util.Properties; >+import java.util.Set; >+import java.util.Vector; >+ >+import javax.net.ssl.KeyManager; >+import javax.net.ssl.KeyManagerFactory; >+import javax.net.ssl.SSLContext; >+import javax.net.ssl.SSLSocketFactory; >+import javax.net.ssl.TrustManager; >+import javax.net.ssl.TrustManagerFactory; >+ >+import org.eclipse.paho.client.mqttv3.MqttSecurityException; >+//import org.eclipse.paho.client.mqttv3.internal.comms.MqttDirectException; >+//import org.eclipse.paho.client.mqttv3.internal.comms.MqttSSLInitException; >+import org.eclipse.paho.client.mqttv3.logging.Logger; >+ >+ >+/** >+ * An SSLSocketFactoryFactory provides a socket factory and a server socket >+ * factory that then can be used to create SSL client sockets or SSL server >+ * sockets. >+ * <p> >+ * The SSLSocketFactoryFactory is configured using IBM SSL properties, i.e. >+ * properties of the format "com.ibm.ssl.propertyName", e.g. >+ * "com.ibm.ssl.keyStore". The class supports multiple configurations, each >+ * configuration is identified using a name or configuration ID. The >+ * configuration ID with "null" is used as a default configuration. When a >+ * socket factory is being created for a given configuration, properties of that >+ * configuration are first picked. If a property is not defined there, then that >+ * property is looked up in the default configuration. Finally, if a property >+ * element is still not found, then the corresponding system property is >+ * inspected, i.e. javax.net.ssl.keyStore. If the system property is not set >+ * either, then the system's default value is used (if available) or an >+ * exception is thrown. >+ * <p> >+ * The SSLSocketFacotryFactory can be reconfigured at any time. A >+ * reconfiguration does not affect existing socket factories. >+ * <p> >+ * All properties share the same key space; i.e. the configuration ID is not >+ * part of the property keys. >+ * <p> >+ * The methods should be called in the following order: >+ * <ol> >+ * <li><b>isSupportedOnJVM()</b>: to check whether this class is supported on >+ * the runtime platform. Not all runtimes support SSL/TLS.</li> >+ * <li><b>SSLSocketFactoryFactory()</b>: the constructor. Clients >+ * (in the same JVM) may share an SSLSocketFactoryFactory, or have one each.</li> >+ * <li><b>initialize(properties, configID)</b>: 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</li> >+ * <li><b>getEnabledCipherSuites(configID)</b>: to later set the enabled >+ * cipher suites on the socket [see below].</li> >+ * </ol> >+ * <ul> >+ * <li><i>For an MQTT server:</i></li> >+ * <ol> >+ * <li><b>getKeyStore(configID)</b>: Optionally, to check that if there is no >+ * keystore, then that all the enabled cipher suits are anonymous.</li> >+ * <li><b>createServerSocketFactory(configID)</b>: to create an >+ * SSLServerSocketFactory.</li> >+ * <li><b>getClientAuthentication(configID)</b>: to later set on the >+ * SSLServerSocket (itself created from the SSLServerSocketFactory) whether >+ * client authentication is needed.</li> >+ * </ol> >+ * <li><i>For an MQTT client:</i></li> >+ * <ol> >+ * <li><b>createSocketFactory(configID)</b>: to create an SSLSocketFactory.</li> >+ * </ol> >+ * </ul> >+ */ >+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<b.length) { >+ c[j++] = (char) ((b[i++] & 0xFF) + ((b[i++] & 0xFF)<<8)); >+ } >+ return c; >+ } >+ >+ /** >+ * Convert char array to byte array, where each char is split into two >+ * bytes. >+ * >+ * @param c >+ * char array >+ * @return byte array >+ */ >+ public static byte[] toByte(char[] c) { >+ if(c==null) return null; >+ byte[] b=new byte[c.length*2]; >+ int i=0; int j=0; >+ while(j<c.length) { >+ b[i++] = (byte) (c[j] & 0xFF); >+ b[i++] = (byte) ((c[j++] >> 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<password.length;i++) { >+// password[i]=' '; >+// } >+// putOrRemove(p, KEYSTOREPWD, ePasswd); >+// } >+ >+ /** >+ * Sets the keystore provider. The corresponding provider must be installed >+ * in the system. Example values: "IBMJCE" or "IBMJCEFIPS". >+ * >+ * @param configID >+ * The configuration identifier for selecting a configuration or >+ * null for the default configuration. >+ * @param provider >+ * The name of a java cryptography extension >+ */ >+// public void setKeyStoreProvider(String configID, String provider) { >+// Properties p = getOrCreate(configID); >+// putOrRemove(p, KEYSTOREPROVIDER, provider); >+// } >+ >+ /** >+ * Sets the keystore type. For example, PKCS12, JKS or JCEKS. The types that >+ * are supported depend on the keystore provider. >+ * >+ * @param configID >+ * The configuration identifier for selecting a configuration or >+ * null for the default configuration. >+ * @param type >+ * The keystore type >+ */ >+// public void setKeyStoreType(String configID, String type) { >+// Properties p = getOrCreate(configID); >+// putOrRemove(p, KEYSTORETYPE, type); >+// } >+ >+ /** >+ * Sets a custom key manager and the algorithm that it uses. The keymanager >+ * is specified in the format "algorithm|provider", for example >+ * "IbmX509|IBMJSSE2". The provider might be empty, in which case the >+ * default provider is configured with the specified algorithm. The key >+ * manager must implement the javax.net.ssl.X509KeyManager interface. >+ * >+ * @param configID >+ * The configuration identifier for selecting a configuration or >+ * null for the default configuration. >+ * @param keymanager >+ * An algorithm, provider pair as secified above. >+ */ >+// public void setCustomKeyManager(String configID, String keymanager) { >+// Properties p = getOrCreate(configID); >+// putOrRemove(p, CUSTOMKEYMGR, keymanager); >+// } >+ >+ /** >+ * Sets the filename of the truststore object. >+ * >+ * @param configID >+ * The configuration identifier for selecting a configuration or >+ * null for the default configuration. >+ * @param trustStore >+ * A filename that points to a valid truststore. >+ */ >+// public void setTrustStore(String configID, String trustStore) { >+// Properties p = getOrCreate(configID); >+// putOrRemove(p, TRUSTSTORE, trustStore); >+// } >+ >+ /** >+ * Sets the password that is used for the truststore. 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 truststore password. >+ */ >+// public void setTrustStorePassword(String configID, char[] password) { >+// Properties p = getOrCreate(configID); >+// // convert password, using XOR-based scrambling. >+// String ePasswd = obfuscate(password); >+// for(int i=0;i<password.length;i++) { >+// password[i]=' '; >+// } >+// putOrRemove(p, TRUSTSTOREPWD, ePasswd); >+// } >+ >+ /** >+ * Sets the truststore provider. The corresponding provider must be >+ * installed in the system. Example values: "IBMJCE" or "IBMJCEFIPS". >+ * >+ * @param configID >+ * The configuration identifier for selecting a configuration or >+ * null for the default configuration. >+ * @param provider >+ * The name of a java cryptography extension. >+ */ >+// public void setTrustStoreProvider(String configID, String provider) { >+// Properties p = getOrCreate(configID); >+// putOrRemove(p, TRUSTSTOREPROVIDER, provider); >+// } >+ >+ /** >+ * Sets the truststore type. For example, PKCS12, JKS or JCEKS. The types >+ * that are supported depend on the truststore provider. >+ * >+ * @param configID >+ * The configuration identifier for selecting a configuration or >+ * null for the default configuration. >+ * @param type >+ * The truststore type. >+ */ >+// public void setTrustStoreType(String configID, String type) { >+// Properties p = getOrCreate(configID); >+// putOrRemove(p, TRUSTSTORETYPE, type); >+// } >+ >+ /** >+ * Sets a custom trust managers and the algorithm that it uses. The >+ * trustmanager is specified in the format "algorithm|provider", for example >+ * "IbmX509|IBMJSSE2". The provider might be empty, in which case the >+ * default provider is configured with the specified algorithm. The trust >+ * manager must implement the javax.net.ssl.X509TrustManager interface. >+ * >+ * @param configID >+ * The configuration identifier for selecting a configuration or >+ * null for the default configuration. >+ * @param trustmanager >+ * An algorithm, provider pair as secified above. >+ */ >+// public void setCustomTrustManager(String configID, String trustmanager) { >+// Properties p = getOrCreate(configID); >+// putOrRemove(p, CUSTOMTRUSTMGR, trustmanager); >+// } >+ >+ /** >+ * Sets the list of enabled ciphers. For a list of acceptable values, see >+ * the documentation of the underlying JSSE. >+ * >+ * @param configID >+ * The configuration identifier for selecting a configuration or >+ * null for the default configuration. >+ * @param ciphers >+ * An array of cipher suite names such as >+ * SSL_RSA_WITH_AES_128_CBC_SHA. >+ */ >+// public void setEnabledCipherSuites(String configID, String[] ciphers) { >+// if (ciphers == null) >+// return; >+// Properties p = getOrCreate(configID); >+// String cipherSet = packCipherSuites(ciphers); >+// putOrRemove(p, CIPHERSUITES, cipherSet); >+// } >+ >+ /** >+ * Specifies whether the client is required to provide a valid certificate >+ * to the client during SSL negotiation. >+ * >+ * @param configID >+ * The configuration identifier for selecting a configuration or >+ * null for the default configuration. >+ * @param clientAuth >+ * true, if clients are required to authenticate, false >+ * otherwise. >+ */ >+// public void setClientAuthentication(String configID, boolean clientAuth) { >+// Properties p = getOrCreate(configID); >+// p.put(CLIENTAUTH, Boolean.toString(clientAuth)); >+// } >+ >+ /** >+ * Returns the property of a given key or null if it doesn't exist. It first >+ * scans the indicated configuration, then the default configuration, then >+ * the system properties. >+ * >+ * @param configID >+ * The configuration identifier for selecting a configuration or >+ * null for the default configuration. >+ * @param ibmKey >+ * @param sysProperty >+ * The key for the System property. >+ * @return the property of a given key or null if it doesn't exist. >+ */ >+ private String getProperty(String configID, String ibmKey, String sysProperty) { >+ String res = null; >+ res = getPropertyFromConfig(configID, ibmKey); >+ if ( res != null ) { >+ return res; >+ } >+ // scan system property, if it exists. >+ if (sysProperty != null) { >+ res = System.getProperty(sysProperty); >+ } >+ return res; >+ } >+ >+ /** >+ * Returns the property of a given key or null if it doesn't exist. It first >+ * scans the indicated configuration, then the default configuration >+ * >+ * @param configID >+ * The configuration identifier for selecting a configuration or >+ * null for the default configuration. >+ * @param ibmKey >+ * @return the property of a given key or null if it doesn't exist. It first >+ * scans the indicated configuration, then the default configuration >+ */ >+ private String getPropertyFromConfig(String configID, String ibmKey) { >+ String res = null; >+ Properties p =null; >+ if(configID!=null) {; >+ p = (Properties) configs.get(configID); >+ } >+ if (p != null) { >+ res = p.getProperty(ibmKey); >+ if (res != null) >+ return res; >+ } >+ // not found in config. try default properties. >+ p = (Properties) this.defaultProperties; >+ if (p != null) { >+ res = p.getProperty(ibmKey); >+ if (res != null) >+ return res; >+ } >+ return res; >+ } >+ >+ /** >+ * Gets the SSL protocol variant of the indicated configuration or the >+ * default configuration. >+ * >+ * @param configID >+ * The configuration identifier for selecting a configuration or >+ * null for the default configuration. >+ * @return The SSL protocol variant. >+ */ >+ public String getSSLProtocol(String configID) { >+ return getProperty(configID, SSLPROTOCOL, null); >+ } >+ >+ /** >+ * Gets the JSSE provider of the indicated configuration >+ * >+ * @param configID >+ * The configuration identifier for selecting a configuration or >+ * null for the default configuration. >+ * @return The JSSE provider. >+ */ >+ public String getJSSEProvider(String configID) { >+ return getProperty(configID, JSSEPROVIDER, null); >+ } >+ >+// /** >+// * Get the XPD Keystore if running on the XPD platform (otherwise null). >+// * >+// * @return the XPD Keystore if running on the XPD platform (otherwise null). >+// * @throws MqttDirectException >+// */ >+// private KeyStore getXPDKeystore() throws MqttDirectException { >+// KeyStore keyStore = null; >+// try { >+// Class secPlatClass = Class.forName("com.ibm.rcp.security.auth.SecurePlatform"); >+// Method m = secPlatClass.getMethod("getKeyStore", null); >+// Object secPlat = m.invoke(null,null); // getKeyStore is static >+// m = secPlatClass.getMethod("isLoggedIn", null); >+// Boolean b = (Boolean) m.invoke(secPlat, null); >+// if (b.booleanValue()) { >+// // login to secure platform was done. >+// m = secPlatClass.getMethod("getKeyStore", null); >+// keyStore = (KeyStore) m.invoke(secPlat, null); >+// } >+// } catch (ClassNotFoundException e) { >+// /* >+// * DEVELOPER NOTE: This is not an error. This means that we are not >+// * running on XPD runtime and therefore we can not get XPD keystore. >+// * [Next step for the caller, is try to get the keystore from System >+// * properties (see getKeyStore() method).] >+// */ >+// } catch (IllegalAccessException e) { >+// Object[] inserts = { e.getLocalizedMessage() }; >+// throw new MqttSSLInitException(3026, inserts, e); >+// } catch (SecurityException e) { >+// Object[] inserts = { e.getLocalizedMessage() }; >+// throw new MqttSSLInitException(3026, inserts, e); >+// } catch (NoSuchMethodException e) { >+// Object[] inserts = { e.getLocalizedMessage() }; >+// throw new MqttSSLInitException(3026, inserts, e); >+// } catch (IllegalArgumentException e) { >+// Object[] inserts = { e.getLocalizedMessage() }; >+// throw new MqttSSLInitException(3026, inserts, e); >+// } catch (InvocationTargetException e) { >+// Object[] inserts = { e.getLocalizedMessage() }; >+// throw new MqttSSLInitException(3026, inserts, e); >+// } >+// return keyStore; >+// } >+ >+ /** >+ * Gets the name of the keystore file that is used. >+ * >+ * @param configID >+ * The configuration identifier for selecting a configuration or >+ * null for the default configuration. >+ * @return The name of the file that contains the keystore. >+ */ >+ public String getKeyStore(String configID) { //throws MqttDirectException { >+ String ibmKey = KEYSTORE; >+ String sysProperty = SYSKEYSTORE; >+ >+ String res = null; >+ res = getPropertyFromConfig(configID, ibmKey); >+ if ( res != null ) { >+ return res; >+ } >+ >+// // check for the XPD keystore here >+// if ( ibmKey != null && ibmKey.equals(KEYSTORE) ) { >+// KeyStore keyStore = getXPDKeystore(); >+// if (keyStore != null) >+// return res = "Lotus Expeditor"; >+// } >+ >+ // scan system property, if it exists. >+ if (sysProperty != null) { >+ res = System.getProperty(sysProperty); >+ } >+ >+ return res; >+ } >+ >+ /** >+ * Gets the plain-text password that is used for the keystore. >+ * >+ * @param configID >+ * The configuration identifier for selecting a configuration or >+ * null for the default configuration. >+ * @return The password in plain text. >+ */ >+ public char[] getKeyStorePassword(String configID) { >+ String pw = getProperty(configID, KEYSTOREPWD, SYSKEYSTOREPWD); >+ char[] r=null; >+ if (pw!=null) { >+ if (pw.startsWith(xorTag)) { >+ r = deObfuscate(pw); >+ } else { >+ r = pw.toCharArray(); >+ } >+ } >+ return r; >+ } >+ >+ /** >+ * Gets the type of keystore. >+ * >+ * @param configID >+ * The configuration identifier for selecting a configuration or >+ * null for the default configuration. >+ * @return The keystore type. >+ */ >+ public String getKeyStoreType(String configID) { >+ return getProperty(configID, KEYSTORETYPE, SYSKEYSTORETYPE); >+ } >+ >+ /** >+ * Gets the keystore provider. >+ * >+ * @param configID >+ * The configuration identifier for selecting a configuration or >+ * null for the default configuration. >+ * @return The name of the keystore provider. >+ */ >+ public String getKeyStoreProvider(String configID) { >+ return getProperty(configID, KEYSTOREPROVIDER, null); >+ } >+ >+ /** >+ * Gets the key manager algorithm that is used. >+ * >+ * @param configID >+ * The configuration identifier for selecting a configuration or >+ * null for the default configuration. >+ * @return The key manager algorithm. >+ */ >+ public String getKeyManager(String configID) { >+ return getProperty(configID, KEYSTOREMGR, SYSKEYMGRALGO); >+ } >+ >+ /** >+ * Gets the name of the truststore file that is used. >+ * >+ * @param configID >+ * The configuration identifier for selecting a configuration or >+ * null for the default configuration. >+ * @return The name of the file that contains the truststore. >+ */ >+ public String getTrustStore(String configID) { >+ return getProperty(configID, TRUSTSTORE, SYSTRUSTSTORE); >+ } >+ >+ /** >+ * Gets the plain-text password that is used for the truststore. >+ * >+ * @param configID >+ * The configuration identifier for selecting a configuration or >+ * null for the default configuration. >+ * @return The password in plain text. >+ */ >+ public char[] getTrustStorePassword(String configID) { >+ String pw = getProperty(configID, TRUSTSTOREPWD, SYSTRUSTSTOREPWD); >+ char[] r=null; >+ if (pw!=null) { >+ if(pw.startsWith(xorTag)) { >+ r = deObfuscate(pw); >+ } else { >+ r = pw.toCharArray(); >+ } >+ } >+ return r; >+ } >+ >+ /** >+ * Gets the type of truststore. >+ * >+ * @param configID >+ * The configuration identifier for selecting a configuration or >+ * null for the default configuration. >+ * @return The truststore type. >+ */ >+ public String getTrustStoreType(String configID) { >+ return getProperty(configID, TRUSTSTORETYPE, null); >+ } >+ >+ /** >+ * Gets the truststore provider. >+ * >+ * @param configID >+ * The configuration identifier for selecting a configuration or >+ * null for the default configuration. >+ * @return The name of the truststore provider. >+ */ >+ public String getTrustStoreProvider(String configID) { >+ return getProperty(configID, TRUSTSTOREPROVIDER, null); >+ } >+ >+ /** >+ * Gets the trust manager algorithm that is used. >+ * >+ * @param configID >+ * The configuration identifier for selecting a configuration or >+ * null for the default configuration. >+ * @return The trust manager algorithm. >+ */ >+ public String getTrustManager(String configID) { >+ return getProperty(configID, TRUSTSTOREMGR, SYSTRUSTMGRALGO); >+ } >+ >+ /** >+ * Returns an array with the enabled ciphers. >+ * >+ * @param configID >+ * The configuration identifier for selecting a configuration or >+ * null for the default configuration. >+ * @return an array with the enabled ciphers >+ */ >+ public String[] getEnabledCipherSuites(String configID) { >+ String ciphers = getProperty(configID, CIPHERSUITES, null); >+ String[] res = unpackCipherSuites(ciphers); >+ return res; >+ } >+ >+ /** >+ * Returns whether client authentication is required. >+ * >+ * @param configID >+ * The configuration identifier for selecting a configuration or >+ * null for the default configuration. >+ * @return true, if clients are required to authenticate, false otherwise. >+ */ >+ public boolean getClientAuthentication(String configID) { >+ String auth = getProperty(configID, CLIENTAUTH, null); >+ boolean res = false; >+ if (auth != null) { >+ res = Boolean.valueOf(auth).booleanValue(); >+ } >+ return res; >+ } >+ >+ /** >+ * Initializes key- and truststore. Returns an SSL context factory. If no >+ * SSLProtocol is already set, uses DEFAULT_PROTOCOL >+ * >+ * @see org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory#DEFAULT_PROTOCOL >+ * >+ * @param configID >+ * The configuration ID >+ * @return An SSL context factory. >+ * @throws MqttDirectException >+ */ >+ private SSLContext getSSLContext(String configID) >+ throws MqttSecurityException{ >+ final String METHOD_NAME = "getSSLContext"; >+ SSLContext ctx = null; >+ >+ String protocol = getSSLProtocol(configID); >+ if (protocol == null) { >+ protocol = DEFAULT_PROTOCOL; >+ } >+ if (logger != null) { >+ // 12000 "SSL initialization: configID = {0}, protocol = {1}" >+ logger.fine(CLASS_NAME, METHOD_NAME, "12000", new Object[] {configID!=null ? configID : "null (broker defaults)", >+ protocol}); >+ } >+ >+ String provider = getJSSEProvider(configID); >+ try { >+ if (provider == null) { >+ ctx = SSLContext.getInstance(protocol); >+ } else { >+ ctx = SSLContext.getInstance(protocol, provider); >+ } >+ if (logger != null) { >+ // 12001 "SSL initialization: configID = {0}, provider = {1}" >+ logger.fine(CLASS_NAME, METHOD_NAME, "12001", new Object[] {configID!=null ? configID : "null (broker defaults)", >+ ctx.getProvider().getName()}); >+ } >+ >+ String keyStoreName = getProperty(configID, KEYSTORE, null); >+ KeyStore keyStore=null; >+ KeyManagerFactory keyMgrFact=null; >+ KeyManager[] keyMgr=null; >+// if(keyStoreName==null) { >+// // try to instantiate XPD keyStore. >+// keyStore=getXPDKeystore(); >+// if (logger != null) { >+// if (keyStore == null) { >+// // 12002 "SSL initialization: configID = {0}, XPD keystore not available" >+// logger.fine(CLASS_NAME, METHOD_NAME, "12002", new Object[]{configID!=null ? configID : "null (broker defaults)"}); >+// } else { >+// // 12003 "SSL initialization: configID = {0}, XPD keystore available" >+// logger.fine(CLASS_NAME, METHOD_NAME, "12003", new Object[]{configID!=null ? configID : "null (broker defaults)"}); >+// } >+// } >+// } >+ >+ if(keyStore==null) { >+ if(keyStoreName==null) { >+ /* >+ * No keystore in config, XPD keystore not available. Try to >+ * get config from system properties. >+ */ >+ keyStoreName = getProperty(configID, KEYSTORE, SYSKEYSTORE); >+ } >+ if (logger != null) { >+ // 12004 "SSL initialization: configID = {0}, keystore = {1}" >+ logger.fine(CLASS_NAME, METHOD_NAME, "12004", new Object[]{configID!=null ? configID : "null (broker defaults)", >+ keyStoreName!=null ? keyStoreName : "null"}); >+ } >+ >+ char[] keyStorePwd=getKeyStorePassword(configID); >+ if (logger != null) { >+ // 12005 "SSL initialization: configID = {0}, keystore password = {1}" >+ logger.fine(CLASS_NAME, METHOD_NAME, "12005", new Object[]{configID!=null ? configID : "null (broker defaults)", >+ keyStorePwd!=null ? obfuscate(keyStorePwd) : "null"}); >+ } >+ >+ String keyStoreType=getKeyStoreType(configID); >+ if(keyStoreType==null) { >+ keyStoreType = KeyStore.getDefaultType(); >+ } >+ if (logger != null) { >+ // 12006 "SSL initialization: configID = {0}, keystore type = {1}" >+ logger.fine(CLASS_NAME, METHOD_NAME, "12006", new Object[]{configID!=null ? configID : "null (broker defaults)", >+ keyStoreType!=null ? keyStoreType : "null"}); >+ } >+ >+ String keyMgrAlgo = KeyManagerFactory.getDefaultAlgorithm(); >+ String keyMgrProvider = getKeyStoreProvider(configID); >+ String keyManager = getKeyManager(configID); >+ if (keyManager != null) { >+ keyMgrAlgo = keyManager; >+ } >+ >+ if(keyStoreName!=null && keyStoreType!=null && keyMgrAlgo!=null) { >+ try { >+ keyStore=KeyStore.getInstance(keyStoreType); >+ keyStore.load(new FileInputStream(keyStoreName), keyStorePwd); >+ if(keyMgrProvider!=null) { >+ keyMgrFact = KeyManagerFactory.getInstance(keyMgrAlgo, keyMgrProvider); >+ } else { >+ keyMgrFact = KeyManagerFactory.getInstance(keyMgrAlgo); >+ } >+ if (logger != null) { >+ // 12010 "SSL initialization: configID = {0}, keystore manager algorithm = {1}" >+ logger.fine(CLASS_NAME, METHOD_NAME, "12010", new Object[]{configID!=null ? configID : "null (broker defaults)", >+ keyMgrAlgo!=null ? keyMgrAlgo : "null"}); >+ // 12009 "SSL initialization: configID = {0}, keystore manager provider = {1}" >+ logger.fine(CLASS_NAME, METHOD_NAME, "12009", new Object[]{configID!=null ? configID : "null (broker defaults)", >+ keyMgrFact.getProvider().getName()}); >+ } >+ keyMgrFact.init(keyStore, keyStorePwd); >+ keyMgr=keyMgrFact.getKeyManagers(); >+ } catch (KeyStoreException e) { >+ throw new MqttSecurityException(e); >+ } catch (CertificateException e) { >+ throw new MqttSecurityException(e); >+ } catch (FileNotFoundException e) { >+ throw new MqttSecurityException(e); >+ } catch (IOException e) { >+ throw new MqttSecurityException(e); >+ } catch (UnrecoverableKeyException e) { >+ throw new MqttSecurityException(e); >+ } >+ } >+ } >+ // keystore loaded, keymanagers instantiated if possible >+ // now the same for the truststore. >+ String trustStoreName = getTrustStore(configID); >+ if (logger != null) { >+ // 12011 "SSL initialization: configID = {0}, truststore = {1}" >+ logger.fine(CLASS_NAME, METHOD_NAME, "12011", new Object[]{configID!=null ? configID : "null (broker defaults)", >+ trustStoreName!=null ? trustStoreName : "null"}); >+ } >+ KeyStore trustStore=null; >+ TrustManagerFactory trustMgrFact=null; >+ TrustManager[] trustMgr=null; >+ char[] trustStorePwd=getTrustStorePassword(configID); >+ if (logger != null) { >+ // 12012 "SSL initialization: configID = {0}, truststore password = {1}" >+ logger.fine(CLASS_NAME, METHOD_NAME, "12012", new Object[]{configID!=null ? configID : "null (broker defaults)", >+ trustStorePwd!=null ? obfuscate(trustStorePwd) : "null"}); >+ } >+ String trustStoreType=getTrustStoreType(configID); >+ if(trustStoreType==null) { >+ trustStoreType = KeyStore.getDefaultType(); >+ } >+ if (logger != null) { >+ // 12013 "SSL initialization: configID = {0}, truststore type = {1}" >+ logger.fine(CLASS_NAME, METHOD_NAME, "12013", new Object[]{configID!=null ? configID : "null (broker defaults)", >+ trustStoreType!=null ? trustStoreType : "null"}); >+ } >+ >+ String trustMgrAlgo = TrustManagerFactory.getDefaultAlgorithm(); >+ String trustMgrProvider = getTrustStoreProvider(configID); >+ String trustManager = getTrustManager(configID); >+ if (trustManager != null) { >+ trustMgrAlgo = trustManager; >+ } >+ >+ if(trustStoreName!=null && trustStoreType!=null && trustMgrAlgo!=null) { >+ try { >+ trustStore=KeyStore.getInstance(trustStoreType); >+ trustStore.load(new FileInputStream(trustStoreName), trustStorePwd); >+ if(trustMgrProvider!=null) { >+ trustMgrFact = TrustManagerFactory.getInstance(trustMgrAlgo, trustMgrProvider); >+ } else { >+ trustMgrFact = TrustManagerFactory.getInstance(trustMgrAlgo); >+ } >+ if (logger != null) { >+ >+ // 12017 "SSL initialization: configID = {0}, truststore manager algorithm = {1}" >+ logger.fine(CLASS_NAME, METHOD_NAME, "12017", new Object[]{configID!=null ? configID : "null (broker defaults)", >+ trustMgrAlgo!=null ? trustMgrAlgo : "null"}); >+ >+ // 12016 "SSL initialization: configID = {0}, truststore manager provider = {1}" >+ logger.fine(CLASS_NAME, METHOD_NAME, "12016", new Object[]{configID!=null ? configID : "null (broker defaults)", >+ trustMgrFact.getProvider().getName()}); >+ } >+ trustMgrFact.init(trustStore); >+ trustMgr=trustMgrFact.getTrustManagers(); >+ } catch (KeyStoreException e) { >+ throw new MqttSecurityException(e); >+ } catch (CertificateException e) { >+ throw new MqttSecurityException(e); >+ } catch (FileNotFoundException e) { >+ throw new MqttSecurityException(e); >+ } catch (IOException e) { >+ throw new MqttSecurityException(e); >+ } >+ } >+ // done. >+ ctx.init(keyMgr, trustMgr, null); >+ } catch (NoSuchAlgorithmException e) { >+ throw new MqttSecurityException(e); >+ } catch (NoSuchProviderException e) { >+ throw new MqttSecurityException(e); >+ } catch (KeyManagementException e) { >+ throw new MqttSecurityException(e); >+ } >+ return ctx; >+ } >+ >+// /** >+// * Returns an SSL server socket factory for the given configuration. If no >+// * SSLProtocol is already set, uses DEFAULT_PROTOCOL. Throws >+// * IllegalArgumentException if the server socket factory could not be >+// * created due to underlying configuration problems. >+// * >+// * @see org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory#DEFAULT_PROTOCOL >+// * >+// * @param configID >+// * The configuration identifier for selecting a configuration. >+// * @return An SSLServerSocketFactory >+// * @throws MqttDirectException >+// */ >+// public SSLServerSocketFactory createServerSocketFactory(String configID) >+// throws MqttDirectException { >+// final String METHOD_NAME = "createServerSocketFactory"; >+// SSLContext ctx = getSSLContext(configID); >+// if (logger != null) { >+// // 12018 "SSL initialization: configID = {0}, application-enabled cipher suites = {1}" >+// logger.fine(CLASS_NAME, METHOD_NAME, "12018", new Object[]{configID!=null ? configID : "null (broker defaults)", >+// getEnabledCipherSuites(configID)!=null ? getProperty(configID, CIPHERSUITES, null) : "null (using platform-enabled cipher suites)"}); >+// >+// // 12019 "SSL initialization: configID = {0}, client authentication = {1}" >+// logger.fine(CLASS_NAME, METHOD_NAME, "12019", new Object[]{configID!=null ? configID : "null (broker defaults)", >+// new Boolean (getClientAuthentication(configID)).toString()}); >+// } >+// >+// return ctx.getServerSocketFactory(); >+// } >+ >+ /** >+ * Returns an SSL socket factory for the given configuration. If no >+ * SSLProtocol is already set, uses DEFAULT_PROTOCOL. Throws >+ * IllegalArgumentException if the socket factory could not be created due >+ * to underlying configuration problems. >+ * >+ * @see org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory#DEFAULT_PROTOCOL >+ * @param configID >+ * The configuration identifier for selecting a configuration. >+ * @return An SSLSocketFactory >+ * @throws MqttDirectException >+ */ >+ public SSLSocketFactory createSocketFactory(String configID) >+ throws MqttSecurityException { >+ final String METHOD_NAME = "createSocketFactory"; >+ SSLContext ctx = getSSLContext(configID); >+ if (logger != null) { >+ // 12020 "SSL initialization: configID = {0}, application-enabled cipher suites = {1}" >+ logger.fine(CLASS_NAME, METHOD_NAME, "12020", new Object[]{configID!=null ? configID : "null (broker defaults)", >+ getEnabledCipherSuites(configID)!=null ? getProperty(configID, CIPHERSUITES, null) : "null (using platform-enabled cipher suites)"}); >+ } >+ >+ return ctx.getSocketFactory(); >+ } >+ >+} >diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/security/SimpleBase64Encoder.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/security/SimpleBase64Encoder.java >new file mode 100644 >index 0000000..afc4d46 >--- /dev/null >+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/security/SimpleBase64Encoder.java >@@ -0,0 +1,123 @@ >+/* >+ * 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.security; >+ >+public class SimpleBase64Encoder { >+ >+ // if this string is changed, then the decode method must also be adapted. >+ private final static String PWDCHARS_STRING = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; >+ private final static char[] PWDCHARS_ARRAY = PWDCHARS_STRING.toCharArray(); >+ >+ /** >+ * Encodes an array of byte into a string of printable ASCII characters >+ * using a base-64 encoding. >+ * @param bytes The array of bytes to e encoded >+ * @return The encoded array. >+ */ >+ public static String encode(byte[] bytes) { >+ // Allocate a string buffer. >+ int len = bytes.length; >+ final StringBuffer encoded = new StringBuffer((len+2)/3*4); >+ int i=0; >+ int j=len; >+ while(j>=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 <code>CountingInputStream</code> 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 <code>MqttInputStream</code> lets applications read instances of >+ * <code>MqttWireMessage</code>. >+ */ >+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 <code>MqttWireMessage</code> 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 <code>MqttOutputStream</code> lets applications write instances of >+ * <code>MqttWireMessage</code>. >+ */ >+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 <code>MqttWireMessage</code> 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 <code>false</code> 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<qos.length;i++) { >+ MqttMessage.validateQos(qos[i]); >+ } >+ } >+ >+ protected byte getMessageInfo() { >+ return (byte)(2 | (this.duplicate?8:0)); >+ } >+ >+ protected byte[] getVariableHeader() 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 byte[] getPayload() throws MqttException { >+ try { >+ ByteArrayOutputStream baos = new ByteArrayOutputStream(); >+ DataOutputStream dos = new DataOutputStream(baos); >+ for (int i=0; i<names.length; i++) { >+ this.encodeUTF8(dos,names[i]); >+ dos.writeByte(qos[i]); >+ } >+ return baos.toByteArray(); >+ } >+ catch (IOException ex) { >+ throw new MqttException(ex); >+ } >+ } >+ >+ public boolean isRetryable() { >+ return true; >+ } >+} >diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttUnsubAck.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttUnsubAck.java >new file mode 100644 >index 0000000..32ed349 >--- /dev/null >+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttUnsubAck.java >@@ -0,0 +1,38 @@ >+/* >+ * 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 UNSUBACK. >+ */ >+public class MqttUnsubAck extends MqttAck { >+ >+ public MqttUnsubAck(byte info, byte[] data) throws IOException { >+ super(MqttWireMessage.MESSAGE_TYPE_UNSUBACK); >+ ByteArrayInputStream bais = new ByteArrayInputStream(data); >+ DataInputStream dis = new DataInputStream(bais); >+ msgId = dis.readUnsignedShort(); >+ dis.close(); >+ } >+ >+ protected byte[] getVariableHeader() throws MqttException { >+ // Not needed, as the client never encodes an UNSUBACK >+ return new byte[0]; >+ } >+} >diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttUnsubscribe.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttUnsubscribe.java >new file mode 100644 >index 0000000..b74d4b7 >--- /dev/null >+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttUnsubscribe.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.ByteArrayOutputStream; >+import java.io.DataOutputStream; >+import java.io.IOException; >+ >+import org.eclipse.paho.client.mqttv3.MqttException; >+ >+ >+/** >+ * An on-the-wire representation of an MQTT UNSUBSCRIBE message. >+ */ >+public class MqttUnsubscribe extends MqttWireMessage { >+ >+ private String[] names; >+ >+ /** >+ * Constructs an MqttUnsubscribe >+ */ >+ public MqttUnsubscribe(String[] names) { >+ super(MqttWireMessage.MESSAGE_TYPE_UNSUBSCRIBE); >+ this.names = names; >+ } >+ >+ protected byte getMessageInfo() { >+ return (byte)( 2 | (this.duplicate?8:0)); >+ } >+ >+ protected byte[] getVariableHeader() 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 byte[] getPayload() throws MqttException { >+ ByteArrayOutputStream baos = new ByteArrayOutputStream(); >+ DataOutputStream dos = new DataOutputStream(baos); >+ for (int i=0; i<names.length; i++) { >+ this.encodeUTF8(dos, names[i]); >+ } >+ return baos.toByteArray(); >+ } >+ public boolean isRetryable() { >+ return true; >+ } >+} >diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttWireMessage.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttWireMessage.java >new file mode 100644 >index 0000000..f7200bc >--- /dev/null >+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttWireMessage.java >@@ -0,0 +1,329 @@ >+/* >+ * 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 java.io.InputStream; >+import java.io.UnsupportedEncodingException; >+ >+import org.eclipse.paho.client.mqttv3.MqttException; >+import org.eclipse.paho.client.mqttv3.MqttPersistable; >+import org.eclipse.paho.client.mqttv3.internal.ExceptionHelper; >+ >+ >+/** >+ * An on-the-wire representation of an MQTT message. >+ */ >+public abstract class MqttWireMessage { >+ protected static final String STRING_ENCODING = "UTF-8"; >+ >+ public static final byte MESSAGE_TYPE_CONNECT = 1; >+ public static final byte MESSAGE_TYPE_CONNACK = 2; >+ public static final byte MESSAGE_TYPE_PUBLISH = 3; >+ public static final byte MESSAGE_TYPE_PUBACK = 4; >+ public static final byte MESSAGE_TYPE_PUBREC = 5; >+ public static final byte MESSAGE_TYPE_PUBREL = 6; >+ public static final byte MESSAGE_TYPE_PUBCOMP = 7; >+ public static final byte MESSAGE_TYPE_SUBSCRIBE = 8; >+ public static final byte MESSAGE_TYPE_SUBACK = 9; >+ public static final byte MESSAGE_TYPE_UNSUBSCRIBE = 10; >+ public static final byte MESSAGE_TYPE_UNSUBACK = 11; >+ public static final byte MESSAGE_TYPE_PINGREQ = 12; >+ public static final byte MESSAGE_TYPE_PINGRESP = 13; >+ public static final byte MESSAGE_TYPE_DISCONNECT = 14; >+ >+ /** The type of the message (e.g. CONNECT, PUBLISH, PUBACK) */ >+ private byte type; >+ /** The MQTT message ID */ >+ protected int msgId; >+ >+ protected boolean duplicate = false; >+ >+ private byte[] encodedHeader = null; >+ >+ public MqttWireMessage(byte type) { >+ this.type = type; >+ // Use zero as the default message ID. Can't use -1, as that is serialized >+ // as 65535, which would be a valid ID. >+ this.msgId = 0; >+ } >+ >+ /** >+ * Sub-classes should override this to encode the message info. >+ * Only the least-significant four bits will be used. >+ */ >+ abstract protected byte getMessageInfo(); >+ >+ /** >+ * Sub-classes should override this method to supply the payload bytes. >+ */ >+ public byte[] getPayload() throws MqttException { >+ return new byte[0]; >+ } >+ >+ /** >+ * Returns the type of the message. >+ */ >+ public byte getType() { >+ return type; >+ } >+ >+ /** >+ * Returns the MQTT message ID. >+ */ >+ public int getMessageId() { >+ return msgId; >+ } >+ >+ /** >+ * Sets the MQTT message ID. >+ */ >+ public void setMessageId(int msgId) { >+ this.msgId = msgId; >+ } >+ >+ /** >+ * Returns a key associated with the message. For most message types >+ * this will be unique. For connect, disconnect and ping only one >+ * message of this type is allowed so a fixed key will be returned >+ * @return key a key associated with the message >+ */ >+ public String getKey() { >+ return new Integer(getMessageId()).toString(); >+ } >+ >+ public byte[] getHeader() throws MqttException { >+ if (encodedHeader == null) { >+ try { >+ int first = ((getType() & 0x0f) << 4) ^ (getMessageInfo() & 0x0f); >+ byte[] varHeader = getVariableHeader(); >+ int remLen = varHeader.length + getPayload().length; >+ >+ ByteArrayOutputStream baos = new ByteArrayOutputStream(); >+ DataOutputStream dos = new DataOutputStream(baos); >+ dos.writeByte(first); >+ dos.write(encodeMBI(remLen)); >+ dos.write(varHeader); >+ dos.flush(); >+ encodedHeader = baos.toByteArray(); >+ } catch(IOException ioe) { >+ throw new MqttException(ioe); >+ } >+ } >+ return encodedHeader; >+ } >+ >+ protected abstract byte[] getVariableHeader() throws MqttException; >+ >+ >+ /** >+ * Returns whether or not this message needs to include a message ID. >+ */ >+ public boolean isMessageIdRequired() { >+ return true; >+ } >+ >+ public static MqttWireMessage createWireMessage(MqttPersistable data) throws MqttException { >+ byte[] payload = data.getPayloadBytes(); >+ // The persistable interface allows a message to be restored entirely in the header array >+ // Need to treat these two arrays as a single array of bytes and use the decoding >+ // logic to identify the true header/payload split >+ if (payload == null) { >+ payload = new byte[0]; >+ } >+ MultiByteArrayInputStream mbais = new MultiByteArrayInputStream( >+ data.getHeaderBytes(), >+ data.getHeaderOffset(), >+ data.getHeaderLength(), >+ payload, >+ data.getPayloadOffset(), >+ data.getPayloadLength()); >+ return createWireMessage(mbais); >+ } >+ >+ public static MqttWireMessage createWireMessage(byte[] bytes) throws MqttException { >+ ByteArrayInputStream bais = new ByteArrayInputStream(bytes); >+ return createWireMessage(bais); >+ } >+ >+ private static MqttWireMessage createWireMessage(InputStream inputStream) throws MqttException { >+ try { >+ CountingInputStream counter = new CountingInputStream(inputStream); >+ DataInputStream in = new DataInputStream(counter); >+ int first = in.readUnsignedByte(); >+ byte type = (byte) (first >> 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 (pos<lengthA) { >+ result = bytesA[offsetA+pos]; >+ } else if (pos<lengthA+lengthB) { >+ result = bytesB[offsetB+pos-lengthA]; >+ } else { >+ return -1; >+ } >+ if (result < 0) { >+ result += 256; >+ } >+ pos++; >+ return result; >+ } >+ >+} >diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MultiByteInteger.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MultiByteInteger.java >new file mode 100644 >index 0000000..b88de04 >--- /dev/null >+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MultiByteInteger.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; >+ >+/** >+ * Represents a Multi-Byte Integer (MBI), as defined by the MQTT V3 >+ * specification. >+ */ >+public class MultiByteInteger { >+ private long value; >+ private int length; >+ >+ public MultiByteInteger(long value) { >+ this(value, -1); >+ } >+ >+ public MultiByteInteger(long value, int length) { >+ this.value = value; >+ this.length = length; >+ } >+ >+ /** >+ * Returns the number of bytes read when decoding this MBI. >+ */ >+ public int getEncodedLength() { >+ return length; >+ } >+ >+ /** >+ * Returns the value of this MBI. >+ */ >+ public long getValue() { >+ return value; >+ } >+} >diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/logging/JSR47Logger.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/logging/JSR47Logger.java >new file mode 100644 >index 0000000..4a53875 >--- /dev/null >+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/logging/JSR47Logger.java >@@ -0,0 +1,272 @@ >+/* >+ * 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.text.MessageFormat; >+import java.util.MissingResourceException; >+import java.util.ResourceBundle; >+import java.util.logging.Handler; >+import java.util.logging.LogRecord; >+import java.util.logging.MemoryHandler; >+ >+/** >+ * Implementation of the the logger interface that uses java.uti.logging >+ * >+ * A Logger that utilises Java's built in logging facility - java.util.logging. >+ * <p>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. >+ */ >+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<handlers.length; i++) { >+ if (handlers[i] instanceof java.util.logging.MemoryHandler) { >+ synchronized (handlers[i]) { >+ mHand = ((java.util.logging.MemoryHandler)handlers[i]); >+ mHand.push(); >+ return; >+ } // synchronized (handler). >+ } >+ } // for handlers... >+ dumpMemoryTrace47(logger.getParent()); >+ } >+ } >+ >+} >diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/logging/Logger.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/logging/Logger.java >new file mode 100644 >index 0000000..b72bdd8 >--- /dev/null >+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/logging/Logger.java >@@ -0,0 +1,572 @@ >+/* >+ * 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.util.ResourceBundle; >+ >+/** >+ * A Logger object is used to send log and trace messages to a platform >+ * specific logging implementation. Loggers are named, using a hierarchical >+ * dot-separated name-space. >+ * Logger names can be arbitrary strings, but they should normally be based on >+ * the component or the package name of the logged component >+ * >+ * Logger objects may be obtained by calls on one of the getLogger factory >+ * methods. These will either create a new Logger or return a suitable existing >+ * Logger. >+ * >+ * <p> >+ * 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. >+ * <p> >+ * 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. >+ * <p> >+ * The levels in descending order are: >+ * <ul> >+ * <li>SEVERE (log - highest value)</li> >+ * <li>WARNING (log)</li> >+ * <li>INFO (log)</li> >+ * <li>CONFIG (log)</li> >+ * <li>FINE (trace)</li> >+ * <li>FINER (trace)</li> >+ * <li>FINEST (trace - lowest value)</li> >+ * </ul> >+ * <p> >+ */ >+public interface Logger { >+ /** >+ * SEVERE is a message level indicating a serious failure. >+ * <p> >+ * 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. >+ * <p> >+ * 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. >+ * <p> >+ * 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. >+ * <p> >+ * 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. >+ * <p> >+ * 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. >+ * <p> >+ * 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. >+ * <p> >+ * 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 <code>SimpleFormatter</code> 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=<location>\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 @@ >+<body> >+Provides facilities to write and format log and trace to help debug problems. >+ >+<p>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. >+ >+<p>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. >+ >+</body> >\ 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 @@ >+<body> >+Contains a programming interface enabling applications to communicate with an MQTT server >+ >+<p> >+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: >+<ul> >+ <li>Where the network is expensive, has low bandwidth or is unreliable such as mobile and vsat networks >+ <li>When run on an embedded or mobile device with limited processor, memory or battery >+</ul> >+<p>Features of the protocol include: >+<ul> >+ <li>The publish/subscribe message pattern to provide one-to-many message >+ distribution and decoupling of applications >+ <li>A messaging transport that is agnostic to the content of the payload >+ <li>The use of TCP/IP to provide network connectivity >+ <li>The use of SSL/TLS to provide network security and trust >+ <li>Three qualities of service for message delivery which are maintained across >+ network, client and server breaks. >+ <ul> >+ <li>"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. >+ <li>"At least once", where messages are assured to arrive but duplicates may occur. >+ <li>"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. >+ </ul> >+ 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 >+ <li>A small transport overhead (the fixed-length header is just 2 bytes), and >+ protocol exchanges minimised to reduce network traffic >+ <li>A mechanism to notify interested parties to an abnormal disconnection of >+ a client using the Last Will and Testament feature >+</ul> >+ >+<p>The basic means of operating the client is:</p> >+<ol> >+ <li>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.</li> >+ <li><code>connect</code> to the server</li> >+ <li>Exchange messages with the server: >+ <ul> >+ <li><code>publish messages</code> to the server, >+ via a <code>topic</code>.</li> >+ <li><code>subscribe</code> to one more <code>topics</code>. 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 <code>callback</code> >+ </ul> >+ <li><code>disconnect</code> from the server.</li> >+</ol> >+ >+<p>The programming model and concepts like the protocol are small and easy to use. Key concepts >+to use when creating MQTT application include: >+<ul> >+ <li>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. >+ <li>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. >+ <li>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: >+ <ul> >+ <li>Message delivery will match the quality of service specified when the message was published even across >+ failures of the network, client or server >+ <li>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. >+ </ul> >+ If set to true: >+ <ul> >+ <li>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. >+ <li>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. >+ <li>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 >+ </ul> >+ <li>When subscribing for messages the subscription can be for an absolute topic or a wildcarded topic. >+ <li>When unsubscribing the topic to be unsubscribed must match one specified on an earlier subscribe. >+ <li>There are two MQTT client libraries to choose from: >+ <ol> >+ <li>{@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: >+ <ul> >+ <li>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. >+ <li>Pass a {@link org.eclipse.paho.client.mqttv3.IMqttActionListener IMqttActionListener} >+ to the operation. The listener will then be called back when the operation completes. >+ <li>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. >+ </ul> >+ <li>{@link org.eclipse.paho.client.mqttv3.IMqttClient MqttClient} where methods block until >+ the operation has completed. >+ </ol> >+ <li>For both the blocking and non-blocking clients some operations are asynchronous. This includes: >+ <ul> >+ <li>Notification that a new message has arrived: >+ {@link org.eclipse.paho.client.mqttv3.MqttCallback#messageArrived messageArrived}. >+ <li>Notification that the connection to the server has broken: >+ {@link org.eclipse.paho.client.mqttv3.MqttCallback#connectionLost connectionLost}. >+ <li>Notification that a message has been delivered to the server: >+ {@link org.eclipse.paho.client.mqttv3.MqttCallback#deliveryComplete deliveryComplete}. >+ </ul> >+ A client registers interest in these notifications by registering a >+ {@link org.eclipse.paho.client.mqttv3.MqttCallback MqttCallback} on the client >+ <li>There are a number of programs that demonstrate the different modes of >+ writing MQTT applications >+ <ul> >+ <li>{@link org.eclipse.paho.sample.mqttv3app.Sample} uses the blocking client interface >+ <li>{@link org.eclipse.paho.sample.mqttv3app.SampleAsyncCallBack} uses the asynchronous client with >+ callbacks which are notified when an operation completes >+ <li>{@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. >+ </ul> >+</ul> >+ >+</body> >\ 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<clientId.length();i++) { >+ char c = clientId.charAt(i); >+ if (isSafeChar(c)) { >+ keyBuffer.append(c); >+ } >+ } >+ keyBuffer.append("-"); >+ for (int i=0;i<theConnection.length();i++) { >+ char c = theConnection.charAt(i); >+ if (isSafeChar(c)) { >+ keyBuffer.append(c); >+ } >+ } >+ String key = keyBuffer.toString(); >+ >+ clientDir = new File(dataDir,key); >+ >+ if (!clientDir.exists()) { >+ clientDir.mkdir(); >+ } >+ >+ try { >+ fileLock = new FileLock(clientDir,LOCK_FILENAME); >+ } catch (Exception e) { >+ throw new MqttPersistenceException(MqttPersistenceException.REASON_CODE_PERSISTENCE_IN_USE); >+ } >+ >+ // Scan the directory for .backup files. These will >+ // still exist if the JVM exited during addMessage, before >+ // the new message was written to disk and the backup removed. >+ restoreBackups(clientDir); >+ >+ } >+ >+ /** >+ * Checks whether the persistence has been opened. >+ * @throws MqttPersistenceException if the persistence has not been opened. >+ */ >+ private void checkIsOpen() throws MqttPersistenceException { >+ if (clientDir == null) { >+ throw new MqttPersistenceException(); >+ } >+ } >+ >+ public void close() throws MqttPersistenceException { >+ >+// checkIsOpen(); >+ if (fileLock != null) { >+ fileLock.release(); >+ } >+ >+ if (getFiles().length == 0) { >+ clientDir.delete(); >+ } >+ clientDir = null; >+ } >+ >+ /** >+ * Writes the specified persistent data to the previously specified persistence directory. >+ * This method uses a safe overwrite policy to ensure IO errors do not lose messages. >+ * @param message >+ * @throws MqttPersistenceException >+ */ >+ public void put(String key, MqttPersistable message) throws MqttPersistenceException { >+ checkIsOpen(); >+ File file = new File(clientDir, key+MESSAGE_FILE_EXTENSION); >+ File backupFile = new File(clientDir, key+MESSAGE_FILE_EXTENSION+MESSAGE_BACKUP_FILE_EXTENSION); >+ >+ if (file.exists()) { >+ // Backup the existing file so the overwrite can be rolled-back >+ boolean result = file.renameTo(backupFile); >+ if (!result) { >+ backupFile.delete(); >+ file.renameTo(backupFile); >+ } >+ } >+ try { >+ FileOutputStream fos = new FileOutputStream(file); >+ fos.write(message.getHeaderBytes(), message.getHeaderOffset(), message.getHeaderLength()); >+ if (message.getPayloadBytes()!=null) { >+ fos.write(message.getPayloadBytes(), message.getPayloadOffset(), message.getPayloadLength()); >+ } >+ fos.getFD().sync(); >+ fos.close(); >+ if (backupFile.exists()) { >+ // The write has completed successfully, delete the backup >+ backupFile.delete(); >+ } >+ } >+ catch (IOException ex) { >+ throw new MqttPersistenceException(ex); >+ } >+ finally { >+ if (backupFile.exists()) { >+ // The write has failed - restore the backup >+ boolean result = backupFile.renameTo(file); >+ if (!result) { >+ file.delete(); >+ backupFile.renameTo(file); >+ } >+ } >+ } >+ } >+ >+ public MqttPersistable get(String key) throws MqttPersistenceException { >+ checkIsOpen(); >+ MqttPersistable result; >+ try { >+ File file = new File(clientDir, key+MESSAGE_FILE_EXTENSION); >+ FileInputStream fis = new FileInputStream(file); >+ int size = fis.available(); >+ byte[] data = new byte[size]; >+ int read = 0; >+ while (read<size) { >+ read += fis.read(data,read,size-read); >+ } >+ fis.close(); >+ result = new MqttPersistentData(key, data, 0, data.length, null, 0, 0); >+ } >+ catch(IOException ex) { >+ throw new MqttPersistenceException(ex); >+ } >+ return result; >+ } >+ >+ >+ /** >+ * Deletes the data with the specified key from the previously specified persistence directory. >+ */ >+ public void remove(String key) throws MqttPersistenceException { >+ checkIsOpen(); >+ File file = new File(clientDir, key+MESSAGE_FILE_EXTENSION); >+ if (file.exists()) { >+ file.delete(); >+ } >+ } >+ >+ /** >+ * Returns all of the persistent data from the previously specified persistence directory. >+ * @return all of the persistent data from the persistence directory. >+ * @throws MqttPersistenceException >+ */ >+ public Enumeration keys() throws MqttPersistenceException { >+ checkIsOpen(); >+ File[] files = getFiles(); >+ Vector result = new Vector(files.length); >+ for (int i=0;i<files.length;i++) { >+ String filename = files[i].getName(); >+ String key = filename.substring(0,filename.length()-MESSAGE_FILE_EXTENSION.length()); >+ result.addElement(key); >+ } >+ return result.elements(); >+ } >+ >+ private File[] getFiles() throws MqttPersistenceException { >+ checkIsOpen(); >+ File[] files = clientDir.listFiles(FILE_FILTER); >+ if (files == null) { >+ throw new MqttPersistenceException(); >+ } >+ return files; >+ } >+ >+ private boolean isSafeChar(char c) { >+ return Character.isJavaIdentifierPart(c) || c=='-'; >+ } >+ >+ /** >+ * Identifies any backup files in the specified directory and restores them >+ * to their original file. This will overwrite any existing file of the same >+ * name. This is safe as a stray backup file will only exist if a problem >+ * occured whilst writing to the original file. >+ * @param dir The directory in which to scan and restore backups >+ */ >+ private void restoreBackups(File dir) throws MqttPersistenceException { >+ File[] files = dir.listFiles(new FileFilter() { >+ public boolean accept(File f) { >+ return f.getName().endsWith(MESSAGE_BACKUP_FILE_EXTENSION); >+ } >+ }); >+ if (files == null) { >+ throw new MqttPersistenceException(); >+ } >+ >+ for (int i=0;i<files.length;i++) { >+ File originalFile = new File(dir,files[i].getName().substring(0,files[i].getName().length()-MESSAGE_BACKUP_FILE_EXTENSION.length())); >+ boolean result = files[i].renameTo(originalFile); >+ if (!result) { >+ originalFile.delete(); >+ files[i].renameTo(originalFile); >+ } >+ } >+ } >+ >+ public boolean containsKey(String key) throws MqttPersistenceException { >+ checkIsOpen(); >+ File file = new File(clientDir, key+MESSAGE_FILE_EXTENSION); >+ return file.exists(); >+ } >+ >+ public void clear() throws MqttPersistenceException { >+ checkIsOpen(); >+ File[] files = getFiles(); >+ for (int i=0; i<files.length; i++) { >+ files[i].delete(); >+ } >+ } >+} >diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/persist/package.html b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/persist/package.html >new file mode 100644 >index 0000000..dd20b5e >--- /dev/null >+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/persist/package.html >@@ -0,0 +1,12 @@ >+<body> >+Contains implementations of the MqttClientPersistence interface. >+ >+<p> >+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. >+ >+</body> >\ 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 @@ >+<body> >+Provides helpers and utilities. >+ >+ >+</body> >\ 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. >- * >- * <p>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. >- * </p> >- */ >-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. >- * <p> >- * It provides applications a simple programming interface to all features of the MQTT version 3.1 >- * specification including: >- * <ul> >- * <li>connect >- * <li>publish >- * <li>subscribe >- * <li>unsubscribe >- * <li>disconnect >- * </ul> >- * </p> >- * <p> >- * There are two styles of MQTT client, this one and {@link IMqttClient}. >- * <ul> >- * <li>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.</li> >- * <li>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. </li> >- * </ul> >- * </p> >- * <p> >- * 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}</p> >- * >- * <p>There are two forms of non-blocking method: >- * <ol> >- * <li> >- * <code><pre> >- * IMqttToken token = asyncClient.method(parms) >- * </pre></code> >- * <p>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: >- * <code><pre> >- * IMqttToken conToken; >- * conToken = asyncClient.client.connect(conToken); >- * ... do some work... >- * conToken.waitForCompletion(); >- * </pre></code> >- * /p> >- * <p>To turn a method into a blocking invocation the following form can be used: >- * <code><pre> >- * IMqttToken token; >- * token = asyncClient.method(parms).waitForCompletion(); >- * </pre></code> >- >- * </li> >- * >- * <li> >- * <code><pre> >- * IMqttToken token method(parms, Object userContext, IMqttActionListener callback) >- * </pre></code> >- * <p>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: >- * <code><pre> >- * 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. >- * </pre></code> >- * </p> >- * </li> >- * </ol> >- * <p>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.</p> >- * >- */ >-public interface IMqttAsyncClient { >- /** >- * Connects to an MQTT server using the default options. >- * <p>The default options are specified in {@link MqttConnectOptions} class. >- * </p> >- * >- * @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. >- * <p>The connection will be established using the options specified in the >- * {@link MqttConnectOptions} parameter. >- * </p> >- * >- * @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. >- * <p>The default options are specified in {@link MqttConnectOptions} class. >- * </p> >- * >- * @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. >- * <p>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. >- * </p> >- * <p>The method returns control before the connect completes. Completion can >- * be tracked by: >- * <ul> >- * <li>Waiting on the returned token {@link IMqttToken#waitForCompletion()} or</li> >- * <li>Passing in a callback {@link IMqttActionListener}</li> >- * </ul> >- * </p> >- * >- * @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. >- * <p>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. >- * </p> >- * >- * @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. >- * <p>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. >- * </p> >- * @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. >- * <p>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. >- * </p> >- * >- * @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. >- * <p> >- * 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.</p> >- * <p>This method must not be called from inside {@link MqttCallback} methods.</p> >- * <p>The method returns control before the disconnect completes. Completion can >- * be tracked by: >- * <ul> >- * <li>Waiting on the returned token {@link IMqttToken#waitForCompletion()} or</li> >- * <li>Passing in a callback {@link IMqttActionListener}</li> >- * </ul> >- * </p> >- * >- * @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 <code>true</code> if connected, <code>false</code> otherwise. >- */ >- public boolean isConnected(); >- >- /** >- * Returns the client ID used by this client. >- * <p>All clients connected to the >- * same server or server farm must have a unique ID. >- * </p> >- * >- * @return the client ID used by this client. >- */ >- public String getClientId(); >- >- /** >- * Returns the address of the server used by this client. >- * <p>The format of the returned String is the same as that used on the constructor. >- * </p> >- * >- * @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 >- * <p>A convenience method, which will >- * create a new {@link MqttMessage} object with a byte array payload and the >- * specified QoS, and then publish it. >- * </p> >- * >- * @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 >- * <p>A convenience method, which will >- * create a new {@link MqttMessage} object with a byte array payload and the >- * specified QoS, and then publish it. >- * </p> >- * >- * @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. >- * <p> >- * 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: >- * <ul> >- * <li>The connection is re-established with the same clientID >- * <li>The original connection was made with (@link MqttConnectOptions#setCleanSession(boolean)} >- * set to false >- * <li>The connection is re-established with (@link MqttConnectOptions#setCleanSession(boolean)} >- * set to false >- * <liDepending when the failure occurs QOS 0 messages may not be delivered. >- * </ul> >- * </p> >- * >- * <p>When building an application, >- * the design of the topic tree should take into account the following principles >- * of topic name syntax and semantics:</p> >- * >- * <ul> >- * <li>A topic must be at least one character long.</li> >- * <li>Topic names are case sensitive. For example, <em>ACCOUNTS</em> and <em>Accounts</em> are >- * two different topics.</li> >- * <li>Topic names can include the space character. For example, <em>Accounts >- * payable</em> is a valid topic.</li> >- * <li>A leading "/" creates a distinct topic. For example, <em>/finance</em> is >- * different from <em>finance</em>. <em>/finance</em> matches "+/+" and "/+", but >- * not "+".</li> >- * <li>Do not include the null character (Unicode<samp class="codeph"> \x0000</samp>) in >- * any topic.</li> >- * </ul> >- * >- * <p>The following principles apply to the construction and content of a topic >- * tree:</p> >- * >- * <ul> >- * <li>The length is limited to 64k but within that there are no limits to the >- * number of levels in a topic tree.</li> >- * <li>There can be any number of root nodes; that is, there can be any number >- * of topic trees.</li> >- * </ul> >- * </p> >- * <p>The method returns control before the publish completes. Completion can >- * be tracked by: >- * <ul> >- * <li>Setting an {@link IMqttAsyncClient#setCallback(MqttCallback)} where the >- * {@link MqttCallback#deliveryComplete(IMqttDeliveryToken)} >- * method will be called.</li> >- * <li>Waiting on the returned token {@link MqttToken#waitForCompletion()} or</li> >- * <li>Passing in a callback {@link IMqttActionListener} to this method</li> >- * </ul> >- * </p> >- * >- * @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. >- * >- * <p>Provides an optimised way to subscribe to multiple topics compared to >- * subscribing to each one individually.</p> >- * >- * @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. >- * <p>Provides an optimised way to subscribe to multiple topics compared to >- * subscribing to each one individually.</p> >- * <p>The {@link #setCallback(MqttCallback)} method >- * should be called before this method, otherwise any received messages >- * will be discarded. >- * </p> >- * <p> >- * If (@link MqttConnectOptions#setCleanSession(boolean)} was set to true >- * when when connecting to the server then the subscription remains in place >- * until either: >- * <ul> >- * <li>The client disconnects</li> >- * <li>An unsubscribe method is called to un-subscribe the topic</li> >- * </li> >- * </p> >- * <p> >- * If (@link MqttConnectOptions#setCleanSession(boolean)} was set to false >- * when connecting to the server then the subscription remains in place >- * until either: >- * <ul> >- * <li>An unsubscribe method is called to un-subscribe the topic</li> >- * <li>The next time the client connects with CleanSession set to true</ul> >- * </li> >- * 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 <bold>same client ID</bold> the server will >- * deliver the stored messages to the client. >- * </p> >- * >- * <p>The "topic filter" string used when subscribing >- * may contain special characters, which allow you to subscribe to multiple topics >- * at once.</p> >- * <p>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. >- * <dl> >- * <dt>Topic level separator</dt> >- * <dd>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.</dd> >- * >- * <dt>Multi-level wildcard</dt> >- * <dd><p>The number sign (#) is a wildcard character that matches >- * any number of levels within a topic. For example, if you subscribe to >- * <span><span class="filepath">finance/stock/ibm/#</span></span>, you receive >- * messages on these topics: >- * <pre> finance/stock/ibm<br /> finance/stock/ibm/closingprice<br /> finance/stock/ibm/currentprice</pre> >- * </p> >- * <p>The multi-level wildcard >- * can represent zero or more levels. Therefore, <em>finance/#</em> can also match >- * the singular <em>finance</em>, where <em>#</em> represents zero levels. The topic >- * level separator is meaningless in this context, because there are no levels >- * to separate.</p> >- * >- * <p>The <span>multi-level</span> wildcard can >- * be specified only on its own or next to the topic level separator character. >- * Therefore, <em>#</em> and <em>finance/#</em> are both valid, but <em>finance#</em> is >- * not valid. <span>The multi-level wildcard must be the last character >- * used within the topic tree. For example, <em>finance/#</em> is valid but >- * <em>finance/#/closingprice</em> is not valid.</span></p></dd> >- * >- * <dt>Single-level wildcard</dt> >- * <dd><p>The plus sign (+) is a wildcard character that matches only one topic >- * level. For example, <em>finance/stock/+</em> matches >- * <em>finance/stock/ibm</em> and <em>finance/stock/xyz</em>, >- * but not <em>finance/stock/ibm/closingprice</em>. Also, because the single-level >- * wildcard matches only a single level, <em>finance/+</em> does not match <em>finance</em>.</p> >- * >- * <p>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, >- * <em>+</em> and <em>finance/+</em> are both valid, but <em>finance+</em> is >- * not valid. <span>The single-level wildcard can be used at the end of the >- * topic tree or within the topic tree. >- * For example, <em>finance/+</em> and <em>finance/+/ibm</em> are both valid.</span></p> >- * </dd> >- * </dl> >- * </p> >- * <p>The method returns control before the subscribe completes. Completion can >- * be tracked by: >- * <ul> >- * <li>Waiting on the supplied token {@link MqttToken#waitForCompletion()} or</li> >- * <li>Passing in a callback {@link IMqttActionListener} to this method</li> >- * </ul> >- * </p> >- * >- * @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 >- * <p> >- * 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. >- * </p> >- * <p>The topic(s) specified on the unsubscribe must match the topic(s) >- * specified in the original subscribe request for the unsubscribe to succeed >- * </p> >- * <p>The method returns control before the unsubscribe completes. Completion can >- * be tracked by: >- * <ul> >- * <li>Waiting on the returned token {@link MqttToken#waitForCompletion()} or</li> >- * <li>Passing in a callback {@link IMqttActionListener} to this method</li> >- * </ul> >- * </p> >- * >- * @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. >- * <p>There are a number of events that the listener will be notified about. >- * These include: >- * <ul> >- * <li>A new message has arrived and is ready to be processed</li> >- * <li>The connection to the server has been lost</li> >- * <li>Delivery of a message to the server has completed</li> >- * </ul> >- * </p> >- * <p>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.<p> >- * @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. >- * <p>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. >- * </p> >- * <p>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</P> >- * @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. >- * <p> >- * This interface allows applications to utilise all features of the MQTT version 3.1 >- * specification including: >- * <ul> >- * <li>connect >- * <li>publish >- * <li>subscribe >- * <li>unsubscribe >- * <li>disconnect >- * </ul> >- * </p> >- * <p> >- * There are two styles of MQTT client, this one and {@link IMqttAsyncClient}. >- * <ul> >- * <li>IMqttClient provides a set of methods that block and return control to the application >- * program once the MQTT action has completed.</li> >- * <li>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.</li> >- * </ul> >- * </p> >- * <p> >- * 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: >- * <code><pre> >- * IMqttToken token; >- * token = asyncClient.method(parms).waitForCompletion(); >- * </pre></code> >- * 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.</p> >- */ >-public interface IMqttClient { //extends IMqttAsyncClient { >- /** >- * Connects to an MQTT server using the default options. >- * <p>The default options are specified in {@link MqttConnectOptions} class. >- * </p> >- * >- * @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. >- * <p>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. >- * </p> >- * <p>This is a blocking method that returns once connect completes</p> >- * >- * @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. >- * <p>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. >- * </p> >- * >- * @see #disconnect(long) >- */ >- public void disconnect() throws MqttException; >- >- /** >- * Disconnects from the server. >- * <p> >- * 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.</p> >- * >- * <p>This is a blocking method that returns once disconnect completes</p> >- * >- * @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. >- * <p>The {@link #setCallback(MqttCallback)} method >- * should be called before this method, otherwise any received messages >- * will be discarded. >- * </p> >- * <p> >- * If (@link MqttConnectOptions#setCleanSession(boolean)} was set to true >- * when when connecting to the server then the subscription remains in place >- * until either: >- * <ul> >- * <li>The client disconnects</li> >- * <li>An unsubscribe method is called to un-subscribe the topic</li> >- * </li> >- * </p> >- * <p> >- * If (@link MqttConnectOptions#setCleanSession(boolean)} was set to false >- * when when connecting to the server then the subscription remains in place >- * until either: >- * <ul> >- * <li>An unsubscribe method is called to un-subscribe the topic</li> >- * <li>The client connects with CleanSession set to true</ul> >- * </li> >- * 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 <bold>same client ID</bold> the server will >- * deliver the stored messages to the client. >- * </p> >- * >- * <p>The "topic filter" string used when subscribing >- * may contain special characters, which allow you to subscribe to multiple topics >- * at once.</p> >- * <p>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. >- * <dl> >- * <dt>Topic level separator</dt> >- * <dd>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.</dd> >- * >- * <dt>Multi-level wildcard</dt> >- * <dd><p>The number sign (#) is a wildcard character that matches >- * any number of levels within a topic. For example, if you subscribe to >- * <span><span class="filepath">finance/stock/ibm/#</span></span>, you receive >- * messages on these topics: >- * <pre> finance/stock/ibm<br /> finance/stock/ibm/closingprice<br /> finance/stock/ibm/currentprice</pre> >- * </p> >- * <p>The multi-level wildcard >- * can represent zero or more levels. Therefore, <em>finance/#</em> can also match >- * the singular <em>finance</em>, where <em>#</em> represents zero levels. The topic >- * level separator is meaningless in this context, because there are no levels >- * to separate.</p> >- * >- * <p>The <span>multi-level</span> wildcard can >- * be specified only on its own or next to the topic level separator character. >- * Therefore, <em>#</em> and <em>finance/#</em> are both valid, but <em>finance#</em> is >- * not valid. <span>The multi-level wildcard must be the last character >- * used within the topic tree. For example, <em>finance/#</em> is valid but >- * <em>finance/#/closingprice</em> is not valid.</span></p></dd> >- * >- * <dt>Single-level wildcard</dt> >- * <dd><p>The plus sign (+) is a wildcard character that matches only one topic >- * level. For example, <em>finance/stock/+</em> matches >- * <em>finance/stock/ibm</em> and <em>finance/stock/xyz</em>, >- * but not <em>finance/stock/ibm/closingprice</em>. Also, because the single-level >- * wildcard matches only a single level, <em>finance/+</em> does not match <em>finance</em>.</p> >- * >- * <p>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, >- * <em>+</em> and <em>finance/+</em> are both valid, but <em>finance+</em> is >- * not valid. <span>The single-level wildcard can be used at the end of the >- * topic tree or within the topic tree. >- * For example, <em>finance/+</em> and <em>finance/+/ibm</em> are both valid.</span></p> >- * </dd> >- * </dl> >- * </p> >- * >- * <p>This is a blocking method that returns once subscribe completes</p> >- * >- * @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 >- * <p> >- * 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. >- * </p> >- * <p>The topic(s) specified on the unsubscribe must match the topic(s) >- * specified in the original subscribe request for the subscribe to succeed >- * </p> >- * >- * <p>This is a blocking method that returns once unsubscribe completes</p> >- * >- * @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 >- * <p>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. >- * </p> >- * >- * @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. >- * <p> >- * 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: >- * <ul> >- * <li>The connection is re-established with the same clientID >- * <li>The original connection was made with (@link MqttConnectOptions#setCleanSession(boolean)} >- * set to false >- * <li>The connection is re-established with (@link MqttConnectOptions#setCleanSession(boolean)} >- * set to false >- * </ul> >- * </p> >- * <p>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: >- * <ul> >- * <li>Register a {@link #setCallback(MqttCallback)} callback on the client and the delivery complete >- * callback will be notified once a delivery of a message completes >- * <li>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. >- * </ul> >- * </p> >- * >- * <p>When building an application, >- * the design of the topic tree should take into account the following principles >- * of topic name syntax and semantics:</p> >- * >- * <ul> >- * <li>A topic must be at least one character long.</li> >- * <li>Topic names are case sensitive. For example, <em>ACCOUNTS</em> and <em>Accounts</em> are >- * two different topics.</li> >- * <li>Topic names can include the space character. For example, <em>Accounts >- * payable</em> is a valid topic.</li> >- * <li>A leading "/" creates a distinct topic. For example, <em>/finance</em> is >- * different from <em>finance</em>. <em>/finance</em> matches "+/+" and "/+", but >- * not "+".</li> >- * <li>Do not include the null character (Unicode<samp class="codeph"> \x0000</samp>) in >- * any topic.</li> >- * </ul> >- * >- * <p>The following principles apply to the construction and content of a topic >- * tree:</p> >- * >- * <ul> >- * <li>The length is limited to 64k but within that there are no limits to the >- * number of levels in a topic tree.</li> >- * <li>There can be any number of root nodes; that is, there can be any number >- * of topic trees.</li> >- * </ul> >- * </p> >- * >- * <p>This is a blocking method that returns once publish completes</p> * >- * >- * @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. >- * <p>There are a number of events that listener will be notified about. These include >- * <ul> >- * <li>A new message has arrived and is ready to be processed</li> >- * <li>The connection to the server has been lost</li> >- * <li>Delivery of a message to the server has completed.</li> >- * </ul> >- * </p> >- * <p>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<p> >- * @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. >- * <p>An alternative method that should be used in preference to this one when publishing a message is: >- * <ul> >- * <li>{@link MqttClient#publish(String, MqttMessage)} to publish a message in a blocking manner >- * <li>or use publish methods on the non blocking client like {@link IMqttAsyncClient#publish(String, MqttMessage, Object, IMqttActionListener)} >- * </ul> >- * </p> >- * <p>When building an application, >- * the design of the topic tree should take into account the following principles >- * of topic name syntax and semantics:</p> >- * >- * <ul> >- * <li>A topic must be at least one character long.</li> >- * <li>Topic names are case sensitive. For example, <em>ACCOUNTS</em> and <em>Accounts</em> are >- * two different topics.</li> >- * <li>Topic names can include the space character. For example, <em>Accounts >- * payable</em> is a valid topic.</li> >- * <li>A leading "/" creates a distinct topic. For example, <em>/finance</em> is >- * different from <em>finance</em>. <em>/finance</em> matches "+/+" and "/+", but >- * not "+".</li> >- * <li>Do not include the null character (Unicode<samp class="codeph"> \x0000</samp>) in >- * any topic.</li> >- * </ul> >- * >- * <p>The following principles apply to the construction and content of a topic >- * tree:</p> >- * >- * <ul> >- * <li>The length is limited to 64k but within that there are no limits to the >- * number of levels in a topic tree.</li> >- * <li>There can be any number of root nodes; that is, there can be any number >- * of topic trees.</li> >- * </ul> >- * </p> >- * >- * @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 <code>true</code> if connected, <code>false</code> otherwise. >- */ >- public boolean isConnected(); >- >- /** >- * Returns the client ID used by this client. >- * <p>All clients connected to the >- * same server or server farm must have a unique ID. >- * </p> >- * >- * @return the client ID used by this client. >- */ >- public String getClientId(); >- >- /** >- * Returns the address of the server used by this client, as a URI. >- * <p>The format is the same as specified on the constructor. >- * </p> >- * >- * @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. >- * <p>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. >- * </p> >- * <p>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</P> >- * @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 >- * >- * <p>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 >- * <ul> >- * <li>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. >- * <li>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. >- * </ul> >- * <p> >- * An action is in progress until either: >- * <ul> >- * <li>isComplete() returns true or >- * <li>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. >- * </ul> >- * </p> >- * >- */ >- >-public interface IMqttDeliveryToken extends IMqttToken { >- /** >- * Returns the message associated with this token. >- * <p>Until the message has been delivered, the message being delivered will >- * be returned. Once the message has been delivered <code>null</code> 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. >- * >- * <p>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.<p> >- * <p> >- * An action is in progress until either: >- * <ul> >- * <li>isComplete() returns true or >- * <li>getException() is not null. >- * </ul> >- * </p> >- * >- */ >-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. >- * <p>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. </p> >- * <p>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. >- * </p> >- * >- * @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. >- * <p>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. >- * </p> >- */ >- public boolean isComplete(); >- >- /** >- * Returns an exception providing more detail if an operation failed >- * <p>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 >- * </p> >- * @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. >- * <p>Once a listener is registered it will be invoked when the action the token >- * is associated with either succeeds or fails. >- * </p> >- * @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. >- * <p>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</p> >- * @param userContext to associate with an action >- */ >- public void setUserContext(Object userContext); >- >- /** >- * Retrieve the context associated with an action. >- * <p>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. </p> >- >- * @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. >- * >- * <p>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. >- * </p> >- * <p>An application can connect to an MQTT server using: >- * <ul> >- * <li>A plain TCP socket >- * <li>An secure SSL/TLS socket >- * </ul> >- * </p> >- * <p>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. >- * </p> >- * <p>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. >- * </p> >- * <p>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. >- * </p> >- * <p>The message store interface is pluggable. Different stores can be used by implementing >- * the {@link MqttClientPersistence} interface and passing it to the clients constructor. >- * </p> >- * >- * @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. >- * <p> >- * 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: >- * <ul> >- * <li><code>tcp://localhost:1883</code></li> >- * <li><code>ssl://localhost:8883</code></li> >- * </ul> >- * If the port is not specified, it will >- * default to 1883 for "tcp://" URIs, and 8883 for "ssl://" URIs. >- * </p> >- * <p> >- * 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. >- * </p> >- * <p> >- * In Java SE, SSL can be configured in one of several ways, which the >- * client will use in the following order: >- * </p> >- * <ul> >- * <li><strong>Supplying an <code>SSLSocketFactory</code></strong> - applications can >- * use {@link MqttConnectOptions#setSocketFactory(SocketFactory)} to supply >- * a factory with the appropriate SSL settings.</li> >- * <li><strong>SSL Properties</strong> - applications can supply SSL settings as a >- * simple Java Properties using {@link MqttConnectOptions#setSSLProperties(Properties)}.</li> >- * <li><strong>Use JVM settings</strong> - There are a number of standard >- * Java system properties that can be used to configure key and trust stores.</li> >- * </ul> >- * >- * <p>In Java ME, the platform settings are used for SSL connections.</p> >- * >- * <p>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. >- * <p> >- * 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: >- * <ul> >- * <li><code>tcp://localhost:1883</code></li> >- * <li><code>ssl://localhost:8883</code></li> >- * </ul> >- * If the port is not specified, it will >- * default to 1883 for "tcp://" URIs, and 8883 for "ssl://" URIs. >- * </p> >- * <p> >- * 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. >- * </p> >- * <p> >- * In Java SE, SSL can be configured in one of several ways, which the >- * client will use in the following order: >- * </p> >- * <ul> >- * <li><strong>Supplying an <code>SSLSocketFactory</code></strong> - applications can >- * use {@link MqttConnectOptions#setSocketFactory(SocketFactory)} to supply >- * a factory with the appropriate SSL settings.</li> >- * <li><strong>SSL Properties</strong> - applications can supply SSL settings as a >- * simple Java Properties using {@link MqttConnectOptions#setSSLProperties(Properties)}.</li> >- * <li><strong>Use JVM settings</strong> - There are a number of standard >- * Java system properties that can be used to configure key and trust stores.</li> >- * </ul> >- * >- * <p>In Java ME, the platform settings are used for SSL connections.</p> >- * <p> >- * 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. >- * </p> >- * <p>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 <code>null</code>.</p> >- * >- * @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=<init> 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. >- * <p>There are two alternative methods that should be used in preference to this one when publishing a message: >- * <ul> >- * <li>{@link MqttAsyncClient#publish(String, MqttMessage, MqttDeliveryToken)} to publish a message in a non-blocking manner or >- * <li>{@link MqttClient#publishBlock(String, MqttMessage, MqttDeliveryToken)} to publish a message in a blocking manner >- * </ul> >- * </p> >- * <p>When you build an application, >- * the design of the topic tree should take into account the following principles >- * of topic name syntax and semantics:</p> >- * >- * <ul> >- * <li>A topic must be at least one character long.</li> >- * <li>Topic names are case sensitive. For example, <em>ACCOUNTS</em> and <em>Accounts</em> are >- * two different topics.</li> >- * <li>Topic names can include the space character. For example, <em>Accounts >- * payable</em> is a valid topic.</li> >- * <li>A leading "/" creates a distinct topic. For example, <em>/finance</em> is >- * different from <em>finance</em>. <em>/finance</em> matches "+/+" and "/+", but >- * not "+".</li> >- * <li>Do not include the null character (Unicode<samp class="codeph"> \x0000</samp>) in >- * any topic.</li> >- * </ul> >- * >- * <p>The following principles apply to the construction and content of a topic >- * tree:</p> >- * >- * <ul> >- * <li>The length is limited to 64k but within that there are no limits to the >- * number of levels in a topic tree.</li> >- * <li>There can be any number of root nodes; that is, there can be any number >- * of topic trees.</li> >- * </ul> >- * </p> >- * >- * @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;i<topicFilters.length;i++) { >- if (i>0) { >- 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;i<topicFilters.length;i++) { >- if (i>0) { >- 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. >- * <p>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.</p> >- * @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 >- * <p>Checks the topic does not contain a wild card character.</p> >- * @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. >- * >- * <p> >- * This method is invoked synchronously by the MQTT client. An >- * acknowledgement is not sent back to the server until this >- * method returns cleanly.</p> >- * <p> >- * If an implementation of this method throws an <code>Exception</code>, 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.</p> >- * <p> >- * 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.</p> >- * <p> >- * 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.</p> >- * <p> >- * 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.</p> >- * >- * @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. >- * >- * <p>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. >- * </p> >- * <p>An application can connect to an MQTT server using: >- * <ul> >- * <li>A plain TCP socket >- * <li>An secure SSL/TLS socket >- * </ul> >- * </p> >- * <p>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. >- * </p> >- * <p>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. >- * </p> >- * <p>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. </p> >- * <p>The message store interface is pluggable. Different stores can be used by implementing >- * the {@link MqttClientPersistence} interface and passing it to the clients constructor. >- * </p> >- * >- * @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. >- * <p> >- * 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: >- * <ul> >- * <li><code>tcp://localhost:1883</code></li> >- * <li><code>ssl://localhost:8883</code></li> >- * </ul> >- * If the port is not specified, it will >- * default to 1883 for "tcp://" URIs, and 8883 for "ssl://" URIs. >- * </p> >- * <p> >- * 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. >- * </p> >- * <p> >- * In Java SE, SSL can be configured in one of several ways, which the >- * client will use in the following order: >- * </p> >- * <ul> >- * <li><strong>Supplying an <code>SSLSocketFactory</code></strong> - applications can >- * use {@link MqttConnectOptions#setSocketFactory(SocketFactory)} to supply >- * a factory with the appropriate SSL settings.</li> >- * <li><strong>SSL Properties</strong> - applications can supply SSL settings as a >- * simple Java Properties using {@link MqttConnectOptions#setSSLProperties(Properties)}.</li> >- * <li><strong>Use JVM settings</strong> - There are a number of standard >- * Java system properties that can be used to configure key and trust stores.</li> >- * </ul> >- * >- * <p>In Java ME, the platform settings are used for SSL connections.</p> >- * >- * <p>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. >- * <p> >- * 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: >- * <ul> >- * <li><code>tcp://localhost:1883</code></li> >- * <li><code>ssl://localhost:8883</code></li> >- * </ul> >- * If the port is not specified, it will >- * default to 1883 for "tcp://" URIs, and 8883 for "ssl://" URIs. >- * </p> >- * <p> >- * 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. >- * </p> >- * <p> >- * In Java SE, SSL can be configured in one of several ways, which the >- * client will use in the following order: >- * </p> >- * <ul> >- * <li><strong>Supplying an <code>SSLSocketFactory</code></strong> - applications can >- * use {@link MqttConnectOptions#setSocketFactory(SocketFactory)} to supply >- * a factory with the appropriate SSL settings.</li> >- * <li><strong>SSL Properties</strong> - applications can supply SSL settings as a >- * simple Java Properties using {@link MqttConnectOptions#setSSLProperties(Properties)}.</li> >- * <li><strong>Use JVM settings</strong> - There are a number of standard >- * Java system properties that can be used to configure key and trust stores.</li> >- * </ul> >- * >- * <p>In Java ME, the platform settings are used for SSL connections.</p> >- * <p> >- * 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. >- * </p> >- * <p>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 <code>null</code>.</p> >- * >- * @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; i<qos.length; i++) { >- qos[i] = 1; >- } >- this.subscribe(topicFilters, qos); >- } >- >- /* >- * @see IMqttClient#subscribe(String, int) >- */ >- public void subscribe(String topicFilter, int qos) throws MqttException { >- this.subscribe(new String[] {topicFilter}, new int[] {qos}); >- } >- >- /* >- * @see IMqttClient#subscribe(String[], int[]) >- */ >- public void subscribe(String[] topicFilters, int[] qos) throws MqttException { >- aClient.subscribe(topicFilters, qos, null,null).waitForCompletion(getTimeToWait()); >- } >- >- /* >- * @see IMqttClient#unsubscribe(String) >- */ >- public void unsubscribe(String topicFilter) throws MqttException { >- unsubscribe(new String[] {topicFilter}); >- } >- >- /* >- * @see IMqttClient#unsubscribe(String[]) >- */ >- public void unsubscribe(String[] topicFilters) throws MqttException { >- aClient.unsubscribe(topicFilters, null,null).waitForCompletion(getTimeToWait()); >- } >- >- /* >- * @see IMqttClient#publishBlock(String, byte[], int, boolean) >- */ >- public void publish(String topic, byte[] payload,int qos, boolean retained) throws MqttException, >- MqttPersistenceException { >- MqttMessage message = new MqttMessage(payload); >- message.setQos(qos); >- message.setRetained(retained); >- this.publish(topic, message); >- } >- >- /* >- * @see IMqttClient#publishBlock(String, MqttMessage) >- */ >- public void publish(String topic, MqttMessage message) throws MqttException, >- MqttPersistenceException { >- aClient.publish(topic, message, null, null).waitForCompletion(getTimeToWait()); >- } >- >- /** >- * Set the maximum time to wait for an action to complete >- * <p>Set the maximum time to wait for an action to complete before >- * returning control to the invoking application. Control is returned >- * when: >- * <ul> >- * <li>the action completes >- * <li>or when the timeout if exceeded >- * <li>or when the client is disconnect/shutdown >- * <ul> >- * 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. >- * </p> >- * @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. >- * <p>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.</p> >- * @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. >- * <p> >- * 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.</p> >- * <p> >- * It is up to the persistence interface to log any exceptions or error information >- * which may be required when diagnosing a persistence failure.</p> >- */ >-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 <code>MqttConnectOptions</code> object using the >- * default values. >- * >- * The defaults are: >- * <ul> >- * <li>The keepalive interval is 60 seconds</li> >- * <li>Clean Session is true</li> >- * <li>The message delivery retry interval is 15 seconds</li> >- * <li>The connection timeout period is 30 seconds</li> >- * <li>No Will message is set</li> >- * <li>A standard SocketFactory is used</li> >- * </ul> >- * 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. >- * <p>The default value is 60 seconds</p> >- * >- * @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 >- * <code>null</code> if one has not been set. >- */ >- public SocketFactory getSocketFactory() { >- return socketFactory; >- } >- >- /** >- * Sets the <code>SocketFactory</code> to use. This allows an application >- * to apply its own policies around the creation of network sockets. If >- * using an SSL connection, an <code>SSLSocketFactory</code> 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 <code>null</code> 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 >- * <code>IllegalStateException</code> being thrown. >- * @return the message to use, or <code>null</code> 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 >- * <em>not</em> used if a SocketFactory has been set using >- * {@link #setSocketFactory(SocketFactory)}. >- * The following properties can be used:</p> >- * <dl> >- * <dt>com.ibm.ssl.protocol</dt> >- * <dd>One of: SSL, SSLv3, TLS, TLSv1, SSL_TLS.</dd> >- * <dt>com.ibm.ssl.contextProvider >- * <dd>Underlying JSSE provider. For example "IBMJSSE2" or "SunJSSE"</dd> >- * >- * <dt>com.ibm.ssl.keyStore</dt> >- * <dd>The name of the file that contains the KeyStore object that you >- * want the KeyManager to use. For example /mydir/etc/key.p12</dd> >- * >- * <dt>com.ibm.ssl.keyStorePassword</dt> >- * <dd>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: >- * <code>com.ibm.micro.security.Password.obfuscate(char[] password)</code>. >- * 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.</dd> >- * >- * <dt>com.ibm.ssl.keyStoreType</dt> >- * <dd>Type of key store, for example "PKCS12", "JKS", or "JCEKS".</dd> >- * >- * <dt>com.ibm.ssl.keyStoreProvider</dt> >- * <dd>Key store provider, for example "IBMJCE" or "IBMJCEFIPS".</dd> >- * >- * <dt>com.ibm.ssl.trustStore</dt> >- * <dd>The name of the file that contains the KeyStore object that you >- * want the TrustManager to use.</dd> >- * >- * <dt>com.ibm.ssl.trustStorePassword</dt> >- * <dd>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: >- * <code>com.ibm.micro.security.Password.obfuscate(char[] password)</code>. >- * 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.</dd> >- * >- * <dt>com.ibm.ssl.trustStoreType</dt> >- * <dd>The type of KeyStore object that you want the default TrustManager to use. >- * Same possible values as "keyStoreType".</dd> >- * >- * <dt>com.ibm.ssl.trustStoreProvider</dt> >- * <dd>Trust store provider, for example "IBMJCE" or "IBMJCEFIPS".</dd> >- * >- * <dt>com.ibm.ssl.enabledCipherSuites</dt> >- * <dd>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.</dd> >- * >- * <dt>com.ibm.ssl.keyManager</dt> >- * <dd>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". >- * </dd> >- * >- * <dt>com.ibm.ssl.trustManager</dt> >- * <dd>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". >- * </dd> >- * </dl> >- */ >- 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. >- * >- * <p> >- * Used to track the the delivery progress of a message when a publish is >- * executed in a non-blocking manner (run in the background)</p> >- * >- * @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. >- * <p>Until the message has been delivered, the message being delivered will >- * be returned. Once the message has been delivered <code>null</code> 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 <code>SocketFactory</code> do not match. >- * URIs beginning <code>tcp://</code> must use a <code>javax.net.SocketFactory</code>, >- * and URIs beginning <code>ssl://</code> must use a <code>javax.net.ssl.SSLSocketFactory</code>. >- */ >- 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 <code>MqttException</code> 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 <code>MqttException</code> with the specified >- * <code>Throwable</code> 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 <code>MqttException</code> with the specified >- * <code>Throwable</code> 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 <code>null</code>. >- */ >- public Throwable getCause() { >- return cause; >- } >- >- /** >- * Returns the detail message for this exception. >- * @return the detail message, which may be <code>null</code>. >- */ >- public String getMessage() { >- return MessageCatalog.getMessage(reasonCode); >- } >- >- /** >- * Returns a <code>String</code> representation of this exception. >- * @return a <code>String</code> 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: >- * <ul> >- * <li>Message QoS set to 1</li> >- * <li>Message will not be "retained" by the server</li> >- * </ul> >- */ >- 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 <code>true</code> 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 <code>false</code> will clear the >- * retained message from the server. The default value is <code>false</code> >- * >- * @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. >- * <ul> >- * <li>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".</li> >- * >- * <li>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 <code>MqttConnectOptions</code>. >- * 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.</li> >- * >- * <li>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 <code>MqttConnectOptions</code>. >- * If a persistence mechanism is not specified, the message will not be >- * delivered in the event of a client failure.</li> >- * >- * 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 <code>true</code> if the values can be changed, >- * <code>false</code> 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 <code>true</code> 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. >- * <p> >- * 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.</p> >- * <p> >- * 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: >- * <ul> >- * <li>It could return the data as it was passed in >- * originally, with the same byte arrays and offsets.</li> >- * <li>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</li> >- * <li>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.</li> >- * </ul> >- * </p> >- */ >-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 <code>MqttPersistenceException</code> >- */ >- public MqttPersistenceException() { >- super(REASON_CODE_CLIENT_EXCEPTION); >- } >- >- /** >- * Constructs a new <code>MqttPersistenceException</code> 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 <code>MqttPersistenceException</code> with the specified >- * <code>Throwable</code> as the underlying reason. >- * @param cause the underlying cause of the exception. >- */ >- public MqttPersistenceException(Throwable cause) { >- super(cause); >- } >- /** >- * Constructs a new <code>MqttPersistenceException</code> with the specified >- * <code>Throwable</code> 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 <code>MqttSecurityException</code> 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 <code>MqttSecurityException</code> with the specified >- * <code>Throwable</code> as the underlying reason. >- * @param cause the underlying cause of the exception. >- */ >- public MqttSecurityException(Throwable cause) { >- super(cause); >- } >- /** >- * Constructs a new <code>MqttSecurityException</code> with the specified >- * code and <code>Throwable</code> 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. >- * <p> >- * A token that implements the ImqttToken interface is returned from all non-blocking >- * method with the exception of publish. >- * </p> >- * >- * @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<toks.length; i++) { >- toks[i].internalTok.setException(null); >- } >- >- // Save the conncet token in tokenStore as failure can occur before send >- tokenStore.saveToken(conToken,conPacket); >- >- // Connect to the server at the network level e.g. TCP socket and then >- // start the background processing threads before sending the connect >- // packet. >- networkModule.start(); >- receiver = new CommsReceiver(clientComms, clientState, tokenStore, networkModule.getInputStream()); >- receiver.start("MQTT Rec: "+getClient().getClientId()); >- sender = new CommsSender(clientComms, clientState, tokenStore, networkModule.getOutputStream()); >- sender.start("MQTT Snd: "+getClient().getClientId()); >- callback.start("MQTT Call: "+getClient().getClientId()); >- >- internalSend(conPacket, conToken); >- } catch (MqttException ex) { >- //@TRACE 212=connect failed: unexpected exception >- log.fine(className, methodName, "212", null, ex); >- mqttEx = ex; >- } catch (Exception ex) { >- //@TRACE 209=connect failed: unexpected exception >- log.fine(className, methodName, "209", null, ex); >- mqttEx = ExceptionHelper.createMqttException(ex); >- } >- >- if (mqttEx != null) { >- shutdownConnection(conToken, mqttEx); >- } >- } >- } >- >- // Kick off the disconnect processing in the background so that it does not block. For instance >- // the quiesce >- private class DisconnectBG implements Runnable { >- Thread dBg = null; >- MqttDisconnect disconnect; >- long quiesceTimeout; >- MqttToken token; >- >- DisconnectBG(MqttDisconnect disconnect, long quiesceTimeout, MqttToken token ) { >- this.disconnect = disconnect; >- this.quiesceTimeout = quiesceTimeout; >- this.token = token; >- } >- >- void start() { >- dBg = new Thread(this, "MQTT Disc: "+getClient().getClientId()); >- dBg.start(); >- } >- public void run() { >- final String methodName = "disconnectBG:run"; >- //@TRACE 221=> >- 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, "<Init>", "" ); >- >- 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 = "<Init>"; >- >- 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.<BR> >- * 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.<BR> >- * 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;i<enabledCiphers.length;i++) { >- if (i>0) { >- 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<getTopics().length; i++) { >- tok.append(getTopics()[i]+", "); >- } >- } >- tok.append(" ,usercontext="+getUserContext()); >- tok.append(" ,isComplete="+isComplete()); >- tok.append(" ,isNotified="+isNotified()); >- tok.append(" ,exception="+getException()); >- tok.append(" ,actioncallback="+getActionCallback()); >- >- return tok.toString(); >- } >- >-} >diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/logcat.properties b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/logcat.properties >deleted file mode 100644 >index 1063c9e..0000000 >--- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/logcat.properties >+++ /dev/null >@@ -1,132 +0,0 @@ >-0=MQTT Catalog >-200=internalSend key={0} message={1} token={2} >-213=fail: token in use: key={0} message={1} token={2} >-208=failed: not connected >-224=failed: not disconnected >-214=state=CONNECTING >-207=connect failed: not disconnected {0} >-215=state=CONNECTED >-204=connect failed: rc={0} >-216=state=DISCONNECTING >-217=state=DISCONNECTED >-222=> >-223=failed: in closed state >-211=failed: already disconnected >-219=failed: already disconnecting >-210=failed: called on callback thread >-218=state=DISCONNECTING >-220=> >-212=connect failed: unexpected exception >-209=connect failed: unexpected exception >-221=> >-603=clearState >-602=key={0} exception >-601=key={0} message={1} >-600=> >-604=inbound QoS 2 publish key={0} message={1} >-605=outbound QoS 2 pubrel key={0} message={1} >-606=outbound QoS 2 completed key={0} message={1} >-607=outbound QoS 2 publish key={0} message={1} >-608=outbound QoS 1 publish key={0} message={1} >-609=removing orphaned pubrel key={0} >-610=QoS 2 publish key={0} >-611=QoS 2 pubrel key={0} >-612=QoS 1 publish key={0} >-613= sending {0} msgs at max inflight window >-628=pending publish key={0} qos={1} message={2} >-615=pending send key={0} message {1} >-618=key={0} QoS={1} >-620=ping needed. keepAlive={0} lastOutboundActivity={1} lastInboundActivity={2} >-619=Timed out as no activity, keepAlive={0} lastOutboundActivity={1} lastInboundActivity={2} >-644=nothing to send, wait for {0} ms >-621=no outstanding flows and not connected >-617=+1 inflightpubrels={0} >-623=+1 actualInFlight={0} >-622=inflight window full >-625=key={0} >-646=-1 actualInFlight={0} >-626=quiescing={0} actualInFlight={1} pendingFlows={2} inFlightPubRels={3} callbackQuiesce={4} tokens={5} >-627=received key={0} message={1} >-651=received key={0} message={1} >-629=received key={0} token={1} message={2} >-650=removed Qos 1 publish. key={0} >-645=removed QoS 2 publish/pubrel. key={0}, -1 inFlightPubRels={1} >-648=key{0}, msg={1}, excep={2} >-649=key={0},excep={1} >-631=connected >-632=reason {0} >-633=disconnected >-637=timeout={0} >-639=wait for outstanding: actualInFlight={0} pendingFlows={1} inFlightPubRels={2} tokens={3} >-640=finished >-638=notifying queueLock holders >-641=remove publish from persistence. key={0} >-700=stopping >-701=notify workAvailable and wait for run >-703=stopped >-704=wait for workAvailable >-706=notify spaceAvailable >-714=callback threw exception >-705=callback and notify for key={0} >-708=call connectionLost >-720=exception from connectionLost {0} >-716=call onSuccess key={0} >-717=call onFailure key {0} >-709=wait for spaceAvailable >-710=new msg avail, notify workAvailable >-711=quiesce notify spaceAvailable >-713=call messageArrived key={0} topic={1} >-715=new workAvailable. key={0} >-719=callback threw ex: >-855=starting >-850=stopping >-851=stopped >-852=network read message >-856=Stopping, MQttException >-853=Stopping due to IOException >-854=< >-800=stopping sender >-801=stopped >-802=network send key={0} msg={1} >-803=get message returned null, stopping} >-805=< >-804=exception >-308=<> >-306=key={0} >-302=existing key={0} message={1} token={2} >-303=creating new token key={0} message={1} token={2} >-300=key={0} message={1} >-307=key={0} token={1} >-309=resp={0} >-310=> >-311=> >-312=> >-305=> {0} tokens >-260=setEnabledCiphers ciphers={0} >-252=connect to host {0} port {1} timeout {2} >-250=Failed to create TCP socket >-407=key={0} wait max={1} token={2} >-406=key={0} timed out token={1} >-400=>key={0} timeout={1} sent={2} completed={3} hasException={4} response={5} token={6} >-408=key={0} wait max={1} >-401=failed with exception >-402=key={0} response={1} >-404=>key={0} response={1} excep={2} >-411=>key={0} response={1} excep={2} >-409=wait key={0} >-403=> key={0} >-410=> key={0} >-101=<init> ClientID={0} ServerURI={1} PersistenceType={2} >-115=URI={0} >-103=cleanSession={0} connectionTimeout={1} TimekeepAlive={2} userName={3} password={4} will={5} userContext={6} callback={7} >-104=> quiesceTimeout={0} userContext={1} callback={2} >-105=< exception >-108=< >-106=Subscribe topic={0} userContext={1} callback={2} >-109=< >-107=Unsubscribe topic={0} userContext={1} callback={2} >-110=< >-111=< topic={0} message={1}userContext={1} callback={2} >-112=< >-113=< >-114=> >diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages.properties b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages.properties >deleted file mode 100644 >index d04e82d..0000000 >--- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages.properties >+++ /dev/null >@@ -1,35 +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 >-# */ >-# NLS_MESSAGEFORMAT_VAR >-# NLS_ENCODING=UNICODE >-1=Invalid protocol version >-2=Invalid client ID >-3=Broker unavailable >-4=Bad user name or password >-5=Not authorized to connect >-6=Unexpected error >-32000=Timed out waiting for a response from the server >-32100=Client is connected >-32101=Client is disconnected >-32102=Client is currently disconnecting >-32103=Unable to connect to server >-32104=Client is not connected >-32105=The specified SocketFactory type does not match the broker URI >-32106=SSL configuration error >-32107=Disconnecting is not allowed from a callback method >-32108=Unrecognized packet >-32109=Connection lost >-32110=Connect already in progress >-32111=Client is closed >-32200=Persistence already in use >-32201=Token already in use >-32202=Too many publishes in progress >diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_cs.properties b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_cs.properties >deleted file mode 100644 >index baec64f..0000000 >--- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_cs.properties >+++ /dev/null >@@ -1,30 +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 >-# */ >-# NLS_MESSAGEFORMAT_VAR >-# NLS_ENCODING=UNICODE >-1=Neplatn\u00e1 verze protokolu >-2=Neplatn\u00e9 ID klienta >-3=Nedostupn\u00fd zprost\u0159edkovatel >-4=Chybn\u00e9 jm\u00e9no u\u017eivatele nebo heslo >-5=Chyb\u00ed autorizace pro p\u0159ipojen\u00ed >-32000=Vypr\u0161en\u00ed \u010dasov\u00e9ho limitu pro odpov\u011b\u010f ze serveru >-32100=Klient je ji\u017e p\u0159ipojen >-32101=Klient je ji\u017e odpojen >-32102=Klient se aktu\u00e1ln\u011b odpojuje >-32103=Nelze se p\u0159ipojit k serveru >-32104=Klient nen\u00ed p\u0159ipojen >-32105=Ur\u010den\u00fd typ polo\u017eky SocketFactory neodpov\u00edd\u00e1 identifik\u00e1toru URI zprost\u0159edkovatele. >-32106=Chyba konfigurace zabezpe\u010den\u00ed SSL >-32107=Z metody zp\u011btn\u00e9ho vol\u00e1n\u00ed nen\u00ed povoleno odpojen\u00ed >-32108=Nerozpoznan\u00fd paket >-32109=P\u0159ipojen\u00ed bylo ztraceno. >-32200=Perzistence je ji\u017e pou\u017e\u00edv\u00e1na. >diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_de.properties b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_de.properties >deleted file mode 100644 >index d810e23..0000000 >--- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_de.properties >+++ /dev/null >@@ -1,30 +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 >-# */ >-# NLS_MESSAGEFORMAT_VAR >-# NLS_ENCODING=UNICODE >-1=Protokollversion ung\u00fcltig >-2=Client-ID ung\u00fcltig >-3=Broker nicht verf\u00fcgbar >-4=Benutzername oder Kennwort falsch >-5=Keine Berechtigung f\u00fcr Verbindung >-32000=Zeitlimit\u00fcberschreitung beim Warten auf eine Antwort vom Server >-32100=Verbindung zu Client besteht bereits >-32101=Verbindung zu Client ist bereits getrennt >-32102=Verbindung zu Client wird derzeit getrennt >-32103=Verbindung zu Server kann nicht hergestellt werden >-32104=Keine Verbindung zu Client >-32105=Der angegebene Socket-Factorytyp entspricht nicht der Broker-URI >-32106=SSL-Konfigurationsfehler >-32107=Trennung einer Verbindung \u00fcber eine Callback-Methode ist nicht zul\u00e4ssig >-32108=Paket nicht erkannt >-32109=Verbindung wurde getrennt >-32200=Persistenz wird bereits verwendet >diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_es.properties b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_es.properties >deleted file mode 100644 >index 3dea170..0000000 >--- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_es.properties >+++ /dev/null >@@ -1,30 +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 >-# */ >-# NLS_MESSAGEFORMAT_VAR >-# NLS_ENCODING=UNICODE >-1=Versi\u00f3n de protocolo incorrecta >-2=Identificador de cliente incorrecto >-3=Intermediario no disponible >-4=Nombre de usuario o contrase\u00f1a incorrecto >-5=No autorizado a conectarse >-32000=Tiempo de espera excedido al esperar una respuesta del servidor >-32100=Cliente ya conectado >-32101=Cliente ya desconectado >-32102=El cliente se est\u00e1 desconectando >-32103=No es posible conectarse al servidor >-32104=El cliente no est\u00e1 conectado >-32105=El tipo SocketFactory especificado no coincide con el URI del intermediario >-32106=Error de configuraci\u00f3n SSL >-32107=No se permite la desconexi\u00f3n desde un m\u00e9todo de devoluci\u00f3n de llamada >-32108=Paquete no reconocido >-32109=Se ha perdido la conexi\u00f3n >-32200=La persistencia ya se est\u00e1 utilizando >diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_fr.properties b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_fr.properties >deleted file mode 100644 >index c3650d5..0000000 >--- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_fr.properties >+++ /dev/null >@@ -1,30 +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 >-# */ >-# NLS_MESSAGEFORMAT_VAR >-# NLS_ENCODING=UNICODE >-1=Version de protocole incorrecte >-2=ID client incorrect >-3=Courtier indisponible >-4=Nom d'utilisateur ou mot de passe incorrect >-5=L'utilisateur n'est pas autoris\u00e9 \u00e0 se connecter >-32000=Expiration du d\u00e9lai d'attente d'une r\u00e9ponse du serveur >-32100=Client d\u00e9j\u00e0 connect\u00e9 >-32101=Client d\u00e9j\u00e0 d\u00e9connect\u00e9 >-32102=Client en cours de d\u00e9connexion >-32103=Impossible de se connecter au serveur >-32104=Client non connect\u00e9 >-32105=Le type SocketFactory sp\u00e9cifi\u00e9 ne correspond pas \u00e0 l'URI de courtier >-32106=Erreur de configuration SSL >-32107=D\u00e9connexion non autoris\u00e9e pour une m\u00e9thode de rappel >-32108=Paquet non reconnu >-32109=Connexion perdue >-32200=La persistance est d\u00e9j\u00e0 en cours d'utilisation >diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_hu.properties b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_hu.properties >deleted file mode 100644 >index 59a1b34..0000000 >--- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_hu.properties >+++ /dev/null >@@ -1,30 +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 >-# */ >-# NLS_MESSAGEFORMAT_VAR >-# NLS_ENCODING=UNICODE >-1=\u00c9rv\u00e9nytelen protokoll v\u00e1ltozat >-2=\u00c9rv\u00e9nytelen \u00fcgyf\u00e9lazonos\u00edt\u00f3 >-3=K\u00f6zvet\u00edt\u0151 nem el\u00e9rhet\u0151 >-4=Rossz felhaszn\u00e1l\u00f3i n\u00e9v vagy jelsz\u00f3 >-5=Nem jogosult csatlakozni >-32000=T\u00fall\u00e9pte a megengedett id\u0151t a kiszolg\u00e1l\u00f3 v\u00e1lasz\u00e1ra v\u00e1rva >-32100=Az \u00fcgyf\u00e9l m\u00e1r csatlakozik >-32101=Az \u00fcgyf\u00e9l m\u00e1r sz\u00e9tkapcsolt >-32102=Az \u00fcgyf\u00e9l \u00e9pp megszak\u00edtja a kapcsolatot >-32103=Nem lehet kapcsol\u00f3dni a kiszolg\u00e1l\u00f3hoz >-32104=Az \u00fcgyf\u00e9l nincs csatlakoztatva >-32105=A megadott SocketFactory t\u00edpus nem illeszkedik a k\u00f6zvet\u00edt\u0151 URI azonos\u00edt\u00f3hoz >-32106=SSL konfigur\u00e1ci\u00f3s hiba >-32107=A megszak\u00edt\u00e1s visszah\u00edv\u00e1s met\u00f3dusb\u00f3l nem enged\u00e9lyezett >-32108=Ismeretlen csomag >-32109=Kapcsolat elveszett >-32200=A megmarad\u00f3 \u00e1llapot m\u00e1r haszn\u00e1latban van >diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_it.properties b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_it.properties >deleted file mode 100644 >index 7b68976..0000000 >--- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_it.properties >+++ /dev/null >@@ -1,30 +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 >-# */ >-# NLS_MESSAGEFORMAT_VAR >-# NLS_ENCODING=UNICODE >-1=Versione di protocollo non valida >-2=ID client non valido >-3=Broker non disponibile >-4=Nome utente o password non validi >-5=Non autorizzato per la connessione >-32000=Scaduto in attesa di una risposta dal server >-32100=Client gi\u00e0 connesso >-32101=Client gi\u00e0 disconnesso >-32102=Client in fase di disconnessione >-32103=Impossibile effettuare la connessione al server >-32104=Client non connesso >-32105=Il tipo SocketFactory specificato non corrisponde all'URI del broker >-32106=Errore di configurazione SSL >-32107=Disconnessione non consentita da un metodo callback >-32108=Pacchetto non riconosciuto >-32109=Connessione persa >-32200=Persistenza gi\u00e0 in uso >diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_ja.properties b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_ja.properties >deleted file mode 100644 >index a6ac091..0000000 >--- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_ja.properties >+++ /dev/null >@@ -1,30 +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 >-# */ >-# NLS_MESSAGEFORMAT_VAR >-# NLS_ENCODING=UNICODE >-1=\u7121\u52b9\u306a\u30d7\u30ed\u30c8\u30b3\u30eb\u30fb\u30d0\u30fc\u30b8\u30e7\u30f3\u3067\u3059 >-2=\u7121\u52b9\u306a\u30af\u30e9\u30a4\u30a2\u30f3\u30c8 ID \u3067\u3059 >-3=\u30d6\u30ed\u30fc\u30ab\u30fc\u304c\u4f7f\u7528\u4e0d\u53ef\u3067\u3059 >-4=\u9593\u9055\u3063\u305f\u30e6\u30fc\u30b6\u30fc\u540d\u307e\u305f\u306f\u30d1\u30b9\u30ef\u30fc\u30c9\u3067\u3059 >-5=\u63a5\u7d9a\u3059\u308b\u6a29\u9650\u304c\u3042\u308a\u307e\u305b\u3093 >-32000=\u30b5\u30fc\u30d0\u30fc\u304b\u3089\u306e\u5fdc\u7b54\u5f85\u6a5f\u304c\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8\u306b\u306a\u308a\u307e\u3057\u305f >-32100=\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306f\u65e2\u306b\u63a5\u7d9a\u6e08\u307f\u3067\u3059 >-32101=\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306f\u65e2\u306b\u5207\u65ad\u6e08\u307f\u3067\u3059 >-32102=\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306f\u73fe\u5728\u5207\u65ad\u4e2d\u3067\u3059 >-32103=\u30b5\u30fc\u30d0\u30fc\u306b\u63a5\u7d9a\u3067\u304d\u307e\u305b\u3093 >-32104=\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306f\u63a5\u7d9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093 >-32105=\u6307\u5b9a\u3055\u308c\u305f SocketFactory \u30bf\u30a4\u30d7\u306f\u30d6\u30ed\u30fc\u30ab\u30fc URI \u3068\u4e00\u81f4\u3057\u307e\u305b\u3093 >-32106=SSL \u69cb\u6210\u30a8\u30e9\u30fc\u3067\u3059 >-32107=\u30b3\u30fc\u30eb\u30d0\u30c3\u30af\u30fb\u30e1\u30bd\u30c3\u30c9\u304b\u3089\u306e\u5207\u65ad\u306f\u8a31\u53ef\u3055\u308c\u307e\u305b\u3093 >-32108=\u8b58\u5225\u3055\u308c\u3066\u3044\u306a\u3044\u30d1\u30b1\u30c3\u30c8\u3067\u3059 >-32109=\u63a5\u7d9a\u55aa\u5931 >-32200=\u30d1\u30fc\u30b7\u30b9\u30bf\u30f3\u30b9\u306f\u3059\u3067\u306b\u4f7f\u7528\u4e2d\u3067\u3059\u3002 >diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_ko.properties b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_ko.properties >deleted file mode 100644 >index c75b34d..0000000 >--- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_ko.properties >+++ /dev/null >@@ -1,30 +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 >-# */ >-# NLS_MESSAGEFORMAT_VAR >-# NLS_ENCODING=UNICODE >-1=\uc62c\ubc14\ub974\uc9c0 \uc54a\uc740 \ud504\ub85c\ud1a0\ucf5c \ubc84\uc804 >-2=\uc62c\ubc14\ub974\uc9c0 \uc54a\uc740 \ud074\ub77c\uc774\uc5b8\ud2b8 ID >-3=\ube0c\ub85c\ucee4 \uc0ac\uc6a9 \ubd88\uac00\ub2a5 >-4=\uc798\ubabb\ub41c \uc0ac\uc6a9\uc790 \uc774\ub984 \ub610\ub294 \ube44\ubc00\ubc88\ud638 >-5=\uc5f0\uacb0\ud560 \uc218 \uc788\ub294 \uad8c\ud55c\uc774 \ubd80\uc5ec\ub418\uc9c0 \uc54a\uc74c >-32000=\uc11c\ubc84\uc5d0\uc11c \uc751\ub2f5\uc744 \uae30\ub2e4\ub9ac\ub294 \uc911 \uc81c\ud55c\uc2dc\uac04 \ucd08\uacfc >-32100=\ud074\ub77c\uc774\uc5b8\ud2b8\uac00 \uc774\ubbf8 \uc5f0\uacb0\ub428 >-32101=\ud074\ub77c\uc774\uc5b8\ud2b8\uac00 \uc774\ubbf8 \uc5f0\uacb0\uc774 \ub04a\uae40 >-32102=\ud604\uc7ac \ud074\ub77c\uc774\uc5b8\ud2b8\uac00 \uc5f0\uacb0\uc744 \ub04a\ub294 \uc911 >-32103=\uc11c\ubc84\uc5d0 \uc5f0\uacb0\ud560 \uc218 \uc5c6\uc74c >-32104=\ud074\ub77c\uc774\uc5b8\ud2b8\uac00 \uc5f0\uacb0\ub418\uc9c0 \uc54a\uc74c >-32105=\uc9c0\uc815\ub41c SocketFactory \uc720\ud615\uc774 \ube0c\ub85c\ucee4 URI\uc640 \uc77c\uce58\ud558\uc9c0 \uc54a\uc74c >-32106=SSL \uad6c\uc131 \uc624\ub958 >-32107=\ucf5c\ubc31 \uba54\uc18c\ub4dc\ub85c\ubd80\ud130 \uc5f0\uacb0\uc744 \ub04a\ub294 \uac83\uc774 \ud5c8\uc6a9\ub418\uc9c0 \uc54a\uc74c >-32108=\uc778\uc2dd\ub418\uc9c0 \uc54a\uc740 \ud328\ud0b7 >-32109=\uc5f0\uacb0 \uc720\uc2e4 >-32200=\uc9c0\uc18d \ud30c\uc77c\uc744 \uc774\ubbf8 \uc0ac\uc6a9 \uc911 >diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_pl.properties b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_pl.properties >deleted file mode 100644 >index 6c1222a..0000000 >--- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_pl.properties >+++ /dev/null >@@ -1,30 +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 >-# */ >-# NLS_MESSAGEFORMAT_VAR >-# NLS_ENCODING=UNICODE >-1=Niepoprawna wersja protoko\u0142u >-2=Niepoprawny identyfikator klienta >-3=Broker niedost\u0119pny >-4=Niepoprawna nazwa u\u017cytkownika lub has\u0142o >-5=Brak autoryzacji do nawi\u0105zania po\u0142\u0105czenia >-32000=Przekroczono limit czasu oczekiwania na odpowied\u017a z serwera >-32100=Po\u0142\u0105czenie z klientem zosta\u0142o ju\u017c nawi\u0105zane >-32101=Klient ju\u017c si\u0119 roz\u0142\u0105czy\u0142 >-32102=Klient roz\u0142\u0105cza si\u0119 >-32103=Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia z serwerem >-32104=Po\u0142\u0105czenie z klientem nie jest nawi\u0105zane >-32105=Podany typ fabryki SocketFactory nie jest zgodny z identyfikatorem URI brokera >-32106=B\u0142\u0105d konfiguracji protoko\u0142u SSL >-32107=Roz\u0142\u0105czenie nie jest dozwolone w metodzie procedury zwrotnej >-32108=Nierozpoznany pakiet >-32109=Utracono po\u0142\u0105czenie >-32200=Trwa\u0142o\u015b\u0107 jest ju\u017c w u\u017cyciu >diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_pt_BR.properties b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_pt_BR.properties >deleted file mode 100644 >index e4560de..0000000 >--- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_pt_BR.properties >+++ /dev/null >@@ -1,30 +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 >-# */ >-# NLS_MESSAGEFORMAT_VAR >-# NLS_ENCODING=UNICODE >-1=Vers\u00e3o de protocolo inv\u00e1lida >-2=ID de cliente inv\u00e1lido >-3=Broker indispon\u00edvel >-4=Nome de usu\u00e1rio ou senha inv\u00e1lidos >-5=N\u00e3o autorizado a conectar >-32000=Tempo limite atingido ao aguardar por uma resposta do servidor >-32100=Cliente j\u00e1 conectado >-32101=Cliente j\u00e1 desconectado >-32102=Cliente desconectando atualmente >-32103=N\u00e3o \u00e9 poss\u00edvel se conectar ao servidor >-32104=O cliente n\u00e3o est\u00e1 conectado >-32105=O tipo SocketFactory especificado n\u00e3o corresponde ao URI do broker >-32106=Erro de configura\u00e7\u00e3o de SSL >-32107=A desconex\u00e3o n\u00e3o \u00e9 permitida a partir de um m\u00e9todo de retorno de chamada >-32108=Pacote n\u00e3o reconhecido >-32109=Conex\u00e3o perdida >-32200=Persist\u00eancia j\u00e1 em uso >diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_ru.properties b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_ru.properties >deleted file mode 100644 >index 9ca99a0..0000000 >--- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_ru.properties >+++ /dev/null >@@ -1,30 +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 >-# */ >-# NLS_MESSAGEFORMAT_VAR >-# NLS_ENCODING=UNICODE >-1=\u041d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0430 >-2=\u041d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u0418\u0414 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 >-3=\u041f\u043e\u0441\u0440\u0435\u0434\u043d\u0438\u043a \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d >-4=\u041e\u0448\u0438\u0431\u043e\u0447\u043d\u043e\u0435 \u0438\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438\u043b\u0438 \u043f\u0430\u0440\u043e\u043b\u044c >-5=\u041d\u0435\u0442 \u043f\u0440\u0430\u0432 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043d\u0430 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 >-32000=\u0422\u0430\u043c-\u0430\u0443\u0442 \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u044f \u043e\u0442\u0432\u0435\u0442\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 >-32100=\u041a\u043b\u0438\u0435\u043d\u0442 \u0443\u0436\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d >-32101=\u041a\u043b\u0438\u0435\u043d\u0442 \u0443\u0436\u0435 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d >-32102=\u041a\u043b\u0438\u0435\u043d\u0442 \u043e\u0442\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f >-32103=\u041d\u0435 \u0443\u0434\u0430\u0435\u0442\u0441\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443 >-32104=\u041a\u043b\u0438\u0435\u043d\u0442 \u043d\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d >-32105=\u0422\u0438\u043f SocketFactory \u043d\u0435 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 URI \u043f\u043e\u0441\u0440\u0435\u0434\u043d\u0438\u043a\u0430 >-32106=\u041e\u0448\u0438\u0431\u043a\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 SSL >-32107=\u041e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043d\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u043e \u0432 \u043c\u0435\u0442\u043e\u0434\u0435 \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430 >-32108=\u041d\u0435\u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u043d\u043d\u044b\u0439 \u043f\u0430\u043a\u0435\u0442 >-32109=\u0421\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043f\u043e\u0442\u0435\u0440\u044f\u043d\u043e >-32200=\u0425\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u0443\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f >diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_zh_CN.properties b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_zh_CN.properties >deleted file mode 100644 >index cd3ae0d..0000000 >--- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_zh_CN.properties >+++ /dev/null >@@ -1,30 +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 >-# */ >-# NLS_MESSAGEFORMAT_VAR >-# NLS_ENCODING=UNICODE >-1=\u65e0\u6548\u534f\u8bae\u7248\u672c >-2=\u65e0\u6548\u5ba2\u6237\u673a\u6807\u8bc6 >-3=\u4ee3\u7406\u7a0b\u5e8f\u4e0d\u53ef\u7528 >-4=\u9519\u8bef\u7684\u7528\u6237\u540d\u6216\u5bc6\u7801 >-5=\u65e0\u6743\u8fde\u63a5 >-32000=\u7b49\u5f85\u6765\u81ea\u670d\u52a1\u5668\u7684\u54cd\u5e94\u65f6\u8d85\u65f6 >-32100=\u5ba2\u6237\u673a\u5df2\u8fde\u63a5 >-32101=\u5ba2\u6237\u673a\u5df2\u65ad\u5f00\u8fde\u63a5 >-32102=\u5ba2\u6237\u673a\u6b63\u5728\u65ad\u5f00\u8fde\u63a5 >-32103=\u65e0\u6cd5\u8fde\u63a5\u81f3\u670d\u52a1\u5668 >-32104=\u5ba2\u6237\u673a\u672a\u8fde\u63a5 >-32105=\u6307\u5b9a\u7684 SocketFactory \u7c7b\u578b\u4e0e\u4ee3\u7406\u7a0b\u5e8f URI \u4e0d\u5339\u914d >-32106=SSL \u914d\u7f6e\u9519\u8bef >-32107=\u4e0d\u5141\u8bb8\u901a\u8fc7\u56de\u8c03\u65b9\u6cd5\u65ad\u5f00\u8fde\u63a5 >-32108=\u4e0d\u53ef\u8bc6\u522b\u7684\u5305 >-32109=\u5df2\u65ad\u5f00\u8fde\u63a5 >-32200=\u6301\u4e45\u6027\u5df2\u5728\u4f7f\u7528\u4e2d >diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_zh_TW.properties b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_zh_TW.properties >deleted file mode 100644 >index 441b90e..0000000 >--- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_zh_TW.properties >+++ /dev/null >@@ -1,30 +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 >-# */ >-# NLS_MESSAGEFORMAT_VAR >-# NLS_ENCODING=UNICODE >-1=\u901a\u8a0a\u5354\u5b9a\u7248\u672c\u7121\u6548 >-2=\u7528\u6236\u7aef ID \u7121\u6548 >-3=\u5206\u914d\u7ba1\u7406\u7cfb\u7d71\u7121\u6cd5\u4f7f\u7528 >-4=\u4f7f\u7528\u8005\u540d\u7a31\u6216\u5bc6\u78bc\u4e0d\u7576 >-5=\u672a\u7372\u6388\u6b0a\u9023\u63a5 >-32000=\u7b49\u5f85\u4f3a\u670d\u5668\u7684\u56de\u61c9\u6642\u903e\u6642 >-32100=\u7528\u6236\u7aef\u5df2\u9023\u63a5 >-32101=\u7528\u6236\u7aef\u5df2\u4e2d\u65b7\u9023\u7dda >-32102=\u7528\u6236\u7aef\u76ee\u524d\u6b63\u5728\u4e2d\u65b7\u9023\u7dda >-32103=\u7121\u6cd5\u9023\u63a5\u5230\u4f3a\u670d\u5668 >-32104=\u7528\u6236\u7aef\u672a\u9023\u63a5 >-32105=\u6307\u5b9a\u7684 SocketFactory \u985e\u578b\u8207\u5206\u914d\u7ba1\u7406\u7cfb\u7d71 URI \u4e0d\u7b26 >-32106=SSL \u914d\u7f6e\u932f\u8aa4 >-32107=\u4e0d\u5bb9\u8a31\u8207\u56de\u547c\u65b9\u6cd5\u4e2d\u65b7\u9023\u7dda >-32108=\u5c01\u5305\u7121\u6cd5\u8fa8\u8b58 >-32109=\u9023\u7dda\u907a\u5931 >-32200=\u6301\u7e8c\u6027\u5df2\u5728\u4f7f\u7528\u4e2d >diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/security/SSLSocketFactoryFactory.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/security/SSLSocketFactoryFactory.java >deleted file mode 100644 >index c7ba080..0000000 >--- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/security/SSLSocketFactoryFactory.java >+++ /dev/null >@@ -1,1352 +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.security; >- >-import java.io.FileInputStream; >-import java.io.FileNotFoundException; >-import java.io.IOException; >-import java.security.KeyManagementException; >-import java.security.KeyStore; >-import java.security.KeyStoreException; >-import java.security.NoSuchAlgorithmException; >-import java.security.NoSuchProviderException; >-import java.security.UnrecoverableKeyException; >-import java.security.cert.CertificateException; >-import java.util.Hashtable; >-import java.util.Iterator; >-import java.util.Properties; >-import java.util.Set; >-import java.util.Vector; >- >-import javax.net.ssl.KeyManager; >-import javax.net.ssl.KeyManagerFactory; >-import javax.net.ssl.SSLContext; >-import javax.net.ssl.SSLSocketFactory; >-import javax.net.ssl.TrustManager; >-import javax.net.ssl.TrustManagerFactory; >- >-import org.eclipse.paho.client.mqttv3.MqttSecurityException; >-//import org.eclipse.paho.client.mqttv3.internal.comms.MqttDirectException; >-//import org.eclipse.paho.client.mqttv3.internal.comms.MqttSSLInitException; >-import org.eclipse.paho.client.mqttv3.logging.Logger; >- >- >-/** >- * An SSLSocketFactoryFactory provides a socket factory and a server socket >- * factory that then can be used to create SSL client sockets or SSL server >- * sockets. >- * <p> >- * The SSLSocketFactoryFactory is configured using IBM SSL properties, i.e. >- * properties of the format "com.ibm.ssl.propertyName", e.g. >- * "com.ibm.ssl.keyStore". The class supports multiple configurations, each >- * configuration is identified using a name or configuration ID. The >- * configuration ID with "null" is used as a default configuration. When a >- * socket factory is being created for a given configuration, properties of that >- * configuration are first picked. If a property is not defined there, then that >- * property is looked up in the default configuration. Finally, if a property >- * element is still not found, then the corresponding system property is >- * inspected, i.e. javax.net.ssl.keyStore. If the system property is not set >- * either, then the system's default value is used (if available) or an >- * exception is thrown. >- * <p> >- * The SSLSocketFacotryFactory can be reconfigured at any time. A >- * reconfiguration does not affect existing socket factories. >- * <p> >- * All properties share the same key space; i.e. the configuration ID is not >- * part of the property keys. >- * <p> >- * The methods should be called in the following order: >- * <ol> >- * <li><b>isSupportedOnJVM()</b>: to check whether this class is supported on >- * the runtime platform. Not all runtimes support SSL/TLS.</li> >- * <li><b>SSLSocketFactoryFactory()</b>: the constructor. Clients >- * (in the same JVM) may share an SSLSocketFactoryFactory, or have one each.</li> >- * <li><b>initialize(properties, configID)</b>: 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</li> >- * <li><b>getEnabledCipherSuites(configID)</b>: to later set the enabled >- * cipher suites on the socket [see below].</li> >- * </ol> >- * <ul> >- * <li><i>For an MQTT server:</i></li> >- * <ol> >- * <li><b>getKeyStore(configID)</b>: Optionally, to check that if there is no >- * keystore, then that all the enabled cipher suits are anonymous.</li> >- * <li><b>createServerSocketFactory(configID)</b>: to create an >- * SSLServerSocketFactory.</li> >- * <li><b>getClientAuthentication(configID)</b>: to later set on the >- * SSLServerSocket (itself created from the SSLServerSocketFactory) whether >- * client authentication is needed.</li> >- * </ol> >- * <li><i>For an MQTT client:</i></li> >- * <ol> >- * <li><b>createSocketFactory(configID)</b>: to create an SSLSocketFactory.</li> >- * </ol> >- * </ul> >- */ >-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<b.length) { >- c[j++] = (char) ((b[i++] & 0xFF) + ((b[i++] & 0xFF)<<8)); >- } >- return c; >- } >- >- /** >- * Convert char array to byte array, where each char is split into two >- * bytes. >- * >- * @param c >- * char array >- * @return byte array >- */ >- public static byte[] toByte(char[] c) { >- if(c==null) return null; >- byte[] b=new byte[c.length*2]; >- int i=0; int j=0; >- while(j<c.length) { >- b[i++] = (byte) (c[j] & 0xFF); >- b[i++] = (byte) ((c[j++] >> 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<password.length;i++) { >-// password[i]=' '; >-// } >-// putOrRemove(p, KEYSTOREPWD, ePasswd); >-// } >- >- /** >- * Sets the keystore provider. The corresponding provider must be installed >- * in the system. Example values: "IBMJCE" or "IBMJCEFIPS". >- * >- * @param configID >- * The configuration identifier for selecting a configuration or >- * null for the default configuration. >- * @param provider >- * The name of a java cryptography extension >- */ >-// public void setKeyStoreProvider(String configID, String provider) { >-// Properties p = getOrCreate(configID); >-// putOrRemove(p, KEYSTOREPROVIDER, provider); >-// } >- >- /** >- * Sets the keystore type. For example, PKCS12, JKS or JCEKS. The types that >- * are supported depend on the keystore provider. >- * >- * @param configID >- * The configuration identifier for selecting a configuration or >- * null for the default configuration. >- * @param type >- * The keystore type >- */ >-// public void setKeyStoreType(String configID, String type) { >-// Properties p = getOrCreate(configID); >-// putOrRemove(p, KEYSTORETYPE, type); >-// } >- >- /** >- * Sets a custom key manager and the algorithm that it uses. The keymanager >- * is specified in the format "algorithm|provider", for example >- * "IbmX509|IBMJSSE2". The provider might be empty, in which case the >- * default provider is configured with the specified algorithm. The key >- * manager must implement the javax.net.ssl.X509KeyManager interface. >- * >- * @param configID >- * The configuration identifier for selecting a configuration or >- * null for the default configuration. >- * @param keymanager >- * An algorithm, provider pair as secified above. >- */ >-// public void setCustomKeyManager(String configID, String keymanager) { >-// Properties p = getOrCreate(configID); >-// putOrRemove(p, CUSTOMKEYMGR, keymanager); >-// } >- >- /** >- * Sets the filename of the truststore object. >- * >- * @param configID >- * The configuration identifier for selecting a configuration or >- * null for the default configuration. >- * @param trustStore >- * A filename that points to a valid truststore. >- */ >-// public void setTrustStore(String configID, String trustStore) { >-// Properties p = getOrCreate(configID); >-// putOrRemove(p, TRUSTSTORE, trustStore); >-// } >- >- /** >- * Sets the password that is used for the truststore. 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 truststore password. >- */ >-// public void setTrustStorePassword(String configID, char[] password) { >-// Properties p = getOrCreate(configID); >-// // convert password, using XOR-based scrambling. >-// String ePasswd = obfuscate(password); >-// for(int i=0;i<password.length;i++) { >-// password[i]=' '; >-// } >-// putOrRemove(p, TRUSTSTOREPWD, ePasswd); >-// } >- >- /** >- * Sets the truststore provider. The corresponding provider must be >- * installed in the system. Example values: "IBMJCE" or "IBMJCEFIPS". >- * >- * @param configID >- * The configuration identifier for selecting a configuration or >- * null for the default configuration. >- * @param provider >- * The name of a java cryptography extension. >- */ >-// public void setTrustStoreProvider(String configID, String provider) { >-// Properties p = getOrCreate(configID); >-// putOrRemove(p, TRUSTSTOREPROVIDER, provider); >-// } >- >- /** >- * Sets the truststore type. For example, PKCS12, JKS or JCEKS. The types >- * that are supported depend on the truststore provider. >- * >- * @param configID >- * The configuration identifier for selecting a configuration or >- * null for the default configuration. >- * @param type >- * The truststore type. >- */ >-// public void setTrustStoreType(String configID, String type) { >-// Properties p = getOrCreate(configID); >-// putOrRemove(p, TRUSTSTORETYPE, type); >-// } >- >- /** >- * Sets a custom trust managers and the algorithm that it uses. The >- * trustmanager is specified in the format "algorithm|provider", for example >- * "IbmX509|IBMJSSE2". The provider might be empty, in which case the >- * default provider is configured with the specified algorithm. The trust >- * manager must implement the javax.net.ssl.X509TrustManager interface. >- * >- * @param configID >- * The configuration identifier for selecting a configuration or >- * null for the default configuration. >- * @param trustmanager >- * An algorithm, provider pair as secified above. >- */ >-// public void setCustomTrustManager(String configID, String trustmanager) { >-// Properties p = getOrCreate(configID); >-// putOrRemove(p, CUSTOMTRUSTMGR, trustmanager); >-// } >- >- /** >- * Sets the list of enabled ciphers. For a list of acceptable values, see >- * the documentation of the underlying JSSE. >- * >- * @param configID >- * The configuration identifier for selecting a configuration or >- * null for the default configuration. >- * @param ciphers >- * An array of cipher suite names such as >- * SSL_RSA_WITH_AES_128_CBC_SHA. >- */ >-// public void setEnabledCipherSuites(String configID, String[] ciphers) { >-// if (ciphers == null) >-// return; >-// Properties p = getOrCreate(configID); >-// String cipherSet = packCipherSuites(ciphers); >-// putOrRemove(p, CIPHERSUITES, cipherSet); >-// } >- >- /** >- * Specifies whether the client is required to provide a valid certificate >- * to the client during SSL negotiation. >- * >- * @param configID >- * The configuration identifier for selecting a configuration or >- * null for the default configuration. >- * @param clientAuth >- * true, if clients are required to authenticate, false >- * otherwise. >- */ >-// public void setClientAuthentication(String configID, boolean clientAuth) { >-// Properties p = getOrCreate(configID); >-// p.put(CLIENTAUTH, Boolean.toString(clientAuth)); >-// } >- >- /** >- * Returns the property of a given key or null if it doesn't exist. It first >- * scans the indicated configuration, then the default configuration, then >- * the system properties. >- * >- * @param configID >- * The configuration identifier for selecting a configuration or >- * null for the default configuration. >- * @param ibmKey >- * @param sysProperty >- * The key for the System property. >- * @return the property of a given key or null if it doesn't exist. >- */ >- private String getProperty(String configID, String ibmKey, String sysProperty) { >- String res = null; >- res = getPropertyFromConfig(configID, ibmKey); >- if ( res != null ) { >- return res; >- } >- // scan system property, if it exists. >- if (sysProperty != null) { >- res = System.getProperty(sysProperty); >- } >- return res; >- } >- >- /** >- * Returns the property of a given key or null if it doesn't exist. It first >- * scans the indicated configuration, then the default configuration >- * >- * @param configID >- * The configuration identifier for selecting a configuration or >- * null for the default configuration. >- * @param ibmKey >- * @return the property of a given key or null if it doesn't exist. It first >- * scans the indicated configuration, then the default configuration >- */ >- private String getPropertyFromConfig(String configID, String ibmKey) { >- String res = null; >- Properties p =null; >- if(configID!=null) {; >- p = (Properties) configs.get(configID); >- } >- if (p != null) { >- res = p.getProperty(ibmKey); >- if (res != null) >- return res; >- } >- // not found in config. try default properties. >- p = (Properties) this.defaultProperties; >- if (p != null) { >- res = p.getProperty(ibmKey); >- if (res != null) >- return res; >- } >- return res; >- } >- >- /** >- * Gets the SSL protocol variant of the indicated configuration or the >- * default configuration. >- * >- * @param configID >- * The configuration identifier for selecting a configuration or >- * null for the default configuration. >- * @return The SSL protocol variant. >- */ >- public String getSSLProtocol(String configID) { >- return getProperty(configID, SSLPROTOCOL, null); >- } >- >- /** >- * Gets the JSSE provider of the indicated configuration >- * >- * @param configID >- * The configuration identifier for selecting a configuration or >- * null for the default configuration. >- * @return The JSSE provider. >- */ >- public String getJSSEProvider(String configID) { >- return getProperty(configID, JSSEPROVIDER, null); >- } >- >-// /** >-// * Get the XPD Keystore if running on the XPD platform (otherwise null). >-// * >-// * @return the XPD Keystore if running on the XPD platform (otherwise null). >-// * @throws MqttDirectException >-// */ >-// private KeyStore getXPDKeystore() throws MqttDirectException { >-// KeyStore keyStore = null; >-// try { >-// Class secPlatClass = Class.forName("com.ibm.rcp.security.auth.SecurePlatform"); >-// Method m = secPlatClass.getMethod("getKeyStore", null); >-// Object secPlat = m.invoke(null,null); // getKeyStore is static >-// m = secPlatClass.getMethod("isLoggedIn", null); >-// Boolean b = (Boolean) m.invoke(secPlat, null); >-// if (b.booleanValue()) { >-// // login to secure platform was done. >-// m = secPlatClass.getMethod("getKeyStore", null); >-// keyStore = (KeyStore) m.invoke(secPlat, null); >-// } >-// } catch (ClassNotFoundException e) { >-// /* >-// * DEVELOPER NOTE: This is not an error. This means that we are not >-// * running on XPD runtime and therefore we can not get XPD keystore. >-// * [Next step for the caller, is try to get the keystore from System >-// * properties (see getKeyStore() method).] >-// */ >-// } catch (IllegalAccessException e) { >-// Object[] inserts = { e.getLocalizedMessage() }; >-// throw new MqttSSLInitException(3026, inserts, e); >-// } catch (SecurityException e) { >-// Object[] inserts = { e.getLocalizedMessage() }; >-// throw new MqttSSLInitException(3026, inserts, e); >-// } catch (NoSuchMethodException e) { >-// Object[] inserts = { e.getLocalizedMessage() }; >-// throw new MqttSSLInitException(3026, inserts, e); >-// } catch (IllegalArgumentException e) { >-// Object[] inserts = { e.getLocalizedMessage() }; >-// throw new MqttSSLInitException(3026, inserts, e); >-// } catch (InvocationTargetException e) { >-// Object[] inserts = { e.getLocalizedMessage() }; >-// throw new MqttSSLInitException(3026, inserts, e); >-// } >-// return keyStore; >-// } >- >- /** >- * Gets the name of the keystore file that is used. >- * >- * @param configID >- * The configuration identifier for selecting a configuration or >- * null for the default configuration. >- * @return The name of the file that contains the keystore. >- */ >- public String getKeyStore(String configID) { //throws MqttDirectException { >- String ibmKey = KEYSTORE; >- String sysProperty = SYSKEYSTORE; >- >- String res = null; >- res = getPropertyFromConfig(configID, ibmKey); >- if ( res != null ) { >- return res; >- } >- >-// // check for the XPD keystore here >-// if ( ibmKey != null && ibmKey.equals(KEYSTORE) ) { >-// KeyStore keyStore = getXPDKeystore(); >-// if (keyStore != null) >-// return res = "Lotus Expeditor"; >-// } >- >- // scan system property, if it exists. >- if (sysProperty != null) { >- res = System.getProperty(sysProperty); >- } >- >- return res; >- } >- >- /** >- * Gets the plain-text password that is used for the keystore. >- * >- * @param configID >- * The configuration identifier for selecting a configuration or >- * null for the default configuration. >- * @return The password in plain text. >- */ >- public char[] getKeyStorePassword(String configID) { >- String pw = getProperty(configID, KEYSTOREPWD, SYSKEYSTOREPWD); >- char[] r=null; >- if (pw!=null) { >- if (pw.startsWith(xorTag)) { >- r = deObfuscate(pw); >- } else { >- r = pw.toCharArray(); >- } >- } >- return r; >- } >- >- /** >- * Gets the type of keystore. >- * >- * @param configID >- * The configuration identifier for selecting a configuration or >- * null for the default configuration. >- * @return The keystore type. >- */ >- public String getKeyStoreType(String configID) { >- return getProperty(configID, KEYSTORETYPE, SYSKEYSTORETYPE); >- } >- >- /** >- * Gets the keystore provider. >- * >- * @param configID >- * The configuration identifier for selecting a configuration or >- * null for the default configuration. >- * @return The name of the keystore provider. >- */ >- public String getKeyStoreProvider(String configID) { >- return getProperty(configID, KEYSTOREPROVIDER, null); >- } >- >- /** >- * Gets the key manager algorithm that is used. >- * >- * @param configID >- * The configuration identifier for selecting a configuration or >- * null for the default configuration. >- * @return The key manager algorithm. >- */ >- public String getKeyManager(String configID) { >- return getProperty(configID, KEYSTOREMGR, SYSKEYMGRALGO); >- } >- >- /** >- * Gets the name of the truststore file that is used. >- * >- * @param configID >- * The configuration identifier for selecting a configuration or >- * null for the default configuration. >- * @return The name of the file that contains the truststore. >- */ >- public String getTrustStore(String configID) { >- return getProperty(configID, TRUSTSTORE, SYSTRUSTSTORE); >- } >- >- /** >- * Gets the plain-text password that is used for the truststore. >- * >- * @param configID >- * The configuration identifier for selecting a configuration or >- * null for the default configuration. >- * @return The password in plain text. >- */ >- public char[] getTrustStorePassword(String configID) { >- String pw = getProperty(configID, TRUSTSTOREPWD, SYSTRUSTSTOREPWD); >- char[] r=null; >- if (pw!=null) { >- if(pw.startsWith(xorTag)) { >- r = deObfuscate(pw); >- } else { >- r = pw.toCharArray(); >- } >- } >- return r; >- } >- >- /** >- * Gets the type of truststore. >- * >- * @param configID >- * The configuration identifier for selecting a configuration or >- * null for the default configuration. >- * @return The truststore type. >- */ >- public String getTrustStoreType(String configID) { >- return getProperty(configID, TRUSTSTORETYPE, null); >- } >- >- /** >- * Gets the truststore provider. >- * >- * @param configID >- * The configuration identifier for selecting a configuration or >- * null for the default configuration. >- * @return The name of the truststore provider. >- */ >- public String getTrustStoreProvider(String configID) { >- return getProperty(configID, TRUSTSTOREPROVIDER, null); >- } >- >- /** >- * Gets the trust manager algorithm that is used. >- * >- * @param configID >- * The configuration identifier for selecting a configuration or >- * null for the default configuration. >- * @return The trust manager algorithm. >- */ >- public String getTrustManager(String configID) { >- return getProperty(configID, TRUSTSTOREMGR, SYSTRUSTMGRALGO); >- } >- >- /** >- * Returns an array with the enabled ciphers. >- * >- * @param configID >- * The configuration identifier for selecting a configuration or >- * null for the default configuration. >- * @return an array with the enabled ciphers >- */ >- public String[] getEnabledCipherSuites(String configID) { >- String ciphers = getProperty(configID, CIPHERSUITES, null); >- String[] res = unpackCipherSuites(ciphers); >- return res; >- } >- >- /** >- * Returns whether client authentication is required. >- * >- * @param configID >- * The configuration identifier for selecting a configuration or >- * null for the default configuration. >- * @return true, if clients are required to authenticate, false otherwise. >- */ >- public boolean getClientAuthentication(String configID) { >- String auth = getProperty(configID, CLIENTAUTH, null); >- boolean res = false; >- if (auth != null) { >- res = Boolean.valueOf(auth).booleanValue(); >- } >- return res; >- } >- >- /** >- * Initializes key- and truststore. Returns an SSL context factory. If no >- * SSLProtocol is already set, uses DEFAULT_PROTOCOL >- * >- * @see org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory#DEFAULT_PROTOCOL >- * >- * @param configID >- * The configuration ID >- * @return An SSL context factory. >- * @throws MqttDirectException >- */ >- private SSLContext getSSLContext(String configID) >- throws MqttSecurityException{ >- final String METHOD_NAME = "getSSLContext"; >- SSLContext ctx = null; >- >- String protocol = getSSLProtocol(configID); >- if (protocol == null) { >- protocol = DEFAULT_PROTOCOL; >- } >- if (logger != null) { >- // 12000 "SSL initialization: configID = {0}, protocol = {1}" >- logger.fine(CLASS_NAME, METHOD_NAME, "12000", new Object[] {configID!=null ? configID : "null (broker defaults)", >- protocol}); >- } >- >- String provider = getJSSEProvider(configID); >- try { >- if (provider == null) { >- ctx = SSLContext.getInstance(protocol); >- } else { >- ctx = SSLContext.getInstance(protocol, provider); >- } >- if (logger != null) { >- // 12001 "SSL initialization: configID = {0}, provider = {1}" >- logger.fine(CLASS_NAME, METHOD_NAME, "12001", new Object[] {configID!=null ? configID : "null (broker defaults)", >- ctx.getProvider().getName()}); >- } >- >- String keyStoreName = getProperty(configID, KEYSTORE, null); >- KeyStore keyStore=null; >- KeyManagerFactory keyMgrFact=null; >- KeyManager[] keyMgr=null; >-// if(keyStoreName==null) { >-// // try to instantiate XPD keyStore. >-// keyStore=getXPDKeystore(); >-// if (logger != null) { >-// if (keyStore == null) { >-// // 12002 "SSL initialization: configID = {0}, XPD keystore not available" >-// logger.fine(CLASS_NAME, METHOD_NAME, "12002", new Object[]{configID!=null ? configID : "null (broker defaults)"}); >-// } else { >-// // 12003 "SSL initialization: configID = {0}, XPD keystore available" >-// logger.fine(CLASS_NAME, METHOD_NAME, "12003", new Object[]{configID!=null ? configID : "null (broker defaults)"}); >-// } >-// } >-// } >- >- if(keyStore==null) { >- if(keyStoreName==null) { >- /* >- * No keystore in config, XPD keystore not available. Try to >- * get config from system properties. >- */ >- keyStoreName = getProperty(configID, KEYSTORE, SYSKEYSTORE); >- } >- if (logger != null) { >- // 12004 "SSL initialization: configID = {0}, keystore = {1}" >- logger.fine(CLASS_NAME, METHOD_NAME, "12004", new Object[]{configID!=null ? configID : "null (broker defaults)", >- keyStoreName!=null ? keyStoreName : "null"}); >- } >- >- char[] keyStorePwd=getKeyStorePassword(configID); >- if (logger != null) { >- // 12005 "SSL initialization: configID = {0}, keystore password = {1}" >- logger.fine(CLASS_NAME, METHOD_NAME, "12005", new Object[]{configID!=null ? configID : "null (broker defaults)", >- keyStorePwd!=null ? obfuscate(keyStorePwd) : "null"}); >- } >- >- String keyStoreType=getKeyStoreType(configID); >- if(keyStoreType==null) { >- keyStoreType = KeyStore.getDefaultType(); >- } >- if (logger != null) { >- // 12006 "SSL initialization: configID = {0}, keystore type = {1}" >- logger.fine(CLASS_NAME, METHOD_NAME, "12006", new Object[]{configID!=null ? configID : "null (broker defaults)", >- keyStoreType!=null ? keyStoreType : "null"}); >- } >- >- String keyMgrAlgo = KeyManagerFactory.getDefaultAlgorithm(); >- String keyMgrProvider = getKeyStoreProvider(configID); >- String keyManager = getKeyManager(configID); >- if (keyManager != null) { >- keyMgrAlgo = keyManager; >- } >- >- if(keyStoreName!=null && keyStoreType!=null && keyMgrAlgo!=null) { >- try { >- keyStore=KeyStore.getInstance(keyStoreType); >- keyStore.load(new FileInputStream(keyStoreName), keyStorePwd); >- if(keyMgrProvider!=null) { >- keyMgrFact = KeyManagerFactory.getInstance(keyMgrAlgo, keyMgrProvider); >- } else { >- keyMgrFact = KeyManagerFactory.getInstance(keyMgrAlgo); >- } >- if (logger != null) { >- // 12010 "SSL initialization: configID = {0}, keystore manager algorithm = {1}" >- logger.fine(CLASS_NAME, METHOD_NAME, "12010", new Object[]{configID!=null ? configID : "null (broker defaults)", >- keyMgrAlgo!=null ? keyMgrAlgo : "null"}); >- // 12009 "SSL initialization: configID = {0}, keystore manager provider = {1}" >- logger.fine(CLASS_NAME, METHOD_NAME, "12009", new Object[]{configID!=null ? configID : "null (broker defaults)", >- keyMgrFact.getProvider().getName()}); >- } >- keyMgrFact.init(keyStore, keyStorePwd); >- keyMgr=keyMgrFact.getKeyManagers(); >- } catch (KeyStoreException e) { >- throw new MqttSecurityException(e); >- } catch (CertificateException e) { >- throw new MqttSecurityException(e); >- } catch (FileNotFoundException e) { >- throw new MqttSecurityException(e); >- } catch (IOException e) { >- throw new MqttSecurityException(e); >- } catch (UnrecoverableKeyException e) { >- throw new MqttSecurityException(e); >- } >- } >- } >- // keystore loaded, keymanagers instantiated if possible >- // now the same for the truststore. >- String trustStoreName = getTrustStore(configID); >- if (logger != null) { >- // 12011 "SSL initialization: configID = {0}, truststore = {1}" >- logger.fine(CLASS_NAME, METHOD_NAME, "12011", new Object[]{configID!=null ? configID : "null (broker defaults)", >- trustStoreName!=null ? trustStoreName : "null"}); >- } >- KeyStore trustStore=null; >- TrustManagerFactory trustMgrFact=null; >- TrustManager[] trustMgr=null; >- char[] trustStorePwd=getTrustStorePassword(configID); >- if (logger != null) { >- // 12012 "SSL initialization: configID = {0}, truststore password = {1}" >- logger.fine(CLASS_NAME, METHOD_NAME, "12012", new Object[]{configID!=null ? configID : "null (broker defaults)", >- trustStorePwd!=null ? obfuscate(trustStorePwd) : "null"}); >- } >- String trustStoreType=getTrustStoreType(configID); >- if(trustStoreType==null) { >- trustStoreType = KeyStore.getDefaultType(); >- } >- if (logger != null) { >- // 12013 "SSL initialization: configID = {0}, truststore type = {1}" >- logger.fine(CLASS_NAME, METHOD_NAME, "12013", new Object[]{configID!=null ? configID : "null (broker defaults)", >- trustStoreType!=null ? trustStoreType : "null"}); >- } >- >- String trustMgrAlgo = TrustManagerFactory.getDefaultAlgorithm(); >- String trustMgrProvider = getTrustStoreProvider(configID); >- String trustManager = getTrustManager(configID); >- if (trustManager != null) { >- trustMgrAlgo = trustManager; >- } >- >- if(trustStoreName!=null && trustStoreType!=null && trustMgrAlgo!=null) { >- try { >- trustStore=KeyStore.getInstance(trustStoreType); >- trustStore.load(new FileInputStream(trustStoreName), trustStorePwd); >- if(trustMgrProvider!=null) { >- trustMgrFact = TrustManagerFactory.getInstance(trustMgrAlgo, trustMgrProvider); >- } else { >- trustMgrFact = TrustManagerFactory.getInstance(trustMgrAlgo); >- } >- if (logger != null) { >- >- // 12017 "SSL initialization: configID = {0}, truststore manager algorithm = {1}" >- logger.fine(CLASS_NAME, METHOD_NAME, "12017", new Object[]{configID!=null ? configID : "null (broker defaults)", >- trustMgrAlgo!=null ? trustMgrAlgo : "null"}); >- >- // 12016 "SSL initialization: configID = {0}, truststore manager provider = {1}" >- logger.fine(CLASS_NAME, METHOD_NAME, "12016", new Object[]{configID!=null ? configID : "null (broker defaults)", >- trustMgrFact.getProvider().getName()}); >- } >- trustMgrFact.init(trustStore); >- trustMgr=trustMgrFact.getTrustManagers(); >- } catch (KeyStoreException e) { >- throw new MqttSecurityException(e); >- } catch (CertificateException e) { >- throw new MqttSecurityException(e); >- } catch (FileNotFoundException e) { >- throw new MqttSecurityException(e); >- } catch (IOException e) { >- throw new MqttSecurityException(e); >- } >- } >- // done. >- ctx.init(keyMgr, trustMgr, null); >- } catch (NoSuchAlgorithmException e) { >- throw new MqttSecurityException(e); >- } catch (NoSuchProviderException e) { >- throw new MqttSecurityException(e); >- } catch (KeyManagementException e) { >- throw new MqttSecurityException(e); >- } >- return ctx; >- } >- >-// /** >-// * Returns an SSL server socket factory for the given configuration. If no >-// * SSLProtocol is already set, uses DEFAULT_PROTOCOL. Throws >-// * IllegalArgumentException if the server socket factory could not be >-// * created due to underlying configuration problems. >-// * >-// * @see org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory#DEFAULT_PROTOCOL >-// * >-// * @param configID >-// * The configuration identifier for selecting a configuration. >-// * @return An SSLServerSocketFactory >-// * @throws MqttDirectException >-// */ >-// public SSLServerSocketFactory createServerSocketFactory(String configID) >-// throws MqttDirectException { >-// final String METHOD_NAME = "createServerSocketFactory"; >-// SSLContext ctx = getSSLContext(configID); >-// if (logger != null) { >-// // 12018 "SSL initialization: configID = {0}, application-enabled cipher suites = {1}" >-// logger.fine(CLASS_NAME, METHOD_NAME, "12018", new Object[]{configID!=null ? configID : "null (broker defaults)", >-// getEnabledCipherSuites(configID)!=null ? getProperty(configID, CIPHERSUITES, null) : "null (using platform-enabled cipher suites)"}); >-// >-// // 12019 "SSL initialization: configID = {0}, client authentication = {1}" >-// logger.fine(CLASS_NAME, METHOD_NAME, "12019", new Object[]{configID!=null ? configID : "null (broker defaults)", >-// new Boolean (getClientAuthentication(configID)).toString()}); >-// } >-// >-// return ctx.getServerSocketFactory(); >-// } >- >- /** >- * Returns an SSL socket factory for the given configuration. If no >- * SSLProtocol is already set, uses DEFAULT_PROTOCOL. Throws >- * IllegalArgumentException if the socket factory could not be created due >- * to underlying configuration problems. >- * >- * @see org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory#DEFAULT_PROTOCOL >- * @param configID >- * The configuration identifier for selecting a configuration. >- * @return An SSLSocketFactory >- * @throws MqttDirectException >- */ >- public SSLSocketFactory createSocketFactory(String configID) >- throws MqttSecurityException { >- final String METHOD_NAME = "createSocketFactory"; >- SSLContext ctx = getSSLContext(configID); >- if (logger != null) { >- // 12020 "SSL initialization: configID = {0}, application-enabled cipher suites = {1}" >- logger.fine(CLASS_NAME, METHOD_NAME, "12020", new Object[]{configID!=null ? configID : "null (broker defaults)", >- getEnabledCipherSuites(configID)!=null ? getProperty(configID, CIPHERSUITES, null) : "null (using platform-enabled cipher suites)"}); >- } >- >- return ctx.getSocketFactory(); >- } >- >-} >diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/security/SimpleBase64Encoder.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/security/SimpleBase64Encoder.java >deleted file mode 100644 >index afc4d46..0000000 >--- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/security/SimpleBase64Encoder.java >+++ /dev/null >@@ -1,123 +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.security; >- >-public class SimpleBase64Encoder { >- >- // if this string is changed, then the decode method must also be adapted. >- private final static String PWDCHARS_STRING = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; >- private final static char[] PWDCHARS_ARRAY = PWDCHARS_STRING.toCharArray(); >- >- /** >- * Encodes an array of byte into a string of printable ASCII characters >- * using a base-64 encoding. >- * @param bytes The array of bytes to e encoded >- * @return The encoded array. >- */ >- public static String encode(byte[] bytes) { >- // Allocate a string buffer. >- int len = bytes.length; >- final StringBuffer encoded = new StringBuffer((len+2)/3*4); >- int i=0; >- int j=len; >- while(j>=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 <code>CountingInputStream</code> 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 <code>MqttInputStream</code> lets applications read instances of >- * <code>MqttWireMessage</code>. >- */ >-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 <code>MqttWireMessage</code> 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 <code>MqttOutputStream</code> lets applications write instances of >- * <code>MqttWireMessage</code>. >- */ >-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 <code>MqttWireMessage</code> 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 <code>false</code> 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<qos.length;i++) { >- MqttMessage.validateQos(qos[i]); >- } >- } >- >- protected byte getMessageInfo() { >- return (byte)(2 | (this.duplicate?8:0)); >- } >- >- protected byte[] getVariableHeader() 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 byte[] getPayload() throws MqttException { >- try { >- ByteArrayOutputStream baos = new ByteArrayOutputStream(); >- DataOutputStream dos = new DataOutputStream(baos); >- for (int i=0; i<names.length; i++) { >- this.encodeUTF8(dos,names[i]); >- dos.writeByte(qos[i]); >- } >- return baos.toByteArray(); >- } >- catch (IOException ex) { >- throw new MqttException(ex); >- } >- } >- >- public boolean isRetryable() { >- return true; >- } >-} >diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttUnsubAck.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttUnsubAck.java >deleted file mode 100644 >index 32ed349..0000000 >--- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttUnsubAck.java >+++ /dev/null >@@ -1,38 +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 UNSUBACK. >- */ >-public class MqttUnsubAck extends MqttAck { >- >- public MqttUnsubAck(byte info, byte[] data) throws IOException { >- super(MqttWireMessage.MESSAGE_TYPE_UNSUBACK); >- ByteArrayInputStream bais = new ByteArrayInputStream(data); >- DataInputStream dis = new DataInputStream(bais); >- msgId = dis.readUnsignedShort(); >- dis.close(); >- } >- >- protected byte[] getVariableHeader() throws MqttException { >- // Not needed, as the client never encodes an UNSUBACK >- return new byte[0]; >- } >-} >diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttUnsubscribe.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttUnsubscribe.java >deleted file mode 100644 >index b74d4b7..0000000 >--- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttUnsubscribe.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.ByteArrayOutputStream; >-import java.io.DataOutputStream; >-import java.io.IOException; >- >-import org.eclipse.paho.client.mqttv3.MqttException; >- >- >-/** >- * An on-the-wire representation of an MQTT UNSUBSCRIBE message. >- */ >-public class MqttUnsubscribe extends MqttWireMessage { >- >- private String[] names; >- >- /** >- * Constructs an MqttUnsubscribe >- */ >- public MqttUnsubscribe(String[] names) { >- super(MqttWireMessage.MESSAGE_TYPE_UNSUBSCRIBE); >- this.names = names; >- } >- >- protected byte getMessageInfo() { >- return (byte)( 2 | (this.duplicate?8:0)); >- } >- >- protected byte[] getVariableHeader() 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 byte[] getPayload() throws MqttException { >- ByteArrayOutputStream baos = new ByteArrayOutputStream(); >- DataOutputStream dos = new DataOutputStream(baos); >- for (int i=0; i<names.length; i++) { >- this.encodeUTF8(dos, names[i]); >- } >- return baos.toByteArray(); >- } >- public boolean isRetryable() { >- return true; >- } >-} >diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttWireMessage.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttWireMessage.java >deleted file mode 100644 >index f7200bc..0000000 >--- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttWireMessage.java >+++ /dev/null >@@ -1,329 +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 java.io.InputStream; >-import java.io.UnsupportedEncodingException; >- >-import org.eclipse.paho.client.mqttv3.MqttException; >-import org.eclipse.paho.client.mqttv3.MqttPersistable; >-import org.eclipse.paho.client.mqttv3.internal.ExceptionHelper; >- >- >-/** >- * An on-the-wire representation of an MQTT message. >- */ >-public abstract class MqttWireMessage { >- protected static final String STRING_ENCODING = "UTF-8"; >- >- public static final byte MESSAGE_TYPE_CONNECT = 1; >- public static final byte MESSAGE_TYPE_CONNACK = 2; >- public static final byte MESSAGE_TYPE_PUBLISH = 3; >- public static final byte MESSAGE_TYPE_PUBACK = 4; >- public static final byte MESSAGE_TYPE_PUBREC = 5; >- public static final byte MESSAGE_TYPE_PUBREL = 6; >- public static final byte MESSAGE_TYPE_PUBCOMP = 7; >- public static final byte MESSAGE_TYPE_SUBSCRIBE = 8; >- public static final byte MESSAGE_TYPE_SUBACK = 9; >- public static final byte MESSAGE_TYPE_UNSUBSCRIBE = 10; >- public static final byte MESSAGE_TYPE_UNSUBACK = 11; >- public static final byte MESSAGE_TYPE_PINGREQ = 12; >- public static final byte MESSAGE_TYPE_PINGRESP = 13; >- public static final byte MESSAGE_TYPE_DISCONNECT = 14; >- >- /** The type of the message (e.g. CONNECT, PUBLISH, PUBACK) */ >- private byte type; >- /** The MQTT message ID */ >- protected int msgId; >- >- protected boolean duplicate = false; >- >- private byte[] encodedHeader = null; >- >- public MqttWireMessage(byte type) { >- this.type = type; >- // Use zero as the default message ID. Can't use -1, as that is serialized >- // as 65535, which would be a valid ID. >- this.msgId = 0; >- } >- >- /** >- * Sub-classes should override this to encode the message info. >- * Only the least-significant four bits will be used. >- */ >- abstract protected byte getMessageInfo(); >- >- /** >- * Sub-classes should override this method to supply the payload bytes. >- */ >- public byte[] getPayload() throws MqttException { >- return new byte[0]; >- } >- >- /** >- * Returns the type of the message. >- */ >- public byte getType() { >- return type; >- } >- >- /** >- * Returns the MQTT message ID. >- */ >- public int getMessageId() { >- return msgId; >- } >- >- /** >- * Sets the MQTT message ID. >- */ >- public void setMessageId(int msgId) { >- this.msgId = msgId; >- } >- >- /** >- * Returns a key associated with the message. For most message types >- * this will be unique. For connect, disconnect and ping only one >- * message of this type is allowed so a fixed key will be returned >- * @return key a key associated with the message >- */ >- public String getKey() { >- return new Integer(getMessageId()).toString(); >- } >- >- public byte[] getHeader() throws MqttException { >- if (encodedHeader == null) { >- try { >- int first = ((getType() & 0x0f) << 4) ^ (getMessageInfo() & 0x0f); >- byte[] varHeader = getVariableHeader(); >- int remLen = varHeader.length + getPayload().length; >- >- ByteArrayOutputStream baos = new ByteArrayOutputStream(); >- DataOutputStream dos = new DataOutputStream(baos); >- dos.writeByte(first); >- dos.write(encodeMBI(remLen)); >- dos.write(varHeader); >- dos.flush(); >- encodedHeader = baos.toByteArray(); >- } catch(IOException ioe) { >- throw new MqttException(ioe); >- } >- } >- return encodedHeader; >- } >- >- protected abstract byte[] getVariableHeader() throws MqttException; >- >- >- /** >- * Returns whether or not this message needs to include a message ID. >- */ >- public boolean isMessageIdRequired() { >- return true; >- } >- >- public static MqttWireMessage createWireMessage(MqttPersistable data) throws MqttException { >- byte[] payload = data.getPayloadBytes(); >- // The persistable interface allows a message to be restored entirely in the header array >- // Need to treat these two arrays as a single array of bytes and use the decoding >- // logic to identify the true header/payload split >- if (payload == null) { >- payload = new byte[0]; >- } >- MultiByteArrayInputStream mbais = new MultiByteArrayInputStream( >- data.getHeaderBytes(), >- data.getHeaderOffset(), >- data.getHeaderLength(), >- payload, >- data.getPayloadOffset(), >- data.getPayloadLength()); >- return createWireMessage(mbais); >- } >- >- public static MqttWireMessage createWireMessage(byte[] bytes) throws MqttException { >- ByteArrayInputStream bais = new ByteArrayInputStream(bytes); >- return createWireMessage(bais); >- } >- >- private static MqttWireMessage createWireMessage(InputStream inputStream) throws MqttException { >- try { >- CountingInputStream counter = new CountingInputStream(inputStream); >- DataInputStream in = new DataInputStream(counter); >- int first = in.readUnsignedByte(); >- byte type = (byte) (first >> 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 (pos<lengthA) { >- result = bytesA[offsetA+pos]; >- } else if (pos<lengthA+lengthB) { >- result = bytesB[offsetB+pos-lengthA]; >- } else { >- return -1; >- } >- if (result < 0) { >- result += 256; >- } >- pos++; >- return result; >- } >- >-} >diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MultiByteInteger.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MultiByteInteger.java >deleted file mode 100644 >index b88de04..0000000 >--- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MultiByteInteger.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; >- >-/** >- * Represents a Multi-Byte Integer (MBI), as defined by the MQTT V3 >- * specification. >- */ >-public class MultiByteInteger { >- private long value; >- private int length; >- >- public MultiByteInteger(long value) { >- this(value, -1); >- } >- >- public MultiByteInteger(long value, int length) { >- this.value = value; >- this.length = length; >- } >- >- /** >- * Returns the number of bytes read when decoding this MBI. >- */ >- public int getEncodedLength() { >- return length; >- } >- >- /** >- * Returns the value of this MBI. >- */ >- public long getValue() { >- return value; >- } >-} >diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/logging/JSR47Logger.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/logging/JSR47Logger.java >deleted file mode 100644 >index 4a53875..0000000 >--- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/logging/JSR47Logger.java >+++ /dev/null >@@ -1,272 +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.text.MessageFormat; >-import java.util.MissingResourceException; >-import java.util.ResourceBundle; >-import java.util.logging.Handler; >-import java.util.logging.LogRecord; >-import java.util.logging.MemoryHandler; >- >-/** >- * Implementation of the the logger interface that uses java.uti.logging >- * >- * A Logger that utilises Java's built in logging facility - java.util.logging. >- * <p>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. >- */ >-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<handlers.length; i++) { >- if (handlers[i] instanceof java.util.logging.MemoryHandler) { >- synchronized (handlers[i]) { >- mHand = ((java.util.logging.MemoryHandler)handlers[i]); >- mHand.push(); >- return; >- } // synchronized (handler). >- } >- } // for handlers... >- dumpMemoryTrace47(logger.getParent()); >- } >- } >- >-} >diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/logging/Logger.java b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/logging/Logger.java >deleted file mode 100644 >index b72bdd8..0000000 >--- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/logging/Logger.java >+++ /dev/null >@@ -1,572 +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.util.ResourceBundle; >- >-/** >- * A Logger object is used to send log and trace messages to a platform >- * specific logging implementation. Loggers are named, using a hierarchical >- * dot-separated name-space. >- * Logger names can be arbitrary strings, but they should normally be based on >- * the component or the package name of the logged component >- * >- * Logger objects may be obtained by calls on one of the getLogger factory >- * methods. These will either create a new Logger or return a suitable existing >- * Logger. >- * >- * <p> >- * 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. >- * <p> >- * 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. >- * <p> >- * The levels in descending order are: >- * <ul> >- * <li>SEVERE (log - highest value)</li> >- * <li>WARNING (log)</li> >- * <li>INFO (log)</li> >- * <li>CONFIG (log)</li> >- * <li>FINE (trace)</li> >- * <li>FINER (trace)</li> >- * <li>FINEST (trace - lowest value)</li> >- * </ul> >- * <p> >- */ >-public interface Logger { >- /** >- * SEVERE is a message level indicating a serious failure. >- * <p> >- * 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. >- * <p> >- * 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. >- * <p> >- * 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. >- * <p> >- * 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. >- * <p> >- * 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. >- * <p> >- * 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. >- * <p> >- * 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 <code>SimpleFormatter</code> 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=<location>\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 @@ >-<body> >-Provides facilities to write and format log and trace to help debug problems. >- >-<p>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. >- >-<p>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. >- >-</body> >\ 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 @@ >-<body> >-Contains a programming interface enabling applications to communicate with an MQTT server >- >-<p> >-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: >-<ul> >- <li>Where the network is expensive, has low bandwidth or is unreliable such as mobile and vsat networks >- <li>When run on an embedded or mobile device with limited processor, memory or battery >-</ul> >-<p>Features of the protocol include: >-<ul> >- <li>The publish/subscribe message pattern to provide one-to-many message >- distribution and decoupling of applications >- <li>A messaging transport that is agnostic to the content of the payload >- <li>The use of TCP/IP to provide network connectivity >- <li>The use of SSL/TLS to provide network security and trust >- <li>Three qualities of service for message delivery which are maintained across >- network, client and server breaks. >- <ul> >- <li>"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. >- <li>"At least once", where messages are assured to arrive but duplicates may occur. >- <li>"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. >- </ul> >- 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 >- <li>A small transport overhead (the fixed-length header is just 2 bytes), and >- protocol exchanges minimised to reduce network traffic >- <li>A mechanism to notify interested parties to an abnormal disconnection of >- a client using the Last Will and Testament feature >-</ul> >- >-<p>The basic means of operating the client is:</p> >-<ol> >- <li>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.</li> >- <li><code>connect</code> to the server</li> >- <li>Exchange messages with the server: >- <ul> >- <li><code>publish messages</code> to the server, >- via a <code>topic</code>.</li> >- <li><code>subscribe</code> to one more <code>topics</code>. 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 <code>callback</code> >- </ul> >- <li><code>disconnect</code> from the server.</li> >-</ol> >- >-<p>The programming model and concepts like the protocol are small and easy to use. Key concepts >-to use when creating MQTT application include: >-<ul> >- <li>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. >- <li>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. >- <li>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: >- <ul> >- <li>Message delivery will match the quality of service specified when the message was published even across >- failures of the network, client or server >- <li>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. >- </ul> >- If set to true: >- <ul> >- <li>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. >- <li>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. >- <li>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 >- </ul> >- <li>When subscribing for messages the subscription can be for an absolute topic or a wildcarded topic. >- <li>When unsubscribing the topic to be unsubscribed must match one specified on an earlier subscribe. >- <li>There are two MQTT client libraries to choose from: >- <ol> >- <li>{@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: >- <ul> >- <li>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. >- <li>Pass a {@link org.eclipse.paho.client.mqttv3.IMqttActionListener IMqttActionListener} >- to the operation. The listener will then be called back when the operation completes. >- <li>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. >- </ul> >- <li>{@link org.eclipse.paho.client.mqttv3.IMqttClient MqttClient} where methods block until >- the operation has completed. >- </ol> >- <li>For both the blocking and non-blocking clients some operations are asynchronous. This includes: >- <ul> >- <li>Notification that a new message has arrived: >- {@link org.eclipse.paho.client.mqttv3.MqttCallback#messageArrived messageArrived}. >- <li>Notification that the connection to the server has broken: >- {@link org.eclipse.paho.client.mqttv3.MqttCallback#connectionLost connectionLost}. >- <li>Notification that a message has been delivered to the server: >- {@link org.eclipse.paho.client.mqttv3.MqttCallback#deliveryComplete deliveryComplete}. >- </ul> >- A client registers interest in these notifications by registering a >- {@link org.eclipse.paho.client.mqttv3.MqttCallback MqttCallback} on the client >- <li>There are a number of programs that demonstrate the different modes of >- writing MQTT applications >- <ul> >- <li>{@link org.eclipse.paho.sample.mqttv3app.Sample} uses the blocking client interface >- <li>{@link org.eclipse.paho.sample.mqttv3app.SampleAsyncCallBack} uses the asynchronous client with >- callbacks which are notified when an operation completes >- <li>{@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. >- </ul> >-</ul> >- >-</body> >\ 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<clientId.length();i++) { >- char c = clientId.charAt(i); >- if (isSafeChar(c)) { >- keyBuffer.append(c); >- } >- } >- keyBuffer.append("-"); >- for (int i=0;i<theConnection.length();i++) { >- char c = theConnection.charAt(i); >- if (isSafeChar(c)) { >- keyBuffer.append(c); >- } >- } >- String key = keyBuffer.toString(); >- >- clientDir = new File(dataDir,key); >- >- if (!clientDir.exists()) { >- clientDir.mkdir(); >- } >- >- try { >- fileLock = new FileLock(clientDir,LOCK_FILENAME); >- } catch (Exception e) { >- throw new MqttPersistenceException(MqttPersistenceException.REASON_CODE_PERSISTENCE_IN_USE); >- } >- >- // Scan the directory for .backup files. These will >- // still exist if the JVM exited during addMessage, before >- // the new message was written to disk and the backup removed. >- restoreBackups(clientDir); >- >- } >- >- /** >- * Checks whether the persistence has been opened. >- * @throws MqttPersistenceException if the persistence has not been opened. >- */ >- private void checkIsOpen() throws MqttPersistenceException { >- if (clientDir == null) { >- throw new MqttPersistenceException(); >- } >- } >- >- public void close() throws MqttPersistenceException { >- >-// checkIsOpen(); >- if (fileLock != null) { >- fileLock.release(); >- } >- >- if (getFiles().length == 0) { >- clientDir.delete(); >- } >- clientDir = null; >- } >- >- /** >- * Writes the specified persistent data to the previously specified persistence directory. >- * This method uses a safe overwrite policy to ensure IO errors do not lose messages. >- * @param message >- * @throws MqttPersistenceException >- */ >- public void put(String key, MqttPersistable message) throws MqttPersistenceException { >- checkIsOpen(); >- File file = new File(clientDir, key+MESSAGE_FILE_EXTENSION); >- File backupFile = new File(clientDir, key+MESSAGE_FILE_EXTENSION+MESSAGE_BACKUP_FILE_EXTENSION); >- >- if (file.exists()) { >- // Backup the existing file so the overwrite can be rolled-back >- boolean result = file.renameTo(backupFile); >- if (!result) { >- backupFile.delete(); >- file.renameTo(backupFile); >- } >- } >- try { >- FileOutputStream fos = new FileOutputStream(file); >- fos.write(message.getHeaderBytes(), message.getHeaderOffset(), message.getHeaderLength()); >- if (message.getPayloadBytes()!=null) { >- fos.write(message.getPayloadBytes(), message.getPayloadOffset(), message.getPayloadLength()); >- } >- fos.getFD().sync(); >- fos.close(); >- if (backupFile.exists()) { >- // The write has completed successfully, delete the backup >- backupFile.delete(); >- } >- } >- catch (IOException ex) { >- throw new MqttPersistenceException(ex); >- } >- finally { >- if (backupFile.exists()) { >- // The write has failed - restore the backup >- boolean result = backupFile.renameTo(file); >- if (!result) { >- file.delete(); >- backupFile.renameTo(file); >- } >- } >- } >- } >- >- public MqttPersistable get(String key) throws MqttPersistenceException { >- checkIsOpen(); >- MqttPersistable result; >- try { >- File file = new File(clientDir, key+MESSAGE_FILE_EXTENSION); >- FileInputStream fis = new FileInputStream(file); >- int size = fis.available(); >- byte[] data = new byte[size]; >- int read = 0; >- while (read<size) { >- read += fis.read(data,read,size-read); >- } >- fis.close(); >- result = new MqttPersistentData(key, data, 0, data.length, null, 0, 0); >- } >- catch(IOException ex) { >- throw new MqttPersistenceException(ex); >- } >- return result; >- } >- >- >- /** >- * Deletes the data with the specified key from the previously specified persistence directory. >- */ >- public void remove(String key) throws MqttPersistenceException { >- checkIsOpen(); >- File file = new File(clientDir, key+MESSAGE_FILE_EXTENSION); >- if (file.exists()) { >- file.delete(); >- } >- } >- >- /** >- * Returns all of the persistent data from the previously specified persistence directory. >- * @return all of the persistent data from the persistence directory. >- * @throws MqttPersistenceException >- */ >- public Enumeration keys() throws MqttPersistenceException { >- checkIsOpen(); >- File[] files = getFiles(); >- Vector result = new Vector(files.length); >- for (int i=0;i<files.length;i++) { >- String filename = files[i].getName(); >- String key = filename.substring(0,filename.length()-MESSAGE_FILE_EXTENSION.length()); >- result.addElement(key); >- } >- return result.elements(); >- } >- >- private File[] getFiles() throws MqttPersistenceException { >- checkIsOpen(); >- File[] files = clientDir.listFiles(FILE_FILTER); >- if (files == null) { >- throw new MqttPersistenceException(); >- } >- return files; >- } >- >- private boolean isSafeChar(char c) { >- return Character.isJavaIdentifierPart(c) || c=='-'; >- } >- >- /** >- * Identifies any backup files in the specified directory and restores them >- * to their original file. This will overwrite any existing file of the same >- * name. This is safe as a stray backup file will only exist if a problem >- * occured whilst writing to the original file. >- * @param dir The directory in which to scan and restore backups >- */ >- private void restoreBackups(File dir) throws MqttPersistenceException { >- File[] files = dir.listFiles(new FileFilter() { >- public boolean accept(File f) { >- return f.getName().endsWith(MESSAGE_BACKUP_FILE_EXTENSION); >- } >- }); >- if (files == null) { >- throw new MqttPersistenceException(); >- } >- >- for (int i=0;i<files.length;i++) { >- File originalFile = new File(dir,files[i].getName().substring(0,files[i].getName().length()-MESSAGE_BACKUP_FILE_EXTENSION.length())); >- boolean result = files[i].renameTo(originalFile); >- if (!result) { >- originalFile.delete(); >- files[i].renameTo(originalFile); >- } >- } >- } >- >- public boolean containsKey(String key) throws MqttPersistenceException { >- checkIsOpen(); >- File file = new File(clientDir, key+MESSAGE_FILE_EXTENSION); >- return file.exists(); >- } >- >- public void clear() throws MqttPersistenceException { >- checkIsOpen(); >- File[] files = getFiles(); >- for (int i=0; i<files.length; i++) { >- files[i].delete(); >- } >- } >-} >diff --git a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/persist/package.html b/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/persist/package.html >deleted file mode 100644 >index dd20b5e..0000000 >--- a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/persist/package.html >+++ /dev/null >@@ -1,12 +0,0 @@ >-<body> >-Contains implementations of the MqttClientPersistence interface. >- >-<p> >-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. >- >-</body> >\ 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 @@ >-<body> >-Provides helpers and utilities. >- >- >-</body> >\ 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 @@ >+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> >+ >+ <parent> >+ <groupId>org.eclipse.paho</groupId> >+ <artifactId>paho-parent</artifactId> >+ <version>0.9.0</version> >+ </parent> >+ >+ <modelVersion>4.0.0</modelVersion> >+ <artifactId>paho-mqtt-client-sample</artifactId> >+ <version>0.9.0</version> >+ <packaging>jar</packaging> >+ <name>Paho :: A sample MQTT Client</name> >+ <description>A sample MQTT Client.</description> >+ <url>${paho.url}</url> >+ >+ <dependencies> >+ <dependency> >+ <groupId>org.eclipse.paho</groupId> >+ <artifactId>paho-mqtt-client</artifactId> >+ <version>${project.version}</version> >+ <scope>compile</scope> >+ </dependency> >+ </dependencies> >+</project> >\ 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: >+ * <ol> >+ * <li>Sample (this one) which uses the API which blocks until the operation completes</li> >+ * <li>SampleAsyncWait shows how to use the asynchronous API with waiters that block until >+ * an action completes</li> >+ * <li>SampleAsyncCallBack shows how to use the asynchronous API where events are >+ * used to notify the application when an action completes<li> >+ * </ol> >+ * >+ * 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<args.length; i++) { >+ // Check this is a valid argument >+ if (args[i].length() == 2 && args[i].startsWith("-")) { >+ char arg = args[i].charAt(1); >+ // Handle arguments that take no-value >+ switch(arg) { >+ case 'h': case '?': printHelp(); return; >+ case 'q': quietMode = true; continue; >+ } >+ >+ // Now handle the arguments that take a value and >+ // ensure one is specified >+ if (i == args.length -1 || args[i+1].charAt(0) == '-') { >+ System.out.println("Missing value for argument: "+args[i]); >+ printHelp(); >+ return; >+ } >+ switch(arg) { >+ case 'a': action = args[++i]; break; >+ case 't': topic = args[++i]; break; >+ case 'm': message = args[++i]; break; >+ case 's': qos = Integer.parseInt(args[++i]); break; >+ case 'b': broker = args[++i]; break; >+ case 'p': port = Integer.parseInt(args[++i]); break; >+ case 'i': clientId = args[++i]; break; >+ case 'c': cleanSession = Boolean.valueOf(args[++i]).booleanValue(); break; >+ case 'k': System.getProperties().put("javax.net.ssl.keyStore", args[++i]); break; >+ case 'w': System.getProperties().put("javax.net.ssl.keyStorePassword", args[++i]); break; >+ case 'r': System.getProperties().put("javax.net.ssl.trustStore", args[++i]); break; >+ case 'v': ssl = Boolean.valueOf(args[++i]).booleanValue(); break; >+ case 'u': userName = args[++i]; break; >+ case 'z': password = args[++i]; break; >+ default: >+ System.out.println("Unrecognised argument: "+args[i]); >+ printHelp(); >+ return; >+ } >+ } else { >+ System.out.println("Unrecognised argument: "+args[i]); >+ printHelp(); >+ return; >+ } >+ } >+ >+ // Validate the provided arguments >+ if (!action.equals("publish") && !action.equals("subscribe")) { >+ System.out.println("Invalid action: "+action); >+ printHelp(); >+ return; >+ } >+ if (qos < 0 || qos > 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 <Enter> 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 <topic>] [-m <message text>]\n" + >+ " [-s 0|1|2] -b <hostname|IP address>] [-p <brokerport>] [-i <clientID>]\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 <topic> instead of the default\n" + >+ " (publish: \"Sample/Java/v3\", subscribe: \"Sample/#\")\n" + >+ " -m Use <message text> 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_<action>\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 <enter> 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: >+ * <ol> >+ * <li>Sample (this one) which uses the API which blocks until the operation completes</li> >+ * <li>SampleAsyncWait shows how to use the asynchronous API with waiters that block until >+ * an action completes</li> >+ * <li>SampleAsyncCallBack shows how to use the asynchronous API where events are >+ * used to notify the application when an action completes<li> >+ * </ol> >+ * >+ * 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<args.length; i++) { >+ // Check this is a valid argument >+ if (args[i].length() == 2 && args[i].startsWith("-")) { >+ char arg = args[i].charAt(1); >+ // Handle arguments that take no-value >+ switch(arg) { >+ case 'h': case '?': printHelp(); return; >+ case 'q': quietMode = true; continue; >+ } >+ >+ // Now handle the arguments that take a value and >+ // ensure one is specified >+ if (i == args.length -1 || args[i+1].charAt(0) == '-') { >+ System.out.println("Missing value for argument: "+args[i]); >+ printHelp(); >+ return; >+ } >+ switch(arg) { >+ case 'a': action = args[++i]; break; >+ case 't': topic = args[++i]; break; >+ case 'm': message = args[++i]; break; >+ case 's': qos = Integer.parseInt(args[++i]); break; >+ case 'b': broker = args[++i]; break; >+ case 'p': port = Integer.parseInt(args[++i]); break; >+ case 'i': clientId = args[++i]; break; >+ case 'c': cleanSession = Boolean.valueOf(args[++i]).booleanValue(); break; >+ case 'k': System.getProperties().put("javax.net.ssl.keyStore", args[++i]); break; >+ case 'w': System.getProperties().put("javax.net.ssl.keyStorePassword", args[++i]); break; >+ case 'r': System.getProperties().put("javax.net.ssl.trustStore", args[++i]); break; >+ case 'v': ssl = Boolean.valueOf(args[++i]).booleanValue(); break; >+ case 'u': userName = args[++i]; break; >+ case 'z': password = args[++i]; break; >+ default: >+ System.out.println("Unrecognised argument: "+args[i]); >+ printHelp(); >+ return; >+ } >+ } else { >+ System.out.println("Unrecognised argument: "+args[i]); >+ printHelp(); >+ return; >+ } >+ } >+ >+ // Validate the provided arguments >+ if (!action.equals("publish") && !action.equals("subscribe")) { >+ System.out.println("Invalid action: "+action); >+ printHelp(); >+ return; >+ } >+ if (qos < 0 || qos > 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 <Enter> 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 <topic>] [-m <message text>]\n" + >+ " [-s 0|1|2] -b <hostname|IP address>] [-p <brokerport>] [-i <clientID>]\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 <topic> instead of the default\n" + >+ " (publish: \"Sample/Java/v3\", subscribe: \"Sample/#\")\n" + >+ " -m Use <message text> 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_<action>\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 <enter> 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: >+ * <ol> >+ * <li>Sample (this one) which uses the API which blocks until the operation completes</li> >+ * <li>SampleAsyncWait shows how to use the asynchronous API with waiters that block until >+ * an action completes</li> >+ * <li>SampleAsyncCallBack shows how to use the asynchronous API where events are >+ * used to notify the application when an action completes<li> >+ * </ol> >+ * >+ * 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<args.length; i++) { >+ // Check this is a valid argument >+ if (args[i].length() == 2 && args[i].startsWith("-")) { >+ char arg = args[i].charAt(1); >+ // Handle arguments that take no-value >+ switch(arg) { >+ case 'h': case '?': printHelp(); return; >+ case 'q': quietMode = true; continue; >+ } >+ >+ // Now handle the arguments that take a value and >+ // ensure one is specified >+ if (i == args.length -1 || args[i+1].charAt(0) == '-') { >+ System.out.println("Missing value for argument: "+args[i]); >+ printHelp(); >+ return; >+ } >+ switch(arg) { >+ case 'a': action = args[++i]; break; >+ case 't': topic = args[++i]; break; >+ case 'm': message = args[++i]; break; >+ case 's': qos = Integer.parseInt(args[++i]); break; >+ case 'b': broker = args[++i]; break; >+ case 'p': port = Integer.parseInt(args[++i]); break; >+ case 'i': clientId = args[++i]; break; >+ case 'c': cleanSession = Boolean.valueOf(args[++i]).booleanValue(); break; >+ case 'k': System.getProperties().put("javax.net.ssl.keyStore", args[++i]); break; >+ case 'w': System.getProperties().put("javax.net.ssl.keyStorePassword", args[++i]); break; >+ case 'r': System.getProperties().put("javax.net.ssl.trustStore", args[++i]); break; >+ case 'v': ssl = Boolean.valueOf(args[++i]).booleanValue(); break; >+ case 'u': userName = args[++i]; break; >+ case 'z': password = args[++i]; break; >+ default: >+ System.out.println("Unrecognised argument: "+args[i]); >+ printHelp(); >+ return; >+ } >+ } else { >+ System.out.println("Unrecognised argument: "+args[i]); >+ printHelp(); >+ return; >+ } >+ } >+ >+ // Validate the provided arguments >+ if (!action.equals("publish") && !action.equals("subscribe")) { >+ System.out.println("Invalid action: "+action); >+ printHelp(); >+ return; >+ } >+ if (qos < 0 || qos > 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 <Enter> 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 <topic>] [-m <message text>]\n" + >+ " [-s 0|1|2] -b <hostname|IP address>] [-p <brokerport>] [-i <clientID>]\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 <topic> instead of the default\n" + >+ " (publish: \"Sample/Java/v3\", subscribe: \"Sample/#\")\n" + >+ " -m Use <message text> 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_<action>\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 <enter> 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 @@ >+<body> >+Contains a set of sample applications that show how to use the MQTT programming interface. >+</body> >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: >- * <ol> >- * <li>Sample (this one) which uses the API which blocks until the operation completes</li> >- * <li>SampleAsyncWait shows how to use the asynchronous API with waiters that block until >- * an action completes</li> >- * <li>SampleAsyncCallBack shows how to use the asynchronous API where events are >- * used to notify the application when an action completes<li> >- * </ol> >- * >- * 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<args.length; i++) { >- // Check this is a valid argument >- if (args[i].length() == 2 && args[i].startsWith("-")) { >- char arg = args[i].charAt(1); >- // Handle arguments that take no-value >- switch(arg) { >- case 'h': case '?': printHelp(); return; >- case 'q': quietMode = true; continue; >- } >- >- // Now handle the arguments that take a value and >- // ensure one is specified >- if (i == args.length -1 || args[i+1].charAt(0) == '-') { >- System.out.println("Missing value for argument: "+args[i]); >- printHelp(); >- return; >- } >- switch(arg) { >- case 'a': action = args[++i]; break; >- case 't': topic = args[++i]; break; >- case 'm': message = args[++i]; break; >- case 's': qos = Integer.parseInt(args[++i]); break; >- case 'b': broker = args[++i]; break; >- case 'p': port = Integer.parseInt(args[++i]); break; >- case 'i': clientId = args[++i]; break; >- case 'c': cleanSession = Boolean.valueOf(args[++i]).booleanValue(); break; >- case 'k': System.getProperties().put("javax.net.ssl.keyStore", args[++i]); break; >- case 'w': System.getProperties().put("javax.net.ssl.keyStorePassword", args[++i]); break; >- case 'r': System.getProperties().put("javax.net.ssl.trustStore", args[++i]); break; >- case 'v': ssl = Boolean.valueOf(args[++i]).booleanValue(); break; >- case 'u': userName = args[++i]; break; >- case 'z': password = args[++i]; break; >- default: >- System.out.println("Unrecognised argument: "+args[i]); >- printHelp(); >- return; >- } >- } else { >- System.out.println("Unrecognised argument: "+args[i]); >- printHelp(); >- return; >- } >- } >- >- // Validate the provided arguments >- if (!action.equals("publish") && !action.equals("subscribe")) { >- System.out.println("Invalid action: "+action); >- printHelp(); >- return; >- } >- if (qos < 0 || qos > 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 <Enter> 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 <topic>] [-m <message text>]\n" + >- " [-s 0|1|2] -b <hostname|IP address>] [-p <brokerport>] [-i <clientID>]\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 <topic> instead of the default\n" + >- " (publish: \"Sample/Java/v3\", subscribe: \"Sample/#\")\n" + >- " -m Use <message text> 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_<action>\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 <enter> 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: >- * <ol> >- * <li>Sample (this one) which uses the API which blocks until the operation completes</li> >- * <li>SampleAsyncWait shows how to use the asynchronous API with waiters that block until >- * an action completes</li> >- * <li>SampleAsyncCallBack shows how to use the asynchronous API where events are >- * used to notify the application when an action completes<li> >- * </ol> >- * >- * 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<args.length; i++) { >- // Check this is a valid argument >- if (args[i].length() == 2 && args[i].startsWith("-")) { >- char arg = args[i].charAt(1); >- // Handle arguments that take no-value >- switch(arg) { >- case 'h': case '?': printHelp(); return; >- case 'q': quietMode = true; continue; >- } >- >- // Now handle the arguments that take a value and >- // ensure one is specified >- if (i == args.length -1 || args[i+1].charAt(0) == '-') { >- System.out.println("Missing value for argument: "+args[i]); >- printHelp(); >- return; >- } >- switch(arg) { >- case 'a': action = args[++i]; break; >- case 't': topic = args[++i]; break; >- case 'm': message = args[++i]; break; >- case 's': qos = Integer.parseInt(args[++i]); break; >- case 'b': broker = args[++i]; break; >- case 'p': port = Integer.parseInt(args[++i]); break; >- case 'i': clientId = args[++i]; break; >- case 'c': cleanSession = Boolean.valueOf(args[++i]).booleanValue(); break; >- case 'k': System.getProperties().put("javax.net.ssl.keyStore", args[++i]); break; >- case 'w': System.getProperties().put("javax.net.ssl.keyStorePassword", args[++i]); break; >- case 'r': System.getProperties().put("javax.net.ssl.trustStore", args[++i]); break; >- case 'v': ssl = Boolean.valueOf(args[++i]).booleanValue(); break; >- case 'u': userName = args[++i]; break; >- case 'z': password = args[++i]; break; >- default: >- System.out.println("Unrecognised argument: "+args[i]); >- printHelp(); >- return; >- } >- } else { >- System.out.println("Unrecognised argument: "+args[i]); >- printHelp(); >- return; >- } >- } >- >- // Validate the provided arguments >- if (!action.equals("publish") && !action.equals("subscribe")) { >- System.out.println("Invalid action: "+action); >- printHelp(); >- return; >- } >- if (qos < 0 || qos > 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 <Enter> 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 <topic>] [-m <message text>]\n" + >- " [-s 0|1|2] -b <hostname|IP address>] [-p <brokerport>] [-i <clientID>]\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 <topic> instead of the default\n" + >- " (publish: \"Sample/Java/v3\", subscribe: \"Sample/#\")\n" + >- " -m Use <message text> 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_<action>\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 <enter> 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: >- * <ol> >- * <li>Sample (this one) which uses the API which blocks until the operation completes</li> >- * <li>SampleAsyncWait shows how to use the asynchronous API with waiters that block until >- * an action completes</li> >- * <li>SampleAsyncCallBack shows how to use the asynchronous API where events are >- * used to notify the application when an action completes<li> >- * </ol> >- * >- * 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<args.length; i++) { >- // Check this is a valid argument >- if (args[i].length() == 2 && args[i].startsWith("-")) { >- char arg = args[i].charAt(1); >- // Handle arguments that take no-value >- switch(arg) { >- case 'h': case '?': printHelp(); return; >- case 'q': quietMode = true; continue; >- } >- >- // Now handle the arguments that take a value and >- // ensure one is specified >- if (i == args.length -1 || args[i+1].charAt(0) == '-') { >- System.out.println("Missing value for argument: "+args[i]); >- printHelp(); >- return; >- } >- switch(arg) { >- case 'a': action = args[++i]; break; >- case 't': topic = args[++i]; break; >- case 'm': message = args[++i]; break; >- case 's': qos = Integer.parseInt(args[++i]); break; >- case 'b': broker = args[++i]; break; >- case 'p': port = Integer.parseInt(args[++i]); break; >- case 'i': clientId = args[++i]; break; >- case 'c': cleanSession = Boolean.valueOf(args[++i]).booleanValue(); break; >- case 'k': System.getProperties().put("javax.net.ssl.keyStore", args[++i]); break; >- case 'w': System.getProperties().put("javax.net.ssl.keyStorePassword", args[++i]); break; >- case 'r': System.getProperties().put("javax.net.ssl.trustStore", args[++i]); break; >- case 'v': ssl = Boolean.valueOf(args[++i]).booleanValue(); break; >- case 'u': userName = args[++i]; break; >- case 'z': password = args[++i]; break; >- default: >- System.out.println("Unrecognised argument: "+args[i]); >- printHelp(); >- return; >- } >- } else { >- System.out.println("Unrecognised argument: "+args[i]); >- printHelp(); >- return; >- } >- } >- >- // Validate the provided arguments >- if (!action.equals("publish") && !action.equals("subscribe")) { >- System.out.println("Invalid action: "+action); >- printHelp(); >- return; >- } >- if (qos < 0 || qos > 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 <Enter> 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 <topic>] [-m <message text>]\n" + >- " [-s 0|1|2] -b <hostname|IP address>] [-p <brokerport>] [-i <clientID>]\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 <topic> instead of the default\n" + >- " (publish: \"Sample/Java/v3\", subscribe: \"Sample/#\")\n" + >- " -m Use <message text> 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_<action>\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 <enter> 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 @@ >-<body> >-Contains a set of sample applications that show how to use the MQTT programming interface. >-</body> >diff --git a/pom.xml b/pom.xml >new file mode 100644 >index 0000000..f3d26c8 >--- /dev/null >+++ b/pom.xml >@@ -0,0 +1,99 @@ >+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> >+ <modelVersion>4.0.0</modelVersion> >+ <groupId>org.eclipse.paho</groupId> >+ <artifactId>paho-parent</artifactId> >+ <version>0.9.0</version> >+ <packaging>pom</packaging> >+ <name>Paho :: Administrative Parent</name> >+ <description>Administrative parent pom for Paho modules</description> >+ <url>${paho.url}</url> >+ <inceptionYear>2011</inceptionYear> >+ >+ >+ <issueManagement> >+ <system>bugzilla</system> >+ <url>https://bugs.eclipse.org/bugs/enter_bug.cgi?product=Paho</url> >+ </issueManagement> >+ <mailingLists> >+ <mailingList> >+ <name>Paho Developer Mailing List</name> >+ <archive>http://dev.eclipse.org/mhonarc/lists/paho-dev</archive> >+ <subscribe>https://dev.eclipse.org/mailman/listinfo/paho-dev</subscribe> >+ <unsubscribe>https://dev.eclipse.org/mailman/listinfo/paho-dev</unsubscribe> >+ </mailingList> >+ </mailingLists> >+ >+ <developers> >+ <developer> >+ <name>Nick O'Leary</name> >+ <url>http://knolleary.net/</url> >+ <organization>IBM</organization> >+ <organizationUrl>http://www.ibm.com</organizationUrl> >+ </developer> >+ <developer> >+ <name>Dave Locke</name> >+ <organization>IBM</organization> >+ <organizationUrl>http://www.ibm.com</organizationUrl> >+ </developer> >+ </developers> >+ >+ <contributors> >+ <contributor> >+ <name>Nicolas Deverge</name> >+ <email>ndeverge@ekito.fr</email> >+ <organization>ekito</organization> >+ <organizationUrl>http://www.ekito.fr</organizationUrl> >+ </contributor> >+ </contributors> >+ >+ >+ <licenses> >+ <license> >+ <name>Eclipse Public License - Version 1.0</name> >+ <url>http://www.eclipse.org/org/documents/epl-v10.php</url> >+ </license> >+ </licenses> >+ >+ <scm> >+ <connection>scm:git://git.eclipse.org/gitroot/paho/org.eclipse.paho.mqtt.java.git</connection> >+ <developerConnection>scm:git://git.eclipse.org/gitroot/paho/org.eclipse.paho.mqtt.java.git</developerConnection> >+ <url>http://git.eclipse.org/c/paho/org.eclipse.paho.mqtt.java.git/tree/</url> >+ </scm> >+ >+ <properties> >+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> >+ <paho.url>http://www.eclipse.org/paho</paho.url> >+ </properties> >+ >+ <build> >+ <plugins> >+ <plugin> >+ <groupId>org.apache.maven.plugins</groupId> >+ <artifactId>maven-compiler-plugin</artifactId> >+ <version>2.3.2</version> >+ <configuration> >+ <source>1.2</source> >+ <target>1.2</target> >+ <compilerVersion>1.5</compilerVersion> >+ </configuration> >+ </plugin> >+ </plugins> >+ </build> >+ >+ <modules> >+ <module>org.eclipse.paho.client.mqttv3</module> >+ <module>org.eclipse.paho.client.mqttv3.internal.traceformat</module> >+ <module>org.eclipse.paho.sample.mqttv3app</module> >+ </modules> >+ >+ <dependencyManagement> >+ <dependencies> >+ <dependency> >+ <groupId>org.eclipse.paho</groupId> >+ <artifactId>jpaho-mqtt-client</artifactId> >+ <version>${project.version}</version> >+ </dependency> >+ </dependencies> >+ </dependencyManagement> >+</project> >\ No newline at end of file
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 382471
:
219620
| 229674 |
233087