View | Details | Raw Unified | Return to bug 382471
Collapse All | Expand All

(-)a/org.eclipse.paho.client.mqttv3.internal.traceformat/pom.xml (+26 lines)
Added Link Here
1
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
3
4
    <parent>
5
        <groupId>org.eclipse.paho</groupId>
6
        <artifactId>paho-parent</artifactId>
7
        <version>0.9.0</version>
8
    </parent>
9
10
    <modelVersion>4.0.0</modelVersion>
11
    <artifactId>paho-mqtt-client-traceformatter</artifactId>
12
    <version>0.9.0</version>
13
    <packaging>jar</packaging>
14
    <name>Paho :: MQTT Client trace formatter</name>
15
    <description>A trace formatter for the MQTT Client library. It currently outputs HTML files.</description> 
16
    <url>${paho.url}</url>
17
    
18
    <dependencies>
19
        <dependency>
20
          <groupId>org.eclipse.paho</groupId>
21
          <artifactId>paho-mqtt-client</artifactId>
22
          <version>${project.version}</version>
23
          <scope>compile</scope>
24
        </dependency>
25
    </dependencies>
26
</project>
(-)a/org.eclipse.paho.client.mqttv3.internal.traceformat/src/main/java/org/eclipse/paho/client/mqttv3/internal/logBuilder/LogMessageExtractor.java (+151 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.logBuilder;
13
14
import java.io.BufferedReader;
15
import java.io.File;
16
import java.io.FileOutputStream;
17
import java.io.FileReader;
18
import java.io.PrintStream;
19
import java.util.HashMap;
20
import java.util.regex.Matcher;
21
import java.util.regex.Pattern;
22
23
/** 
24
 * Scan all Paho source files and extract NLSable trace and log records. 
25
 * 
26
 * This needs to be run any time new trace/log records are added
27
 * or changed. The logcat.properties file in the mqttv3.internal.nls 
28
 * is updated to match the trace records in the paho source files. 
29
 */
30
public class LogMessageExtractor {
31
32
	public static void main(String[] args) {
33
		if (args == null) {
34
			args = new String[] {};
35
		}
36
		if (args.length != 0 && args.length != 2 && args.length != 4) {
37
			usageAndExit();
38
		}
39
		// Set defaults by assuming this is run from an eclipse workspace with paho projects loaded
40
		String dir = "../org.eclipse.paho.client.mqttv3/src";
41
		String file = dir+"/org/eclipse/paho/client/mqttv3/internal/nls/logcat.properties";
42
				
43
		for (int i=0;i<args.length; i+=2) {
44
			if (args[i].equals("-d")) {
45
				dir = args[i+1];
46
			} else if (args[i].equals("-o")) {
47
				file = args[i+1];
48
			} else {
49
				System.out.println("Unknown arg: "+args[i]);
50
				usageAndExit();
51
			}
52
		}
53
		
54
		try {
55
			LogMessageExtractor tpe = new LogMessageExtractor(dir, file);
56
			tpe.parse();
57
		} catch (Exception e) {
58
			e.printStackTrace();
59
			System.exit(1);
60
		}
61
	}
62
	
63
	private static void usageAndExit() {
64
		System.out.println("usage:\n org.eclipse.paho.client.mqttv3.internal.trace.TracePointExtractor [-d baseDir] [-o outputFile]");
65
		System.out.println("  -d baseDir        the source base directory [.]");
66
		System.out.println("  -o outputFile     the output file.          [./trace.properties]");
67
		System.exit(1);
68
	}
69
	
70
	
71
	private String basedir;
72
	private String outputfile;
73
	private Pattern pattern;
74
	private PrintStream out;
75
	private HashMap points;
76
	
77
	public LogMessageExtractor(String basedir, String outputfile) {
78
		this.basedir = (new File(basedir)).getAbsolutePath();
79
		this.outputfile = outputfile;
80
		this.pattern = Pattern.compile("^\\s*//\\s*@TRACE\\s*(\\d+)=(.*?)\\s*$");
81
		this.points = new HashMap();
82
	}
83
	public void parse() throws Exception {
84
		System.out.println("Scanning source directories: "+this.basedir);
85
		System.out.println("Outputing results to: "+this.outputfile);
86
		this.out = new PrintStream(new FileOutputStream(this.outputfile));
87
		out.println("0=MQTT Catalog");
88
		short rc = scanDirectory(new File(this.basedir));
89
		this.out.close();
90
		if (rc == 0 ) {
91
			System.out.println("Finished");
92
		} else {
93
			System.out.println("Problems found");
94
			throw new Exception();
95
		}
96
	}
97
	
98
	public short scanDirectory(File f) throws Exception {
99
		short rc = 0;
100
		if (f.isFile() && f.getName().endsWith(".java")) {
101
			short rc1 = parseFile(f);
102
			if (rc1>0) {
103
				rc = rc1;;
104
			}
105
		} else {
106
			if (f.isDirectory()) {
107
				File[] files = f.listFiles();
108
				for (int i=0;i<files.length;i++) {
109
					short rc1 = scanDirectory(files[i]);
110
					if (rc1>0) {
111
						rc = rc1;;
112
					}
113
				}
114
			}
115
		}
116
		return rc;
117
	}
118
	
119
	public short parseFile(File f) throws Exception {
120
		String filename = f.getAbsolutePath();
121
		String classname = filename.substring(this.basedir.length()+1).replaceAll("/",".");
122
		classname = classname.substring(0,classname.length()-5);
123
		BufferedReader in = new BufferedReader(new FileReader(f));
124
		String line;
125
		int lineNo = 1;
126
		short rc = 0;
127
		
128
		while ( (line = in.readLine()) != null) {
129
			Matcher m = pattern.matcher(line);
130
			if (m.matches()) {
131
				String number = m.group(1);
132
				if (this.points.containsKey(number)) {
133
					System.out.println("Duplicate Trace Point: "+number);
134
					System.out.println(" "+this.points.get(number));
135
					System.out.println(" "+classname+":"+lineNo);
136
					rc=1;
137
				}
138
				// The original extractor put out 4 values for each trace point
139
//				out.println(number+".class="+classname);
140
//				out.println(number+".line="+lineNo);
141
//				out.println(number+".value="+m.group(2));
142
				this.points.put(number, classname+":"+lineNo);
143
				out.println(number+"="+m.group(2));
144
			}
145
			lineNo++;
146
		}
147
		in.close();
148
		
149
		return rc;
150
	}
151
}
(-)a/org.eclipse.paho.client.mqttv3.internal.traceformat/src/org/eclipse/paho/client/mqttv3/internal/logBuilder/LogMessageExtractor.java (-151 lines)
Lines 1-151 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.logBuilder;
13
14
import java.io.BufferedReader;
15
import java.io.File;
16
import java.io.FileOutputStream;
17
import java.io.FileReader;
18
import java.io.PrintStream;
19
import java.util.HashMap;
20
import java.util.regex.Matcher;
21
import java.util.regex.Pattern;
22
23
/** 
24
 * Scan all Paho source files and extract NLSable trace and log records. 
25
 * 
26
 * This needs to be run any time new trace/log records are added
27
 * or changed. The logcat.properties file in the mqttv3.internal.nls 
28
 * is updated to match the trace records in the paho source files. 
29
 */
30
public class LogMessageExtractor {
31
32
	public static void main(String[] args) {
33
		if (args == null) {
34
			args = new String[] {};
35
		}
36
		if (args.length != 0 && args.length != 2 && args.length != 4) {
37
			usageAndExit();
38
		}
39
		// Set defaults by assuming this is run from an eclipse workspace with paho projects loaded
40
		String dir = "../org.eclipse.paho.client.mqttv3/src";
41
		String file = dir+"/org/eclipse/paho/client/mqttv3/internal/nls/logcat.properties";
42
				
43
		for (int i=0;i<args.length; i+=2) {
44
			if (args[i].equals("-d")) {
45
				dir = args[i+1];
46
			} else if (args[i].equals("-o")) {
47
				file = args[i+1];
48
			} else {
49
				System.out.println("Unknown arg: "+args[i]);
50
				usageAndExit();
51
			}
52
		}
53
		
54
		try {
55
			LogMessageExtractor tpe = new LogMessageExtractor(dir, file);
56
			tpe.parse();
57
		} catch (Exception e) {
58
			e.printStackTrace();
59
			System.exit(1);
60
		}
61
	}
62
	
63
	private static void usageAndExit() {
64
		System.out.println("usage:\n org.eclipse.paho.client.mqttv3.internal.trace.TracePointExtractor [-d baseDir] [-o outputFile]");
65
		System.out.println("  -d baseDir        the source base directory [.]");
66
		System.out.println("  -o outputFile     the output file.          [./trace.properties]");
67
		System.exit(1);
68
	}
69
	
70
	
71
	private String basedir;
72
	private String outputfile;
73
	private Pattern pattern;
74
	private PrintStream out;
75
	private HashMap points;
76
	
77
	public LogMessageExtractor(String basedir, String outputfile) {
78
		this.basedir = (new File(basedir)).getAbsolutePath();
79
		this.outputfile = outputfile;
80
		this.pattern = Pattern.compile("^\\s*//\\s*@TRACE\\s*(\\d+)=(.*?)\\s*$");
81
		this.points = new HashMap();
82
	}
83
	public void parse() throws Exception {
84
		System.out.println("Scanning source directories: "+this.basedir);
85
		System.out.println("Outputing results to: "+this.outputfile);
86
		this.out = new PrintStream(new FileOutputStream(this.outputfile));
87
		out.println("0=MQTT Catalog");
88
		short rc = scanDirectory(new File(this.basedir));
89
		this.out.close();
90
		if (rc == 0 ) {
91
			System.out.println("Finished");
92
		} else {
93
			System.out.println("Problems found");
94
			throw new Exception();
95
		}
96
	}
97
	
98
	public short scanDirectory(File f) throws Exception {
99
		short rc = 0;
100
		if (f.isFile() && f.getName().endsWith(".java")) {
101
			short rc1 = parseFile(f);
102
			if (rc1>0) {
103
				rc = rc1;;
104
			}
105
		} else {
106
			if (f.isDirectory()) {
107
				File[] files = f.listFiles();
108
				for (int i=0;i<files.length;i++) {
109
					short rc1 = scanDirectory(files[i]);
110
					if (rc1>0) {
111
						rc = rc1;;
112
					}
113
				}
114
			}
115
		}
116
		return rc;
117
	}
118
	
119
	public short parseFile(File f) throws Exception {
120
		String filename = f.getAbsolutePath();
121
		String classname = filename.substring(this.basedir.length()+1).replaceAll("/",".");
122
		classname = classname.substring(0,classname.length()-5);
123
		BufferedReader in = new BufferedReader(new FileReader(f));
124
		String line;
125
		int lineNo = 1;
126
		short rc = 0;
127
		
128
		while ( (line = in.readLine()) != null) {
129
			Matcher m = pattern.matcher(line);
130
			if (m.matches()) {
131
				String number = m.group(1);
132
				if (this.points.containsKey(number)) {
133
					System.out.println("Duplicate Trace Point: "+number);
134
					System.out.println(" "+this.points.get(number));
135
					System.out.println(" "+classname+":"+lineNo);
136
					rc=1;
137
				}
138
				// The original extractor put out 4 values for each trace point
139
//				out.println(number+".class="+classname);
140
//				out.println(number+".line="+lineNo);
141
//				out.println(number+".value="+m.group(2));
142
				this.points.put(number, classname+":"+lineNo);
143
				out.println(number+"="+m.group(2));
144
			}
145
			lineNo++;
146
		}
147
		in.close();
148
		
149
		return rc;
150
	}
151
}
(-)a/org.eclipse.paho.client.mqttv3/pom.xml (+80 lines)
Added Link Here
1
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
3
4
    <parent>
5
        <groupId>org.eclipse.paho</groupId>
6
        <artifactId>paho-parent</artifactId>
7
        <version>0.9.0</version>
8
    </parent>
9
10
    <modelVersion>4.0.0</modelVersion>
11
    <artifactId>paho-mqtt-client</artifactId>
12
    <version>0.9.0</version>
13
    <packaging>jar</packaging>
14
    <name>Paho :: MQTT Client library</name>
15
    <description>MQTT Client library</description> 
16
    <url>${paho.url}</url>
17
18
    <properties>
19
       <build.level>${maven.build.timestamp}</build.level>
20
       <maven.build.timestamp.format>yyMMdd</maven.build.timestamp.format>
21
    </properties>
22
    
23
    <build>
24
    <resources>
25
          <resource>
26
              <directory>src/main/java</directory>
27
              <filtering>true</filtering>
28
          </resource>
29
      </resources>
30
    <plugins>
31
      <plugin>
32
        <groupId>org.apache.felix</groupId>
33
        <artifactId>maven-bundle-plugin</artifactId>
34
        <version>2.3.7</version>
35
        <extensions>true</extensions>
36
        <executions>
37
          <execution>
38
            <id>generate-manifest</id>
39
            <goals>
40
              <goal>manifest</goal>
41
            </goals>
42
            <configuration>
43
              <instructions>
44
                <Build-Level>L${build.level}</Build-Level>
45
                <Bundle-ClassPath>.</Bundle-ClassPath>
46
                <Bundle-SymbolicName>$(maven-symbolicname);singleton:=true</Bundle-SymbolicName>
47
                <Import-Package>javax.net;resolution:=optional, javax.net.ssl;resolution:=optional</Import-Package>
48
              </instructions>
49
            </configuration>
50
          </execution>
51
        </executions>
52
      </plugin>
53
      <plugin>
54
        <groupId>org.apache.maven.plugins</groupId>
55
        <artifactId>maven-jar-plugin</artifactId>
56
        <version>2.3.2</version>
57
        <executions>
58
          <execution>
59
            <id>artifact-jar</id>
60
            <goals>
61
              <goal>jar</goal>
62
            </goals>
63
          </execution>
64
          <execution>
65
            <id>test-jar</id>
66
            <goals>
67
              <goal>test-jar</goal>
68
            </goals>
69
          </execution>
70
        </executions>
71
        <configuration>
72
          <archive>
73
            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
74
          </archive>
75
        </configuration>
76
      </plugin>
77
    </plugins>
78
   </build>
79
80
</project>
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/IMqttActionListener.java (+29 lines)
Added Link Here
1
package org.eclipse.paho.client.mqttv3;
2
3
/**
4
 * Implementors of this interface will be notified when an asynchronous action completes.
5
 * 
6
 * <p>A listener is registered on an MqttToken and a token is associated
7
 * with an action like connect or publish. When used with tokens on the MqttAsyncClient 
8
 * the listener will be called back on the MQTT clients thread. The listener will be informed 
9
 * if the action succeeds or fails. It is important that the listener returns control quickly 
10
 * otherwise the operation of the MQTT client will be stalled.
11
 * </p>  
12
 */
13
public interface IMqttActionListener {
14
	/**
15
	 * This method is invoked when an action has completed successfully.  
16
	 * @param asyncActionToken associated with the action that has completed
17
	 */
18
	public void onSuccess(IMqttToken asyncActionToken );
19
	/**
20
	 * This method is invoked when an action fails.  
21
	 * If a client is disconnected while an action is in progress 
22
	 * onFailure will be called. For connections
23
	 * that use clean session set to false, any QOS 1 and 2 messages that 
24
	 * are in the process of being delivered will be delivered to the requested
25
	 * quality of service next time the client connects.  
26
	 * @param asyncActionToken associated with the action that has failed
27
	 */
28
	public void onFailure(IMqttToken asyncActionToken, Throwable exception);
29
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/IMqttAsyncClient.java (+713 lines)
Added Link Here
1
package org.eclipse.paho.client.mqttv3;
2
3
/**
4
 * Enables an application to communicate with an MQTT server using using non-blocking methods. 
5
 * <p>
6
 * It provides applications a simple programming interface to all features of the MQTT version 3.1
7
 * specification including: 
8
 * <ul>
9
 * <li>connect
10
 * <li>publish
11
 * <li>subscribe
12
 * <li>unsubscribe
13
 * <li>disconnect
14
 * </ul>
15
 * </p>
16
 * <p>
17
 * There are two styles of MQTT client, this one and {@link IMqttClient}.
18
 * <ul>
19
 * <li>IMqttAsyncClient provides a set of non blocking methods that return control to the
20
 * invoking application after initial validation of parameters and state. The main processing is
21
 * performed in the background so as not to block the application programs thread. This non 
22
 * blocking approach is handy when the application needs to carry on processing while the 
23
 * MQTT action takes place. For instance connecting to an MQTT server can take time, using 
24
 * the non blocking connect method allows an application to display a busy indicator while the
25
 * connect action takes place in the background. Non blocking methods are particularly useful 
26
 * in event oriented programs and graphical programs where invoking methods that take time 
27
 * to complete on the the main or GUI thread can cause problems. The non-blocking interface
28
 * can also be used in blocking form.</li>
29
 * <li>IMqttClient provides a set of methods that block and return control to the application
30
 * program once the MQTT action has completed. It is a thin layer that sits on top of  
31
 * IMqttAsyncClient implementation and is provided mainly for compatibility with earlier
32
 * versions of the MQTT client. In most circumstances it is recommended to use IMqttAsyncClient
33
 * based clients which allow an application to mix both non-blocking and blocking calls. </li>
34
 * </ul>
35
 * </p>
36
 * <p>
37
 * An application is not restricted to using one style, if an IMqttAsyncClient based client is used
38
 * as both blocking and non-blocking methods can be used in the same application. If an IMqttClient
39
 * based client is used then only blocking methods are available to the application.  
40
 * For more details on the blocking client see {@link IMqttClient}</p>
41
 * 
42
 * <p>There are two forms of non-blocking method:
43
 * <ol>
44
 *   <li>
45
 *     <code><pre>
46
 *     IMqttToken token = asyncClient.method(parms)
47
 *     </pre></code>
48
 *     <p>In this form the method returns a token that can be used to track the 
49
 *     progress of the action (method). The method provides a waitForCompletion() 
50
 *     method that once invoked will block until the action completes. Once 
51
 *     completed there are method on the token that can be used to check if the
52
 *     action completed successfully or not. For example
53
 * 	   to wait until a connect completes:
54
 *     <code><pre>
55
 *      IMqttToken conToken;
56
 *   	conToken = asyncClient.client.connect(conToken);
57
 *     ... do some work...
58
 *   	conToken.waitForCompletion();
59
 *     </pre></code>
60
 *	   /p>
61
 *     <p>To turn a method into a blocking invocation the following form can be used:
62
 *     <code><pre>
63
 *     IMqttToken token;
64
 *     token = asyncClient.method(parms).waitForCompletion();
65
 *     </pre></code>
66
67
 *   </li>
68
 *
69
 *   <li>
70
 *     <code><pre>
71
 *     IMqttToken token method(parms, Object userContext, IMqttActionListener callback)
72
 *     </pre></code>
73
 *     <p>In this form a callback is registered with the method. The callback will be 
74
 *     notified when the action succeeds or fails. The callback is invoked on the thread 
75
 *     managed by the MQTT client so it is important that processing is minimised in the 
76
 *     callback. If not the operation of the MQTT client will be inhibited. For example
77
 *      to be notified (called back) when a connect completes: 
78
 *     <code><pre>
79
 *     	IMqttToken conToken;	
80
 *	    conToken = asyncClient.connect("some context",new new MqttAsyncActionListener() {			
81
 *			public void onSuccess(IMqttToken asyncActionToken) {
82
 *				log("Connected");
83
 *			}
84
 *				
85
 *			public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
86
 *				log ("connect failed" +exception);
87
 *			}
88
 *		  });
89
 *	    An optional context object can be passed into the method which will then be made
90
 *      available in the callback. The context is stored by the MQTT client) in the token
91
 *      which is then returned to the invoker. The token is provided to the callback methods
92
 *      where the context can then be accessed. 
93
 *     </pre></code> 
94
 *     </p> 
95
 *   </li>
96
 * </ol>
97
 *   <p>To understand when the delivery of a message is complete either of the two methods above
98
 *   can be used to either wait on or be notified when the publish completes.  An alternative is to  
99
 *   use the {@link MqttCallback#deliveryComplete(IMqttDeliveryToken)} method which will
100
 *   also be notified when a message has been delivered to the requested quality of service.</p> 
101
 *   
102
 */
103
public interface IMqttAsyncClient {
104
	/**
105
	 * Connects to an MQTT server using the default options.  
106
	 * <p>The default options are specified in {@link MqttConnectOptions} class.
107
	 * </p> 
108
	 *  
109
	 * @throws MqttSecurityException  for security related problems
110
	 * @throws MqttException  for non security related problems 
111
	 * @return token used to track and wait for the connect to complete. The token
112
	 * will be passed to the callback methtods if a callback is set.
113
	 * @see #connect(MqttConnectOptions, Object, IMqttActionListener)
114
	 */
115
	public IMqttToken connect() throws MqttException, MqttSecurityException;
116
	
117
	/**
118
	 * Connects to an MQTT server using the provided connect options. 
119
	 * <p>The connection will be established using the options specified in the 
120
	 * {@link MqttConnectOptions} parameter. 
121
	 * </p> 
122
	 *  
123
	 * @param options a set of connection parameters that override the defaults.
124
	 * @throws MqttSecurityException  for security related problems
125
	 * @throws MqttException  for non security related problems 
126
	 * @return token used to track and wait for the connect to complete. The token
127
	 * will be passed to any callback that has been set.
128
	 * @see #connect(MqttConnectOptions, Object, IMqttActionListener)
129
	 */
130
	public IMqttToken connect(MqttConnectOptions options) throws MqttException, MqttSecurityException ;
131
	/**
132
	 * Connects to an MQTT server using the default options. 
133
	 * <p>The default options are specified in {@link MqttConnectOptions} class.
134
	 * </p> 
135
	 *  
136
	 * @param userContext optional object used to pass context to the callback. Use 
137
	 * null if not required.
138
	 * @param callback optional listener that will be notified when the connect completes. Use 
139
	 * null if not required.
140
	 * @throws MqttSecurityException  for security related problems
141
	 * @throws MqttException  for non security related problems 
142
	 * @return token used to track and wait for the connect to complete. The token
143
	 * will be passed to any callback that has been set.
144
	 * @see #connect(MqttConnectOptions, Object, IMqttActionListener)
145
	 */
146
	public IMqttToken connect(Object userContext, IMqttActionListener callback) throws MqttException, MqttSecurityException;
147
	
148
	
149
	/**
150
	 * Connects to an MQTT server using the specified options. 
151
	 * <p>The server to connect to is specified on the constructor.
152
	 * It is recommended to call {@link #setCallback(MqttCallback)} prior to
153
	 * connecting in order that messages destined for the client can be accepted
154
	 * as soon as the client is connected.
155
	 * </p> 
156
	 * <p>The method returns control before the connect completes. Completion can 
157
	 * be tracked by:
158
	 * <ul>
159
	 * <li>Waiting on the returned token {@link IMqttToken#waitForCompletion()} or</li>
160
	 * <li>Passing in a callback {@link IMqttActionListener}</li>
161
	 * </ul>
162
	 * </p> 
163
	 * 
164
	 * @param options a set of connection parameters that override the defaults. 
165
	 * @param userContext optional object for used to pass context to the callback. Use 
166
	 * null if not required.
167
	 * @param callback optional listener that will be notified when the connect completes. Use 
168
	 * null if not required.
169
	 * @return token used to track and wait for the connect to complete. The token
170
	 * will be passed to any callback that has been set. 
171
	 * @throws MqttSecurityException  for security related problems
172
	 * @throws MqttException  for non security related problems including communication errors
173
	 */
174
	public IMqttToken connect(MqttConnectOptions options, Object userContext, IMqttActionListener callback) throws MqttException, MqttSecurityException;
175
176
	/**
177
	 * Disconnects from the server. 
178
	 * <p>An attempt is made to quiesce the client allowing outstanding
179
	 * work to complete before disconnecting. It will wait
180
	 * for a maximum of 30 seconds for work to quiesce before disconnecting. 
181
 	 * This method must not be called from inside {@link MqttCallback} methods.
182
 	 * </p>
183
 	 * 
184
	 * @return token used to track and wait for disconnect to complete. The token
185
	 * will be passed to any callback that has been set.
186
	 * @throws MqttException for problems encountered while disconnecting 
187
	 * @see #disconnect(long, Object, IMqttActionListener)
188
	 */
189
	public IMqttToken disconnect( ) throws MqttException;
190
	
191
	/**
192
	 * Disconnects from the server. 
193
	 * <p>An attempt is made to quiesce the client allowing outstanding
194
	 * work to complete before disconnecting. It will wait
195
	 * for a maximum of the specified quiesce time  for work to complete before disconnecting. 
196
 	 * This method must not be called from inside {@link MqttCallback} methods.
197
 	 * </p>
198
	 * @param quiesceTimeout the amount of time in milliseconds to allow for 
199
	 * existing work to finish before disconnecting.  A value of zero or less 
200
	 * means the client will not quiesce.  
201
	 * @return token used to track and wait for disconnect to complete. The token
202
	 * will be passed to the callback methtods if a callback is set.
203
	 * @throws MqttException for problems encountered while disconnecting 
204
	 * @see #disconnect(long, Object, IMqttActionListener)
205
	 */
206
	public IMqttToken disconnect(long quiesceTimeout) throws MqttException;
207
	
208
	/**
209
	 * Disconnects from the server. 
210
	 * <p>An attempt is made to quiesce the client allowing outstanding
211
	 * work to complete before disconnecting. It will wait
212
	 * for a maximum of 30 seconds for work to quiesce before disconnecting. 
213
 	 * This method must not be called from inside {@link MqttCallback} methods.
214
 	 * </p>
215
 	 * 
216
 	 * @param userContext optional object used to pass context to the callback. Use 
217
	 * null if not required.
218
	 * @param callback optional listener that will be notified when the disconnect completes. Use 
219
	 * null if not required.
220
	 * @return token used to track and wait for the disconnect to complete. The token
221
	 * will be passed to any callback that has been set.
222
	 * @throws MqttException for problems encountered while disconnecting
223
	 * @see #disconnect(long, Object, IMqttActionListener)
224
	 */
225
	public IMqttToken disconnect( Object userContext, IMqttActionListener callback) throws MqttException;
226
227
	/**
228
	 * Disconnects from the server.
229
	 * <p>
230
	 * The client will wait for {@link MqttCallback} methods to 
231
	 * complete. It will then wait for up to the quiesce timeout to allow for
232
	 * work which has already been initiated to complete. For instance when a QOS 2
233
	 * message has started flowing to the server but the QOS 2 flow has not completed.It 
234
	 * prevents new messages being accepted and does not send any messages that have 
235
	 * been accepted but not yet started delivery across the network to the server. When 
236
	 * work has completed or after the quiesce timeout, the client will disconnect from 
237
	 * the server. If the cleansession flag was set to false and is set to false the 
238
	 * next time a connection is made QoS 1 and 2 messages that 
239
	 * were not previously delivered will be delivered.</p>
240
	 * <p>This method must not be called from inside {@link MqttCallback} methods.</p>
241
	 * <p>The method returns control before the disconnect completes. Completion can 
242
	 * be tracked by:
243
	 * <ul>
244
	 * <li>Waiting on the returned token {@link IMqttToken#waitForCompletion()} or</li>
245
	 * <li>Passing in a callback {@link IMqttActionListener}</li>
246
	 * </ul>
247
	 * </p> 
248
	 * 
249
	 * @param quiesceTimeout the amount of time in milliseconds to allow for 
250
	 * existing work to finish before disconnecting.  A value of zero or less 
251
	 * means the client will not quiesce.
252
 	 * @param userContext optional object used to pass context to the callback. Use 
253
	 * null if not required.
254
	 * @param callback optional listener that will be notified when the disconnect completes. Use 
255
	 * null if not required.
256
	 * @return token used to track and wait for the connect to complete. The token
257
	 * will be passed to any callback that has been set.
258
	 * @throws MqttException for problems encountered while disconnecting
259
	 */
260
	public IMqttToken disconnect(long quiesceTimeout, Object userContext, IMqttActionListener callback) throws MqttException;
261
	
262
263
	/**
264
	 * Determines if this client is currently connected to the server.
265
	 * 
266
	 * @return <code>true</code> if connected, <code>false</code> otherwise.
267
	 */
268
	public boolean isConnected();
269
270
	/**
271
	 * Returns the client ID used by this client. 
272
	 * <p>All clients connected to the
273
	 * same server or server farm must have a unique ID.
274
	 * </p> 
275
	 * 
276
	 * @return the client ID used by this client.
277
	 */
278
	public String getClientId();
279
280
	/**
281
	 * Returns the address of the server used by this client.
282
	 * <p>The format of the returned String is the same as that used on the constructor.
283
	 * </p>
284
	 * 
285
	 * @return the server's address, as a URI String.
286
	 * @see MqttAsyncClient#MqttAsyncClient(String, String)
287
	 */
288
	public String getServerURI();
289
290
	/**
291
	 * Publishes a message to a topic on the server
292
	 * <p>A convenience method, which will 
293
	 * create a new {@link MqttMessage} object with a byte array payload and the
294
	 * specified QoS, and then publish it. 
295
	 * </p>
296
	 *
297
	 * @param topic to deliver the message to, for example "finance/stock/ibm".
298
	 * @param payload the byte array to use as the payload
299
	 * @param qos the Quality of Service to deliver the message at. Valid values are 0, 1 or 2.
300
	 * @param retained whether or not this message should be retained by the server.
301
	 * @return token used to track and wait for the publish to complete. The token
302
	 * will be passed to any callback that has been set. 
303
	 * @throws MqttPersistenceException when a problem occurs storing the message
304
	 * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2.
305
	 * @throws MqttException for other errors encountered while publishing the message.
306
	 * For instance if too many messages are being processed. 
307
	 * @see #publish(String, MqttMessage, Object, IMqttActionListener)
308
	 * @see MqttMessage#setQos(int)
309
	 * @see MqttMessage#setRetained(boolean)
310
	 */
311
	public IMqttDeliveryToken publish(String topic, byte[] payload, int qos, 
312
			boolean retained ) throws MqttException, MqttPersistenceException;
313
	
314
	/**
315
	 * Publishes a message to a topic on the server
316
 	 * <p>A convenience method, which will 
317
	 * create a new {@link MqttMessage} object with a byte array payload and the
318
	 * specified QoS, and then publish it. 
319
	 * </p>
320
	 *
321
	 * @param topic  to deliver the message to, for example "finance/stock/ibm".
322
	 * @param payload the byte array to use as the payload
323
	 * @param qos the Quality of Service to deliver the message at.  Valid values are 0, 1 or 2.
324
	 * @param retained whether or not this message should be retained by the server.
325
	 * @param userContext optional object used to pass context to the callback. Use 
326
	 * null if not required.
327
	 * @param callback optional listener that will be notified when message delivery
328
	 * hsa completed to the requested quality of service
329
	 * @return token used to track and wait for the publish to complete. The token
330
	 * will be passed to any callback that has been set. 
331
	 * @throws MqttPersistenceException when a problem occurs storing the message
332
	 * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2.
333
	 * @throws MqttException for other errors encountered while publishing the message.
334
	 * For instance client not connected. 
335
	 * @see #publish(String, MqttMessage, Object, IMqttActionListener)
336
	 * @see MqttMessage#setQos(int)
337
	 * @see MqttMessage#setRetained(boolean)
338
	 */
339
	public IMqttDeliveryToken publish(String topic, byte[] payload, int qos, 
340
			boolean retained, Object userContext, IMqttActionListener callback ) throws MqttException, MqttPersistenceException;
341
342
	/**
343
	 * Publishes a message to a topic on the server
344
	 * Takes an {@link MqttMessage} message and delivers it to the server at the 
345
	 * requested quality of service.
346
	 *
347
	 * @param topic  to deliver the message to, for example "finance/stock/ibm".
348
	 * @param message to deliver to the server
349
	 * @return token used to track and wait for the publish to complete. The token
350
	 * will be passed to any callback that has been set. 
351
	 * @throws MqttPersistenceException when a problem occurs storing the message
352
	 * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2.
353
	 * @throws MqttException for other errors encountered while publishing the message.
354
	 * For instance client not connected. 
355
	 * @see #publish(String, MqttMessage, Object, IMqttActionListener)
356
	 */
357
	public IMqttDeliveryToken publish(String topic, MqttMessage message ) throws MqttException, MqttPersistenceException;
358
	
359
	/**
360
	 * Publishes a message to a topic on the server. 
361
	 * <p>
362
	 * Once this method has returned cleanly, the message has been accepted for publication by the
363
	 * client and will be delivered on a background thread. 
364
	 * In the event the connection fails or the client stops. Messages will be delivered to the 
365
	 * requested quality of service once the connection is re-established to the server on condition that: 
366
	 * <ul>
367
	 * <li>The connection is re-established with the same clientID
368
	 * <li>The original connection was made with (@link MqttConnectOptions#setCleanSession(boolean)} 
369
	 * set to false
370
	 * <li>The connection is re-established with (@link MqttConnectOptions#setCleanSession(boolean)} 
371
	 * set to false
372
	 * <liDepending when the failure occurs QOS 0 messages may not be delivered.
373
	 * </ul> 
374
	 * </p>
375
	 * 
376
	 * <p>When building an application,
377
	 * the design of the topic tree should take into account the following principles
378
	 * of topic name syntax and semantics:</p>
379
	 * 
380
	 * <ul>
381
	 * 	<li>A topic must be at least one character long.</li>
382
	 * 	<li>Topic names are case sensitive.  For example, <em>ACCOUNTS</em> and <em>Accounts</em> are
383
	 * 	two different topics.</li>
384
	 * 	<li>Topic names can include the space character.  For example, <em>Accounts
385
	 * 	payable</em> is a valid topic.</li>
386
	 * 	<li>A leading "/" creates a distinct topic.  For example, <em>/finance</em> is
387
	 * 	different from <em>finance</em>. <em>/finance</em> matches "+/+" and "/+", but
388
	 * 	not "+".</li>
389
	 * 	<li>Do not include the null character (Unicode<samp class="codeph"> \x0000</samp>) in
390
	 * 	any topic.</li>
391
	 * </ul>
392
	 * 
393
	 * <p>The following principles apply to the construction and content of a topic
394
	 * tree:</p>
395
	 * 
396
	 * <ul>
397
	 * 	<li>The length is limited to 64k but within that there are no limits to the
398
	 * 	number of levels in a topic tree.</li>
399
	 * 	<li>There can be any number of root nodes; that is, there can be any number
400
	 * 	of topic trees.</li>
401
	 * 	</ul>
402
	 * </p>
403
	 * <p>The method returns control before the publish completes. Completion can 
404
	 * be tracked by:
405
	 * <ul>
406
	 * <li>Setting an {@link IMqttAsyncClient#setCallback(MqttCallback)} where the 
407
	 * {@link MqttCallback#deliveryComplete(IMqttDeliveryToken)}
408
	 * method will be called.</li> 
409
	 * <li>Waiting on the returned token {@link MqttToken#waitForCompletion()} or</li>
410
	 * <li>Passing in a callback {@link IMqttActionListener} to this method</li> 
411
	 * </ul>
412
	 * </p> 
413
	 *
414
	 * @param topic  to deliver the message to, for example "finance/stock/ibm".
415
	 * @param message to deliver to the server
416
	 * @param userContext optional object used to pass context to the callback. Use 
417
	 * null if not required.
418
	 * @param callback optional listener that will be notified when message delivery
419
	 * has completed to the requested quality of service
420
	 * @return token used to track and wait for the publish to complete. The token
421
	 * will be passed to callback methtods if set. 
422
 	 * @throws MqttPersistenceException when a problem occurs storing the message
423
	 * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2.
424
	 * @throws MqttException for other errors encountered while publishing the message.
425
	 * For instance client not connected. 
426
	 * @see MqttMessage
427
	 */
428
	public IMqttDeliveryToken publish(String topic, MqttMessage message, 
429
			Object userContext, IMqttActionListener callback) throws MqttException, MqttPersistenceException;
430
	
431
	/**
432
	 * Subscribe to a topic, which may include wildcards.
433
	 * 
434
	 * @see #subscribe(String[], int[], Object, IMqttActionListener)
435
	 * 
436
	 * @param topicFilter the topic to subscribe to, which can include wildcards.
437
	 * @param qos the maximum quality of service at which to subscribe. Messages 
438
	 * published at a lower quality of service will be received at the published 
439
	 * QOS.  Messages published at a higher quality of service will be received using 
440
	 * the QOS specified on the subscribe.
441
	 * @return token used to track and wait for the subscribe to complete. The token
442
	 * will be passed to callback methtods if set. 
443
	 * @throws MqttException if there was an error registering the subscription.
444
	 */
445
	public IMqttToken subscribe(String topicFilter, int qos) throws MqttException;
446
447
	/**
448
	 * Subscribe to a topic, which may include wildcards.
449
	 * 
450
	 * @see #subscribe(String[], int[], Object, IMqttActionListener)
451
	 * 
452
	 * @param topicFilter the topic to subscribe to, which can include wildcards.
453
	 * @param qos the maximum quality of service at which to subscribe. Messages 
454
	 * published at a lower quality of service will be received at the published 
455
	 * QOS.  Messages published at a higher quality of service will be received using 
456
	 * the QOS specified on the subscribe.
457
	 * @param userContext optional object used to pass context to the callback. Use 
458
	 * null if not required.
459
	 * @param callback optional listener that will be notified when subscribe 
460
	 * has completed  
461
	 * @return token used to track and wait for the subscribe to complete. The token
462
	 * will be passed to callback methtods if set. 
463
	 * @throws MqttException if there was an error registering the subscription.
464
	 */
465
	public IMqttToken subscribe(String topicFilter, int qos, Object userContext, IMqttActionListener callback)
466
	throws MqttException;
467
468
	/**
469
	 * Subscribe to multiple topics, each of which may include wildcards.
470
	 * 
471
	 * <p>Provides an optimised way to subscribe to multiple topics compared to 
472
	 * subscribing to each one individually.</p> 
473
	 * 
474
	 * @see #subscribe(String[], int[], Object, IMqttActionListener)
475
	 * 
476
	 * @param topicFilters one or more topics to subscribe to, which can include wildcards
477
	 * @param qos the maximum quality of service at which to subscribe. Messages 
478
	 * published at a lower quality of service will be received at the published 
479
	 * QOS.  Messages published at a higher quality of service will be received using 
480
	 * the QOS specified on the subscribe.
481
	 * @return token used to track and wait for the subscribe to complete. The token
482
	 * will be passed to callback methtods if set. 
483
	 * @throws MqttException if there was an error registering the subscription.
484
	 */
485
	public IMqttToken subscribe(String[] topicFilters, int[] qos) throws MqttException;
486
487
	/**
488
	 * Subscribes to multiple topics, each of which may include wildcards.
489
 	 * <p>Provides an optimised way to subscribe to multiple topics compared to 
490
	 * subscribing to each one individually.</p> 
491
	 * <p>The {@link #setCallback(MqttCallback)} method 
492
	 * should be called before this method, otherwise any received messages 
493
	 * will be discarded.
494
	 * </p>
495
	 * <p>
496
	 * If (@link MqttConnectOptions#setCleanSession(boolean)} was set to true 
497
	 * when when connecting to the server then the subscription remains in place
498
	 * until either:
499
	 * <ul>
500
	 * <li>The client disconnects</li>
501
	 * <li>An unsubscribe method is called to un-subscribe the topic</li>
502
	 * </li>
503
	 * </p>
504
	 * <p>
505
	 * If (@link MqttConnectOptions#setCleanSession(boolean)} was set to false 
506
	 * when connecting to the server then the subscription remains in place
507
	 * until either:
508
	 * <ul>
509
	 * <li>An unsubscribe method is called to un-subscribe the topic</li>
510
	 * <li>The next time the client connects with CleanSession set to true</ul>
511
	 * </li>
512
	 * With CleanSession set to false the MQTT server will store messages on 
513
	 * behalf of the client when the client is not connected. The next time the 
514
	 * client connects with the <bold>same client ID</bold> the server will 
515
	 * deliver the stored messages to the client.
516
	 * </p>  
517
	 * 
518
	 * <p>The "topic filter" string used when subscribing
519
	 * may contain special characters, which allow you to subscribe to multiple topics
520
	 * at once.</p>
521
	 * <p>The topic level separator is used to introduce structure into the topic, and
522
	 * can therefore be specified within the topic for that purpose.  The multi-level
523
	 * wildcard and single-level wildcard can be used for subscriptions, but they
524
	 * cannot be used within a topic by the publisher of a message.
525
	 * <dl>
526
	 * 	<dt>Topic level separator</dt>
527
	 * 	<dd>The forward slash (/) is used to separate each level within
528
	 * 	a topic tree and provide a hierarchical structure to the topic space. The
529
	 * 	use of the topic level separator is significant when the two wildcard characters
530
	 * 	are encountered in topics specified by subscribers.</dd>
531
	 * 
532
	 * 	<dt>Multi-level wildcard</dt>
533
	 * 	<dd><p>The number sign (#) is a wildcard character that matches
534
	 * 	any number of levels within a topic. For example, if you subscribe to 
535
	 *  <span><span class="filepath">finance/stock/ibm/#</span></span>, you receive
536
	 * 	messages on these topics:
537
	 *  <pre>   finance/stock/ibm<br />   finance/stock/ibm/closingprice<br />   finance/stock/ibm/currentprice</pre>
538
	 *  </p>
539
	 *  <p>The multi-level wildcard
540
	 *  can represent zero or more levels. Therefore, <em>finance/#</em> can also match
541
	 * 	the singular <em>finance</em>, where <em>#</em> represents zero levels. The topic
542
	 * 	level separator is meaningless in this context, because there are no levels
543
	 * 	to separate.</p>
544
	 * 
545
	 * 	<p>The <span>multi-level</span> wildcard can
546
	 * 	be specified only on its own or next to the topic level separator character.
547
	 * 	Therefore, <em>#</em> and <em>finance/#</em> are both valid, but <em>finance#</em> is
548
	 * 	not valid. <span>The multi-level wildcard must be the last character
549
	 *  used within the topic tree. For example, <em>finance/#</em> is valid but 
550
	 *  <em>finance/#/closingprice</em> is 	not valid.</span></p></dd>
551
	 * 
552
	 * 	<dt>Single-level wildcard</dt>
553
	 * 	<dd><p>The plus sign (+) is a wildcard character that matches only one topic
554
	 * 	level. For example, <em>finance/stock/+</em> matches 
555
	 * <em>finance/stock/ibm</em> and <em>finance/stock/xyz</em>,
556
	 * 	but not <em>finance/stock/ibm/closingprice</em>. Also, because the single-level
557
	 * 	wildcard matches only a single level, <em>finance/+</em> does not match <em>finance</em>.</p>
558
	 * 	
559
	 * 	<p>Use
560
	 * 	the single-level wildcard at any level in the topic tree, and in conjunction
561
	 * 	with the multilevel wildcard. Specify the single-level wildcard next to the
562
	 * 	topic level separator, except when it is specified on its own. Therefore, 
563
	 *  <em>+</em> and <em>finance/+</em> are both valid, but <em>finance+</em> is 
564
	 *  not valid. <span>The single-level wildcard can be used at the end of the 
565
	 *  topic tree or within the topic tree.
566
	 * 	For example, <em>finance/+</em> and <em>finance/+/ibm</em> are both valid.</span></p>
567
	 * 	</dd>
568
	 * </dl>
569
	 * </p>
570
	 * <p>The method returns control before the subscribe completes. Completion can 
571
	 * be tracked by:
572
	 * <ul>
573
	 * <li>Waiting on the supplied token {@link MqttToken#waitForCompletion()} or</li>
574
	 * <li>Passing in a callback {@link IMqttActionListener} to this method</li>
575
	 * </ul>
576
	 * </p> 
577
	 * 
578
	 * @param topicFilters one or more topics to subscribe to, which can include wildcards
579
	 * @param qos the maximum quality of service to subscribe each topic at.Messages 
580
	 * published at a lower quality of service will be received at the published 
581
	 * QOS.  Messages published at a higher quality of service will be received using 
582
	 * the QOS specified on the subscribe.
583
	 * @param userContext optional object used to pass context to the callback. Use 
584
	 * null if not required.
585
	 * @param callback optional listener that will be notified when subscribe 
586
	 * has completed  
587
	 * @return token used to track and wait for the subscribe to complete. The token
588
	 * will be passed to callback methtods if set. 
589
	 * @throws MqttException if there was an error registering the subscription.
590
	 * @throws IllegalArgumentException if the two supplied arrays are not the same size.
591
	 */
592
	public IMqttToken subscribe(String[] topicFilters, int[] qos, Object userContext, IMqttActionListener callback)
593
			throws MqttException;
594
	
595
	/**
596
	 * Requests the server unsubscribe the client from a topic. 
597
	 * 
598
	 * @see #unsubscribe(String[], Object, IMqttActionListener)
599
 	 * @param topicFilter the topic to unsubscribe from. It must match a topicFilter
600
	 * specified on an earlier subscribe.
601
	 * @return token used to track and wait for the unsubscribe to complete. The token
602
	 * will be passed to callback methtods if set. 
603
	 * @throws MqttException if there was an error unregistering the subscription.
604
	 */
605
	public IMqttToken unsubscribe(String topicFilter) throws MqttException;
606
	
607
	/**
608
	 * Requests the server unsubscribe the client from one or more topics. 
609
	 * 
610
	 * @see #unsubscribe(String[], Object, IMqttActionListener)
611
	 * 
612
	 * @param topicFilters one or more topics to unsubscribe from. Each topicFilter
613
	 * must match one specified on an earlier subscribe.	 * 
614
	 * @return token used to track and wait for the unsubscribe to complete. The token
615
	 * will be passed to callback methtods if set. 
616
	 * @throws MqttException if there was an error unregistering the subscription.
617
	 */	
618
	public IMqttToken unsubscribe(String[] topicFilters) throws MqttException;
619
	
620
	/**
621
	 * Requests the server unsubscribe the client from a topics. 
622
	 * 
623
	 * @see #unsubscribe(String[], Object, IMqttActionListener)
624
	 * 
625
	 * @param topicFilter the topic to unsubscribe from. It must match a topicFilter
626
	 * specified on an earlier subscribe.
627
	 * @param userContext optional object used to pass context to the callback. Use 
628
	 * null if not required.
629
	 * @param callback optional listener that will be notified when unsubscribe 
630
	 * has completed  
631
	 * @return token used to track and wait for the unsubscribe to complete. The token
632
	 * will be passed to callback methtods if set. 
633
	 * @throws MqttException if there was an error unregistering the subscription.
634
	 */		
635
	public IMqttToken unsubscribe(String topicFilter, Object userContext, IMqttActionListener callback)
636
			throws MqttException;
637
	
638
	/**
639
	 * Requests the server unsubscribe the client from one or more topics
640
	 * <p>
641
	 * Unsubcribing is the opposite of subscribing. When the server receives the 
642
	 * unsubscribe request it looks to see if it can find a matching subscription for the
643
	 * client and then removes it. After this point the server will send no more
644
	 * messages to the client for this subscription.  
645
	 * </p>
646
	 * <p>The topic(s) specified on the unsubscribe must match the topic(s) 
647
	 * specified in the original subscribe request for the unsubscribe to succeed
648
	 * </p>
649
	 * <p>The method returns control before the unsubscribe completes. Completion can 
650
	 * be tracked by:
651
	 * <ul>
652
	 * <li>Waiting on the returned token {@link MqttToken#waitForCompletion()} or</li>
653
	 * <li>Passing in a callback {@link IMqttActionListener} to this method</li>
654
	 * </ul>
655
	 * </p> 
656
	 * 
657
	 * @param topicFilters one or more topics to unsubscribe from. Each topicFilter
658
	 * must match one specified on an earlier subscribe.
659
	 * @param userContext optional object used to pass context to the callback. Use 
660
	 * null if not required.
661
	 * @param callback optional listener that will be notified when unsubscribe 
662
	 * has completed  
663
	 * @return token used to track and wait for the unsubscribe to complete. The token
664
	 * will be passed to callback methtods if set. 
665
	 * @throws MqttException if there was an error unregistering the subscription.
666
	 */
667
	public IMqttToken unsubscribe(String[] topicFilters, Object userContext, IMqttActionListener callback)
668
			throws MqttException;
669
670
671
	/**
672
	 * Sets a callback listener to use for events that happen asynchronously. 
673
	 * <p>There are a number of events that the listener will be notified about.
674
	 * These include:
675
	 * <ul>
676
	 * <li>A new message has arrived and is ready to be processed</li>
677
	 * <li>The connection to the server has been lost</li> 
678
	 * <li>Delivery of a message to the server has completed</li>
679
	 * </ul>  
680
	 * </p>
681
	 * <p>Other events that track the progress of an individual operation such 
682
	 * as connect and subscribe can be tracked using the {@link MqttToken} returned from 
683
	 * each non-blocking method or using setting a {@link IMqttActionListener} on the 
684
	 * non-blocking method.<p>
685
	 * @see MqttCallback
686
	 * @param callback which will be invoked for certain asyncrhonous events
687
	 */
688
	public void setCallback(MqttCallback callback);
689
690
	/**
691
	 * Returns the delivery tokens for any outstanding publish operations.
692
	 * <p>If a client has been restarted and there are messages that were in the
693
	 * process of being delivered when the client stopped this method  
694
	 * returns a token for each in-flight message enabling the delivery to be tracked
695
	 * Alternately the {@link MqttCallback#deliveryComplete(IMqttDeliveryToken)} 
696
	 * callback can be used to track the delivery of outstanding messages.
697
	 * </p>
698
	 * <p>If a client connects with cleansession true then there will be no
699
	 * delivery tokens as the cleansession option deletes all earlier state.
700
	 * For state to be remembered the client must connect with cleansession
701
	 * set to false</P>
702
	 * @return zero or more delivery tokens 
703
	 */
704
	public IMqttDeliveryToken[] getPendingDeliveryTokens();
705
706
	/**
707
	 * Close the client
708
	 * Releases all resource associated with the client. After the client has 
709
	 * been closed it cannot be reused. For instance attempts to connect will fail. 
710
	 * @throws MqttException  if the client is not disconnected. 
711
	 */
712
	public void close() throws MqttException;
713
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/IMqttClient.java (+462 lines)
Added Link Here
1
package org.eclipse.paho.client.mqttv3;
2
3
/**
4
 * Enables an application to communicate with an MQTT server using using blocking methods. 
5
 * <p>
6
 * This interface allows applications to utilise all features of the MQTT version 3.1
7
 * specification including: 
8
 * <ul>
9
 * <li>connect
10
 * <li>publish
11
 * <li>subscribe
12
 * <li>unsubscribe
13
 * <li>disconnect
14
 * </ul>
15
 * </p>
16
 * <p>
17
 * There are two styles of MQTT client, this one and {@link IMqttAsyncClient}.
18
 * <ul>
19
 * <li>IMqttClient provides a set of methods that block and return control to the application
20
 * program once the MQTT action has completed.</li> 
21
 * <li>IMqttAsyncClient provides a set of non blocking methods that return control to the
22
 * invoking application after initial validation of parameters and state. The main processing is
23
 * performed in the background so as not to block the application programs thread. This non 
24
 * blocking approach is handy when the application wants to carry on processing while the 
25
 * MQTT action takes place. For instance connecting to an MQTT server can take time, using 
26
 * the non blocking connect method allows an application to display a busy indicator while the
27
 * connect action is occurring. Non blocking methods are particularly useful in event oriented 
28
 * programs and graphical programs where issuing methods that take time to complete on the the 
29
 * main or GUI thread can cause problems.</li>
30
 * </ul>
31
 * </p>
32
 * <p>
33
 * The non-blocking client can also be used in a blocking form by turning a non-blocking 
34
 * method into a blocking invocation using the following pettern:
35
 *     <code><pre>
36
 *     IMqttToken token;
37
 *     token = asyncClient.method(parms).waitForCompletion();
38
 *     </pre></code>
39
 * Using the non-blocking client allows an application to use a mixture of blocking and
40
 * non-blocking styles. Using the blocking client only allows an application to use one 
41
 * style. The blocking client provides compatibility with earlier versions 
42
 * of the MQTT client.</p>
43
 */
44
public interface IMqttClient { //extends IMqttAsyncClient {
45
	/**
46
	 * Connects to an MQTT server using the default options. 
47
	 * <p>The default options are specified in {@link MqttConnectOptions} class.
48
	 * </p>  
49
	 *  
50
	 * @throws MqttSecurityException when the server rejects the connect for security 
51
	 * reasons
52
	 * @throws MqttException  for non security related problems 
53
	 * @see #connect(MqttConnectOptions)
54
	 */
55
  public void connect() throws MqttSecurityException, MqttException;
56
57
	/**
58
	 * Connects to an MQTT server using the specified options. 
59
	 * <p>The server to connect to is specified on the constructor.
60
	 * It is recommended to call {@link #setCallback(MqttCallback)} prior to
61
	 * connecting in order that messages destined for the client can be accepted
62
	 * as soon as the client is connected.
63
	 * </p> 
64
	 * <p>This is a blocking method that returns once connect completes</p>	 
65
	 *  
66
	 * @param options a set of connection parameters that override the defaults. 
67
	 * @throws MqttSecurityException when the server rejects the connect for security 
68
	 * reasons
69
	 * @throws MqttException  for non security related problems including communication errors
70
	 */
71
  public void connect(MqttConnectOptions options) throws MqttSecurityException, MqttException;
72
73
	/**
74
	 * Disconnects from the server. 
75
	 * <p>An attempt is made to quiesce the client allowing outstanding
76
	 * work to complete before disconnecting. It will wait
77
	 * for a maximum of 30 seconds for work to quiesce before disconnecting. 
78
	 * This method must not be called from inside {@link MqttCallback} methods.
79
	 * </p>
80
	 * 
81
	 * @see #disconnect(long)
82
	 */
83
  public void disconnect() throws MqttException;
84
85
	/**
86
	 * Disconnects from the server.
87
	 * <p>
88
	 * The client will wait for all {@link MqttCallback} methods to 
89
	 * complete. It will then wait for up to the quiesce timeout to allow for
90
	 * work which has already been initiated to complete - for example, it will
91
	 * wait for the QoS 2 flows from earlier publications to complete. When work has 
92
	 * completed or after the quiesce timeout, the client will disconnect from 
93
	 * the server. If the cleansession flag was set to false and is set to false the 
94
	 * next time a connection is made QoS 1 and 2 messages that 
95
	 * were not previously delivered will be delivered.</p>
96
	 * 
97
	 * <p>This is a blocking method that returns once disconnect completes</p>	 
98
	 * 
99
	 * @param quiesceTimeout the amount of time in milliseconds to allow for 
100
	 * existing work to finish before disconnecting.  A value of zero or less 
101
	 * means the client will not quiesce.
102
	 * @throws MqttException if a problem is encountered while disconnecting
103
	 */
104
  public void disconnect(long quiesceTimeout) throws MqttException;
105
106
	/**
107
	 * Subscribe to a topic, which may include wildcards using a QOS of 1.
108
	 * 
109
	 * @see #subscribe(String[], int[])
110
	 * 
111
	 * @param topicFilter the topic to subscribe to, which can include wildcards.
112
	 * @throws MqttException if there was an error registering the subscription.
113
	 */
114
  public void subscribe(String topicFilter) throws MqttException, MqttSecurityException;
115
116
	/**
117
	 * Subscribes to a one or more topics, which may include wildcards using a QOS of 1.
118
	 * 
119
	 * @see #subscribe(String[], int[])
120
	 * 
121
	 * @param topicFilters the topic to subscribe to, which can include wildcards.
122
	 * @throws MqttException if there was an error registering the subscription.
123
	 */
124
  public void subscribe(String[] topicFilters) throws MqttException;
125
126
	/**
127
	 * Subscribe to a topic, which may include wildcards.
128
	 * 
129
	 * @see #subscribe(String[], int[])
130
	 * 
131
	 * @param topicFilter the topic to subscribe to, which can include wildcards.
132
	 * @param qos the maximum quality of service at which to subscribe. Messages 
133
	 * published at a lower quality of service will be received at the published 
134
	 * QOS.  Messages published at a higher quality of service will be received using 
135
	 * the QOS specified on the subscribe.  
136
	 * @throws MqttException if there was an error registering the subscription.
137
	 */
138
  public void subscribe(String topicFilter, int qos) throws MqttException;
139
140
	/**
141
	 * Subscribes to multiple topics, each of which may include wildcards.
142
	 * <p>The {@link #setCallback(MqttCallback)} method 
143
	 * should be called before this method, otherwise any received messages 
144
	 * will be discarded.
145
	 * </p>
146
	 * <p>
147
	 * If (@link MqttConnectOptions#setCleanSession(boolean)} was set to true 
148
	 * when when connecting to the server then the subscription remains in place
149
	 * until either:
150
	 * <ul>
151
	 * <li>The client disconnects</li>
152
	 * <li>An unsubscribe method is called to un-subscribe the topic</li>
153
	 * </li>
154
	 * </p>
155
	 * <p>
156
	 * If (@link MqttConnectOptions#setCleanSession(boolean)} was set to false 
157
	 * when when connecting to the server then the subscription remains in place
158
	 * until either:
159
	 * <ul>
160
	 * <li>An unsubscribe method is called to un-subscribe the topic</li>
161
	 * <li>The client connects with CleanSession set to true</ul>
162
	 * </li>
163
	 * With CleanSession set to false the MQTT server will store messages on 
164
	 * behalf of the client when the client is not connected. The next time the 
165
	 * client connects with the <bold>same client ID</bold> the server will 
166
	 * deliver the stored messages to the client.
167
	 * </p>  
168
	 * 
169
	 * <p>The "topic filter" string used when subscribing
170
	 * may contain special characters, which allow you to subscribe to multiple topics
171
	 * at once.</p>
172
	 * <p>The topic level separator is used to introduce structure into the topic, and
173
	 * can therefore be specified within the topic for that purpose.  The multi-level
174
	 * wildcard and single-level wildcard can be used for subscriptions, but they
175
	 * cannot be used within a topic by the publisher of a message.
176
	 * <dl>
177
	 * 	<dt>Topic level separator</dt>
178
	 * 	<dd>The forward slash (/) is used to separate each level within
179
	 * 	a topic tree and provide a hierarchical structure to the topic space. The
180
	 * 	use of the topic level separator is significant when the two wildcard characters
181
	 * 	are encountered in topics specified by subscribers.</dd>
182
	 * 
183
	 * 	<dt>Multi-level wildcard</dt>
184
	 * 	<dd><p>The number sign (#) is a wildcard character that matches
185
	 * 	any number of levels within a topic. For example, if you subscribe to 
186
	 *  <span><span class="filepath">finance/stock/ibm/#</span></span>, you receive
187
	 * 	messages on these topics:
188
	 *  <pre>   finance/stock/ibm<br />   finance/stock/ibm/closingprice<br />   finance/stock/ibm/currentprice</pre>
189
	 *  </p>
190
	 *  <p>The multi-level wildcard
191
	 *  can represent zero or more levels. Therefore, <em>finance/#</em> can also match
192
	 * 	the singular <em>finance</em>, where <em>#</em> represents zero levels. The topic
193
	 * 	level separator is meaningless in this context, because there are no levels
194
	 * 	to separate.</p>
195
	 * 
196
	 * 	<p>The <span>multi-level</span> wildcard can
197
	 * 	be specified only on its own or next to the topic level separator character.
198
	 * 	Therefore, <em>#</em> and <em>finance/#</em> are both valid, but <em>finance#</em> is
199
	 * 	not valid. <span>The multi-level wildcard must be the last character
200
	 *  used within the topic tree. For example, <em>finance/#</em> is valid but 
201
	 *  <em>finance/#/closingprice</em> is 	not valid.</span></p></dd>
202
	 * 
203
	 * 	<dt>Single-level wildcard</dt>
204
	 * 	<dd><p>The plus sign (+) is a wildcard character that matches only one topic
205
	 * 	level. For example, <em>finance/stock/+</em> matches 
206
	 * <em>finance/stock/ibm</em> and <em>finance/stock/xyz</em>,
207
	 * 	but not <em>finance/stock/ibm/closingprice</em>. Also, because the single-level
208
	 * 	wildcard matches only a single level, <em>finance/+</em> does not match <em>finance</em>.</p>
209
	 * 	
210
	 * 	<p>Use
211
	 * 	the single-level wildcard at any level in the topic tree, and in conjunction
212
	 * 	with the multilevel wildcard. Specify the single-level wildcard next to the
213
	 * 	topic level separator, except when it is specified on its own. Therefore, 
214
	 *  <em>+</em> and <em>finance/+</em> are both valid, but <em>finance+</em> is 
215
	 *  not valid. <span>The single-level wildcard can be used at the end of the 
216
	 *  topic tree or within the topic tree.
217
	 * 	For example, <em>finance/+</em> and <em>finance/+/ibm</em> are both valid.</span></p>
218
	 * 	</dd>
219
	 * </dl>
220
	 * </p>
221
	 * 
222
	 * <p>This is a blocking method that returns once subscribe completes</p>
223
	 *  
224
	 * @param topicFilters one or more topics to subscribe to, which can include wildcards.
225
	 * @param qos the maximum quality of service to subscribe each topic at.Messages 
226
	 * published at a lower quality of service will be received at the published 
227
	 * QOS.  Messages published at a higher quality of service will be received using 
228
	 * the QOS specified on the subscribe.  
229
	 * @throws MqttException if there was an error registering the subscription.
230
	 * @throws IllegalArgumentException if the two supplied arrays are not the same size.
231
	 */
232
  public void subscribe(String[] topicFilters, int[] qos) throws MqttException;
233
234
	/**
235
	 * Requests the server unsubscribe the client from a topic. 
236
	 * 
237
	 * @see #unsubscribe(String[])
238
	 * @param topicFilter the topic to unsubscribe from. It must match a topicFilter
239
	 * specified on the subscribe.
240
	 * @throws MqttException if there was an error unregistering the subscription.
241
	 */
242
  public void unsubscribe(String topicFilter) throws MqttException;
243
244
	/**
245
	 * Requests the server unsubscribe the client from one or more topics
246
	 * <p>
247
	 * Unsubcribing is the opposite of subscribing. When the server receives the 
248
	 * unsubscribe request it looks to see if it can find a subscription for the
249
	 * client and then removes it. After this point the server will send no more
250
	 * messages to the client for this subscription.  
251
	 * </p>
252
	 * <p>The topic(s) specified on the unsubscribe must match the topic(s) 
253
	 * specified in the original subscribe request for the subscribe to succeed
254
	 * </p>
255
	 * 
256
	 * <p>This is a blocking method that returns once unsubscribe completes</p>
257
	 * 
258
	 * @param topicFilters one or more topics to unsubscribe from. Each topicFilter
259
	 * must match one specified on a subscribe
260
	 * @throws MqttException if there was an error unregistering the subscription.
261
	 */
262
  public void unsubscribe(String[] topicFilters) throws MqttException;
263
264
  
265
	/**
266
	 * Publishes a message to a topic on the server and return once it is delivered
267
	 * <p>This is a convenience method, which will 
268
	 * create a new {@link MqttMessage} object with a byte array payload and the
269
	 * specified QoS, and then publish it.  All other values in the
270
	 * message will be set to the defaults.
271
	 * </p>
272
	 *
273
	 * @param topic  to deliver the message to, for example "finance/stock/ibm".
274
	 * @param payload the byte array to use as the payload
275
	 * @param qos the Quality of Service to deliver the message at.  Valid values are 0, 1 or 2.
276
	 * @param retained whether or not this message should be retained by the server.
277
	 * @throws MqttPersistenceException when a problem with storing the message
278
	 * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2.
279
	 * @throws MqttException for other errors encountered while publishing the message.
280
	 * For instance client not connected. 
281
	 * @see #publish(String, MqttMessage)
282
	 * @see MqttMessage#setQos(int)
283
	 * @see MqttMessage#setRetained(boolean)
284
	 */
285
	public void publish(String topic, byte[] payload, int qos, boolean retained) throws MqttException, MqttPersistenceException;
286
	
287
	/**
288
	 * Publishes a message to a topic on the server. 
289
	 * <p>
290
	 * Delivers a message to the server at the requested quality of service and returns control
291
	 * once the message has been delivered. In the event the connection fails or the client
292
	 * stops, any messages that are in the process of being delivered will be delivered once
293
	 * a connection is re-established to the server on condition that: 
294
	 * <ul>
295
	 * <li>The connection is re-established with the same clientID
296
	 * <li>The original connection was made with (@link MqttConnectOptions#setCleanSession(boolean)} 
297
	 * set to false
298
	 * <li>The connection is re-established with (@link MqttConnectOptions#setCleanSession(boolean)} 
299
	 * set to false
300
	 * </ul> 
301
	 * </p>
302
	 * <p>In the event that the connection breaks or the client stops it is still possible to determine
303
	 * when the delivery of the message completes. Prior to re-establishing the connection to the server:
304
	 * <ul>
305
	 * <li>Register a {@link #setCallback(MqttCallback)} callback on the client and the delivery complete
306
	 * callback will be notified once a delivery of a message completes 
307
	 * <li>or call {@link #getPendingDeliveryTokens()} which will return a token for each message that
308
	 * is in-flight.  The token can be used to wait for delivery to complete.  
309
	 * </ul>
310
	 * </p>
311
	 * 
312
	 * <p>When building an application,
313
	 * the design of the topic tree should take into account the following principles
314
	 * of topic name syntax and semantics:</p>
315
	 * 
316
	 * <ul>
317
	 * 	<li>A topic must be at least one character long.</li>
318
	 * 	<li>Topic names are case sensitive.  For example, <em>ACCOUNTS</em> and <em>Accounts</em> are
319
	 * 	two different topics.</li>
320
	 * 	<li>Topic names can include the space character.  For example, <em>Accounts
321
	 * 	payable</em> is a valid topic.</li>
322
	 * 	<li>A leading "/" creates a distinct topic.  For example, <em>/finance</em> is
323
	 * 	different from <em>finance</em>. <em>/finance</em> matches "+/+" and "/+", but
324
	 * 	not "+".</li>
325
	 * 	<li>Do not include the null character (Unicode<samp class="codeph"> \x0000</samp>) in
326
	 * 	any topic.</li>
327
	 * </ul>
328
	 * 
329
	 * <p>The following principles apply to the construction and content of a topic
330
	 * tree:</p>
331
	 * 
332
	 * <ul>
333
	 * 	<li>The length is limited to 64k but within that there are no limits to the
334
	 * 	number of levels in a topic tree.</li>
335
	 * 	<li>There can be any number of root nodes; that is, there can be any number
336
	 * 	of topic trees.</li>
337
	 * 	</ul>
338
	 * </p>
339
	 * 
340
	 * <p>This is a blocking method that returns once publish completes</p>	 * 
341
	 * 
342
	 * @param topic  to deliver the message to, for example "finance/stock/ibm".
343
	 * @param message to delivery to the server
344
 	 * @throws MqttPersistenceException when a problem with storing the message
345
	 * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2.
346
	 * @throws MqttException for other errors encountered while publishing the message.
347
	 * For instance client not connected. 
348
	 */
349
	public void publish(String topic, MqttMessage message) throws MqttException, MqttPersistenceException;
350
	
351
	/**
352
	 * Sets the callback listener to use for events that happen asynchronously. 
353
	 * <p>There are a number of events that listener will be notified about. These include
354
	 * <ul>
355
	 * <li>A new message has arrived and is ready to be processed</li>
356
	 * <li>The connection to the server has been lost</li> 
357
	 * <li>Delivery of a message to the server has completed.</li>
358
	 * </ul>  
359
	 * </p>
360
	 * <p>Other events that track the progress of an individual operation such 
361
	 * as connect and subscribe can be tracked using the {@link MqttToken} passed to the
362
	 * operation<p>
363
	 * @see MqttCallback
364
	 * @param callback the class to callback when for events related to the client
365
	 */
366
	public void setCallback(MqttCallback callback);
367
368
	/**
369
	 * Get a topic object which can be used to publish messages.
370
	 * <p>An alternative method that should be used in preference to this one when publishing a message is:
371
	 * <ul>
372
	 * <li>{@link MqttClient#publish(String, MqttMessage)} to publish a message in a blocking manner
373
	 * <li>or use publish methods on the non blocking client like {@link IMqttAsyncClient#publish(String, MqttMessage, Object, IMqttActionListener)}
374
	 * </ul>
375
	 * </p>
376
	 * <p>When building an application,
377
	 * the design of the topic tree should take into account the following principles
378
	 * of topic name syntax and semantics:</p>
379
	 * 
380
	 * <ul>
381
	 * 	<li>A topic must be at least one character long.</li>
382
	 * 	<li>Topic names are case sensitive.  For example, <em>ACCOUNTS</em> and <em>Accounts</em> are
383
	 * 	two different topics.</li>
384
	 * 	<li>Topic names can include the space character.  For example, <em>Accounts
385
	 * 	payable</em> is a valid topic.</li>
386
	 * 	<li>A leading "/" creates a distinct topic.  For example, <em>/finance</em> is
387
	 * 	different from <em>finance</em>. <em>/finance</em> matches "+/+" and "/+", but
388
	 * 	not "+".</li>
389
	 * 	<li>Do not include the null character (Unicode<samp class="codeph"> \x0000</samp>) in
390
	 * 	any topic.</li>
391
	 * </ul>
392
	 * 
393
	 * <p>The following principles apply to the construction and content of a topic
394
	 * tree:</p>
395
	 * 
396
	 * <ul>
397
	 * 	<li>The length is limited to 64k but within that there are no limits to the
398
	 * 	number of levels in a topic tree.</li>
399
	 * 	<li>There can be any number of root nodes; that is, there can be any number
400
	 * 	of topic trees.</li>
401
	 * 	</ul>
402
	 * </p>
403
	 *
404
	 * @param topic the topic to use, for example "finance/stock/ibm".
405
	 * @return an MqttTopic object, which can be used to publish messages to 
406
	 * the topic.
407
	 * @throws IllegalArgumentException if the topic contains a '+' or '#' 
408
	 * wildcard character.
409
	 */
410
	public MqttTopic getTopic(String topic);
411
	
412
	/**
413
	 * Determines if this client is currently connected to the server.
414
	 * 
415
	 * @return <code>true</code> if connected, <code>false</code> otherwise.
416
	 */
417
	public boolean isConnected();
418
419
	/**
420
	 * Returns the client ID used by this client. 
421
	 * <p>All clients connected to the
422
	 * same server or server farm must have a unique ID.
423
	 * </p> 
424
	 * 
425
	 * @return the client ID used by this client.
426
	 */
427
	public String getClientId();
428
429
	/**
430
	 * Returns the address of the server used by this client, as a URI.
431
	 * <p>The format is the same as specified on the constructor.
432
	 * </p>
433
	 * 
434
	 * @return the server's address, as a URI String.
435
	 * @see MqttAsyncClient#MqttAsyncClient(String, String)
436
	 */
437
	public String getServerURI();
438
439
	/**
440
	 * Returns the delivery tokens for any outstanding publish operations.
441
	 * <p>If a client has been restarted and there are messages that were in the
442
	 * process of being delivered when the client stopped this method will 
443
	 * return a token for each message enabling the delivery to be tracked
444
	 * Alternately the {@link MqttCallback#deliveryComplete(IMqttDeliveryToken)} 
445
	 * callback can be used to track the delivery of outstanding messages.
446
	 * </p>
447
	 * <p>If a client connects with cleansession true then there will be no
448
	 * delivery tokens as the cleansession option deletes all earlier state.
449
	 * For state to be remembered the client must connect with cleansession
450
	 * set to false</P>
451
	 * @return zero or more delivery tokens 
452
	 */
453
	public IMqttDeliveryToken[] getPendingDeliveryTokens();
454
	
455
	/**
456
	 * Close the client
457
	 * Releases all resource associated with the client. After the client has 
458
	 * been closed it cannot be reused. For instance attempts to connect will fail. 
459
	 * @throws MqttException  if the client is not disconnected. 
460
	 */
461
	public void close() throws MqttException;
462
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/IMqttDeliveryToken.java (+41 lines)
Added Link Here
1
package org.eclipse.paho.client.mqttv3;
2
/**
3
 * Provides a mechanism for tracking the delivery of a message
4
 * 
5
 * <p>A subclass of IMqttToken that allows the delivery of a message to be tracked. 
6
 * Unlike instances of IMqttToken delivery tokens can be used across connection
7
 * and client restarts.  This enables the delivery of a messages to be tracked 
8
 * after failures. There are two approaches
9
 * <ul> 
10
 * <li>A list of delivery tokens for in-flight messages can be obtained using 
11
 * {@link IMqttAsyncClient#getPendingDeliveryTokens()}.  The waitForCompletion
12
 * method can then be used to block until the delivery is complete.
13
 * <li>A {@link MqttCallback} can be set on the client. Once a message has been 
14
 * delivered the {@link MqttCallback#deliveryComplete(IMqttDeliveryToken)} method will
15
 * be called withe delivery token being passed as a parameter. 
16
 * </ul>
17
 * <p> 
18
 * An action is in progress until either:
19
 * <ul>
20
 * <li>isComplete() returns true or 
21
 * <li>getException() is not null. If a client shuts down before delivery is complete. 
22
 * an exception is returned.  As long as the Java Runtime is not stopped a delivery token
23
 * is valid across a connection disconnect and reconnect. In the event the client 
24
 * is shut down the getPendingDeliveryTokens method can be used once the client is 
25
 * restarted to obtain a list of delivery tokens for inflight messages.
26
 * </ul>
27
 * </p>
28
 * 
29
 */
30
31
public interface IMqttDeliveryToken extends IMqttToken {
32
	/**
33
	 * Returns the message associated with this token.
34
	 * <p>Until the message has been delivered, the message being delivered will
35
	 * be returned. Once the message has been delivered <code>null</code> will be 
36
	 * returned.
37
	 * @return the message associated with this token or null if already delivered.
38
	 * @throws MqttException if there was a problem completing retrieving the message
39
	 */
40
	public MqttMessage getMessage() throws MqttException;
41
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/IMqttToken.java (+140 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3;
13
14
15
/**
16
 * Provides a mechanism for tracking the completion of an asynchronous task.
17
 * 
18
 * <p>When using the asynchronous/non-blocking MQTT programming interface all 
19
 * methods /operations that take any time and in particular those that involve 
20
 * any network operation return control to the caller immediately. The operation
21
 * then proceeds to run in the background so as not to block the invoking thread. 
22
 * An IMqttToken is used to track the state of the operation. An application can use the 
23
 * token to wait for an operation to complete. A token is passed to callbacks 
24
 * once the operation completes and provides context linking it to the original 
25
 * request. A token is associated with a single operation.<p>
26
 * <p> 
27
 * An action is in progress until either:
28
 * <ul>
29
 * <li>isComplete() returns true or 
30
 * <li>getException() is not null.
31
 * </ul>
32
 * </p>
33
 * 
34
 */
35
public interface IMqttToken {
36
	
37
	/**
38
	 * Blocks the current thread until the action this token is associated with has
39
	 * completed.
40
	 * 
41
	 * @throws MqttException if there was a problem with the action associated with the token.
42
	 * @see #waitForCompletion(long)
43
	 */
44
	public void waitForCompletion() throws MqttException;
45
	
46
	/**
47
	 * Blocks the current thread until the action this token is associated with has
48
	 * completed. 
49
	 * <p>The timeout specifies the maximum time it will block for. If the action 
50
	 * completes before the timeout then control returns immediately, if not
51
	 * it will block until the timeout expires. </p>
52
	 * <p>If the action being tracked fails or the timeout expires an exception will 
53
	 * be thrown. In the event of a timeout the action may complete after timeout. 
54
	 * </p>
55
	 * 
56
	 * @param timeout the maximum amount of time to wait for, in milliseconds.
57
	 * @throws MqttException if there was a problem with the action associated with the token.
58
	 */
59
	public void waitForCompletion(long timeout) throws MqttException;
60
	
61
	/**
62
	 * Returns whether or not the action has finished.
63
	 * <p>True will be returned both in the case where the action finished successfully
64
	 * and in the case where it failed. If the action failed {@link #getException()} will 
65
	 * be non null. 
66
	 * </p>
67
	 */
68
	public boolean isComplete();
69
		
70
	/**
71
	 * Returns an exception providing more detail if an operation failed 
72
	 * <p>While an action in in progress and when an action completes successfully
73
	 * null will be returned. Certain errors like timeout or shutting down will not
74
	 * set the exception as the action has not failed or completed at that time
75
	 * </p>  
76
	 * @return exception may return an exception if the operation failed. Null will be 
77
	 * returned while action is in progress and if action completes successfully.
78
	 */
79
	public MqttException getException();
80
	
81
	/**
82
	 * Register a listener to be notified when an action completes.
83
	 * <p>Once a listener is registered it will be invoked when the action the token 
84
	 * is associated with either succeeds or fails. 
85
	 * </p>
86
	 * @param listener to be invoked once the action completes
87
	 */
88
	public void setActionCallback(IMqttActionListener listener);
89
	
90
	/**
91
	 * Return the async listener for this token. 
92
	 * @return listener that is set on the token or null if a listener is not registered.
93
	 */
94
	public IMqttActionListener getActionCallback();
95
	
96
	/**
97
	 * Returns the MQTT client that is responsible for processing the asynchronous
98
	 * action 
99
	 */
100
	public IMqttAsyncClient getClient();
101
	
102
	/**
103
	 * Returns the topic string(s) for the action being tracked by this
104
	 * token. If the action has not been initiated or the action has not
105
	 * topic associated with it such as connect then null will be returned.
106
	 * 
107
	 * @return the topic string(s) for the subscribe being tracked by this token or null
108
	 */
109
	public String[] getTopics();
110
	
111
	/**
112
	 * Store some context associated with an action. 
113
	 * <p>Allows the caller of an action to store some context that can be 
114
	 * accessed from within the ActionListener associated with the action. This 
115
	 * can be useful when the same ActionListener is associated with multiple 
116
	 * actions</p>
117
	 * @param userContext to associate with an action
118
	 */
119
	public void setUserContext(Object userContext);
120
	
121
	/**
122
	 * Retrieve the context associated with an action. 
123
	 * <p>Allows the ActionListener associated with an action to retrieve any context
124
	 * that was associated with the action when the action was invoked. If not 
125
	 * context was provided null is returned. </p>
126
 
127
	 * @return Object context associated with an action or null if there is none.
128
	 */
129
	public Object getUserContext();
130
	
131
	/**
132
	 * Returns the message ID of the message that is associated with the token.
133
	 * A message id of zero will be returned for tokens associated with
134
	 * connect, disconnect and ping operations as there can only ever
135
	 * be one of these outstanding at a time. For other operations
136
	 * the MQTT message id flowed over the network.  
137
	 */
138
	public int getMessageId();
139
140
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttAsyncClient.java (+747 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3;
13
14
import java.util.Hashtable;
15
import java.util.Properties;
16
17
import javax.net.SocketFactory;
18
import javax.net.ssl.SSLSocketFactory;
19
20
import org.eclipse.paho.client.mqttv3.internal.ClientComms;
21
import org.eclipse.paho.client.mqttv3.internal.ExceptionHelper;
22
import org.eclipse.paho.client.mqttv3.internal.LocalNetworkModule;
23
import org.eclipse.paho.client.mqttv3.internal.NetworkModule;
24
import org.eclipse.paho.client.mqttv3.internal.SSLNetworkModule;
25
import org.eclipse.paho.client.mqttv3.internal.TCPNetworkModule;
26
import org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory;
27
import org.eclipse.paho.client.mqttv3.internal.wire.MqttDisconnect;
28
import org.eclipse.paho.client.mqttv3.internal.wire.MqttPublish;
29
import org.eclipse.paho.client.mqttv3.internal.wire.MqttSubscribe;
30
import org.eclipse.paho.client.mqttv3.internal.wire.MqttUnsubscribe;
31
import org.eclipse.paho.client.mqttv3.logging.Logger;
32
import org.eclipse.paho.client.mqttv3.logging.LoggerFactory;
33
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
34
import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence;
35
import org.eclipse.paho.client.mqttv3.util.Debug;
36
37
/**
38
 * Lightweight client for talking to an MQTT server using non-blocking methods 
39
 * that allow an operation to run in the background. 
40
 * 
41
 * <p>This class implements the non-blocking {@link IMqttAsyncClient} client interface
42
 * allowing applications to initiate MQTT actions and then carry working while the 
43
 * MQTT action completes on a background thread.
44
 * This implementation is compatible with all Java SE runtimes from 1.4.2 and up. 
45
 * </p>  
46
 * <p>An application can connect to an MQTT server using:
47
 * <ul>
48
 * <li>A plain TCP socket
49
 * <li>An secure SSL/TLS socket
50
 * </ul>
51
 * </p>
52
 * <p>To enable messages to be delivered even across network and client restarts
53
 * messages need to be safely stored until the message has been delivered at the requested
54
 * quality of service. A pluggable persistence mechanism is provided to store the messages. 
55
 * </p>
56
 * <p>By default {@link MqttDefaultFilePersistence} is used to store messages to a file. 
57
 * If persistence is set to null then messages are stored in memory and hence can  be lost
58
 * if the client, Java runtime or device shuts down. 
59
 * </p>
60
 * <p>If connecting with {@link MqttConnectOptions#setCleanSession(boolean)} set to true it 
61
 * is safe to use memory persistence as all state it cleared when a client disconnects. If
62
 * connecting with cleansession set to false, to provide reliable message delivery 
63
 * then a persistent message store should be used such as the default one. 
64
 * </p>
65
 * <p>The message store interface is pluggable. Different stores can be used by implementing
66
 * the {@link MqttClientPersistence} interface and passing it to the clients constructor. 
67
 * </p>
68
 *  
69
 * @see IMqttAsyncClient   
70
 */
71
public class MqttAsyncClient implements IMqttAsyncClient { // DestinationProvider {
72
	
73
	private static final int URI_TYPE_TCP = 0;
74
	private static final int URI_TYPE_SSL = 1;
75
	private static final int URI_TYPE_LOCAL = 2;
76
	
77
	private String clientId;
78
	private String serverURI;
79
	private int serverURIType;
80
	protected ClientComms comms;
81
	private Hashtable topics;
82
	private MqttClientPersistence persistence;
83
	
84
	final static String className = MqttAsyncClient.class.getName();
85
	public Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className);
86
87
	/**
88
	 * Create an MqttAsyncClient that can be used to communicate with an MQTT server.
89
	 * <p> 
90
	 * The address of the server should be a URI, using a scheme of either 
91
	 * "tcp://" for a TCP connection or "ssl://" for a TCP connection secured by SSL/TLS. 
92
	 * For example:
93
	 * <ul>
94
	 * 	<li><code>tcp://localhost:1883</code></li>
95
	 * 	<li><code>ssl://localhost:8883</code></li>
96
	 * </ul> 
97
	 * If the port is not specified, it will
98
	 * default to 1883 for "tcp://" URIs, and 8883 for "ssl://" URIs.
99
	 * </p>
100
	 * <p>
101
	 * A client identified to connect to an MQTT server, it 
102
	 * must be unique across all clients connecting to the same
103
	 * server. A convenience method is provided to generate a random client id that
104
	 * should satisfy this criteria - {@link #generateClientId()}. As the client identifier
105
	 * is used by the server to identify a client when it reconnects, the client must use the
106
	 * same identifier between connections if durable subscriptions are used and reliable 
107
	 * delivery of messages is required.
108
	 * </p>
109
	 * <p>
110
	 * In Java SE, SSL can be configured in one of several ways, which the 
111
	 * client will use in the following order:
112
	 * </p>
113
	 * <ul>
114
	 * 	<li><strong>Supplying an <code>SSLSocketFactory</code></strong> - applications can
115
	 * use {@link MqttConnectOptions#setSocketFactory(SocketFactory)} to supply 
116
	 * a factory with the appropriate SSL settings.</li>
117
	 * 	<li><strong>SSL Properties</strong> - applications can supply SSL settings as a 
118
	 * simple Java Properties using {@link MqttConnectOptions#setSSLProperties(Properties)}.</li>
119
	 * 	<li><strong>Use JVM settings</strong> - There are a number of standard 
120
	 * Java system properties that can be used to configure key and trust stores.</li>
121
	 * </ul>
122
	 * 
123
	 * <p>In Java ME, the platform settings are used for SSL connections.</p>
124
	 *  
125
	 * <p>A default instance of {@link MqttDefaultFilePersistence} is used by
126
	 * the client. To specify a different persistence implementation, or to turn
127
	 * off persistence, use the {@link #MqttAsyncClient(String, String, MqttClientPersistence)} constructor.
128
	 *  
129
	 * @param serverURI the address of the server to connect to, specified as a URI
130
	 * @param clientId a client identifier that is unique on the server being connected to
131
	 * @throws IllegalArgumentException if the URI does not start with
132
	 * "tcp://", "ssl://" or "local://".
133
	 * @throws IllegalArgumentException if the clientId is null or is greater than 23 characters in length
134
	 * @throws MqttException if any other problem was encountered 
135
	 */
136
	public MqttAsyncClient(String serverURI, String clientId) throws MqttException {
137
		this(serverURI,clientId, new MqttDefaultFilePersistence());
138
	}
139
	
140
	/**
141
	 * Create an MqttAsyncClient that can be used to communicate with an MQTT server.
142
	 * <p> 
143
	 * The address of the server should be a URI, using a scheme of either 
144
	 * "tcp://" for a TCP connection or "ssl://" for a TCP connection secured by SSL/TLS. 
145
	 * For example:
146
	 * <ul>
147
	 * 	<li><code>tcp://localhost:1883</code></li>
148
	 * 	<li><code>ssl://localhost:8883</code></li>
149
	 * </ul> 
150
	 * If the port is not specified, it will
151
	 * default to 1883 for "tcp://" URIs, and 8883 for "ssl://" URIs.
152
	 * </p>
153
	 * <p>
154
	 * A client identified to connect to an MQTT server, it 
155
	 * must be unique across all clients connecting to the same
156
	 * server. A convenience method is provided to generate a random client id that
157
	 * should satisfy this criteria - {@link #generateClientId()}. As the client identifier
158
	 * is used by the server to identify a client when it reconnects, the client must use the
159
	 * same identifier between connections if durable subscriptions are used and reliable 
160
	 * delivery of messages is required.
161
	 * </p>
162
	 * <p>
163
	 * In Java SE, SSL can be configured in one of several ways, which the 
164
	 * client will use in the following order:
165
	 * </p>
166
	 * <ul>
167
	 * 	<li><strong>Supplying an <code>SSLSocketFactory</code></strong> - applications can
168
	 * use {@link MqttConnectOptions#setSocketFactory(SocketFactory)} to supply 
169
	 * a factory with the appropriate SSL settings.</li>
170
	 * 	<li><strong>SSL Properties</strong> - applications can supply SSL settings as a 
171
	 * simple Java Properties using {@link MqttConnectOptions#setSSLProperties(Properties)}.</li>
172
	 * 	<li><strong>Use JVM settings</strong> - There are a number of standard 
173
	 * Java system properties that can be used to configure key and trust stores.</li>
174
	 * </ul>
175
	 * 
176
	 * <p>In Java ME, the platform settings are used for SSL connections.</p>
177
	 * <p> 
178
	 * The persistence mechanism is used to enable reliable messaging. 
179
	 * For qualities of server (QoS) 1 or 2 to work, messages must be persisted
180
	 * to disk by both the client and the server.  If this is not done, then
181
	 * a failure in the client or server can result in lost messages. A pluggable 
182
	 * persistence mechanism is supported via the {@link MqttClientPersistence} 
183
	 * interface. A implementer of this interface that safely stores messages
184
	 * must be specified in order for delivery of messages to be reliable. In
185
	 * addition {@link MqttConnectOptions#setCleanSession(boolean)} must be set
186
	 * to false. In the event that only QoS 0 messages are sent or received or 
187
	 * cleansession is set to true then a safe store is not needed. 
188
	 * </p>
189
	 * <p>An implementation of file-based persistence is provided in 
190
	 * class {@link MqttDefaultFilePersistence} which will work in all Java SE based 
191
	 * systems. If no persistence is needed, the persistence parameter 
192
	 * can be explicitly set to <code>null</code>.</p>
193
	 * 
194
	 * @param serverURI the address of the server to connect to, specified as a URI
195
	 * @param clientId a client identifier that is unique on the server being connected to
196
 	 * @param persistence the persistence mechanism to use.
197
	 * @throws IllegalArgumentException if the URI does not start with
198
	 * "tcp://", "ssl://" or "local://".
199
	 * @throws IllegalArgumentException if the clientId is null or is greater than 23 characters in length
200
	 * @throws MqttException if any other problem was encountered 
201
	 */
202
	public MqttAsyncClient(String serverURI, String clientId, MqttClientPersistence persistence) throws MqttException {
203
		
204
		log.setResourceName(clientId);
205
		
206
		if (clientId == null || clientId.length() == 0 || clientId.length() > 23) {
207
			throw new IllegalArgumentException();
208
		}
209
		final String methodName = "MqttAsyncClient";
210
		
211
		this.serverURI = serverURI;
212
		this.serverURIType = validateURI(serverURI);
213
		this.clientId = clientId;
214
		
215
		this.persistence = persistence;
216
		if (this.persistence == null) {
217
			this.persistence = new MemoryPersistence();
218
		}
219
		
220
		// @TRACE 101=<init> ClientID={0} ServerURI={1} PersistenceType={2}  	
221
		log.fine(className,methodName,"101",new Object[]{clientId,serverURI,persistence});
222
223
		this.persistence.open(clientId, serverURI);
224
		this.comms = new ClientComms(this, this.persistence);
225
		this.persistence.close();
226
		this.topics = new Hashtable();
227
228
	}
229
	
230
	private int validateURI(String srvURI) {
231
		if (srvURI.startsWith("tcp://")) {
232
			return URI_TYPE_TCP;
233
		}
234
		else if (srvURI.startsWith("ssl://")) {
235
			return URI_TYPE_SSL;
236
		}
237
		else if (srvURI.startsWith("local://")) {
238
			return URI_TYPE_LOCAL;
239
		}
240
		else {
241
			throw new IllegalArgumentException();
242
		}
243
	}
244
	
245
	/**
246
	 * Factory method to create the correct network module, based on the
247
	 * supplied address URI.
248
	 * 
249
	 * @param address the URI for the server.
250
	 * @return a network module appropriate to the specified address.
251
	 */
252
	protected NetworkModule createNetworkModule(String address, MqttConnectOptions options) throws MqttException, MqttSecurityException {
253
		final String methodName = "createNetworkModule";
254
		// @TRACE 115=URI={0}
255
		log.fine(className,methodName, "115", new Object[] {address});
256
257
		NetworkModule netModule;
258
		String shortAddress;
259
		String host;
260
		int port;
261
		SocketFactory factory = options.getSocketFactory();
262
		switch (serverURIType) {
263
		case URI_TYPE_TCP:
264
			shortAddress = address.substring(6);
265
			host = getHostName(shortAddress);
266
			port = getPort(shortAddress, 1883);
267
			if (factory == null) {
268
				factory = SocketFactory.getDefault();
269
				options.setSocketFactory(factory);
270
			}
271
			else if (factory instanceof SSLSocketFactory) {
272
				throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_SOCKET_FACTORY_MISMATCH);
273
			}
274
			netModule = new TCPNetworkModule(factory, host, port, clientId);
275
			((TCPNetworkModule)netModule).setConnectTimeout(options.getConnectionTimeout());
276
			break;
277
		case URI_TYPE_SSL:
278
			shortAddress = address.substring(6);
279
			host = getHostName(shortAddress);
280
			port = getPort(shortAddress, 8883);
281
			SSLSocketFactoryFactory factoryFactory = null;
282
			if (factory == null) {
283
//				try {
284
					factoryFactory = new SSLSocketFactoryFactory();
285
					Properties sslClientProps = options.getSSLProperties();
286
					if (null != sslClientProps)
287
						factoryFactory.initialize(sslClientProps, null);
288
					factory = factoryFactory.createSocketFactory(null);
289
//				}
290
//				catch (MqttDirectException ex) {
291
//					throw ExceptionHelper.createMqttException(ex.getCause());
292
//				}
293
			}
294
			else if ((factory instanceof SSLSocketFactory) == false) {
295
				throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_SOCKET_FACTORY_MISMATCH);
296
			}
297
298
			// Create the network module...
299
			netModule = new SSLNetworkModule((SSLSocketFactory) factory, host, port, clientId);
300
			((SSLNetworkModule)netModule).setSSLhandshakeTimeout(options.getConnectionTimeout());
301
			// Ciphers suites need to be set, if they are available
302
			if (factoryFactory != null) {
303
				String[] enabledCiphers = factoryFactory.getEnabledCipherSuites(null);
304
				if (enabledCiphers != null) {
305
					((SSLNetworkModule) netModule).setEnabledCiphers(enabledCiphers);
306
				}
307
			}
308
			break;
309
		case URI_TYPE_LOCAL:
310
			netModule = new LocalNetworkModule(address.substring(8));
311
			break;
312
		default:
313
			// This shouldn't happen, as long as validateURI() has been called.
314
			netModule = null;
315
		}
316
		return netModule;
317
	}
318
	
319
	private int getPort(String uri, int defaultPort) {
320
		int port;
321
		int portIndex = uri.lastIndexOf(':');
322
		if (portIndex == -1) {
323
			port = defaultPort;
324
		}
325
		else {
326
			port = Integer.valueOf(uri.substring(portIndex + 1)).intValue();
327
		}
328
		return port;
329
	}
330
	
331
	private String getHostName(String uri) {
332
		int schemeIndex = uri.lastIndexOf('/');
333
		int portIndex = uri.lastIndexOf(':');
334
		if (portIndex == -1) {
335
			portIndex = uri.length();
336
		}
337
		return uri.substring(schemeIndex + 1, portIndex);
338
	}
339
	
340
	/* (non-Javadoc)
341
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#connect(java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
342
	 */
343
	public IMqttToken connect(Object userContext, IMqttActionListener callback) 
344
			throws MqttException, MqttSecurityException {
345
		return this.connect(new MqttConnectOptions(), userContext, callback);
346
	} 
347
	
348
	/* (non-Javadoc)
349
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#connect()
350
	 */
351
	public IMqttToken connect() throws MqttException, MqttSecurityException {
352
		return this.connect(null, null);
353
	}
354
	
355
	/* (non-Javadoc)
356
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#connect(org.eclipse.paho.client.mqttv3.MqttConnectOptions)
357
	 */
358
	public IMqttToken connect(MqttConnectOptions options) throws MqttException, MqttSecurityException {
359
		return this.connect(options, null,null);
360
	}
361
	
362
	/* (non-Javadoc)
363
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#connect(org.eclipse.paho.client.mqttv3.MqttConnectOptions, java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
364
	 */
365
	public IMqttToken connect(MqttConnectOptions options, Object userContext, IMqttActionListener callback) 
366
			throws MqttException, MqttSecurityException {
367
		final String methodName = "connect";
368
		if (comms.isConnected()) {
369
			throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_CONNECTED);
370
		}
371
		if (comms.isConnecting()) {
372
			throw new MqttException(MqttException.REASON_CODE_CONNECT_IN_PROGRESS);
373
		}
374
		if (comms.isDisconnecting()) {
375
			throw new MqttException(MqttException.REASON_CODE_CLIENT_DISCONNECTING);
376
		}
377
		if (comms.isClosed()) {
378
			throw new MqttException(MqttException.REASON_CODE_CLIENT_CLOSED);
379
		}
380
381
		// @TRACE 103=cleanSession={0} connectionTimeout={1} TimekeepAlive={2} userName={3} password={4} will={5} userContext={6} callback={7}
382
		log.fine(className,methodName, "103",
383
				new Object[]{ 
384
				new Boolean(options.isCleanSession()),
385
				new Integer(options.getConnectionTimeout()),
386
				new Integer(options.getKeepAliveInterval()),
387
				options.getUserName(),
388
				((null == options.getPassword())?"[null]":"[notnull]"),
389
				((null == options.getWillMessage())?"[null]":"[notnull]"),
390
				userContext,
391
				callback });
392
		comms.setNetworkModule(createNetworkModule(serverURI, options));
393
394
		this.persistence.open(clientId, serverURI);
395
396
		if (options.isCleanSession()) {
397
			persistence.clear();
398
		}
399
		
400
		MqttToken token = new MqttToken(getClientId());
401
		token.setActionCallback(callback);
402
		token.setUserContext(userContext);
403
		
404
		comms.connect(options, token);
405
		
406
		return token;
407
	}
408
409
	/* (non-Javadoc)
410
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnect(java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
411
	 */
412
	public IMqttToken disconnect( Object userContext, IMqttActionListener callback) throws MqttException {
413
		return this.disconnect(30000, userContext, callback);
414
	}
415
	
416
	/* (non-Javadoc)
417
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnect()
418
	 */
419
	public IMqttToken disconnect() throws MqttException {
420
		return this.disconnect(null, null);
421
	}
422
	
423
	/* (non-Javadoc)
424
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnect(long)
425
	 */
426
	public IMqttToken disconnect(long quiesceTimeout) throws MqttException {
427
		return this.disconnect(quiesceTimeout, null, null);
428
	}
429
	
430
	/* (non-Javadoc)
431
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnect(long, java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
432
	 */
433
	public IMqttToken disconnect(long quiesceTimeout, Object userContext, IMqttActionListener callback) throws MqttException {
434
		final String methodName = "disconnect";
435
		// @TRACE 104=> quiesceTimeout={0} userContext={1} callback={2}
436
		log.fine(className,methodName, "104",new Object[]{ new Long(quiesceTimeout), userContext, callback});
437
		
438
		MqttToken token = new MqttToken(getClientId());
439
		token.setActionCallback(callback);
440
		token.setUserContext(userContext);
441
442
		MqttDisconnect disconnect = new MqttDisconnect();
443
		try {
444
			comms.disconnect(disconnect, quiesceTimeout, token);
445
		}
446
		catch (MqttException ex) {
447
			//@TRACE 105=< exception
448
			log.fine(className,methodName,"105",null,ex);
449
			throw ex;
450
		}
451
		//@TRACE 108=<
452
		log.fine(className,methodName,"108");
453
		
454
		return token;
455
	}
456
457
	/* (non-Javadoc)
458
	 * @see IMqttAsyncClient#isConnected()
459
	 */
460
	public boolean isConnected() {
461
		return comms.isConnected();
462
	}
463
	
464
	/* (non-Javadoc)
465
	 * @see IMqttAsyncClient#getClientId()
466
	 */
467
	public String getClientId() {
468
		return clientId;
469
	}
470
	
471
	/* (non-Javadoc)
472
	 * @see IMqttAsyncClient#getServerURI()
473
	 */
474
	public String getServerURI() {
475
		return serverURI;
476
	}
477
		
478
	/**
479
	 * Get a topic object which can be used to publish messages.
480
	 * <p>There are two alternative methods that should be used in preference to this one when publishing a message:
481
	 * <ul>
482
	 * <li>{@link MqttAsyncClient#publish(String, MqttMessage, MqttDeliveryToken)} to publish a message in a non-blocking manner or 
483
	 * <li>{@link MqttClient#publishBlock(String, MqttMessage, MqttDeliveryToken)} to publish a message in a blocking manner
484
	 * </ul>
485
	 * </p>
486
	 * <p>When you build an application,
487
	 * the design of the topic tree should take into account the following principles
488
	 * of topic name syntax and semantics:</p>
489
	 * 
490
	 * <ul>
491
	 * 	<li>A topic must be at least one character long.</li>
492
	 * 	<li>Topic names are case sensitive.  For example, <em>ACCOUNTS</em> and <em>Accounts</em> are
493
	 * 	two different topics.</li>
494
	 * 	<li>Topic names can include the space character.  For example, <em>Accounts
495
	 * 	payable</em> is a valid topic.</li>
496
	 * 	<li>A leading "/" creates a distinct topic.  For example, <em>/finance</em> is
497
	 * 	different from <em>finance</em>. <em>/finance</em> matches "+/+" and "/+", but
498
	 * 	not "+".</li>
499
	 * 	<li>Do not include the null character (Unicode<samp class="codeph"> \x0000</samp>) in
500
	 * 	any topic.</li>
501
	 * </ul>
502
	 * 
503
	 * <p>The following principles apply to the construction and content of a topic
504
	 * tree:</p>
505
	 * 
506
	 * <ul>
507
	 * 	<li>The length is limited to 64k but within that there are no limits to the
508
	 * 	number of levels in a topic tree.</li>
509
	 * 	<li>There can be any number of root nodes; that is, there can be any number
510
	 * 	of topic trees.</li>
511
	 * 	</ul>
512
	 * </p>
513
	 *
514
	 * @param topic the topic to use, for example "finance/stock/ibm".
515
	 * @return an MqttTopic object, which can be used to publish messages to 
516
	 * the topic.
517
	 * @throws IllegalArgumentException if the topic contains a '+' or '#' 
518
	 * wildcard character.
519
	 */
520
	protected MqttTopic getTopic(String topic) {
521
		validateTopic(topic);
522
		
523
		MqttTopic result = (MqttTopic)topics.get(topic);
524
		if (result == null) {
525
			result = new MqttTopic(topic, comms);
526
			topics.put(topic,result);
527
		}
528
		return result;
529
	}
530
		
531
	/* (non-Javadoc)
532
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang.String, int, java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
533
	 */
534
	public IMqttToken subscribe(String topicFilter, int qos, Object userContext, IMqttActionListener callback) throws MqttException {
535
		return this.subscribe(new String[] {topicFilter}, new int[] {qos}, userContext, callback);
536
	}
537
	
538
	/* (non-Javadoc)
539
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang.String, int)
540
	 */
541
	public IMqttToken subscribe(String topicFilter, int qos) throws MqttException {
542
		return this.subscribe(new String[] {topicFilter}, new int[] {qos}, null, null);
543
	}
544
545
	/* (non-Javadoc)
546
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang.String[], int[])
547
	 */
548
	public IMqttToken subscribe(String[] topicFilters, int[] qos) throws MqttException {
549
		return this.subscribe(topicFilters, qos, null, null);
550
	}
551
552
	/* (non-Javadoc)
553
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang.String[], int[], java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
554
	 */
555
	public IMqttToken subscribe(String[] topicFilters, int[] qos, Object userContext, IMqttActionListener callback) throws MqttException {
556
		final String methodName = "subscribe";
557
558
		if (topicFilters.length != qos.length) {
559
			throw new IllegalArgumentException();
560
		}
561
		String subs = "";
562
		for (int i=0;i<topicFilters.length;i++) {
563
			if (i>0) {
564
				subs+=", ";
565
			}
566
			subs+=topicFilters[i]+":"+qos[i];
567
		}
568
		//@TRACE 106=Subscribe topic={0} userContext={1} callback={2}
569
		log.fine(className,methodName,"106",new Object[]{subs, userContext, callback});
570
		
571
		MqttToken token = new MqttToken(getClientId());
572
		token.setActionCallback(callback);
573
		token.setUserContext(userContext);
574
		token.internalTok.setTopics(topicFilters);
575
		
576
		MqttSubscribe register = new MqttSubscribe(topicFilters, qos);
577
578
		comms.sendNoWait(register, token);
579
		//@TRACE 109=<
580
		log.fine(className,methodName,"109");
581
		
582
		return token;
583
	}
584
585
	/* (non-Javadoc)
586
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#unsubscribe(java.lang.String, java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
587
	 */
588
	public IMqttToken unsubscribe(String topicFilter,  Object userContext, IMqttActionListener callback) throws MqttException {
589
		return unsubscribe(new String[] {topicFilter}, userContext, callback);
590
	}
591
	
592
	/* (non-Javadoc)
593
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#unsubscribe(java.lang.String)
594
	 */
595
	public IMqttToken unsubscribe(String topicFilter) throws MqttException {
596
		return unsubscribe(new String[] {topicFilter}, null, null);
597
	}
598
599
	/* (non-Javadoc)
600
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#unsubscribe(java.lang.String[])
601
	 */
602
	public IMqttToken unsubscribe(String[] topicFilters) throws MqttException {
603
		return unsubscribe(topicFilters, null, null);
604
	}
605
	
606
	/* (non-Javadoc)
607
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#unsubscribe(java.lang.String[], java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
608
	 */
609
	public IMqttToken unsubscribe(String[] topicFilters, Object userContext, IMqttActionListener callback) throws MqttException {
610
		final String methodName = "unsubscribe";
611
		String subs = "";
612
		for (int i=0;i<topicFilters.length;i++) {
613
			if (i>0) {
614
				subs+=", ";
615
			}
616
			subs+=topicFilters[i];
617
		}
618
		//@TRACE 107=Unsubscribe topic={0} userContext={1} callback={2}
619
		log.fine(className, methodName,"107",new Object[]{subs, userContext, callback});
620
621
		MqttToken token = new MqttToken(getClientId());
622
		token.setActionCallback(callback);
623
		token.setUserContext(userContext);
624
		token.internalTok.setTopics(topicFilters);
625
		
626
		MqttUnsubscribe unregister = new MqttUnsubscribe(topicFilters);
627
		
628
		comms.sendNoWait(unregister, token);
629
		//@TRACE 110=<
630
		log.fine(className,methodName,"110");
631
		
632
		return token;
633
	}
634
	
635
	/* (non-Javadoc)
636
	 * @see IMqttAsyncClient#setCallback(MqttCallback)
637
	 */
638
	public void setCallback(MqttCallback callback) {
639
		comms.setCallback(callback);
640
	}
641
	
642
	/**
643
	 * Returns a randomly generated client identifier based on the current user's login
644
	 * name and the system time.
645
	 * <p>When cleanSession is set to false, an application must ensure it uses the 
646
	 * same client identifier when it reconnects to the server to resume state and maintain
647
	 * assured message delivery.</p>
648
	 * @return a generated client identifier
649
	 * @see MqttConnectOptions#setCleanSession(boolean)
650
	 */
651
	public static String generateClientId() {
652
		return (System.getProperty("user.name") + "." + System.currentTimeMillis());
653
	}
654
	
655
	/* (non-Javadoc)
656
	 * @see IMqttAsyncClient#getPendingDeliveryTokens()
657
	 */
658
	public IMqttDeliveryToken[] getPendingDeliveryTokens() {
659
		return comms.getPendingDeliveryTokens();
660
	}
661
662
	/* (non-Javadoc)
663
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#publish(java.lang.String, byte[], int, boolean, java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
664
	 */
665
	public IMqttDeliveryToken publish(String topic, byte[] payload, int qos,
666
			boolean retained, Object userContext, IMqttActionListener callback) throws MqttException,
667
			MqttPersistenceException {
668
		MqttMessage message = new MqttMessage(payload);
669
		message.setQos(qos);
670
		message.setRetained(retained);
671
		return this.publish(topic, message, userContext, callback);
672
	}
673
	/* (non-Javadoc)
674
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#publish(java.lang.String, byte[], int, boolean)
675
	 */
676
	public IMqttDeliveryToken publish(String topic, byte[] payload, int qos,
677
			boolean retained) throws MqttException, MqttPersistenceException {
678
		return this.publish(topic, payload, qos, retained, null, null);
679
	}
680
681
	/* (non-Javadoc)
682
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#publish(java.lang.String, org.eclipse.paho.client.mqttv3.MqttMessage)
683
	 */
684
	public IMqttDeliveryToken publish(String topic, MqttMessage message) throws MqttException, 	MqttPersistenceException {
685
		return this.publish(topic, message, null, null);
686
	}
687
688
	/* (non-Javadoc)
689
	 * @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)
690
	 */
691
	public IMqttDeliveryToken publish(String topic, MqttMessage message, Object userContext, IMqttActionListener callback) throws MqttException,
692
			MqttPersistenceException {
693
		final String methodName = "publish";
694
		//@TRACE 111=< topic={0} message={1}userContext={1} callback={2}
695
		log.fine(className,methodName,"111", new Object[] {topic, userContext, callback});
696
697
		validateTopic(topic);
698
		
699
		MqttDeliveryToken token = new MqttDeliveryToken(getClientId());
700
		token.setActionCallback(callback);
701
		token.setUserContext(userContext);
702
		token.setMessage(message);
703
		token.internalTok.setTopics(new String[] {topic});
704
		
705
		MqttPublish pubMsg = new MqttPublish(topic, message);
706
		comms.sendNoWait(pubMsg, token);
707
		
708
		//@TRACE 112=<
709
		log.fine(className,methodName,"112");
710
		
711
		return token;
712
	}
713
	
714
	/* (non-Javadoc)
715
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#close()
716
	 */
717
	public void close() throws MqttException {
718
		final String methodName = "close";
719
		//@TRACE 113=<
720
		log.fine(className,methodName,"113");
721
		comms.close();
722
		//@TRACE 114=>
723
		log.fine(className,methodName,"114");
724
725
	}
726
	
727
	/**
728
	 * Return a debug object that can be used to help solve problems.
729
	 */
730
	public Debug getDebug() {
731
		return new Debug(clientId,comms);
732
	}
733
	
734
	/**
735
	 * Checks a topic is valid when publishing a message
736
	 * <p>Checks the topic does not contain a wild card character.</p>
737
	 * @param topic to validate
738
	 * @throws IllegalArgumentException if the topic is not valid
739
	 */
740
	static public void validateTopic(String topic) {
741
		if ((topic.indexOf('#') == -1) && (topic.indexOf('+') == -1)) {
742
			return;
743
		}
744
		// The topic string does not comply with topic string rules. 
745
		throw new IllegalArgumentException();
746
	}
747
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttCallback.java (+75 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3;
13
14
15
/**
16
 * Enables an application to be notified when asynchronous 
17
 * events related to the client occur.
18
 * Classes implementing this interface
19
 * can be registered on both types of client: {@link IMqttClient#setCallback(MqttCallback)} 
20
 * and {@link IMqttAsyncClient#setCallback(MqttCallback)} 
21
 */
22
public interface MqttCallback {
23
	/**
24
	 * This method is called when the connection to the server is lost.
25
	 * 
26
	 * @param cause the reason behind the loss of connection.
27
	 */
28
	public void connectionLost(Throwable cause);
29
30
	/**
31
	 * This method is called when a message arrives from the server.
32
	 * 
33
	 * <p>
34
	 * This method is invoked synchronously by the MQTT client. An
35
	 * acknowledgement is not sent back to the server until this
36
	 * method returns cleanly.</p>
37
	 * <p>
38
	 * If an implementation of this method throws an <code>Exception</code>, then the
39
	 * client will be shut down.  When the client is next re-connected, any QoS
40
	 * 1 or 2 messages will be redelivered by the server.</p>
41
	 * <p>
42
	 * Any additional messages which arrive while an
43
	 * implementation of this method is running, will build up in memory, and
44
	 * will then back up on the network.</p>
45
	 * <p>
46
	 * If an application needs to persist data, then it
47
	 * should ensure the data is persisted prior to returning from this method, as
48
	 * after returning from this method, the message is considered to have been
49
	 * delivered, and will not be reproducable.</p>
50
	 * <p>
51
	 * It is possible to send a new message within an implementation of this callback
52
	 * (for example, a response to this message), but the implementation must not 
53
	 * disconnect the client, as it will be impossible to send an acknowledgement for
54
	 * the message being processed, and a deadlock will occur.</p>
55
	 * 
56
	 * @param topic name of the topic on the message was published to
57
	 * @param message the actual message.
58
	 * @throws Exception if a terminal error has occurred, and the client should be
59
	 * shut down.
60
	 */
61
	public void messageArrived(String topic, MqttMessage message) throws Exception;
62
	
63
	/**
64
	 * Called when delivery for a message has been completed, and all 
65
	 * acknowledgements have been received. For QOS 0 messages it is 
66
	 * called once the message has been handed to the network for
67
	 * delivery. For QOS 1 it is called when PUBACK is received and
68
	 * for QOS 2 when PUBCOMP is received. The token will be the same
69
	 * token as that returned when the message was published. 
70
	 * 
71
	 * @param token the delivery token associated with the message.
72
	 */
73
	public void deliveryComplete(IMqttDeliveryToken token);
74
	
75
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttClient.java (+376 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3;
13
14
import java.util.Properties;
15
import javax.net.SocketFactory;
16
17
import org.eclipse.paho.client.mqttv3.logging.Logger;
18
import org.eclipse.paho.client.mqttv3.logging.LoggerFactory;
19
import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence;
20
import org.eclipse.paho.client.mqttv3.util.Debug;
21
22
/**
23
 * Lightweight client for talking to an MQTT server using methods that block 
24
 * until an operation completes.  
25
 * 
26
 * <p>This class implements the blocking {@link IMqttClient} client interface where all 
27
 * actions block until they have completed (or timed out). 
28
 * This implementation is compatible with all Java SE runtimes from 1.4.2 and up.  
29
 * </p>  
30
 * <p>An application can connect to an MQTT server using:
31
 * <ul>
32
 * <li>A plain TCP socket
33
 * <li>An secure SSL/TLS socket
34
 * </ul>
35
 * </p>
36
 * <p>To enable messages to be delivered even across network and client restarts
37
 * messages need to be safely stored until the message has been delivered at the requested
38
 * quality of service. A pluggable persistence mechanism is provided to store the messages. 
39
 * </p>
40
 * <p>By default {@link MqttDefaultFilePersistence} is used to store messages to a file. 
41
 * If persistence is set to null then messages are stored in memory and hence can  be lost
42
 * if the client, Java runtime or device shuts down. 
43
 * </p>
44
 * <p>If connecting with {@link MqttConnectOptions#setCleanSession(boolean)} set to true it 
45
 * is safe to use memory persistence as all state it cleared when a client disconnects. If
46
 * connecting with cleansession set to false, to provide reliable message delivery 
47
 * then a persistent message store should be used such as the default one. </p>
48
 * <p>The message store interface is pluggable. Different stores can be used by implementing
49
 * the {@link MqttClientPersistence} interface and passing it to the clients constructor. 
50
 * </p>
51
 *  
52
 * @see IMqttClient   
53
 */
54
public class MqttClient implements IMqttClient { //), DestinationProvider {
55
	
56
	protected MqttAsyncClient aClient = null;  // Delegate implementation to MqttAshyncClient
57
	protected long timeToWait = -1;				// How long each method should wait for action to complete	
58
	
59
	final static String className = MqttClient.class.getName();
60
	public Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className);
61
	
62
	
63
	/**
64
	 * Create an MqttClient that can be used to communicate with an MQTT server.
65
	 * <p> 
66
	 * The address of the server should be a URI, using a scheme of either 
67
	 * "tcp://" for a TCP connection or "ssl://" for a TCP connection secured by SSL/TLS. 
68
	 * For example:
69
	 * <ul>
70
	 * 	<li><code>tcp://localhost:1883</code></li>
71
	 * 	<li><code>ssl://localhost:8883</code></li>
72
	 * </ul> 
73
	 * If the port is not specified, it will
74
	 * default to 1883 for "tcp://" URIs, and 8883 for "ssl://" URIs.
75
	 * </p>
76
	 * <p>
77
	 * A client identified to connect to an MQTT server, it 
78
	 * must be unique across all clients connecting to the same
79
	 * server. A convenience method is provided to generate a random client id that
80
	 * should satisfy this criteria - {@link #generateClientId()}. As the client identifier
81
	 * is used by the server to identify a client when it reconnects, the client must use the
82
	 * same identifier between connections if durable subscriptions are used and reliable 
83
	 * delivery of messages is required.
84
	 * </p>
85
	 * <p>
86
	 * In Java SE, SSL can be configured in one of several ways, which the 
87
	 * client will use in the following order:
88
	 * </p>
89
	 * <ul>
90
	 * 	<li><strong>Supplying an <code>SSLSocketFactory</code></strong> - applications can
91
	 * use {@link MqttConnectOptions#setSocketFactory(SocketFactory)} to supply 
92
	 * a factory with the appropriate SSL settings.</li>
93
	 * 	<li><strong>SSL Properties</strong> - applications can supply SSL settings as a 
94
	 * simple Java Properties using {@link MqttConnectOptions#setSSLProperties(Properties)}.</li>
95
	 * 	<li><strong>Use JVM settings</strong> - There are a number of standard 
96
	 * Java system properties that can be used to configure key and trust stores.</li>
97
	 * </ul>
98
	 * 
99
	 * <p>In Java ME, the platform settings are used for SSL connections.</p>
100
	 *  
101
	 * <p>A default instance of {@link MqttDefaultFilePersistence} is used by
102
	 * the client. To specify a different persistence implementation, or to turn
103
	 * off persistence, use the {@link #MqttClient(String, String, MqttClientPersistence)} constructor.
104
	 *  
105
	 * @param serverURI the address of the server to connect to, specified as a URI
106
	 * @param clientId a client identifier that is unique on the server being connected to
107
	 * @throws IllegalArgumentException if the URI does not start with
108
	 * "tcp://", "ssl://" or "local://".
109
	 * @throws IllegalArgumentException if the clientId is null or is greater than 23 characters in length
110
	 * @throws MqttException if any other problem was encountered 
111
	 */
112
	public MqttClient(String serverURI, String clientId) throws MqttException {
113
		this(serverURI,clientId, new MqttDefaultFilePersistence());
114
	}
115
	
116
	/**
117
	 * Create an MqttAsyncClient that can be used to communicate with an MQTT server.
118
	 * <p> 
119
	 * The address of the server should be a URI, using a scheme of either 
120
	 * "tcp://" for a TCP connection or "ssl://" for a TCP connection secured by SSL/TLS. 
121
	 * For example:
122
	 * <ul>
123
	 * 	<li><code>tcp://localhost:1883</code></li>
124
	 * 	<li><code>ssl://localhost:8883</code></li>
125
	 * </ul> 
126
	 * If the port is not specified, it will
127
	 * default to 1883 for "tcp://" URIs, and 8883 for "ssl://" URIs.
128
	 * </p>
129
	 * <p>
130
	 * A client identified to connect to an MQTT server, it 
131
	 * must be unique across all clients connecting to the same
132
	 * server. A convenience method is provided to generate a random client id that
133
	 * should satisfy this criteria - {@link #generateClientId()}. As the client identifier
134
	 * is used by the server to identify a client when it reconnects, the client must use the
135
	 * same identifier between connections if durable subscriptions are used and reliable 
136
	 * delivery of messages is required.
137
	 * </p>
138
	 * <p>
139
	 * In Java SE, SSL can be configured in one of several ways, which the 
140
	 * client will use in the following order:
141
	 * </p>
142
	 * <ul>
143
	 * 	<li><strong>Supplying an <code>SSLSocketFactory</code></strong> - applications can
144
	 * use {@link MqttConnectOptions#setSocketFactory(SocketFactory)} to supply 
145
	 * a factory with the appropriate SSL settings.</li>
146
	 * 	<li><strong>SSL Properties</strong> - applications can supply SSL settings as a 
147
	 * simple Java Properties using {@link MqttConnectOptions#setSSLProperties(Properties)}.</li>
148
	 * 	<li><strong>Use JVM settings</strong> - There are a number of standard 
149
	 * Java system properties that can be used to configure key and trust stores.</li>
150
	 * </ul>
151
	 * 
152
	 * <p>In Java ME, the platform settings are used for SSL connections.</p>
153
	 * <p> 
154
	 * The persistence mechanism is used to enable reliable messaging. 
155
	 * For qualities of server (QoS) 1 or 2 to work, messages must be persisted
156
	 * to disk by both the client and the server.  If this is not done, then
157
	 * a failure in the client or server can result in lost messages. A pluggable 
158
	 * persistence mechanism is supported via the {@link MqttClientPersistence} 
159
	 * interface. A implementer of this interface that safely stores messages
160
	 * must be specified in order for delivery of messages to be reliable. In
161
	 * addition {@link MqttConnectOptions#setCleanSession(boolean)} must be set
162
	 * to false. In the event that only QoS 0 messages are sent or received or 
163
	 * cleansession is set to true then a safe store is not needed. 
164
	 * </p>
165
	 * <p>An implementation of file-based persistence is provided in 
166
	 * class {@link MqttDefaultFilePersistence} which will work in all Java SE based 
167
	 * systems. If no persistence is needed, the persistence parameter 
168
	 * can be explicitly set to <code>null</code>.</p>
169
	 * 
170
	 * @param serverURI the address of the server to connect to, specified as a URI
171
	 * @param clientId a client identifier that is unique on the server being connected to
172
 	 * @param persistence the persistence mechanism to use.
173
	 * @throws IllegalArgumentException if the URI does not start with
174
	 * "tcp://", "ssl://" or "local://".
175
	 * @throws IllegalArgumentException if the clientId is null or is greater than 23 characters in length
176
	 * @throws MqttException if any other problem was encountered 
177
	 */
178
	public MqttClient(String serverURI, String clientId, MqttClientPersistence persistence) throws MqttException {
179
		aClient = new MqttAsyncClient(serverURI, clientId, persistence);
180
	}
181
182
	/*
183
	 * @see IMqttClient#connect()
184
	 */
185
	public void connect() throws MqttSecurityException, MqttException {
186
		this.connect(new MqttConnectOptions());
187
	}
188
	
189
	/*
190
	 * @see IMqttClient#connect(MqttConnectOptions)
191
	 */
192
	public void connect(MqttConnectOptions options) throws MqttSecurityException, MqttException {
193
		aClient.connect(options, null, null).waitForCompletion(getTimeToWait());
194
	}
195
196
	/*
197
	 * @see IMqttClient#disconnect()
198
	 */
199
	public void disconnect() throws MqttException {
200
		this.disconnect(30000);
201
	}
202
	
203
	/*
204
	 * @see IMqttClient#disconnect(long)
205
	 */
206
	public void disconnect(long quiesceTimeout) throws MqttException {
207
		aClient.disconnect(quiesceTimeout, null, null).waitForCompletion();
208
	}
209
210
	/* 
211
	 * @see IMqttClient#subscribe(String)
212
	 */
213
	public void subscribe(String topicFilter) throws MqttException {
214
		this.subscribe(new String[] {topicFilter}, new int[] {1});
215
	}
216
	
217
	/* 
218
	 * @see IMqttClient#subscribe(String[])
219
	 */
220
	public void subscribe(String[] topicFilters) throws MqttException {
221
		int[] qos = new int[topicFilters.length];
222
		for (int i=0; i<qos.length; i++) {
223
			qos[i] = 1; 
224
		}
225
		this.subscribe(topicFilters, qos);
226
	}
227
	
228
	/*
229
	 * @see IMqttClient#subscribe(String, int)
230
	 */
231
	public void subscribe(String topicFilter, int qos) throws MqttException {
232
		this.subscribe(new String[] {topicFilter}, new int[] {qos});
233
	}
234
235
	/*
236
	 * @see IMqttClient#subscribe(String[], int[])
237
	 */
238
	public void subscribe(String[] topicFilters, int[] qos) throws MqttException {
239
		aClient.subscribe(topicFilters, qos, null,null).waitForCompletion(getTimeToWait());
240
	}
241
242
	/*
243
	 * @see IMqttClient#unsubscribe(String)
244
	 */
245
	public void unsubscribe(String topicFilter) throws MqttException {
246
		unsubscribe(new String[] {topicFilter});
247
	}
248
	
249
	/*
250
	 * @see IMqttClient#unsubscribe(String[])
251
	 */
252
	public void unsubscribe(String[] topicFilters) throws MqttException {
253
		aClient.unsubscribe(topicFilters, null,null).waitForCompletion(getTimeToWait());
254
	}
255
	
256
	/*
257
	 * @see IMqttClient#publishBlock(String, byte[], int, boolean)
258
	 */
259
	public void publish(String topic, byte[] payload,int qos, boolean retained) throws MqttException,
260
			MqttPersistenceException {
261
		MqttMessage message = new MqttMessage(payload);
262
		message.setQos(qos);
263
		message.setRetained(retained);
264
		this.publish(topic, message);
265
	}
266
267
	/*
268
	 * @see IMqttClient#publishBlock(String, MqttMessage)
269
	 */
270
	public void publish(String topic, MqttMessage message) throws MqttException,
271
			MqttPersistenceException {
272
		aClient.publish(topic, message, null, null).waitForCompletion(getTimeToWait());
273
	}
274
	
275
	/**
276
	 * Set the maximum time to wait for an action to complete
277
	 * <p>Set the maximum time to wait for an action to complete before
278
	 * returning control to the invoking application. Control is returned
279
	 * when: 
280
	 * <ul>
281
	 * <li>the action completes 
282
	 * <li>or when the timeout if exceeded
283
	 * <li>or when the client is disconnect/shutdown
284
	 * <ul>
285
	 * The default value is -1 which means the action will not timeout.
286
	 * In the event of a timeout the action carries on running in the
287
	 * background until it completes. The timeout is used on methods that
288
	 * block while the action is in progress.
289
	 * </p>
290
	 * @param timeToWaitInMillis before the action times out. A value or 0 or -1 will wait until 
291
	 * the action finishes and not timeout. 
292
	 */
293
	public void setTimeToWait(long timeToWaitInMillis) throws IllegalArgumentException{
294
		if (timeToWait < -1) {
295
			throw new IllegalArgumentException();
296
		}
297
		this.timeToWait = timeToWaitInMillis;
298
	}
299
	
300
	/**
301
	 * Return the maximum time to wait for an action to complete. 
302
	 * @see MqttClient#setTimeToWait(long)
303
	 */
304
	public long getTimeToWait() {
305
		return this.timeToWait;
306
	}
307
308
	/* (non-Javadoc)
309
	 * @see org.eclipse.paho.client.mqttv3.IMqttClient#close()
310
	 */
311
	public void close() throws MqttException {
312
		aClient.close();
313
	}
314
315
	/* (non-Javadoc)
316
	 * @see org.eclipse.paho.client.mqttv3.IMqttClient#getClientId()
317
	 */
318
	public String getClientId() {
319
		return aClient.getClientId();
320
	}
321
322
	/* (non-Javadoc)
323
	 * @see org.eclipse.paho.client.mqttv3.IMqttClient#getPendingDeliveryTokens()
324
	 */
325
	public IMqttDeliveryToken[] getPendingDeliveryTokens() {
326
		return aClient.getPendingDeliveryTokens();
327
	}
328
329
	/* (non-Javadoc)
330
	 * @see org.eclipse.paho.client.mqttv3.IMqttClient#getServerURI()
331
	 */
332
	public String getServerURI() {
333
		return aClient.getServerURI();
334
	}
335
336
	/* (non-Javadoc)
337
	 * @see org.eclipse.paho.client.mqttv3.IMqttClient#getTopic(java.lang.String)
338
	 */
339
	public MqttTopic getTopic(String topic) {
340
		return aClient.getTopic(topic);
341
	}
342
343
	/* (non-Javadoc)
344
	 * @see org.eclipse.paho.client.mqttv3.IMqttClient#isConnected()
345
	 */
346
	public boolean isConnected() {
347
		return aClient.isConnected();
348
	}
349
350
	/* (non-Javadoc)
351
	 * @see org.eclipse.paho.client.mqttv3.IMqttClient#setCallback(org.eclipse.paho.client.mqttv3.MqttCallback)
352
	 */
353
	public void setCallback(MqttCallback callback) {
354
		aClient.setCallback(callback);
355
	}
356
	
357
	/**
358
	 * Returns a randomly generated client identifier based on the current user's login
359
	 * name and the system time.
360
	 * <p>When cleanSession is set to false, an application must ensure it uses the 
361
	 * same client identifier when it reconnects to the server to resume state and maintain
362
	 * assured message delivery.</p>
363
	 * @return a generated client identifier
364
	 * @see MqttConnectOptions#setCleanSession(boolean)
365
	 */
366
	public static String generateClientId() {
367
		return MqttAsyncClient.generateClientId();
368
	}
369
	
370
	/**
371
	 * Return a debug object that can be used to help solve problems.
372
	 */
373
	public Debug getDebug() {
374
		return (aClient.getDebug());
375
	}
376
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttClientPersistence.java (+92 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3;
13
14
import java.util.Enumeration;
15
16
/**
17
 * Represents a persistent data store, used to store outbound and inbound messages while they
18
 * are in flight, enabling delivery to the QOS specified. You can specify an implementation
19
 * of this interface using {@link MqttClient#MqttClient(String, String, MqttClientPersistence)},
20
 * which the {@link MqttClient} will use to persist QoS 1 and 2 messages.
21
 * <p>
22
 * If the methods defined throw the MqttPersistenceException then the state of the data persisted
23
 * should remain as prior to the method being called. For example, if {@link #put(String, MqttPersistable)}
24
 * throws an exception at any point then the data will be assumed to not be in the persistent store.
25
 * Similarly if {@link #remove(String)} throws an exception then the data will be
26
 * assumed to still be held in the persistent store.</p>
27
 * <p>
28
 * It is up to the persistence interface to log any exceptions or error information 
29
 * which may be required when diagnosing a persistence failure.</p>
30
 */
31
public interface MqttClientPersistence {
32
	/**
33
	 * Initialise the persistent store.
34
	 * If a persistent store exists for this client ID then open it, otherwise 
35
	 * create a new one. If the persistent store is already open then just return.
36
	 * An application may use the same client ID to connect to many different 
37
	 * servers, so the client ID in conjunction with the
38
	 * connection will uniquely identify the persistence store required.
39
	 * 
40
	 * @param clientId The client for which the persistent store should be opened.
41
	 * @param serverURI The connection string as specified when the MQTT client instance was created.
42
	 * @throws MqttPersistenceException if there was a problem opening the persistent store.
43
	 */
44
	public void open(String clientId, String serverURI) throws MqttPersistenceException;
45
46
	/**
47
	 * Close the persistent store that was previously opened.
48
	 * This will be called when a client application disconnects from the broker.
49
	 * @throws MqttPersistenceException 
50
	 */	
51
	public void close() throws MqttPersistenceException;
52
53
	/**
54
	 * Puts the specified data into the persistent store.
55
	 * @param key the key for the data, which will be used later to retrieve it.
56
	 * @param persistable the data to persist
57
	 * @throws MqttPersistenceException if there was a problem putting the data
58
	 * into the persistent store.
59
	 */
60
	public void put(String key, MqttPersistable persistable) throws MqttPersistenceException;
61
	
62
	/**
63
	 * Gets the specified data out of the persistent store.
64
	 * @param key the key for the data, which was used when originally saving it.
65
	 * @return the un-persisted data
66
	 * @throws MqttPersistenceException if there was a problem getting the data
67
	 * from the persistent store.
68
	 */
69
	public MqttPersistable get(String key) throws MqttPersistenceException;
70
	
71
	/**
72
	 * Remove the data for the specified key.
73
	 */
74
	public void remove(String key) throws MqttPersistenceException;
75
76
	/**
77
	 * Returns an Enumeration over the keys in this persistent data store.
78
	 * @return an enumeration of {@link String} objects.
79
	 */
80
	public Enumeration keys() throws MqttPersistenceException;
81
	
82
	/**
83
	 * Clears persistence, so that it no longer contains any persisted data.
84
	 */
85
	public void clear() throws MqttPersistenceException;
86
	
87
	/**
88
	 * Returns whether or not data is persisted using the specified key.
89
	 * @param key the key for data, which was used when originally saving it.
90
	 */
91
	public boolean containsKey(String key) throws MqttPersistenceException;
92
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttConnectOptions.java (+366 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3;
13
14
import java.util.Properties;
15
16
import javax.net.SocketFactory;
17
18
import org.eclipse.paho.client.mqttv3.util.Debug;
19
20
/**
21
 * Holds the set of options that control how the client connects to a server.
22
 */
23
public class MqttConnectOptions {
24
	/**
25
	 * The default keep alive interval in seconds if one is not specified
26
	 */
27
	public static final int KEEP_ALIVE_INTERVAL_DEFAULT = 60;
28
	/**
29
	 * The default connection timeout in seconds if one is not specified
30
	 */
31
	public static final int CONNECTION_TIMEOUT_DEFAULT = 30;
32
	/**
33
	 * The default clean session setting if one is not specified
34
	 */
35
	public static final boolean CLEAN_SESSION_DEFAULT = true;
36
	
37
	private int keepAliveInterval = KEEP_ALIVE_INTERVAL_DEFAULT;
38
	private String willDestination = null;
39
	private MqttMessage willMessage = null;
40
	private String userName;
41
	private char[] password;
42
	private SocketFactory socketFactory;
43
	private Properties sslClientProps = null;
44
	private boolean cleanSession = CLEAN_SESSION_DEFAULT;
45
	private int connectionTimeout = CONNECTION_TIMEOUT_DEFAULT;
46
	
47
	/**
48
	 * Constructs a new <code>MqttConnectOptions</code> object using the 
49
	 * default values.
50
	 * 
51
	 * The defaults are:
52
	 * <ul>
53
	 * <li>The keepalive interval is 60 seconds</li>
54
	 * <li>Clean Session is true</li>
55
	 * <li>The message delivery retry interval is 15 seconds</li>
56
	 * <li>The connection timeout period is 30 seconds</li> 
57
	 * <li>No Will message is set</li>
58
	 * <li>A standard SocketFactory is used</li>
59
	 * </ul>
60
	 * More information about these values can be found in the setter methods. 
61
	 */
62
	public MqttConnectOptions() {
63
	}
64
	
65
	/**
66
	 * Returns the password to use for the connection.
67
	 * @return the password to use for the connection.
68
	 */
69
	public char[] getPassword() {
70
		return password;
71
	}
72
73
	/**
74
	 * Sets the password to use for the connection.
75
	 */
76
	public void setPassword(char[] password) {
77
		this.password = password;
78
	}
79
80
	/**
81
	 * Returns the user name to use for the connection.
82
	 * @return the user name to use for the connection.
83
	 */
84
	public String getUserName() {
85
		return userName;
86
	}
87
88
	/**
89
	 * Sets the user name to use for the connection.
90
	 * @throws IllegalArgumentException if the user name is blank or only
91
	 * contains whitespace characters.
92
	 */
93
	public void setUserName(String userName) {
94
		if ((userName != null) && (userName.trim().equals(""))) {
95
			throw new IllegalArgumentException();
96
		}
97
		this.userName = userName;
98
	}
99
	
100
	/**
101
	 * Sets the "Last Will and Testament" (LWT) for the connection.
102
	 * In the event that this client unexpectedly loses its connection to the 
103
	 * server, the server will publish a message to itself using the supplied
104
	 * details.
105
	 * 
106
	 * @param topic the topic to publish to.
107
	 * @param payload the byte payload for the message.
108
	 * @param qos the quality of service to publish the message at (0, 1 or 2).
109
	 * @param retained whether or not the message should be retained.
110
	 */
111
	public void setWill(MqttTopic topic, byte[] payload, int qos, boolean retained) {
112
		String topicS = topic.getName();
113
		validateWill(topicS, payload);
114
		this.setWill(topicS, new MqttMessage(payload), qos, retained);
115
	}
116
	
117
	/**
118
	 * Sets the "Last Will and Testament" (LWT) for the connection.
119
	 * In the event that this client unexpectedly loses its connection to the 
120
	 * server, the server will publish a message to itself using the supplied
121
	 * details.
122
	 * 
123
	 * @param topic the topic to publish to.
124
	 * @param payload the byte payload for the message.
125
	 * @param qos the quality of service to publish the message at (0, 1 or 2).
126
	 * @param retained whether or not the message should be retained.
127
	 */
128
	public void setWill(String topic, byte[] payload, int qos, boolean retained) {
129
		validateWill(topic, payload);
130
		this.setWill(topic, new MqttMessage(payload), qos, retained);
131
	}
132
	
133
	
134
	/**
135
	 * Validates the will fields.
136
	 */
137
	private void validateWill(String dest, Object payload) {
138
		if ((dest == null) || (payload == null)) {
139
			throw new IllegalArgumentException();
140
		}
141
		MqttAsyncClient.validateTopic(dest);
142
	}
143
144
	/**
145
	 * Sets up the will information, based on the supplied parameters.
146
	 */
147
	protected void setWill(String topic, MqttMessage msg, int qos, boolean retained) {
148
		willDestination = topic;
149
		willMessage = msg;
150
		willMessage.setQos(qos);
151
		willMessage.setRetained(retained);
152
		// Prevent any more changes to the will message
153
		willMessage.setMutable(false);
154
	}
155
	
156
	/**
157
	 * Returns the "keep alive" interval.
158
	 * @see #setKeepAliveInterval(int)
159
	 * @return the keep alive interval.
160
	 */
161
	public int getKeepAliveInterval() {
162
		return keepAliveInterval;
163
	}
164
165
	/**
166
	 * Sets the "keep alive" interval.
167
	 * This value, measured in seconds, defines the maximum time interval 
168
	 * between messages sent or received. It enables the client to 
169
	 * detect if the server is no longer available, without 
170
	 * having to wait for the TCP/IP timeout. The client will ensure
171
	 * that at least one message travels across the network within each
172
	 * keep alive period.  In the absence of a data-related message during 
173
	 * the time period, the client sends a very small "ping" message, which
174
	 * the server will acknowledge.
175
	 * A value of 0 disables keepalive processing in the client. 
176
	 * <p>The default value is 60 seconds</p>
177
	 * 
178
	 * @param keepAliveInterval the interval, measured in seconds, must be >= 0.
179
	 */
180
	public void setKeepAliveInterval(int keepAliveInterval)throws IllegalArgumentException {
181
		if (keepAliveInterval <0 ) {
182
			throw new IllegalArgumentException();
183
		}
184
		this.keepAliveInterval = keepAliveInterval;
185
	}
186
	
187
	/**
188
	 * Returns the connection timeout value.
189
	 * @see #setConnectionTimeout(int)
190
	 * @return the connection timeout value.
191
	 */
192
	public int getConnectionTimeout() {
193
		return connectionTimeout;
194
	}
195
	
196
	/**
197
	 * Sets the connection timeout value.
198
	 * This value, measured in seconds, defines the maximum time interval
199
	 * the client will wait for the network connection to the MQTT server to be established. 
200
	 * The default timeout is 30 seconds.
201
	 * @param connectionTimeout the timeout value, measured in seconds.
202
	 */
203
	public void setConnectionTimeout(int connectionTimeout) {
204
		this.connectionTimeout = connectionTimeout;
205
	}
206
	
207
	/**
208
	 * Returns the socket factory that will be used when connecting, or
209
	 * <code>null</code> if one has not been set.
210
	 */
211
	public SocketFactory getSocketFactory() {
212
		return socketFactory;
213
	}
214
	
215
	/**
216
	 * Sets the <code>SocketFactory</code> to use.  This allows an application
217
	 * to apply its own policies around the creation of network sockets.  If
218
	 * using an SSL connection, an <code>SSLSocketFactory</code> can be used
219
	 * to supply application-specific security settings.
220
	 * @param socketFactory the factory to use.
221
	 */
222
	public void setSocketFactory(SocketFactory socketFactory) {
223
		this.socketFactory = socketFactory;
224
	}
225
226
	/**
227
	 * Returns the topic to be used for last will and testament (LWT).
228
	 * @return the MqttTopic to use, or <code>null</code> if LWT is not set.
229
	 * @see #setWill(MqttTopic, byte[], int, boolean)
230
	 */
231
	public String getWillDestination() {
232
		return willDestination;
233
	}
234
235
	/**
236
	 * Returns the message to be sent as last will and testament (LWT).
237
	 * The returned object is "read only".  Calling any "setter" methods on 
238
	 * the returned object will result in an 
239
	 * <code>IllegalStateException</code> being thrown.
240
	 * @return the message to use, or <code>null</code> if LWT is not set.
241
	 */
242
	public MqttMessage getWillMessage() {
243
		return willMessage;
244
	}
245
	
246
	/**
247
	 * Returns the SSL properties for the connection.
248
	 * @return the properties for the SSL connection
249
	 */
250
	public Properties getSSLProperties() {
251
		return sslClientProps;
252
	}
253
254
	/**
255
	 * Sets the SSL properties for the connection.  Note that these
256
	 * properties are only valid if an implementation of the Java
257
	 * Secure Socket Extensions (JSSE) is available.  These properties are
258
	 * <em>not</em> used if a SocketFactory has been set using
259
	 * {@link #setSocketFactory(SocketFactory)}.
260
	 * The following properties can be used:</p>
261
	 * <dl>
262
	 * <dt>com.ibm.ssl.protocol</dt>
263
   	 * <dd>One of: SSL, SSLv3, TLS, TLSv1, SSL_TLS.</dd>
264
	 * <dt>com.ibm.ssl.contextProvider
265
   	 * <dd>Underlying JSSE provider.  For example "IBMJSSE2" or "SunJSSE"</dd>
266
	 * 
267
	 * <dt>com.ibm.ssl.keyStore</dt>
268
   	 * <dd>The name of the file that contains the KeyStore object that you 
269
   	 * want the KeyManager to use. For example /mydir/etc/key.p12</dd>
270
	 * 
271
	 * <dt>com.ibm.ssl.keyStorePassword</dt>
272
   	 * <dd>The password for the KeyStore object that you want the KeyManager to use.  
273
   	 * The password can either be in plain-text, 
274
   	 * or may be obfuscated using the static method:
275
     * <code>com.ibm.micro.security.Password.obfuscate(char[] password)</code>.
276
   	 * This obfuscates the password using a simple and insecure XOR and Base64 
277
   	 * encoding mechanism. Note that this is only a simple scrambler to 
278
   	 * obfuscate clear-text passwords.</dd>
279
	 * 
280
	 * <dt>com.ibm.ssl.keyStoreType</dt>
281
   	 * <dd>Type of key store, for example "PKCS12", "JKS", or "JCEKS".</dd>
282
	 * 
283
	 * <dt>com.ibm.ssl.keyStoreProvider</dt>
284
   	 * <dd>Key store provider, for example "IBMJCE" or "IBMJCEFIPS".</dd>
285
	 * 
286
	 * <dt>com.ibm.ssl.trustStore</dt>
287
   	 * <dd>The name of the file that contains the KeyStore object that you 
288
   	 * want the TrustManager to use.</dd> 
289
	 * 
290
	 * <dt>com.ibm.ssl.trustStorePassword</dt>
291
   	 * <dd>The password for the TrustStore object that you want the 
292
   	 * TrustManager to use.  The password can either be in plain-text, 
293
   	 * or may be obfuscated using the static method:
294
     * <code>com.ibm.micro.security.Password.obfuscate(char[] password)</code>.
295
   	 * This obfuscates the password using a simple and insecure XOR and Base64 
296
   	 * encoding mechanism. Note that this is only a simple scrambler to 
297
   	 * obfuscate clear-text passwords.</dd>
298
	 * 
299
	 * <dt>com.ibm.ssl.trustStoreType</dt>
300
   	 * <dd>The type of KeyStore object that you want the default TrustManager to use.  
301
   	 * Same possible values as "keyStoreType".</dd>
302
	 * 
303
	 * <dt>com.ibm.ssl.trustStoreProvider</dt>
304
   	 * <dd>Trust store provider, for example "IBMJCE" or "IBMJCEFIPS".</dd>
305
	 * 
306
	 * <dt>com.ibm.ssl.enabledCipherSuites</dt>
307
	 * <dd>A list of which ciphers are enabled.  Values are dependent on the provider, 
308
	 * for example: SSL_RSA_WITH_AES_128_CBC_SHA;SSL_RSA_WITH_3DES_EDE_CBC_SHA.</dd>
309
	 * 
310
	 * <dt>com.ibm.ssl.keyManager</dt>
311
	 * <dd>Sets the algorithm that will be used to instantiate a KeyManagerFactory object 
312
	 * instead of using the default algorithm available in the platform. Example values: 
313
	 * "IbmX509" or "IBMJ9X509".
314
	 * </dd>
315
	 * 
316
	 * <dt>com.ibm.ssl.trustManager</dt>
317
	 * <dd>Sets the algorithm that will be used to instantiate a TrustManagerFactory object 
318
	 * instead of using the default algorithm available in the platform. Example values: 
319
	 * "PKIX" or "IBMJ9X509".
320
	 * </dd>
321
	 * </dl>
322
	 */
323
	public void setSSLProperties(Properties props) {
324
		this.sslClientProps = props;
325
	}
326
	
327
	/**
328
	 * Returns whether the server should remember state for the client across reconnects.
329
	 * @return the clean session flag
330
	 */
331
	public boolean isCleanSession() {
332
		return this.cleanSession;
333
	}
334
	
335
	/**
336
	 * Sets whether the server should remember state for the client across reconnects.
337
	 * This includes subscriptions and the state of any in-flight messages.
338
 	 */
339
	public void setCleanSession(boolean cleanSession) {
340
		this.cleanSession = cleanSession;
341
	}
342
	
343
	public Properties getDebug() {
344
		Properties p = new Properties();
345
		p.put("CleanSession", new Boolean(isCleanSession()));
346
		p.put("ConTimeout", new Integer(getConnectionTimeout()));
347
		p.put("KeepAliveInterval", new Integer(getKeepAliveInterval()));
348
		p.put("UserName", (getUserName()==null)?"null":getUserName());
349
		p.put("WillDestination", (getWillDestination()==null)?"null":getWillDestination());
350
		if (getSocketFactory()==null) {
351
			p.put("SocketFactory", "null");	
352
		} else {
353
			p.put("SocketFactory", getSocketFactory());
354
		}
355
		if (getSSLProperties()==null) {
356
			p.put("SSLProperties", "null");
357
		} else {
358
			p.put("SSLProperties", getSSLProperties());
359
		}
360
		return p;
361
	}
362
	
363
	public String toString() {
364
		return Debug.dumpProperties(getDebug(), "Connection options");
365
	}
366
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttDeliveryToken.java (+49 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3;
13
14
/**
15
 * Provides a mechanism to track the delivery progress of a message.
16
 * 
17
 * <p>
18
 * Used to track the the delivery progress of a message when a publish is 
19
 * executed in a non-blocking manner (run in the background)</p>
20
 *  
21
 * @see MqttToken
22
 */
23
public class MqttDeliveryToken extends MqttToken implements IMqttDeliveryToken {
24
		
25
	
26
	public MqttDeliveryToken() {
27
		super();
28
	}
29
	
30
	public MqttDeliveryToken(String logContext) {
31
		super(logContext);
32
	}
33
34
	/**
35
	 * Returns the message associated with this token.
36
	 * <p>Until the message has been delivered, the message being delivered will
37
	 * be returned. Once the message has been delivered <code>null</code> will be 
38
	 * returned.
39
	 * @return the message associated with this token or null if already delivered.
40
	 * @throws MqttException if there was a problem completing retrieving the message
41
	 */
42
	public MqttMessage getMessage() throws MqttException {
43
		return internalTok.getMessage();
44
	}
45
	
46
	protected void setMessage(MqttMessage msg) {
47
		internalTok.setMessage(msg);
48
	}
49
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttException.java (+214 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3;
13
14
import org.eclipse.paho.client.mqttv3.internal.MessageCatalog;
15
16
/**
17
 * Thrown if an error occurs communicating with the server.
18
 */
19
public class MqttException extends Exception {
20
	private static final long serialVersionUID = 300L;
21
	
22
	/** 
23
	 * Client encountered an exception.  Use the {@link #getCause()}
24
	 * method to get the underlying reason.
25
	 */
26
	public static final short REASON_CODE_CLIENT_EXCEPTION              = 0x00;
27
28
	// CONNACK return codes
29
	/** The protocol version requested is not supported by the server. */
30
	public static final short REASON_CODE_INVALID_PROTOCOL_VERSION		= 0x01;
31
	/** The server has rejected the supplied client ID */
32
	public static final short REASON_CODE_INVALID_CLIENT_ID      		= 0x02;
33
	/** The broker was not available to handle the request. */
34
	public static final short REASON_CODE_BROKER_UNAVAILABLE             = 0x03;
35
	/** Authentication with the server has failed, due to a bad user name or password. */
36
	public static final short REASON_CODE_FAILED_AUTHENTICATION			= 0x04; 
37
	/** Not authorized to perform the requested operation */
38
	public static final short REASON_CODE_NOT_AUTHORIZED				= 0x05;
39
40
	/** An unexpected error has occurred. */
41
	public static final short REASON_CODE_UNEXPECTED_ERROR				= 0x06;
42
	
43
	/** 
44
	 * Client timed out while waiting for a response from the server.
45
	 * The server is no longer responding to keep-alive messages.
46
	 */
47
	public static final short REASON_CODE_CLIENT_TIMEOUT                = 32000;
48
	/**
49
	 * Internal error, caused by no new message IDs being available.
50
	 */
51
	public static final short REASON_CODE_NO_MESSAGE_IDS_AVAILABLE      = 32001;
52
	
53
	/**
54
	 * The client is already connected.
55
	 */
56
	public static final short REASON_CODE_CLIENT_CONNECTED      = 32100;
57
	/**
58
	 * The client is already disconnected.
59
	 */
60
	public static final short REASON_CODE_CLIENT_ALREADY_DISCONNECTED   = 32101;
61
	/** 
62
	 * The client is currently disconnecting and cannot accept any new work.
63
	 * This can occur when waiting on a token, and then disconnecting the client.  
64
	 * If the message delivery does not complete within the quiesce timeout 
65
	 * period, then the waiting token will be notified with an exception.
66
	 */
67
	public static final short REASON_CODE_CLIENT_DISCONNECTING          = 32102;
68
	
69
	/** Unable to connect to server */
70
	public static final short REASON_CODE_SERVER_CONNECT_ERROR          = 32103;
71
72
	/** 
73
	 * The client is not connected to the server.  The {@link MqttClient#connect()}
74
	 * or {@link MqttClient#connect(MqttConnectOptions)} method must be called
75
	 * first.  It is also possible that the connection was lost - see 
76
	 * {@link MqttClient#setCallback(MqttCallback)} for a way to track lost
77
	 * connections.  
78
	 */
79
	public static final short REASON_CODE_CLIENT_NOT_CONNECTED          = 32104;
80
81
	/** 
82
	 * Server URI and supplied <code>SocketFactory</code> do not match.
83
	 * URIs beginning <code>tcp://</code> must use a <code>javax.net.SocketFactory</code>,
84
	 * and URIs beginning <code>ssl://</code> must use a <code>javax.net.ssl.SSLSocketFactory</code>.
85
	 */
86
	public static final short REASON_CODE_SOCKET_FACTORY_MISMATCH       = 32105;
87
	
88
	/**
89
	 * SSL configuration error.
90
	 */
91
	public static final short REASON_CODE_SSL_CONFIG_ERROR              = 32106;
92
93
	/** 
94
	 * Thrown when an attempt to call {@link MqttClient#disconnect()} has been 
95
	 * made from within a method on {@link MqttCallback}.  These methods are invoked
96
	 * by the client's thread, and must not be used to control disconnection.
97
	 * 
98
	 * @see MqttCallback#messageArrived(String, MqttMessage)
99
	 */
100
	public static final short REASON_CODE_CLIENT_DISCONNECT_PROHIBITED  = 32107;
101
102
	/** 
103
	 * Protocol error: the message was not recognized as a valid MQTT packet.
104
	 * Possible reasons for this include connecting to a non-MQTT server, or
105
	 * connecting to an SSL server port when the client isn't using SSL.
106
	 */
107
	public static final short REASON_CODE_INVALID_MESSAGE				= 32108;
108
109
	/**
110
	 * The client has been unexpectedly disconnected from the server. The {@link #getCause() cause}
111
	 * will provide more details. 
112
	 */
113
	public static final short REASON_CODE_CONNECTION_LOST               = 32109;
114
	
115
	/**
116
	 * A connect operation in already in progress, only one connect can happen
117
	 * at a time.
118
	 */
119
	public static final short REASON_CODE_CONNECT_IN_PROGRESS           = 32110;
120
	
121
	/**
122
	 * The client is closed - no operations are permitted on the client in this
123
	 * state.  New up a new client to continue.
124
	 */
125
	public static final short REASON_CODE_CLIENT_CLOSED		           = 32111;
126
	
127
	/**
128
	 * A request has been made to use a token that is already associated with
129
	 * another action.  If the action is complete the reset() can ve called on the
130
	 * token to allow it to be reused.  
131
	 */
132
	public static final short REASON_CODE_TOKEN_INUSE		           = 32201;
133
	
134
	/**
135
	 * A request has been made to send a message but the maximum number of inflight 
136
	 * messages has already been reached. Once one or more messages have been moved
137
	 * then new messages can be sent.   
138
	 */
139
	public static final short REASON_CODE_MAX_INFLIGHT    			= 32202;
140
141
	private int reasonCode;
142
	private Throwable cause;
143
	
144
	/**
145
	 * Constructs a new <code>MqttException</code> with the specified code
146
	 * as the underlying reason.
147
	 * @param reasonCode the reason code for the exception.
148
	 */
149
	public MqttException(int reasonCode) {
150
		super();
151
		this.reasonCode = reasonCode;
152
	}
153
	
154
	/**
155
	 * Constructs a new <code>MqttException</code> with the specified 
156
	 * <code>Throwable</code> as the underlying reason.
157
	 * @param cause the underlying cause of the exception.
158
	 */
159
	public MqttException(Throwable cause) {
160
		super();
161
		this.reasonCode = REASON_CODE_CLIENT_EXCEPTION;
162
		this.cause = cause;
163
	}
164
165
	/**
166
	 * Constructs a new <code>MqttException</code> with the specified 
167
	 * <code>Throwable</code> as the underlying reason.
168
	 * @param reason the reason code for the exception.
169
	 * @param cause the underlying cause of the exception.
170
	 */
171
	public MqttException(int reason, Throwable cause) {
172
		super();
173
		this.reasonCode = reason;
174
		this.cause = cause;
175
	}
176
177
	
178
	/**
179
	 * Returns the reason code for this exception.
180
	 * @return the code representing the reason for this exception.
181
	 */
182
	public int getReasonCode() {
183
		return reasonCode;
184
	}
185
	
186
	/**
187
	 * Returns the underlying cause of this exception, if available.
188
	 * @return the Throwable that was the root cause of this exception,
189
	 * which may be <code>null</code>.
190
	 */
191
	public Throwable getCause() {
192
		return cause;
193
	}
194
	
195
	/**
196
	 * Returns the detail message for this exception.
197
	 * @return the detail message, which may be <code>null</code>.
198
	 */
199
	public String getMessage() {
200
		return MessageCatalog.getMessage(reasonCode);
201
	}
202
	
203
	/**
204
	 * Returns a <code>String</code> representation of this exception.
205
	 * @return a <code>String</code> representation of this exception.
206
	 */
207
	public String toString() {
208
		String result = getMessage() + " (" + reasonCode + ")";
209
		if (cause != null) {
210
			result = result + " - " + cause.toString();
211
		}
212
		return result;
213
	}
214
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttMessage.java (+215 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3;
13
14
/**
15
 * An MQTT message holds the application payload and options 
16
 * specifying how the message is to be delivered  
17
 * The message includes a "payload" (the body of the message)
18
 * represented as a byte[].
19
 */
20
public class MqttMessage {
21
	
22
	private boolean mutable = true;
23
	private byte[] payload;
24
	private int qos = 1;
25
	private boolean retained = false;
26
	private boolean dup = false;
27
28
	/**
29
	 * Utility method to validate the supplied QoS value.
30
	 * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2.
31
	 */
32
	public static void validateQos(int qos) {
33
		if ((qos < 0) || (qos > 2)) {
34
			throw new IllegalArgumentException();
35
		}
36
	}
37
	
38
	/**
39
	 * Constructs a message with an empty payload, and all other values
40
	 * set to defaults.
41
	 * 
42
	 * The defaults are:
43
	 * <ul>
44
	 *    <li>Message QoS set to 1</li>
45
	 *    <li>Message will not be "retained" by the server</li>
46
	 * </ul>
47
	 */
48
	public MqttMessage() {
49
		setPayload(new byte[]{});
50
	}
51
52
	/**
53
	 * Constructs a message with the specified byte array as a payload,
54
	 * and all other values set to defaults.
55
	 */
56
	public MqttMessage(byte[] payload) {
57
		setPayload(payload);
58
	}
59
	
60
	/**
61
	 * Returns the payload as a byte array.
62
	 * 
63
	 * @return the payload as a byte array.
64
	 */
65
	public byte[] getPayload() {
66
		return payload;
67
	}
68
	
69
	/**
70
	 * Clears the payload, resetting it to be empty.
71
	 * @throws IllegalStateException if this message cannot be edited
72
	 */
73
	public void clearPayload() {
74
		checkMutable();
75
		this.payload = new byte[] {};
76
	}
77
	
78
	/**
79
	 * Sets the payload of this message to be the specified byte array.
80
	 * 
81
	 * @param payload the payload for this message.
82
	 * @throws IllegalStateException if this message cannot be edited
83
	 * @throws NullPointerException if no payload is provided
84
	 */
85
	public void setPayload(byte[] payload) {
86
		checkMutable();
87
		if (payload == null) {
88
			throw new NullPointerException();
89
		}
90
		this.payload = payload;
91
	}
92
	
93
	/**
94
	 * Returns whether or not this message should be/was retained by the server.
95
	 * For messages received from the server, this method returns whether or not
96
	 * the message was from a current publisher, or was "retained" by the server as
97
	 * the last message published on the topic. 
98
	 * 
99
	 * @return <code>true</code> if the message should be, or was, retained by
100
	 * the server.
101
	 * @see #setRetained(boolean)
102
	 */
103
	public boolean isRetained() {
104
		return retained;
105
	}
106
107
	/**
108
	 * Whether or not the publish message should be retained by the messaging engine.
109
	 * Sending a message with the retained set to <code>false</code> will clear the
110
	 * retained message from the server.  The default value is <code>false</code>
111
	 * 
112
	 * @param retained whether or not the messaging engine should retain the message.
113
	 * @throws IllegalStateException if this message cannot be edited
114
	 */
115
	public void setRetained(boolean retained) {
116
		checkMutable();
117
		this.retained = retained;
118
	}
119
120
	/**
121
	 * Returns the quality of service for this message.
122
	 * @return the quality of service to use, either 0, 1, or 2.
123
	 * @see #setQos(int)
124
	 */
125
	public int getQos() {
126
		return qos;
127
	}
128
129
	/**
130
	 * Sets the quality of service for this message.
131
	 * <ul>
132
	 * <li>Quality of service 0 - indicates that a message should 
133
	 * be delivered at most once (zero or one times).  The message will not be persisted to disk,
134
	 * and will not be acknowledged across the network.  This QoS is the fastest,
135
	 * but should only be used for messages which are not valuable - note that  
136
	 * if the server cannot process the message (for example, there
137
	 * is an authorization problem), then an 
138
	 * {@link MqttCallback#deliveryComplete(IMqttDeliveryToken)}. 
139
	 * Also known as "fire and forget".</li>
140
	 * 
141
	 * <li>Quality of service 1 - indicates that a message should 
142
	 * be delivered at least once (one or more times).  The message can only be delivered safely if
143
	 * it can be persisted, so the application must supply a means of
144
	 * persistence using <code>MqttConnectOptions</code>.
145
	 * If a persistence mechanism is not specified, the message will not be 
146
	 * delivered in the event of a client failure.
147
	 * The message will be acknowledged across the network.  
148
	 * This is the default QoS.</li>
149
	 * 
150
	 * <li>Quality of service 2 - indicates that a message should
151
	 * be delivered once.  The message will be persisted to disk, and will
152
	 * be subject to a two-phase acknowledgement across the network.
153
	 * The message can only be delivered safely if
154
	 * it can be persisted, so the application must supply a means of
155
	 * persistence using <code>MqttConnectOptions</code>.
156
	 * If a persistence mechanism is not specified, the message will not be 
157
	 * delivered in the event of a client failure.</li>
158
	 * 
159
	 * If persistence is not configured, QOS 1 and 2 messages will still be delivered
160
	 * in the event of a network or server problem as the client will hold state in memory.
161
	 * If the MQTT client is shutdown or fails and persistence is not configured then 
162
	 * delivery of QOS 1 and 2 messages can not be maintained as client side state will 
163
	 * be lost. 
164
	 * 
165
	 * @param qos the "quality of service" to use.  Set to 0, 1, 2.
166
	 * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2.
167
	 * @throws IllegalStateException if this message cannot be edited
168
	 */
169
	public void setQos(int qos) {
170
		checkMutable();
171
		validateQos(qos);
172
		this.qos = qos;
173
	}
174
175
	/**
176
	 * Returns a string representation of this messages payload.
177
	 * Makes an attempt to return the payload as a string. As the
178
	 * MQTT client has no control over the content of the payload 
179
	 * it may fail. 
180
	 * @return a string representation of this message.
181
	 */
182
	public String toString() {
183
		return new String(payload);
184
	}
185
	
186
	/**
187
	 * Sets the mutability of this object (whether or not its values can be
188
	 * changed.
189
	 * @param mutable <code>true</code> if the values can be changed,
190
	 * <code>false</code> to prevent them from being changed.
191
	 */
192
	protected void setMutable(boolean mutable) {
193
		this.mutable = mutable;
194
	}
195
	
196
	protected void checkMutable() throws IllegalStateException {
197
		if (!mutable) {
198
			throw new IllegalStateException();
199
		}
200
	}
201
	
202
	protected void setDuplicate(boolean dup) {
203
		this.dup = dup;
204
	}
205
	
206
	/**
207
	 * Returns whether or not this message might be a duplicate of one which has
208
	 * already been received.  This will only be set on messages received from
209
	 * the server.
210
	 * @return <code>true</code> if the message might be a duplicate.
211
	 */
212
	public boolean isDuplicate() {
213
		return this.dup;
214
	}
215
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttPersistable.java (+93 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3;
13
14
/**
15
 * Represents an object used to pass data to be persisted across the
16
 * {@link org.eclipse.paho.client.mqttv3.MqttClientPersistence MqttClientPersistence}
17
 * interface.
18
 * <p>
19
 * When data is passed across the interface the header and payload are 
20
 * separated, so that unnecessary message copies may be avoided.
21
 * For example, if a 10 MB payload was published it would be inefficient
22
 * to create a byte array a few bytes larger than 10 MB and copy the
23
 * MQTT message header and payload into a contiguous byte array.</p>
24
 * <p>
25
 * When the request to persist data is made a separate byte array and offset
26
 * is passed for the header and payload. Only the data between
27
 * offset and length need be persisted.
28
 * So for example, a message to be persisted consists of a header byte
29
 * array starting at offset 1 and length 4, plus a payload byte array
30
 * starting at offset 30 and length 40000. There are three ways in which
31
 * the persistence implementation may return data to the client on
32
 * recovery:
33
 * <ul>
34
 * <li>It could return the data as it was passed in
35
 * originally, with the same byte arrays and offsets.</li>
36
 * <li>It could safely just persist and return the bytes from the offset
37
 * for the specified length. For example, return a header byte array with
38
 * offset 0 and length 4, plus a payload byte array with offset 0 and length
39
 * 40000</li>
40
 * <li>It could return the header and payload as a contiguous byte array
41
 * with the header bytes preceeding the payload. The contiguous byte array
42
 * should be set as the header byte array, with the payload byte array being
43
 * null. For example, return a single byte array with offset 0 
44
 * and length 40004.
45
 * This is useful when recovering from a file where the header and payload
46
 * could be written as a contiguous stream of bytes.</li>
47
 * </ul>
48
 * </p>  
49
 */
50
public interface MqttPersistable {
51
52
	/**
53
	 * Returns the header bytes in an array.
54
	 * The bytes start at {@link #getHeaderOffset()}
55
	 * and continue for {@link #getHeaderLength()}.
56
	 * @return the header bytes. 
57
	 */
58
	public byte[] getHeaderBytes() throws MqttPersistenceException;
59
60
	/**
61
	 * Returns the length of the header.
62
	 * @return the header length
63
	 */
64
	public int getHeaderLength() throws MqttPersistenceException;
65
66
	/**
67
	 * Returns the offset of the header within the byte array returned by {@link #getHeaderBytes()}.
68
	 * @return the header offset.
69
	 * 
70
	 */
71
	public int getHeaderOffset() throws MqttPersistenceException;
72
73
	/**
74
	 * Returns the payload bytes in an array.
75
	 * The bytes start at {@link #getPayloadOffset()}
76
	 * and continue for {@link #getPayloadLength()}.
77
	 * @return the payload bytes.  
78
	 */
79
	public byte[] getPayloadBytes() throws MqttPersistenceException;
80
81
	/**
82
	 * Returns the length of the payload.
83
	 * @return the payload length.
84
	 */
85
	public int getPayloadLength() throws MqttPersistenceException;
86
87
	/**
88
	 * Returns the offset of the payload within the byte array returned by {@link #getPayloadBytes()}.
89
	 * @return the payload offset.
90
	 * 
91
	 */
92
	public int getPayloadOffset() throws MqttPersistenceException;
93
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttPersistenceException.java (+56 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3;
13
14
/**
15
 * This exception is thrown by the implementor of the persistence
16
 * interface if there is a problem reading or writing persistent data.
17
 */
18
public class MqttPersistenceException extends MqttException {
19
	private static final long serialVersionUID = 300L;
20
21
	/** Persistence is already being used by another client. */
22
	public static final short REASON_CODE_PERSISTENCE_IN_USE	= 32200;
23
	
24
	/**
25
	 * Constructs a new <code>MqttPersistenceException</code>
26
	 */
27
	public MqttPersistenceException() {
28
		super(REASON_CODE_CLIENT_EXCEPTION);
29
	}
30
	
31
	/**
32
	 * Constructs a new <code>MqttPersistenceException</code> with the specified code
33
	 * as the underlying reason.
34
	 * @param reasonCode the reason code for the exception.
35
	 */
36
	public MqttPersistenceException(int reasonCode) {
37
		super(reasonCode);
38
	}
39
	/**
40
	 * Constructs a new <code>MqttPersistenceException</code> with the specified 
41
	 * <code>Throwable</code> as the underlying reason.
42
	 * @param cause the underlying cause of the exception.
43
	 */
44
	public MqttPersistenceException(Throwable cause) {
45
		super(cause);
46
	}
47
	/**
48
	 * Constructs a new <code>MqttPersistenceException</code> with the specified 
49
	 * <code>Throwable</code> as the underlying reason.
50
	 * @param reason the reason code for the exception.
51
	 * @param cause the underlying cause of the exception.
52
	 */
53
	public MqttPersistenceException(int reason, Throwable cause) {
54
		super(reason, cause);
55
	}
56
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttSecurityException.java (+47 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3;
13
14
/**
15
 * Thrown when a client is not authorized to perform an operation, or
16
 * if there is a problem with the security configuration.
17
 */
18
public class MqttSecurityException extends MqttException {
19
	private static final long serialVersionUID = 300L;
20
21
	/**
22
	 * Constructs a new <code>MqttSecurityException</code> with the specified code
23
	 * as the underlying reason.
24
	 * @param reasonCode the reason code for the exception.
25
	 */
26
	public MqttSecurityException(int reasonCode) {
27
		super(reasonCode);
28
	}
29
30
	/**
31
	 * Constructs a new <code>MqttSecurityException</code> with the specified 
32
	 * <code>Throwable</code> as the underlying reason.
33
	 * @param cause the underlying cause of the exception.
34
	 */
35
	public MqttSecurityException(Throwable cause) {
36
		super(cause);
37
	}
38
	/**
39
	 * Constructs a new <code>MqttSecurityException</code> with the specified 
40
	 * code and <code>Throwable</code> as the underlying reason.
41
	 * @param reasonCode the reason code for the exception.
42
	 * @param cause the underlying cause of the exception.
43
	 */
44
	public MqttSecurityException(int reasonCode, Throwable cause) {
45
		super(reasonCode, cause);
46
	}
47
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttToken.java (+71 lines)
Added Link Here
1
package org.eclipse.paho.client.mqttv3;
2
3
import org.eclipse.paho.client.mqttv3.internal.Token;
4
5
/**
6
 *  Provides a mechanism for tracking the completion of an asynchronous action.
7
 *  <p>
8
 *  A token that implements the ImqttToken interface is returned from all non-blocking 
9
 *  method with the exception of publish. 
10
 *  </p>
11
 *  
12
 * @see IMqttToken
13
 */
14
15
public class MqttToken implements IMqttToken {
16
	/**
17
	 * A reference to the the class that provides most of the implementation of the 
18
	 * MqttToken.  MQTT application programs must not use the internal class.
19
	 */
20
	public Token internalTok = null;
21
		
22
	public MqttToken() {
23
	}
24
	
25
	public MqttToken(String logContext) {
26
		internalTok = new Token(logContext);
27
	}
28
	
29
	public MqttException getException() {
30
		return internalTok.getException();
31
	}
32
33
	public boolean isComplete() {
34
		return internalTok.isComplete();
35
	}
36
37
	public void setActionCallback(IMqttActionListener listener) {
38
		internalTok.setActionCallback(listener);
39
40
	}
41
	public IMqttActionListener getActionCallback() {
42
		return internalTok.getActionCallback();
43
	}
44
45
	public void waitForCompletion() throws MqttException {
46
		internalTok.waitForCompletion(-1);
47
	}
48
49
	public void waitForCompletion(long timeout) throws MqttException {
50
		internalTok.waitForCompletion(timeout);
51
	}
52
	
53
	public IMqttAsyncClient getClient() {
54
		return internalTok.getClient();
55
	}
56
	
57
	public String[] getTopics() {
58
		return internalTok.getTopics();
59
	}
60
61
	public Object getUserContext() {
62
		return internalTok.getUserContext();
63
	}
64
65
	public void setUserContext(Object userContext) {
66
		internalTok.setUserContext(userContext);	}
67
68
	public int getMessageId() {
69
		return internalTok.getMessageID();
70
	}
71
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttTopic.java (+94 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3;
13
14
import org.eclipse.paho.client.mqttv3.internal.ClientComms;
15
import org.eclipse.paho.client.mqttv3.internal.wire.MqttPublish;
16
17
/**
18
 * Represents a topic destination, used for publish/subscribe messaging.
19
 */
20
public class MqttTopic {
21
	
22
	private ClientComms comms;
23
	private String name;
24
	
25
	public MqttTopic(String name, ClientComms comms) {
26
		this.comms = comms;
27
		this.name = name;
28
	}
29
	
30
	/**
31
	 * Publishes a message on the topic.  This is a convenience method, which will 
32
	 * create a new {@link MqttMessage} object with a byte array payload and the
33
	 * specified QoS, and then publish it.  All other values in the
34
	 * message will be set to the defaults. 
35
36
	 * @param payload the byte array to use as the payload
37
	 * @param qos the Quality of Service.  Valid values are 0, 1 or 2.
38
	 * @param retained whether or not this message should be retained by the server.
39
	 * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2.
40
	 * @see #publish(MqttMessage)
41
	 * @see MqttMessage#setQos(int)
42
	 * @see MqttMessage#setRetained(boolean)
43
	 */
44
	public MqttDeliveryToken publish(byte[] payload, int qos, boolean retained) throws MqttException, MqttPersistenceException {
45
		MqttMessage message = new MqttMessage(payload);
46
		message.setQos(qos);
47
		message.setRetained(retained);
48
		return this.publish(message);
49
	}
50
	
51
	/**
52
	 * Publishes the specified message to this topic, but does not wait for delivery 
53
	 * of the message to complete. The returned {@link MqttDeliveryToken token} can be used
54
	 * to track the delivery status of the message.  Once this method has 
55
	 * returned cleanly, the message has been accepted for publication by the
56
	 * client. Message delivery will be completed in the background when a connection 
57
	 * is available.
58
	 * 
59
	 * @param message the message to publish
60
	 * @return an MqttDeliveryToken for tracking the delivery of the message
61
	 */
62
	public MqttDeliveryToken publish(MqttMessage message) throws MqttException, MqttPersistenceException {
63
		MqttDeliveryToken token = new MqttDeliveryToken(comms.getClient().getClientId());
64
		token.setMessage(message);
65
		comms.sendNoWait(createPublish(message), token);
66
		token.internalTok.waitUntilSent();
67
		return token;
68
	}
69
	
70
	/**
71
	 * Returns the name of the queue or topic.
72
	 * 
73
	 * @return the name of this destination.
74
	 */
75
	public String getName() {
76
		return name;
77
	}
78
	
79
	/**
80
	 * Create a PUBLISH packet from the specified message.
81
	 */
82
	private MqttPublish createPublish(MqttMessage message) {
83
		return new MqttPublish(this.getName(), message);
84
	}
85
	
86
	/**
87
	 * Returns a string representation of this topic.
88
	 * @return a string representation of this topic.
89
	 */
90
	public String toString() {
91
		return getName();
92
	}
93
	
94
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/ClientComms.java (+582 lines)
Added Link Here
1
/* 
2
* Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal;
13
14
import java.util.Enumeration;
15
import java.util.Properties;
16
import java.util.Vector;
17
18
import org.eclipse.paho.client.mqttv3.IMqttAsyncClient;
19
import org.eclipse.paho.client.mqttv3.MqttCallback;
20
import org.eclipse.paho.client.mqttv3.MqttClientPersistence;
21
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
22
import org.eclipse.paho.client.mqttv3.MqttDeliveryToken;
23
import org.eclipse.paho.client.mqttv3.MqttException;
24
import org.eclipse.paho.client.mqttv3.MqttPersistenceException;
25
import org.eclipse.paho.client.mqttv3.MqttToken;
26
import org.eclipse.paho.client.mqttv3.MqttTopic;
27
import org.eclipse.paho.client.mqttv3.internal.wire.MqttConnack;
28
import org.eclipse.paho.client.mqttv3.internal.wire.MqttConnect;
29
import org.eclipse.paho.client.mqttv3.internal.wire.MqttDisconnect;
30
import org.eclipse.paho.client.mqttv3.internal.wire.MqttPublish;
31
import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage;
32
import org.eclipse.paho.client.mqttv3.logging.Logger;
33
import org.eclipse.paho.client.mqttv3.logging.LoggerFactory;
34
35
/**
36
 * Handles client communications with the server.  Sends and receives MQTT V3
37
 * messages. 
38
 */
39
public class ClientComms { 
40
	public static String 		VERSION = "${project.version}";
41
	public static String 		BUILD_LEVEL = "L${build.level}";
42
	
43
	private IMqttAsyncClient 	client;
44
	NetworkModule 				networkModule;
45
	CommsReceiver 				receiver;
46
	CommsSender 				sender;
47
	CommsCallback 				callback;
48
	ClientState	 				clientState;
49
	MqttConnectOptions			conOptions;
50
	private MqttClientPersistence persistence;
51
	CommsTokenStore 			tokenStore;
52
	boolean 					stoppingComms = false;
53
	
54
	final static byte CONNECTED		=0;
55
	final static byte CONNECTING	=1;
56
	final static byte DISCONNECTING	=2;
57
	final static byte DISCONNECTED	=3;
58
	final static byte CLOSED		=4;
59
	
60
	private byte 	conState = DISCONNECTED;
61
	Object 			conLock = new Object();  	// Used to syncrhonize connection state
62
	private boolean closePending  = false;
63
	
64
	final static String className = ClientComms.class.getName();
65
	Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className);
66
67
	
68
	/**
69
	 * Creates a new ClientComms object, using the specified module to handle
70
	 * the network calls.
71
	 */
72
	public ClientComms(IMqttAsyncClient client, MqttClientPersistence persistence) throws MqttException {
73
		this.conState = DISCONNECTED;
74
		this.client 	= client;
75
		this.persistence = persistence;
76
		this.tokenStore = new CommsTokenStore(getClient().getClientId());
77
		this.callback 	= new CommsCallback(this);
78
		this.clientState = new ClientState(persistence, tokenStore, this.callback, this);
79
  
80
		callback.setClientState(clientState);
81
		log.setResourceName(getClient().getClientId());
82
	}
83
84
	/** 
85
	 * Sends a message to the server. Does not check if connected this validation must be done
86
	 * by invoking routines.
87
	 * @param message
88
	 * @param token
89
	 * @throws MqttException
90
	 */
91
	void internalSend(MqttWireMessage message, MqttToken token) throws MqttException {
92
		final String methodName = "internalSend";
93
		//@TRACE 200=internalSend key={0} message={1} token={2} 
94
		log.fine(className, methodName, "200", new Object[]{message.getKey(), message, token});
95
		
96
		if (token.getClient() == null ) {
97
			// Associate the client with the token - also marks it as in use.
98
			token.internalTok.setClient(getClient());
99
		} else {
100
			// Token is already in use - cannot reuse
101
			//@TRACE 213=fail: token in use: key={0} message={1} token={2} 
102
			log.fine(className, methodName, "213", new Object[]{message.getKey(), message, token});
103
104
			throw new MqttException(MqttException.REASON_CODE_TOKEN_INUSE);
105
		}
106
107
		try {
108
			// Persist if needed and send the message
109
			this.clientState.send(message, token);
110
		} catch(MqttException e) {
111
			if (message instanceof MqttPublish) {
112
				this.clientState.undo((MqttPublish)message);
113
			}
114
			throw e;
115
		}
116
	}
117
118
	/**
119
	 * Sends a message to the broker if in connected state, but only waits for the message to be 
120
	 * stored, before returning.
121
	 */
122
	public void sendNoWait(MqttWireMessage message, MqttToken token) throws MqttException {
123
		final String methodName = "sendNoWait";
124
		if (isConnected() || 
125
				(!isConnected() && message instanceof MqttConnect) ||
126
				(isDisconnecting() && message instanceof MqttDisconnect)) {
127
			this.internalSend(message, token);
128
		} else {
129
			//@TRACE 208=failed: not connected
130
			log.fine(className, methodName, "208");
131
			throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_NOT_CONNECTED);
132
		}
133
	}
134
	
135
	/**
136
	 * Tidy up
137
	 * - call each main class and let it tidy up e.g. releasing the token 
138
	 *   store which normally survives a disconnect. 
139
	 * @throws MqttException  if not disconnected
140
	 */
141
	public void close() throws MqttException {
142
		final String methodName = "close";
143
		synchronized (conLock) {
144
			if (!isClosed()) {
145
				// Must be disconnected before close can take place
146
				if (!isDisconnected()) {
147
					//@TRACE 224=failed: not disconnected
148
					log.fine(className, methodName, "224");
149
	
150
					if (isConnecting()) {
151
						throw new MqttException(MqttException.REASON_CODE_CONNECT_IN_PROGRESS);
152
					} else if (isConnected()) {
153
						throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_CONNECTED);
154
					} else if (isDisconnecting()) {
155
						closePending = true; 
156
						return;
157
					} 		
158
				}
159
				
160
				conState = CLOSED;
161
				
162
				// ShutdownConnection has already cleaned most things
163
				clientState.close();
164
				clientState = null;
165
				callback = null;
166
				persistence = null;
167
				sender = null;
168
				receiver = null;
169
				networkModule = null;
170
				conOptions = null;
171
				tokenStore = null;
172
			}
173
		}
174
	}
175
	
176
	/**
177
	 * Sends a connect message and waits for an ACK or NACK.
178
	 * Connecting is a special case which will also start up the 
179
	 * network connection, receive thread, and keep alive thread.
180
	 */
181
	public void connect(MqttConnectOptions options, MqttToken token) throws MqttException {
182
		final String methodName = "connect";
183
		synchronized (conLock) {
184
			if (isDisconnected() && !closePending) {
185
				//@TRACE 214=state=CONNECTING
186
				log.fine(className,methodName,"214");
187
				
188
				conState = CONNECTING;
189
				
190
				this.conOptions = options;
191
				
192
				MqttConnect connect = new MqttConnect(client.getClientId(),
193
						options.isCleanSession(),
194
						options.getKeepAliveInterval(),
195
						options.getUserName(),
196
						options.getPassword(),
197
						options.getWillMessage(),
198
						options.getWillDestination());
199
			
200
				this.clientState.setKeepAliveSecs(options.getKeepAliveInterval());
201
				this.clientState.setCleanSession(options.isCleanSession());
202
		
203
				tokenStore.open();
204
				ConnectBG conbg = new ConnectBG(this, token, connect);
205
				conbg.start();
206
			}
207
			else {
208
				// @TRACE 207=connect failed: not disconnected {0}
209
				log.fine(className,methodName,"207", new Object[] {new Byte(conState)});
210
				if (isClosed() || closePending) {
211
					throw new MqttException(MqttException.REASON_CODE_CLIENT_CLOSED);
212
				} else if (isConnecting()) {
213
					throw new MqttException(MqttException.REASON_CODE_CONNECT_IN_PROGRESS);
214
				} else if (isDisconnecting()) {
215
					throw new MqttException(MqttException.REASON_CODE_CLIENT_DISCONNECTING); 
216
				} else {
217
					throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_CONNECTED);
218
				}
219
			}
220
		}
221
	}
222
	
223
	public void connectComplete( MqttConnack cack, MqttException mex) throws MqttException {
224
		final String methodName = "connectComplete";
225
		int rc = cack.getReturnCode();
226
		synchronized (conLock) {
227
			if (rc == 0) {
228
				// We've successfully connected
229
				// @TRACE 215=state=CONNECTED
230
				log.fine(className,methodName,"215");
231
232
				conState = CONNECTED;
233
				return;
234
			} 
235
		}
236
237
		// @TRACE 204=connect failed: rc={0}
238
		log.fine(className,methodName,"204", new Object[]{new Integer(rc)});
239
		throw mex;
240
	}
241
242
	/**
243
	 * Shuts down the connection to the server.
244
	 * This may have been invoked as a result of a user calling disconnect or 
245
	 * an abnormal disconnection.  The method may be invoked multiple times
246
	 * in parallel as each thread when it receives an error uses this method
247
	 * to ensure that shutdown completes successfully. 
248
	 */
249
	public void shutdownConnection(MqttToken token, MqttException reason) {
250
		final String methodName = "shutdownConnection";
251
		boolean wasConnected;
252
		MqttToken endToken = null; 		//Token to notify after disconnect completes
253
		
254
		// This method could concurrently be invoked from many places only allow it
255
		// to run once.
256
		synchronized(conLock) {
257
			if (stoppingComms || closePending) {
258
				return;
259
			}
260
			stoppingComms = true;
261
			
262
			//@TRACE 216=state=DISCONNECTING
263
			log.fine(className,methodName,"216");
264
			
265
			wasConnected = (isConnected() || isDisconnecting());
266
			conState = DISCONNECTING;
267
		}
268
		
269
		// Update the token with the reason for shutdown if it 
270
		// is not already complete. 
271
		if (token != null && !token.isComplete()) {
272
			token.internalTok.setException(reason);
273
		}
274
		
275
		// Stop the thread that is used to call the user back
276
		// when actions complete
277
		if (callback!= null) {callback.stop(); }
278
279
		// Stop the network module, send and receive now not possible
280
		try { 
281
			if (networkModule != null) {networkModule.stop();}
282
		}catch(Exception ioe) {
283
			// Ignore as we are shutting down
284
		}
285
		
286
		// Stop the thread that handles inbound work from the network
287
		if (receiver != null) {receiver.stop();}
288
289
		// Stop any new tokens being saved by app and throwing an exception if they do
290
		tokenStore.quiesce(new MqttException(MqttException.REASON_CODE_CLIENT_DISCONNECTING));
291
		
292
		// Notify any outstanding tokens with the exception of 
293
		// con or discon which may be returned and will be notified at 
294
		// the end 
295
		endToken = handleOldTokens(token, reason);
296
297
		try {
298
			// Clean session handling and tidy up
299
			clientState.disconnected(reason);
300
		}catch(Exception ex) {
301
			// Ignore as we are shutting down
302
		}
303
304
		if (sender != null) { sender.stop(); }
305
		
306
		try {
307
			if (persistence != null) {persistence.close();}
308
		}catch(Exception ex) {
309
			// Ignore as we are shutting down
310
		}
311
		// All disconnect logic has been completed allowing the
312
		// client to be marked as disconnected. 
313
		synchronized(conLock) {
314
			//@TRACE 217=state=DISCONNECTED
315
			log.fine(className,methodName,"217");
316
317
			conState = DISCONNECTED;
318
			stoppingComms = false;
319
		}
320
		
321
		// Internal disconnect processing has completed.  If there
322
		// is a disconnect token or a connect in error notify
323
		// it now. This is done at the end to allow a new connect
324
		// to be processed and now throw a currently disconnecting error.
325
		// any outstanding tokens and unblock any waiters
326
		if (endToken != null & callback != null) {
327
			callback.asyncOperationComplete(endToken);
328
		}
329
 
330
		if (wasConnected && callback != null) {
331
			// Let the user know client has disconnected either normally or abnormally
332
			callback.connectionLost(reason);
333
		}
334
335
		// While disconnecting, close may have been requested - try it now
336
		synchronized(conLock) {
337
			if (closePending) {
338
				try {
339
					close();
340
				} catch (Exception e) { // ignore any errors as closing
341
				}
342
			}
343
		}
344
	}
345
346
	// Tidy up. There may be tokens outstanding as the client was 
347
	// not disconnected/quiseced cleanly! Work out what tokens still 
348
	// need to be notified and waiters unblocked. Store the 
349
	// disconnect or connect token to notify after disconnect is 
350
	// complete.
351
	private MqttToken handleOldTokens(MqttToken token, MqttException reason) {
352
		final String methodName = "handleOldTokens";
353
		//@TRACE 222=>
354
		log.fine(className,methodName,"222");
355
356
		MqttToken tokToNotifyLater = null;
357
		try {
358
			// First the token that was related to the disconnect / shutdown may 
359
			// not be in the token table - temporarily add it if not
360
			if (token != null) {
361
				if (tokenStore.getToken(token.internalTok.getKey())==null) {
362
					tokenStore.saveToken(token, token.internalTok.getKey());
363
				}
364
			}
365
366
			Vector toksToNot = clientState.resolveOldTokens(reason);
367
			Enumeration toksToNotE = toksToNot.elements();
368
			while(toksToNotE.hasMoreElements()) {
369
				MqttToken tok = (MqttToken)toksToNotE.nextElement();
370
				
371
				if (tok.internalTok.getKey().equals(MqttDisconnect.KEY) ||
372
						tok.internalTok.getKey().equals(MqttConnect.KEY)) {
373
					// Its con or discon so remember and notify @ end of disc routine
374
					tokToNotifyLater = tok;
375
				} else {
376
					// notify waiters and callbacks of outstanding tokens 
377
					// that a problem has occurred and disconnect is in 
378
					// progress
379
					callback.asyncOperationComplete(tok);
380
				}
381
			}
382
		}catch(Exception ex) {
383
			// Ignore as we are shutting down
384
		}
385
		return tokToNotifyLater;
386
	}
387
	
388
	public void disconnect(MqttDisconnect disconnect, long quiesceTimeout, MqttToken token) throws MqttException {
389
		final String methodName = "disconnect";
390
		synchronized (conLock){
391
			if (isClosed()) {
392
				//@TRACE 223=failed: in closed state
393
				log.fine(className,methodName,"223");
394
				throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_CLOSED);				
395
			} else if (isDisconnected()) {
396
				//@TRACE 211=failed: already disconnected
397
				log.fine(className,methodName,"211");
398
				throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_ALREADY_DISCONNECTED);
399
			} else if (isDisconnecting()) {
400
				//@TRACE 219=failed: already disconnecting
401
				log.fine(className,methodName,"219");
402
				throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_DISCONNECTING);
403
			} else if (Thread.currentThread() == callback.getThread()) {
404
				//@TRACE 210=failed: called on callback thread
405
				log.fine(className,methodName,"210");
406
				// Not allowed to call disconnect() from the callback, as it will deadlock.
407
				throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_DISCONNECT_PROHIBITED);
408
			} 
409
			
410
			//@TRACE 218=state=DISCONNECTING
411
			log.fine(className,methodName,"218");			
412
			conState = DISCONNECTING;
413
			DisconnectBG discbg = new DisconnectBG(disconnect,quiesceTimeout,token);
414
			discbg.start();
415
		}
416
	}
417
	
418
	public boolean isConnected() {
419
		return conState == CONNECTED;
420
	}
421
	
422
	public boolean isConnecting() {
423
		return conState == CONNECTING;
424
	}
425
	public boolean isDisconnected() {
426
		return conState == DISCONNECTED;
427
	}
428
	
429
	public boolean isDisconnecting() {
430
		return conState == DISCONNECTING;
431
	}
432
	public boolean isClosed() {
433
		return conState == CLOSED;
434
	}
435
436
	
437
	public void setCallback(MqttCallback mqttCallback) {
438
		this.callback.setCallback(mqttCallback);
439
	}
440
	
441
	protected MqttTopic getTopic(String topic) {
442
		return new MqttTopic(topic, this);
443
	}
444
	public void setNetworkModule(NetworkModule networkModule) {
445
		this.networkModule = networkModule;
446
	}
447
	public MqttDeliveryToken[] getPendingDeliveryTokens() {
448
		return tokenStore.getOutstandingDelTokens();
449
	}
450
	protected void deliveryComplete(MqttPublish msg) throws MqttPersistenceException {
451
		this.clientState.deliveryComplete(msg);
452
	}
453
454
	public IMqttAsyncClient getClient() {
455
		return client;
456
	}
457
	
458
	public long getKeepAlive() {
459
		return this.clientState.getKeepAlive();
460
	}
461
	
462
	public ClientState getClientState() {
463
		return clientState;
464
	}
465
	
466
	public MqttConnectOptions getConOptions() {
467
		return conOptions;
468
	}
469
	
470
	public Properties getDebug() {
471
		Properties props = new Properties();
472
		props.put("conState", new Integer(conState));
473
		props.put("serverURI", getClient().getServerURI());
474
		props.put("callback", callback);
475
		props.put("stoppingComms", new Boolean(stoppingComms));
476
		return props;
477
	}
478
479
480
481
	// Kick off the connect processing in the background so that it does not block. For instance
482
	// the socket could take time to create.
483
	private class ConnectBG implements Runnable {
484
		ClientComms 	clientComms = null;
485
		Thread 			cBg = null;
486
		MqttToken 		conToken;
487
		MqttConnect 	conPacket;
488
	
489
		ConnectBG(ClientComms cc, MqttToken cToken, MqttConnect cPacket) {
490
			clientComms = cc;
491
			conToken 	= cToken;
492
			conPacket 	= cPacket;
493
			cBg = new Thread(this, "MQTT Con: "+getClient().getClientId());
494
		}
495
		
496
		void start() {
497
			cBg.start();
498
		}
499
500
		public void run() {
501
			final String methodName = "connectBG:run";
502
			MqttException mqttEx = null;
503
			//@TRACE 220=>
504
			log.fine(className, methodName, "220");
505
506
			try {
507
				// Reset an exception on existing delivery tokens.
508
				// This will have been set if disconnect occured before delivery was 
509
				// fully processed. 
510
				MqttDeliveryToken[] toks = tokenStore.getOutstandingDelTokens();
511
				for (int i=0; i<toks.length; i++) {
512
					toks[i].internalTok.setException(null);
513
				}
514
				
515
				// Save the conncet token in tokenStore as failure can occur before send
516
				tokenStore.saveToken(conToken,conPacket);
517
				
518
				// Connect to the server at the network level e.g. TCP socket and then
519
				// start the background processing threads before sending the connect
520
				// packet.
521
				networkModule.start();
522
				receiver = new CommsReceiver(clientComms, clientState, tokenStore, networkModule.getInputStream());
523
				receiver.start("MQTT Rec: "+getClient().getClientId());
524
				sender = new CommsSender(clientComms, clientState, tokenStore, networkModule.getOutputStream());
525
				sender.start("MQTT Snd: "+getClient().getClientId());
526
				callback.start("MQTT Call: "+getClient().getClientId());
527
528
				internalSend(conPacket, conToken);
529
			} catch (MqttException ex) {
530
				//@TRACE 212=connect failed: unexpected exception
531
				log.fine(className, methodName, "212", null, ex);
532
				mqttEx = ex;	
533
			} catch (Exception ex) {
534
				//@TRACE 209=connect failed: unexpected exception
535
				log.fine(className, methodName, "209", null, ex);
536
				mqttEx =  ExceptionHelper.createMqttException(ex);
537
			}
538
			
539
			if (mqttEx != null) {
540
				shutdownConnection(conToken, mqttEx);
541
			}			
542
		}
543
	}
544
545
	// Kick off the disconnect processing in the background so that it does not block. For instance
546
	// the quiesce 	
547
	private class DisconnectBG implements Runnable {
548
		Thread dBg = null;
549
		MqttDisconnect disconnect;
550
		long quiesceTimeout;
551
		MqttToken token;
552
		
553
		DisconnectBG(MqttDisconnect disconnect, long quiesceTimeout, MqttToken token ) {
554
			this.disconnect = disconnect;
555
			this.quiesceTimeout = quiesceTimeout;
556
			this.token = token;
557
		}
558
		
559
		void start() {
560
			dBg = new Thread(this, "MQTT Disc: "+getClient().getClientId());
561
			dBg.start();
562
		}
563
		public void run() {
564
			final String methodName = "disconnectBG:run";
565
			//@TRACE 221=>
566
			log.fine(className, methodName, "221");
567
568
			// Allow current inbound and outbound work to complete
569
			clientState.quiesce(quiesceTimeout);
570
			try {
571
				internalSend(disconnect, token);
572
				token.internalTok.waitUntilSent();
573
			}
574
			catch (MqttException ex) {
575
			}
576
			finally {
577
				token.internalTok.markComplete(null, null);
578
				shutdownConnection(token, null);
579
			}
580
		}
581
	}
582
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/ClientDefaults.java (+16 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal;
13
14
public class ClientDefaults {
15
	public static final int MAX_MSG_SIZE = 1024 * 1024 * 256; // 256 MB
16
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/ClientState.java (+1146 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal;
13
14
import java.io.EOFException;
15
import java.util.Enumeration;
16
import java.util.Hashtable;
17
import java.util.Properties;
18
import java.util.Vector;
19
20
import org.eclipse.paho.client.mqttv3.MqttClientPersistence;
21
import org.eclipse.paho.client.mqttv3.MqttDeliveryToken;
22
import org.eclipse.paho.client.mqttv3.MqttException;
23
import org.eclipse.paho.client.mqttv3.MqttMessage;
24
import org.eclipse.paho.client.mqttv3.MqttPersistable;
25
import org.eclipse.paho.client.mqttv3.MqttPersistenceException;
26
import org.eclipse.paho.client.mqttv3.MqttToken;
27
import org.eclipse.paho.client.mqttv3.internal.wire.MqttAck;
28
import org.eclipse.paho.client.mqttv3.internal.wire.MqttConnack;
29
import org.eclipse.paho.client.mqttv3.internal.wire.MqttConnect;
30
import org.eclipse.paho.client.mqttv3.internal.wire.MqttPingReq;
31
import org.eclipse.paho.client.mqttv3.internal.wire.MqttPingResp;
32
import org.eclipse.paho.client.mqttv3.internal.wire.MqttPubAck;
33
import org.eclipse.paho.client.mqttv3.internal.wire.MqttPubComp;
34
import org.eclipse.paho.client.mqttv3.internal.wire.MqttPubRec;
35
import org.eclipse.paho.client.mqttv3.internal.wire.MqttPubRel;
36
import org.eclipse.paho.client.mqttv3.internal.wire.MqttPublish;
37
import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage;
38
import org.eclipse.paho.client.mqttv3.logging.Logger;
39
import org.eclipse.paho.client.mqttv3.logging.LoggerFactory;
40
41
/**
42
 * The core of the client, which holds the state information for pending and
43
 * in-flight messages.
44
 * 
45
 * Messages that have been accepted for delivery are moved between several objects 
46
 * while being delivered. 
47
 * 
48
 * 1) When the client is not running messages are stored in a persistent store that 
49
 * implements the MqttClientPersistent Interface. The default is MqttDefaultFilePersistencew
50
 * which stores messages safely across failures and system restarts. If no persistence
51
 * is specified there is a fall back to MemoryPersistence which will maintain the messages
52
 * while the Mqtt client is instantiated. 
53
 * 
54
 * 2) When the client or specifically ClientState is instantiated the messages are 
55
 * read from the persistent store into:
56
 * - outboundqos2 hashtable if a qos 2 publish or pubrel
57
 * - outboundqos1 hashtable if a qos 1 publish
58
 * (see restoreState)
59
 * 
60
 * 3) On Connect, copy messages from the outbound hashtables to the pendingMessages or 
61
 * pendingFlows vector in messageid order.
62
 * - Initial message publish goes onto the pendingmessages buffer. 
63
 * - Pubrel goes onto the pendingflows buffer
64
 * (see restoreInflightMessages)
65
 * 
66
 * 4) Sender thread reads messages from the pendingflows and pendingmessages buffer
67
 * one at a time.  The message is removed from the pendingbuffer but remains on the 
68
 * outbound* hashtable.  The hashtable is the place where the full set of outstanding 
69
 * messages are stored in memory. (Persistence is only used at start up)
70
 *  
71
 * 5) Receiver thread - receives wire messages: 
72
 *  - if QOS 1 then remove from persistence and outboundqos1
73
 *  - if QOS 2 pubrec send pubrel. Updating the outboundqos2 entry with the pubrel
74
 *    and update persistence.
75
 *  - if QOS 2 pubcomp remove from persistence and outboundqos2  
76
 * 
77
 * Notes:
78
 * because of the multi threaded nature of the client it is vital that any changes to this
79
 * class take concurrency into account.  For instance as soon as a flow / message is put on 
80
 * the wire it is possible for the receiving thread to receive the ack and to be processing 
81
 * the response before the sending side has finished processing.  For instance a connect may
82
 * be sent, the conack received before the connect notify send has been processed! 
83
 * 
84
 */
85
public class ClientState {
86
	private static final String PERSISTENCE_SENT_PREFIX = "s-";
87
	private static final String PERSISTENCE_CONFIRMED_PREFIX = "sc-";
88
	private static final String PERSISTENCE_RECEIVED_PREFIX = "r-";
89
	
90
	private static final int MIN_MSG_ID = 1;		// Lowest possible MQTT message ID to use
91
	private static final int MAX_MSG_ID = 65535;	// Highest possible MQTT message ID to use
92
	private int nextMsgId = MIN_MSG_ID - 1;			// The next available message ID to use
93
	private Hashtable inUseMsgIds;					// Used to store a set of in-use message IDs
94
95
	volatile private Vector pendingMessages;
96
	volatile private Vector pendingFlows;
97
	
98
	private CommsTokenStore tokenStore;
99
	private ClientComms clientComms = null;
100
	private CommsCallback callback = null;
101
	private long keepAlive;
102
	private boolean cleanSession;
103
	private MqttClientPersistence persistence;
104
	
105
	private int maxInflight = 10;	
106
	private int actualInFlight = 0;
107
	private int inFlightPubRels = 0;
108
	
109
	private Object queueLock = new Object();
110
	private Object quiesceLock = new Object();
111
	private boolean quiescing = false;
112
	
113
	private long lastOutboundActivity = 0;
114
	private long lastInboundActivity = 0;
115
	private long lastPing = 0;
116
	private MqttWireMessage pingCommand;
117
	private boolean pingOutstanding = false;
118
119
	private boolean connected = false;
120
	
121
	private Hashtable outboundQoS2 = null;
122
	private Hashtable outboundQoS1 = null;
123
	private Hashtable inboundQoS2 = null;
124
	
125
	private final static String className = ClientState.class.getName();
126
	private Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className); 
127
128
	protected ClientState(MqttClientPersistence persistence, CommsTokenStore tokenStore, 
129
			CommsCallback callback, ClientComms clientComms) throws MqttException {
130
		
131
		log.setResourceName(clientComms.getClient().getClientId());
132
		log.finer(className, "<Init>", "" );
133
134
		inUseMsgIds = new Hashtable();
135
		pendingMessages = new Vector(this.maxInflight);
136
		pendingFlows = new Vector();
137
		outboundQoS2 = new Hashtable();
138
		outboundQoS1 = new Hashtable();
139
		inboundQoS2 = new Hashtable();
140
		pingCommand = new MqttPingReq();
141
		inFlightPubRels = 0;
142
		actualInFlight = 0;
143
		
144
		this.persistence = persistence;
145
		this.callback = callback;
146
		this.tokenStore = tokenStore;
147
		this.clientComms = clientComms;		
148
		
149
		restoreState();
150
	}
151
152
	protected void setKeepAliveSecs(long keepAliveSecs) {
153
		this.keepAlive = keepAliveSecs*1000;
154
	}
155
	protected long getKeepAlive() {
156
		return this.keepAlive;
157
	}
158
	protected void setCleanSession(boolean cleanSession) {
159
		this.cleanSession = cleanSession;
160
	}
161
	
162
	private String getSendPersistenceKey(MqttWireMessage message) {
163
		return PERSISTENCE_SENT_PREFIX + message.getMessageId();
164
	}
165
	
166
	private String getSendConfirmPersistenceKey(MqttWireMessage message) {
167
		return PERSISTENCE_CONFIRMED_PREFIX + message.getMessageId();
168
	}
169
	
170
	private String getReceivedPersistenceKey(MqttWireMessage message) {
171
		return PERSISTENCE_RECEIVED_PREFIX + message.getMessageId();
172
	}
173
	
174
	protected void clearState() throws MqttException {
175
		final String methodName = "clearState";
176
		//@TRACE 603=clearState
177
		log.fine(className, methodName,">");
178
179
		persistence.clear();
180
		inUseMsgIds.clear();
181
		pendingMessages.clear();
182
		pendingFlows.clear();
183
		outboundQoS2.clear();
184
		outboundQoS1.clear();
185
		inboundQoS2.clear();
186
		tokenStore.clear();
187
	}
188
	
189
	private MqttWireMessage restoreMessage(String key, MqttPersistable persistable) throws MqttException {
190
		final String methodName = "restoreMessage";
191
		MqttWireMessage message = null;
192
193
		try {
194
			message = MqttWireMessage.createWireMessage(persistable);
195
		}
196
		catch (MqttException ex) {
197
			//@TRACE 602=key={0} exception
198
			log.fine(className, methodName, "602", new Object[] {key}, ex);
199
			if (ex.getCause() instanceof EOFException) {
200
				// Premature end-of-file means that the message is corrupted
201
				if (key != null) {
202
					persistence.remove(key);
203
				}
204
			}
205
			else {
206
				throw ex;
207
			}
208
		}
209
		//@TRACE 601=key={0} message={1}
210
		log.fine(className, methodName, "601", new Object[]{key,message});
211
		return message;
212
	}
213
214
	/**
215
	 * Inserts a new message to the list, ensuring that list is ordered from lowest to highest in terms of the message id's.
216
	 * @param list the list to insert the message into
217
	 * @param newMsg the message to insert into the list
218
	 */
219
	private void insertInOrder(Vector list, MqttWireMessage newMsg) {
220
		int newMsgId = newMsg.getMessageId();
221
		for (int i = 0; i < list.size(); i++) {
222
			MqttWireMessage otherMsg = (MqttWireMessage) list.elementAt(i);
223
			int otherMsgId = otherMsg.getMessageId();
224
			if (otherMsgId > newMsgId) {
225
				list.insertElementAt(newMsg, i);
226
				return;
227
			}
228
		}
229
		list.addElement(newMsg);
230
	}
231
232
	/**
233
	 * Produces a new list with the messages properly ordered according to their message id's.
234
	 * @param list the list containing the messages to produce a new reordered list for 
235
	 * - this will not be modified or replaced, i.e., be read-only to this method
236
	 * @return a new reordered list
237
	 */
238
	private Vector reOrder(Vector list) {
239
240
		// here up the new list
241
		Vector newList = new Vector();
242
243
		if (list.size() == 0) {
244
			return newList; // nothing to reorder
245
		}
246
		
247
		int previousMsgId = 0;
248
		int largestGap = 0;
249
		int largestGapMsgIdPosInList = 0;
250
		for (int i = 0; i < list.size(); i++) {
251
			int currentMsgId = ((MqttWireMessage) list.elementAt(i)).getMessageId();
252
			if (currentMsgId - previousMsgId > largestGap) {
253
				largestGap = currentMsgId - previousMsgId;
254
				largestGapMsgIdPosInList = i;
255
			}
256
			previousMsgId = currentMsgId;
257
		}
258
		int lowestMsgId = ((MqttWireMessage) list.elementAt(0)).getMessageId();
259
		int highestMsgId = previousMsgId; // last in the sorted list
260
		
261
		// we need to check that the gap after highest msg id to the lowest msg id is not beaten
262
		if (MAX_MSG_ID - highestMsgId + lowestMsgId > largestGap) {
263
			largestGapMsgIdPosInList = 0;
264
		}
265
		
266
		// starting message has been located, let's start from this point on
267
		for (int i = largestGapMsgIdPosInList; i < list.size(); i++) {
268
			newList.addElement(list.elementAt(i));
269
		}
270
	
271
		// and any wrapping back to the beginning
272
		for (int i = 0; i < largestGapMsgIdPosInList; i++) {
273
			newList.addElement(list.elementAt(i));
274
		}
275
		
276
		return newList;
277
	}
278
	
279
	/**
280
	 * Restores the state information from persistence.
281
	 */
282
	protected void restoreState() throws MqttException {
283
		final String methodName = "restoreState";
284
		Enumeration messageKeys = persistence.keys();
285
		MqttPersistable persistable;
286
		String key;
287
		int highestMsgId = nextMsgId;
288
		Vector orphanedPubRels = new Vector();
289
		//@TRACE 600=>
290
		log.fine(className, methodName, "600");
291
		
292
		while (messageKeys.hasMoreElements()) {
293
			key = (String) messageKeys.nextElement();
294
			persistable = persistence.get(key);
295
			MqttWireMessage message = restoreMessage(key, persistable);
296
			if (message != null) {
297
				if (key.startsWith(PERSISTENCE_RECEIVED_PREFIX)) {
298
					//@TRACE 604=inbound QoS 2 publish key={0} message={1}
299
					log.fine(className,methodName,"604", new Object[]{key,message});
300
301
					// The inbound messages that we have persisted will be QoS 2 
302
					inboundQoS2.put(new Integer(message.getMessageId()),message);
303
				} else if (key.startsWith(PERSISTENCE_SENT_PREFIX)) {
304
					MqttPublish sendMessage = (MqttPublish) message;
305
					highestMsgId = Math.max(sendMessage.getMessageId(), highestMsgId);
306
					if (persistence.containsKey(getSendConfirmPersistenceKey(sendMessage))) {
307
						MqttPersistable persistedConfirm = persistence.get(getSendConfirmPersistenceKey(sendMessage));
308
						// QoS 2, and CONFIRM has already been sent...
309
						MqttPubRel confirmMessage = (MqttPubRel) restoreMessage(key, persistedConfirm);
310
						if (confirmMessage != null) {
311
							confirmMessage.setDuplicate(true);
312
							//@TRACE 605=outbound QoS 2 pubrel key={0} message={1}
313
							log.fine(className,methodName, "605", new Object[]{key,message});
314
315
							outboundQoS2.put(new Integer(confirmMessage.getMessageId()), confirmMessage);
316
						} else {
317
							//@TRACE 606=outbound QoS 2 completed key={0} message={1}
318
							log.fine(className,methodName, "606", new Object[]{key,message});
319
						}
320
					} else {
321
						// QoS 1 or 2, with no CONFIRM sent...
322
						// Put the SEND to the list of pending messages, ensuring message ID ordering...
323
						sendMessage.setDuplicate(true);
324
						if (sendMessage.getMessage().getQos() == 2) {
325
							//@TRACE 607=outbound QoS 2 publish key={0} message={1}
326
							log.fine(className,methodName, "607", new Object[]{key,message});
327
							
328
							outboundQoS2.put(new Integer(sendMessage.getMessageId()),sendMessage);
329
						} else {
330
							//@TRACE 608=outbound QoS 1 publish key={0} message={1}
331
							log.fine(className,methodName, "608", new Object[]{key,message});
332
333
							outboundQoS1.put(new Integer(sendMessage.getMessageId()),sendMessage);
334
						}
335
					}
336
					MqttDeliveryToken tok = tokenStore.restoreToken(sendMessage);
337
					tok.internalTok.setClient(clientComms.getClient());
338
					inUseMsgIds.put(new Integer(sendMessage.getMessageId()),new Integer(sendMessage.getMessageId()));
339
				}
340
				else if (key.startsWith(PERSISTENCE_CONFIRMED_PREFIX)) {
341
					MqttPubRel pubRelMessage = (MqttPubRel) message;
342
					if (!persistence.containsKey(getSendPersistenceKey(pubRelMessage))) {
343
						orphanedPubRels.addElement(key);
344
					}
345
				}
346
			}
347
		}
348
349
		messageKeys = orphanedPubRels.elements();
350
		while(messageKeys.hasMoreElements()) {
351
			key = (String) messageKeys.nextElement();
352
			//@TRACE 609=removing orphaned pubrel key={0}
353
			log.fine(className,methodName, "609", new Object[]{key});
354
355
			persistence.remove(key);
356
		}
357
		
358
		nextMsgId = highestMsgId;
359
	}
360
	
361
	private void restoreInflightMessages() {
362
		final String methodName = "restoreInflightMessages";
363
		pendingMessages = new Vector(this.maxInflight);
364
		pendingFlows = new Vector();
365
366
		Enumeration keys = outboundQoS2.keys();
367
		while (keys.hasMoreElements()) {
368
			Object key = keys.nextElement();
369
			Object msg = outboundQoS2.get(key);
370
			if (msg instanceof MqttPublish) {
371
				//@TRACE 610=QoS 2 publish key={0}
372
				log.fine(className,methodName, "610", new Object[]{key});
373
374
				insertInOrder(pendingMessages, (MqttPublish)msg);
375
			} else if (msg instanceof MqttPubRel) {
376
				//@TRACE 611=QoS 2 pubrel key={0}
377
				log.fine(className,methodName, "611", new Object[]{key});
378
379
				insertInOrder(pendingFlows, (MqttPubRel)msg);
380
			}
381
		}
382
		keys = outboundQoS1.keys();
383
		while (keys.hasMoreElements()) {
384
			Object key = keys.nextElement();
385
			MqttPublish msg = (MqttPublish)outboundQoS1.get(key);
386
			//@TRACE 612=QoS 1 publish key={0}
387
			log.fine(className,methodName, "612", new Object[]{key});
388
389
			insertInOrder(pendingMessages, msg);
390
		}
391
		
392
		this.pendingFlows = reOrder(pendingFlows);
393
		this.pendingMessages = reOrder(pendingMessages);
394
	}
395
	
396
	/**
397
	 * Submits a message for delivery. This method will block until there is
398
	 * room in the inFlightWindow for the message. The message is put into
399
	 * persistence before returning.
400
	 * 
401
	 * @param message  the message to send
402
	 * @param token the token that can be used to track delivery of the message
403
	 * @throws MqttException
404
	 */
405
	public void send(MqttWireMessage message, MqttToken token) throws MqttException {
406
		final String methodName = "send";
407
		if (message.isMessageIdRequired() && (message.getMessageId() == 0)) {
408
			message.setMessageId(getNextMessageId());
409
		}
410
		if (token != null ) {
411
			try {
412
				token.internalTok.setMessageID(message.getMessageId());
413
			} catch (Exception e) {
414
			}
415
		}
416
			
417
		if (message instanceof MqttPublish) {
418
			synchronized (queueLock) {
419
				if (actualInFlight >= this.maxInflight) {
420
					//@TRACE 613= sending {0} msgs at max inflight window
421
					log.fine(className, methodName, "613", new Object[]{new Integer(actualInFlight)});
422
423
					throw new MqttException(MqttException.REASON_CODE_MAX_INFLIGHT);
424
				}
425
				
426
				MqttMessage innerMessage = ((MqttPublish) message).getMessage();
427
				//@TRACE 628=pending publish key={0} qos={1} message={2}
428
				log.fine(className,methodName,"628", new Object[]{new Integer(message.getMessageId()), new Integer(innerMessage.getQos()), message});
429
430
				switch(innerMessage.getQos()) {
431
					case 2:
432
						outboundQoS2.put(new Integer(message.getMessageId()), message);
433
						persistence.put(getSendPersistenceKey(message), (MqttPublish) message);
434
						break;
435
					case 1:
436
						outboundQoS1.put(new Integer(message.getMessageId()), message);
437
						persistence.put(getSendPersistenceKey(message), (MqttPublish) message);
438
						break;
439
				}
440
				tokenStore.saveToken(token, message);
441
				pendingMessages.addElement(message);
442
				queueLock.notifyAll();
443
			}
444
		} else {
445
			//@TRACE 615=pending send key={0} message {1}
446
			log.fine(className,methodName,"615", new Object[]{new Integer(message.getMessageId()), message});
447
			
448
			if (message instanceof MqttConnect) {
449
				synchronized (queueLock) {
450
					// Add the connect action at the head of the pending queue ensuring it jumps
451
					// ahead of any of other pending actions.
452
					tokenStore.saveToken(token, message);
453
					pendingFlows.insertElementAt(message,0);
454
					queueLock.notifyAll();
455
				}
456
			} else {
457
				if (message instanceof MqttPingReq) {
458
					this.pingCommand = message;
459
				}
460
				else if (message instanceof MqttPubRel) {
461
					outboundQoS2.put(new Integer(message.getMessageId()), message);
462
					persistence.put(getSendConfirmPersistenceKey(message), (MqttPubRel) message);
463
				}
464
				else if (message instanceof MqttPubComp)  {
465
					persistence.remove(getReceivedPersistenceKey(message));
466
				}
467
				
468
				synchronized (queueLock) {
469
					if ( !(message instanceof MqttAck )) {
470
						tokenStore.saveToken(token, message);
471
					}
472
					pendingFlows.addElement(message);
473
					queueLock.notifyAll();
474
				}
475
			}
476
		}
477
	}
478
	
479
	/**
480
	 * This removes the MqttSend message from the outbound queue and persistence.
481
	 * @param message
482
	 * @throws MqttPersistenceException
483
	 */
484
	protected void undo(MqttPublish message) throws MqttPersistenceException {
485
		final String methodName = "undo";
486
		synchronized (queueLock) {
487
			//@TRACE 618=key={0} QoS={1} 
488
			log.fine(className,methodName,"618", new Object[]{new Integer(message.getMessageId()), new Integer(message.getMessage().getQos())});
489
			
490
			if (message.getMessage().getQos() == 1) {
491
				outboundQoS1.remove(new Integer(message.getMessageId()));
492
			} else {
493
				outboundQoS2.remove(new Integer(message.getMessageId()));
494
			}
495
			pendingMessages.removeElement(message);
496
			persistence.remove(getSendPersistenceKey(message));
497
			tokenStore.removeToken(message);
498
			checkQuiesceLock();
499
		}
500
	}
501
	
502
	/**
503
	 * Check and send a ping if needed and check for ping timeout.
504
	 * Need to send a ping if nothing has been sent or received  
505
	 * in the last keepalive interval. It is important to check for 
506
	 * both sent and received packets in order to catch the case where an 
507
	 * app is solely sending QOS 0 messages or receiving QOS 0 messages.
508
	 * QOS 0 message are not good enough for checking a connection is
509
	 * alive as they are one way messages.
510
	 * 
511
	 * If a ping has been sent but no data has been received in the 
512
	 * last keepalive interval then the connection is deamed to be broken. 
513
	 */
514
	private void checkForActivity() throws MqttException {
515
		final String methodName = "checkForActivity";
516
517
		if (connected && this.keepAlive > 0) {
518
			long time = System.currentTimeMillis();
519
		
520
			if (!pingOutstanding) {
521
				// Is a ping required? 
522
				if (time - lastOutboundActivity >= this.keepAlive ||
523
					time - lastInboundActivity >= this.keepAlive) {
524
525
					//@TRACE 620=ping needed. keepAlive={0} lastOutboundActivity={1} lastInboundActivity={2}
526
					log.fine(className,methodName,"620", new Object[]{new Long(this.keepAlive),new Long(lastOutboundActivity),new Long(lastInboundActivity)});
527
					
528
					pingOutstanding = true;
529
					lastPing = time;
530
					MqttToken token = new MqttToken(clientComms.getClient().getClientId());
531
					tokenStore.saveToken(token, pingCommand);
532
					pendingFlows.insertElementAt(pingCommand, 0);
533
				}
534
			} else if (time - lastPing >= this.keepAlive) {
535
				// A ping is outstanding but no packet has been received in KA so connection is deemed broken
536
				//@TRACE 619=Timed out as no activity, keepAlive={0} lastOutboundActivity={1} lastInboundActivity={2}
537
				log.severe(className,methodName,"619", new Object[]{new Long(this.keepAlive),new Long(lastOutboundActivity),new Long(lastInboundActivity)});
538
				
539
				// A ping has already been sent. At this point, assume that the
540
				// broker has hung and the TCP layer hasn't noticed.
541
				throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_TIMEOUT);
542
			}		
543
		}
544
	}
545
	
546
	/**
547
	 * This returns the next piece of work, ie message, for the CommsSender
548
	 * to send over the network.
549
	 * Calls to this method block until either:
550
	 *  - there is a message to be sent
551
	 *  - the keepAlive interval is exceeded, which triggers a ping message
552
	 *    to be returned
553
	 *  - {@link #disconnected(MqttException, boolean)} is called
554
	 * @return the next message to send, or null if the client is disconnected
555
	 */
556
	protected MqttWireMessage get() throws MqttException {
557
		final String methodName = "get";
558
		MqttWireMessage result = null;
559
560
		synchronized (queueLock) {
561
			while (result == null) {		
562
				if (pendingMessages.isEmpty() && pendingFlows.isEmpty()) {
563
					try {
564
						long ttw = getTimeUntilPing();
565
						//@TRACE 644=nothing to send, wait for {0} ms
566
						log.fine(className,methodName, "644", new Object[] {new Long(ttw)});						
567
 
568
						queueLock.wait(getTimeUntilPing());
569
					} catch (InterruptedException e) {
570
					}
571
				}
572
				
573
				// Handle the case where not connected. This should only be the case if: 
574
				// - in the process of disconnecting / shutting down
575
				// - in the process of connecting
576
				if (!connected && 
577
	 				(pendingFlows.isEmpty() || !((MqttWireMessage)pendingFlows.elementAt(0) instanceof MqttConnect))) {
578
					//@TRACE 621=no outstanding flows and not connected
579
					log.fine(className,methodName,"621");
580
581
					return null;
582
				}
583
584
				// Check if there is a need to send a ping to keep the session alive. 
585
				// Note this check is done before processing messages. If not done first
586
				// an app that only publishes QOS 0 messages will prevent keepalive processing
587
				// from functioning.
588
				checkForActivity();
589
				
590
				// Now process any queued flows or messages
591
				if (!pendingFlows.isEmpty()) {
592
					// Process the first "flow" in the queue
593
					result = (MqttWireMessage)pendingFlows.elementAt(0);
594
					pendingFlows.removeElementAt(0);
595
					if (result instanceof MqttPubRel) {
596
						inFlightPubRels++;
597
598
						//@TRACE 617=+1 inflightpubrels={0}
599
						log.fine(className,methodName,"617", new Object[]{new Integer(inFlightPubRels)});
600
					}
601
		
602
					checkQuiesceLock();
603
				} else if (!pendingMessages.isEmpty()) {
604
					// If the inflight window is full then messages are not 
605
					// processed until the inflight window has space. 
606
					if (actualInFlight < this.maxInflight) {
607
						// The in flight window is not full so process the 
608
						// first message in the queue
609
						result = (MqttWireMessage)pendingMessages.elementAt(0);
610
						pendingMessages.removeElementAt(0);
611
						actualInFlight++;
612
	
613
						//@TRACE 623=+1 actualInFlight={0}
614
						log.fine(className,methodName,"623",new Object[]{new Integer(actualInFlight)});
615
					} else {
616
						//@TRACE 622=inflight window full
617
						log.fine(className,methodName,"622");				
618
					}
619
				}			
620
			}
621
		}
622
		return result;
623
	}
624
	
625
	public void setKeepAliveInterval(long interval) {
626
		this.keepAlive = interval;
627
	}
628
	
629
	/**
630
	 * Deduce how long to to wait until a ping is required.
631
	 * 
632
	 * In order to keep the connection alive the server must see activity within 
633
	 * the keepalive interval. If the application is not sending / receiving
634
	 * any messages then the client will send a ping.  This method works out
635
	 * the next time that a ping must be sent in order for the server to 
636
	 * know the client is alive.
637
	 * @return  time before a ping needs to be sent to keep alive the connection
638
	 */
639
	long getTimeUntilPing() {
640
		long pingin = getKeepAlive();
641
		// If KA is zero which means just wait for work or 
642
		// if a ping is outstanding return the KA value
643
		if (connected && (getKeepAlive() > 0) && !pingOutstanding) {
644
		
645
			long time = System.currentTimeMillis();
646
			long timeSinceOut = (time-lastOutboundActivity);
647
			long timeSinceIn = (time-lastInboundActivity);
648
			
649
			if (timeSinceOut > timeSinceIn) {
650
				pingin = (getKeepAlive()-timeSinceOut);
651
			} else {
652
				pingin = (getKeepAlive()-timeSinceIn);
653
			}
654
			
655
			// Unlikely to be negative or zero but in the case it is return a 
656
			// small value > 0 to cause a ping to occur
657
			if (pingin <= 0) {
658
				pingin = 10;
659
			}
660
		}
661
		return (pingin);
662
	}
663
	
664
	/**
665
	 * Called by the CommsSender when a message has been sent
666
	 * @param message
667
	 */
668
	protected void notifySent(MqttWireMessage message) {
669
		final String methodName = "notifySent";
670
		
671
		this.lastOutboundActivity = System.currentTimeMillis();
672
		//@TRACE 625=key={0}
673
		log.fine(className,methodName,"625",new Object[]{message.getKey()});
674
		
675
		MqttToken token = tokenStore.getToken(message);
676
		token.internalTok.notifySent();
677
		if (message instanceof MqttPublish) {
678
			if (((MqttPublish)message).getMessage().getQos() == 0) {
679
				// once a QOS 0 message is sent we can clean up its records straight away as
680
				// we won't be hearing about it again
681
				token.internalTok.markComplete(null, null);
682
				callback.asyncOperationComplete(token);
683
				decrementInFlight();
684
				releaseMessageId(message.getMessageId());
685
				tokenStore.removeToken(message);
686
				checkQuiesceLock();
687
			}
688
		}
689
	}
690
691
	private void decrementInFlight() {
692
		final String methodName = "decrementInFlight";
693
		synchronized (queueLock) {
694
			actualInFlight--;
695
			//@TRACE 646=-1 actualInFlight={0}
696
			log.fine(className,methodName,"646",new Object[]{new Integer(actualInFlight)});
697
			
698
			if (!checkQuiesceLock()) {
699
				queueLock.notifyAll();
700
			}
701
		}
702
	}
703
	
704
	protected boolean checkQuiesceLock() {
705
		final String methodName = "checkQuiesceLock";
706
//		if (quiescing && actualInFlight == 0 && pendingFlows.size() == 0 && inFlightPubRels == 0 && callback.isQuiesced()) {
707
		int tokC = tokenStore.count();
708
		if (quiescing && tokC == 0 && pendingFlows.size() == 0 && callback.isQuiesced()) {
709
			//@TRACE 626=quiescing={0} actualInFlight={1} pendingFlows={2} inFlightPubRels={3} callbackQuiesce={4} tokens={5}
710
			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)});
711
			synchronized (quiesceLock) {
712
				quiesceLock.notifyAll();
713
			}
714
			return true;
715
		}
716
		return false;
717
	}
718
	
719
	/**
720
	 * Called by the CommsReceiver when an ack has arrived. 
721
	 * 
722
	 * @param message
723
	 * @throws MqttException
724
	 */
725
	protected void notifyReceivedAck(MqttAck ack) throws MqttException {
726
		final String methodName = "notifyReceivedAck";
727
		this.lastInboundActivity = System.currentTimeMillis();
728
729
		// @TRACE 627=received key={0} message={1}
730
		log.fine(className, methodName, "627", new Object[] {
731
				new Integer(ack.getMessageId()), ack });
732
733
		MqttToken token = tokenStore.getToken(ack);
734
		MqttException mex = null;
735
736
		if (ack instanceof MqttPubRec) {
737
			// Complete the QOS 2 flow. Unlike all other
738
			// flows, QOS is a 2 phase flow. The second phase sends a
739
			// pubrel - the operation is not complete until a pubcomp
740
			// is received
741
			MqttPubRel rel = new MqttPubRel((MqttPubRec) ack);
742
			this.send(rel, token);
743
		} else if (ack instanceof MqttPubAck || ack instanceof MqttPubComp) {
744
			// QoS 1 & 2 notify users of result before removing from
745
			// persistence
746
			notifyResult(ack, token, mex);
747
			// Do not remove publish / delivery token at this stage
748
			// do this when the persistence is removed later 
749
		} else if (ack instanceof MqttPingResp) {
750
			pingOutstanding = false;		
751
			notifyResult(ack, token, mex);
752
			tokenStore.removeToken(ack);
753
		} else if (ack instanceof MqttConnack) {
754
			int rc = ((MqttConnack) ack).getReturnCode();
755
			if (rc == 0) {
756
				synchronized (queueLock) {
757
					if (cleanSession) {
758
						clearState();
759
						// Add the connect token back in so that users can be  
760
						// notified when connect completes.
761
						tokenStore.saveToken(token,ack);
762
					}
763
					inFlightPubRels = 0;
764
					actualInFlight = 0;
765
					restoreInflightMessages();
766
					connected();
767
				}
768
			} else {
769
				mex = ExceptionHelper.createMqttException(rc);
770
				throw mex;
771
			}
772
773
			clientComms.connectComplete((MqttConnack) ack, mex);
774
			notifyResult(ack, token, mex);
775
			tokenStore.removeToken(ack);
776
777
			// Notify the sender thread that there maybe work for it to do now
778
			synchronized (queueLock) {
779
				queueLock.notifyAll();
780
			}
781
		} else {
782
			// Sub ack or unsuback
783
			notifyResult(ack, token, mex);
784
			releaseMessageId(ack.getMessageId());
785
			tokenStore.removeToken(ack);
786
		}
787
		
788
		checkQuiesceLock();
789
	}
790
791
	/**
792
	 * Called by the CommsReceiver when a message has been received.
793
	 * Handles inbound messages and other flows such as pubrel. 
794
	 * 
795
	 * @param message
796
	 * @throws MqttException
797
	 */
798
	protected void notifyReceivedMsg(MqttWireMessage message) throws MqttException {
799
		final String methodName = "notifyReceivedMsg";
800
		this.lastInboundActivity = System.currentTimeMillis();
801
802
		// @TRACE 651=received key={0} message={1}
803
		log.fine(className, methodName, "651", new Object[] {
804
				new Integer(message.getMessageId()), message });
805
		
806
		if (!quiescing) {
807
			if (message instanceof MqttPublish) {
808
				MqttPublish send = (MqttPublish) message;
809
				switch (send.getMessage().getQos()) {
810
				case 0:
811
				case 1:
812
					if (callback != null) {
813
						callback.messageArrived(send);
814
					}
815
					break;
816
				case 2:
817
					persistence.put(getReceivedPersistenceKey(message),
818
							(MqttPublish) message);
819
					inboundQoS2.put(new Integer(send.getMessageId()), send);
820
					this.send(new MqttPubRec(send), null);
821
				}
822
			} else if (message instanceof MqttPubRel) {
823
				MqttPublish sendMsg = (MqttPublish) inboundQoS2
824
						.get(new Integer(message.getMessageId()));
825
				if (sendMsg != null) {
826
					if (callback != null) {
827
						callback.messageArrived(sendMsg);
828
					}
829
				} else {
830
					// Original publish has already been delivered.
831
					MqttPubComp pubComp = new MqttPubComp(message
832
							.getMessageId());
833
					this.send(pubComp, null);
834
				}
835
			}
836
		}
837
	}
838
839
	
840
	/**
841
	 * Called when waiters and callbacks have processed the message. For
842
	 * messages where delivery is complete the message can be removed from
843
	 * persistence and counters adjusted accordingly. Also tidy up by removing
844
	 * token from store...
845
	 * 
846
	 * @param message
847
	 * @throws MqttException
848
	 */
849
	protected void notifyComplete(MqttToken token) throws MqttException {
850
		final String methodName = "notifyComplete";
851
852
		MqttWireMessage message = token.internalTok.getWireMessage();
853
854
		if (message != null && message instanceof MqttAck) {
855
			// @TRACE 629=received key={0} token={1} message={2}
856
			log.fine(className, methodName, "629", new Object[] {
857
					 new Integer(message.getMessageId()), token, message });
858
859
			MqttAck ack = (MqttAck) message;
860
861
			if (ack instanceof MqttPubAck) {
862
				// QoS 1 - user notified now remove from persistence...
863
				persistence.remove(getSendPersistenceKey(message));
864
				outboundQoS1.remove(new Integer(ack.getMessageId()));
865
				decrementInFlight();
866
				releaseMessageId(message.getMessageId());
867
				tokenStore.removeToken(message);
868
				// @TRACE 650=removed Qos 1 publish. key={0}
869
				log.fine(className, methodName, "650",
870
						new Object[] { new Integer(ack.getMessageId()) });
871
			} else if (ack instanceof MqttPubComp) {
872
				// QoS 2 - user notified now remove from persistence...
873
				persistence.remove(getSendPersistenceKey(message));
874
				persistence.remove(getSendConfirmPersistenceKey(message));
875
				outboundQoS2.remove(new Integer(ack.getMessageId()));
876
877
				inFlightPubRels--;
878
				decrementInFlight();
879
				releaseMessageId(message.getMessageId());
880
				tokenStore.removeToken(message);
881
882
				// @TRACE 645=removed QoS 2 publish/pubrel. key={0}, -1 inFlightPubRels={1}
883
				log.fine(className, methodName, "645", new Object[] {
884
						new Integer(ack.getMessageId()),
885
						new Integer(inFlightPubRels) });
886
			}
887
888
			checkQuiesceLock();
889
		}
890
	}
891
892
	protected void notifyResult(MqttWireMessage ack, MqttToken token, MqttException ex) {
893
		final String methodName = "notifyResult";
894
		// unblock any threads waiting on the token  
895
		token.internalTok.markComplete(ack, ex);
896
						
897
		// Let the user know an async operation has completed and then remove the token
898
		if (ack != null && ack instanceof MqttAck && !(ack instanceof MqttPubRec)) {
899
			//@TRACE 648=key{0}, msg={1}, excep={2}
900
			log.fine(className,methodName, "648", new Object [] {token.internalTok.getKey(), ack, ex});
901
			callback.asyncOperationComplete(token);
902
		}
903
		// There are cases where there is no ack as the operation failed before 
904
		// an ack was received 
905
		if (ack == null ) {
906
			//@TRACE 649=key={0},excep={1}
907
			log.fine(className,methodName, "649", new Object [] { token.internalTok.getKey(), ex});
908
			callback.asyncOperationComplete(token);
909
		}
910
	}
911
	
912
	/**
913
	 * Called when the client has successfully connected to the broker
914
	 */
915
	public void connected() {
916
		final String methodName = "connected";
917
		//@TRACE 631=connected
918
		log.fine(className, methodName, "631");
919
		this.connected = true;
920
	}
921
	
922
	/**
923
	 * 
924
	 * Called during shutdown to work out if there are any tokens still
925
	 * to be notified and waiters to be unblocked.  Notifying and unblocking 
926
	 * takes place after most shutdown processing has completed. The tokenstore
927
	 * is tidied up so it only contains outstanding delivery tokens which are
928
	 * valid after reconnect (if clean session is false)
929
	 * @param reason The root cause of the disconnection, or null if it is a clean disconnect
930
	 */
931
	public Vector resolveOldTokens(MqttException reason) {
932
		final String methodName = "resolveOldTokens";
933
		//@TRACE 632=reason {0}
934
		log.fine(className,methodName,"632", new Object[] {reason});
935
		
936
		// If any outstanding let the user know the reason why it is still
937
		// outstanding by putting the reason shutdown is occurring into the 
938
		// token. 
939
		MqttException shutReason = reason;
940
		if (reason == null) {
941
			shutReason = new MqttException(MqttException.REASON_CODE_CLIENT_DISCONNECTING);
942
		}
943
		
944
		// Set the token up so it is ready to be notified after disconnect
945
		// processing has completed. Do not 
946
		// remove the token from the store if it is a delivery token, it is 
947
		// valid after a reconnect. 
948
		Vector outT = tokenStore.getOutstandingTokens();
949
		Enumeration outTE = outT.elements();
950
		while (outTE.hasMoreElements()) {
951
			MqttToken tok = (MqttToken)outTE.nextElement();
952
			synchronized (tok) {
953
				if (!tok.isComplete() && !tok.internalTok.isCompletePending() && tok.getException() == null) {
954
					tok.internalTok.setException(shutReason);
955
				}
956
			}
957
			if (!(tok instanceof MqttDeliveryToken)) {
958
				// If not a delivery token it is not valid on 
959
				// restart so remove
960
				tokenStore.removeToken(tok.internalTok.getKey());
961
			}					
962
		}
963
		return outT;
964
	}
965
	
966
	/**
967
	 * Called when the client has been disconnected from the broker.
968
	 * @param reason The root cause of the disconnection, or null if it is a clean disconnect
969
	 */
970
	public void disconnected(MqttException reason) {
971
		final String methodName = "disconnected";
972
		//@TRACE 633=disconnected
973
		log.fine(className,methodName,"633", new Object[] {reason});		
974
975
		this.connected = false;
976
977
		try {
978
			if (cleanSession) {
979
				clearState();
980
			}
981
982
			pendingMessages.clear();
983
			pendingFlows.clear();
984
			// Reset pingOutstanding to allow reconnects to assume no previous ping.
985
		    pingOutstanding = false;
986
		    
987
		} catch (MqttException e) {
988
			// Ignore as we have disconnected at this point
989
		}
990
	}
991
	
992
	/**
993
	 * Releases a message ID back into the pool of available message IDs.
994
	 * If the supplied message ID is not in use, then nothing will happen.
995
	 * 
996
	 * @param msgId A message ID that can be freed up for re-use.
997
	 */
998
	private synchronized void releaseMessageId(int msgId) {
999
		inUseMsgIds.remove(new Integer(msgId));
1000
	}
1001
1002
	/**
1003
	 * Get the next MQTT message ID that is not already in use, and marks
1004
	 * it as now being in use.
1005
	 * 
1006
	 * @return the next MQTT message ID to use
1007
	 */
1008
	private synchronized int getNextMessageId() throws MqttException {
1009
		int startingMessageId = nextMsgId;
1010
		// Allow two complete passes of the message ID range. This gives
1011
		// any asynchronous releases a chance to occur
1012
		int loopCount = 0;
1013
	    do {
1014
	        nextMsgId++;
1015
	        if ( nextMsgId > MAX_MSG_ID ) {
1016
	            nextMsgId = MIN_MSG_ID;
1017
	        }
1018
	        if (nextMsgId == startingMessageId) {
1019
	        	loopCount++;
1020
	        	if (loopCount == 2) {
1021
	        		throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_NO_MESSAGE_IDS_AVAILABLE);
1022
	        	}
1023
	        }
1024
	    } while( inUseMsgIds.containsKey( new Integer(nextMsgId) ) );
1025
	    Integer id = new Integer(nextMsgId);
1026
	    inUseMsgIds.put(id, id);
1027
	    return nextMsgId;
1028
	}
1029
	
1030
	/**
1031
	 * Quiesce the client state, preventing any new messages getting sent,
1032
	 * and preventing the callback on any newly received messages.
1033
	 * After the timeout expires, delete any pending messages except for
1034
	 * outbound ACKs, and wait for those ACKs to complete.
1035
	 */
1036
	public void quiesce(long timeout) {
1037
		final String methodName = "quiesce";
1038
		// If the timeout is greater than zero t
1039
		if (timeout > 0 ) {
1040
			//@TRACE 637=timeout={0}
1041
			log.fine(className,methodName, "637",new Object[]{new Long(timeout)});
1042
			synchronized (queueLock) {
1043
				this.quiescing = true;
1044
			}
1045
			// We don't want to handle any new inbound messages
1046
			callback.quiesce();
1047
			notifyQueueLock();
1048
1049
			synchronized (quiesceLock) {
1050
				try {			
1051
					// If token count is not zero there is outbound work to process and 
1052
					// if pending flows is not zero there is outstanding work to complete and
1053
					// if call back is not quiseced there it needs to complete. 
1054
					int tokc = tokenStore.count();
1055
					if (tokc > 0 || pendingFlows.size() >0 || !callback.isQuiesced()) {
1056
						//@TRACE 639=wait for outstanding: actualInFlight={0} pendingFlows={1} inFlightPubRels={2} tokens={3}
1057
						log.fine(className, methodName,"639", new Object[]{new Integer(actualInFlight), new Integer(pendingFlows.size()), new Integer(inFlightPubRels), new Integer(tokc)});
1058
1059
						// wait for outstanding in flight messages to complete and
1060
						// any pending flows to complete
1061
						quiesceLock.wait(timeout);
1062
					}
1063
				}
1064
				catch (InterruptedException ex) {
1065
					// Don't care, as we're shutting down anyway
1066
				}
1067
			}
1068
			
1069
			// Quiesce time up or inflight messsages delivered.  Ensure pending delivery
1070
			// vectors are cleared ready for disconnect to be sent as the final flow.
1071
			synchronized (queueLock) {
1072
				pendingMessages.clear();
1073
				pendingFlows.clear();
1074
				quiescing = false;
1075
				actualInFlight = 0;
1076
			}
1077
			//@TRACE 640=finished
1078
			log.fine(className, methodName, "640");
1079
		}
1080
	}
1081
1082
	protected void notifyQueueLock() {
1083
		final String methodName = "notifyQueueLock";
1084
		synchronized (queueLock) {
1085
			//@TRACE 638=notifying queueLock holders
1086
			log.fine(className,methodName,"638");
1087
			queueLock.notifyAll();
1088
		}
1089
	}
1090
1091
	protected void deliveryComplete(MqttPublish message) throws MqttPersistenceException {
1092
		final String methodName = "deliveryComplete";
1093
1094
		//@TRACE 641=remove publish from persistence. key={0}
1095
		log.fine(className,methodName,"641", new Object[]{new Integer(message.getMessageId())});
1096
		
1097
		persistence.remove(getReceivedPersistenceKey(message));
1098
		inboundQoS2.remove(new Integer(message.getMessageId()));
1099
	}
1100
	
1101
	/**
1102
	 * Tidy up
1103
	 * - ensure that tokens are released as they are maintained over a 
1104
	 * disconnect / connect cycle. 
1105
	 */
1106
	protected void close() {
1107
		inUseMsgIds.clear();
1108
		pendingMessages.clear();
1109
		pendingFlows.clear();
1110
		outboundQoS2.clear();
1111
		outboundQoS1.clear();
1112
		inboundQoS2.clear();
1113
		tokenStore.clear();
1114
		inUseMsgIds = null;
1115
		pendingMessages = null;
1116
		pendingFlows = null;
1117
		outboundQoS2 = null;
1118
		outboundQoS1 = null;
1119
		inboundQoS2 = null;
1120
		tokenStore = null;
1121
		callback = null;
1122
		clientComms = null;
1123
		persistence = null;
1124
		pingCommand = null;	
1125
	}
1126
	
1127
	public Properties getDebug() {
1128
		Properties props = new Properties();
1129
		props.put("In use msgids", inUseMsgIds);
1130
		props.put("pendingMessages", pendingMessages);
1131
		props.put("pendingFlows", pendingFlows);
1132
		props.put("maxInflight", new Integer(maxInflight));
1133
		props.put("nextMsgID", new Integer(nextMsgId));
1134
		props.put("actualInFlight", new Integer(actualInFlight));
1135
		props.put("inFlightPubRels", new Integer(inFlightPubRels));
1136
		props.put("quiescing", new Boolean(quiescing));
1137
		props.put("pingoutstanding", new Boolean(pingOutstanding));
1138
		props.put("lastOutboundActivity", new Long(lastOutboundActivity));
1139
		props.put("lastInboundActivity", new Long(lastInboundActivity));
1140
		props.put("outboundQoS2", outboundQoS2);
1141
		props.put("outboundQoS1", outboundQoS1);
1142
		props.put("inboundQoS2", inboundQoS2);
1143
		props.put("tokens", tokenStore);
1144
		return props;
1145
	}
1146
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/CommsCallback.java (+384 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal;
13
14
import java.util.Vector;
15
16
import org.eclipse.paho.client.mqttv3.IMqttActionListener;
17
import org.eclipse.paho.client.mqttv3.MqttCallback;
18
import org.eclipse.paho.client.mqttv3.MqttDeliveryToken;
19
import org.eclipse.paho.client.mqttv3.MqttException;
20
import org.eclipse.paho.client.mqttv3.MqttToken;
21
import org.eclipse.paho.client.mqttv3.internal.wire.MqttPubAck;
22
import org.eclipse.paho.client.mqttv3.internal.wire.MqttPubComp;
23
import org.eclipse.paho.client.mqttv3.internal.wire.MqttPublish;
24
import org.eclipse.paho.client.mqttv3.logging.Logger;
25
import org.eclipse.paho.client.mqttv3.logging.LoggerFactory;
26
27
/**
28
 * Bridge between Receiver and the external API. This class gets called by
29
 * Receiver, and then converts the comms-centric MQTT message objects into ones
30
 * understood by the external API.
31
 */
32
public class CommsCallback implements Runnable {
33
	private static int INBOUND_QUEUE_SIZE = 10;
34
	private MqttCallback mqttCallback;
35
	private ClientComms clientComms;
36
	private Vector messageQueue;
37
	private Vector completeQueue;
38
	public boolean running = false;
39
	private boolean quiescing = false;
40
	private Object lifecycle = new Object();
41
	private Thread callbackThread;
42
	private Object workAvailable = new Object();
43
	private Object spaceAvailable = new Object();
44
	private ClientState clientState;
45
46
	final static String className = CommsCallback.class.getName();
47
	Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,	className);
48
49
	CommsCallback(ClientComms clientComms) {
50
		this.clientComms = clientComms;
51
		this.messageQueue = new Vector(INBOUND_QUEUE_SIZE);
52
		this.completeQueue = new Vector(INBOUND_QUEUE_SIZE);
53
		log.setResourceName(clientComms.getClient().getClientId());
54
	}
55
56
	public void setClientState(ClientState clientState) {
57
		this.clientState = clientState;
58
	}
59
60
	/**
61
	 * Starts up the Callback thread.
62
	 */
63
	public void start(String threadName) {
64
		synchronized (lifecycle) {
65
			if (running == false) {
66
				// Praparatory work before starting the background thread.
67
				// For safety ensure any old events are cleared.
68
				messageQueue.clear();
69
				completeQueue.clear();
70
71
				running = true;
72
				quiescing = false;
73
				callbackThread = new Thread(this, threadName);
74
				callbackThread.start();
75
			}
76
		}
77
	}
78
79
	/**
80
	 * Stops the callback thread. 
81
	 * This call will block until stop has completed.
82
	 */
83
	public void stop() {
84
		final String methodName = "stop";
85
		synchronized (lifecycle) {
86
			if (running) {
87
				// @TRACE 700=stopping
88
				log.fine(className, methodName, "700");
89
				running = false;
90
				if (!Thread.currentThread().equals(callbackThread)) {
91
					try {
92
						synchronized (workAvailable) {
93
							// @TRACE 701=notify workAvailable and wait for run
94
							// to finish
95
							log.fine(className, methodName, "701");
96
							workAvailable.notifyAll();
97
						}
98
						// Wait for the thread to finish.
99
						callbackThread.join();
100
					} catch (InterruptedException ex) {
101
					}
102
				}
103
			}
104
			callbackThread = null;
105
			// @TRACE 703=stopped
106
			log.fine(className, methodName, "703");
107
		}
108
	}
109
110
	public void setCallback(MqttCallback mqttCallback) {
111
		this.mqttCallback = mqttCallback;
112
	}
113
114
	public void run() {
115
		final String methodName = "run";
116
		while (running) {
117
			try {
118
				// If no work is currently available, then wait until there is some...
119
				try {
120
					synchronized (workAvailable) {
121
						if (running & messageQueue.isEmpty()
122
								&& completeQueue.isEmpty()) {
123
							// @TRACE 704=wait for workAvailable
124
							log.fine(className, methodName, "704");
125
							workAvailable.wait();
126
						}
127
					}
128
				} catch (InterruptedException e) {
129
				}
130
131
				if (running) {
132
					// Check for deliveryComplete callbacks...
133
					if (!completeQueue.isEmpty()) {
134
						// First call the delivery arrived callback if needed
135
						MqttToken token = (MqttToken) completeQueue.elementAt(0);
136
						handleActionComplete(token);
137
						completeQueue.removeElementAt(0);
138
					}
139
					
140
					// Check for messageArrived callbacks...
141
					if (!messageQueue.isEmpty()) {
142
						// Note, there is a window on connect where a publish
143
						// could arrive before we've
144
						// finished the connect logic.
145
						MqttPublish message = (MqttPublish) messageQueue
146
								.elementAt(0);
147
148
						handleMessage(message);
149
						messageQueue.removeElementAt(0);
150
					}
151
				}
152
153
				if (quiescing) {
154
					clientState.checkQuiesceLock();
155
				}
156
157
				synchronized (spaceAvailable) {
158
					// Notify the spaceAvailable lock, to say that there's now
159
					// some space on the queue...
160
161
					// @TRACE 706=notify spaceAvailable
162
					log.fine(className, methodName, "706");
163
					spaceAvailable.notifyAll();
164
				}
165
			} catch (Throwable ex) {
166
				// Users code could throw an Error or Exception e.g. in the case
167
				// of class NoClassDefFoundError
168
				// @TRACE 714=callback threw exception
169
				log.fine(className, methodName, "714", null, ex);
170
				running = false;
171
				clientComms.shutdownConnection(null, new MqttException(ex));
172
			}
173
		}
174
	}
175
176
	private void handleActionComplete(MqttToken token)
177
			throws MqttException {
178
		final String methodName = "handleActionComplete";
179
		synchronized (token) {
180
			// @TRACE 705=callback and notify for key={0}
181
			log.fine(className, methodName, "705",	new Object[] { token.internalTok.getKey() });
182
			
183
			// Unblock any waiters and if pending complete now set completed
184
			token.internalTok.notifyComplete();
185
			
186
 			if (!token.internalTok.isNotified()) {
187
 				// If a callback is registered and delivery has finished 
188
 				// call delivery complete callback. 
189
				if ( mqttCallback != null 
190
					&& token instanceof MqttDeliveryToken 
191
					&& token.isComplete()) {
192
						mqttCallback.deliveryComplete((MqttDeliveryToken) token);
193
				}
194
				// Now call async action completion callbacks
195
				fireActionEvent(token);
196
			}
197
			
198
			// Set notified so we don't tell the user again about this action.
199
			if ( token instanceof MqttDeliveryToken && token.isComplete()) {
200
				token.internalTok.setNotified(true);
201
			}
202
203
			if (token.isComplete()) {
204
				// Finish by doing any post processing such as delete 
205
				// from persistent store but only do so if the action
206
				// is complete
207
				clientState.notifyComplete(token);
208
			}
209
		}
210
	}
211
212
	/**
213
	 * This method is called when the connection to the server is lost. If there
214
	 * is no cause then it was a clean disconnect. The connectionLost callback
215
	 * will be invoked if registered and run on the thread that requested
216
	 * shutdown e.g. receiver or sender thread. If the request was a user
217
	 * initiated disconnect then the disconnect token will be notified.
218
	 * 
219
	 * @param cause  the reason behind the loss of connection.
220
	 */
221
	public void connectionLost(MqttException cause) {
222
		final String methodName = "connectionLost";
223
		// If there was a problem and a client callback has been set inform
224
		// the connection lost listener of the problem.
225
		try {
226
			if (mqttCallback != null && cause != null) {
227
				// @TRACE 708=call connectionLost
228
				log.fine(className, methodName, "708", new Object[] { cause });
229
				mqttCallback.connectionLost(cause);
230
			}
231
		} catch (java.lang.Throwable t) {
232
			// Just log the fact that a throwable has caught connection lost 
233
			// is called during shutdown processing so no need to do anything else
234
			// @TRACE 720=exception from connectionLost {0}
235
			log.fine(className, methodName, "720", new Object[] { t });
236
		}
237
	}
238
239
	/**
240
	 * An action has completed - if a completion listener has been set on the
241
	 * token then invoke it with the outcome of the action.
242
	 * 
243
	 * @param token
244
	 */
245
	public void fireActionEvent(MqttToken token) {
246
		final String methodName = "fireActionEvent";
247
248
		if (token != null) {
249
			IMqttActionListener asyncCB = token.getActionCallback();
250
			if (asyncCB != null) {
251
				if (token.getException() == null) {
252
					// @TRACE 716=call onSuccess key={0}
253
					log.fine(className, methodName, "716",
254
							new Object[] { token.internalTok.getKey() });
255
					asyncCB.onSuccess(token);
256
				} else {
257
					// @TRACE 717=call onFailure key {0}
258
					log.fine(className, methodName, "716",
259
							new Object[] { token.internalTok.getKey() });
260
					asyncCB.onFailure(token, token.getException());
261
				}
262
			}
263
		}
264
	}
265
266
	/**
267
	 * This method is called when a message arrives on a topic. Messages are
268
	 * only added to the queue for inbound messages if the client is not
269
	 * quiescing.
270
	 * 
271
	 * @param sendMessage
272
	 *            the MQTT SEND message.
273
	 */
274
	public void messageArrived(MqttPublish sendMessage) {
275
		final String methodName = "messageArrived";
276
		if (mqttCallback != null) {
277
			// If we already have enough messages queued up in memory, wait
278
			// until some more queue space becomes available. This helps 
279
			// the client protect itself from getting flooded by messages 
280
			// from the server.
281
			synchronized (spaceAvailable) {
282
				if (!quiescing && messageQueue.size() >= INBOUND_QUEUE_SIZE) {
283
					try {
284
						// @TRACE 709=wait for spaceAvailable
285
						log.fine(className, methodName, "709");
286
						spaceAvailable.wait();
287
					} catch (InterruptedException ex) {
288
					}
289
				}
290
			}
291
			if (!quiescing) {
292
				messageQueue.addElement(sendMessage);
293
				// Notify the CommsCallback thread that there's work to do...
294
				synchronized (workAvailable) {
295
					// @TRACE 710=new msg avail, notify workAvailable
296
					log.fine(className, methodName, "710");
297
					workAvailable.notifyAll();
298
				}
299
			}
300
		}
301
	}
302
303
	/**
304
	 * Let the call back thread quiesce. Prevent new inbound messages being
305
	 * added to the process queue and let existing work quiesce. (until the
306
	 * thread is told to shutdown).
307
	 */
308
	public void quiesce() {
309
		final String methodName = "quiesce";
310
		this.quiescing = true;
311
		synchronized (spaceAvailable) {
312
			// @TRACE 711=quiesce notify spaceAvailable
313
			log.fine(className, methodName, "711");
314
			// Unblock anything waiting for space...
315
			spaceAvailable.notifyAll();
316
		}
317
	}
318
319
	public boolean isQuiesced() {
320
		if (quiescing && completeQueue.size() == 0 && messageQueue.size() == 0) {
321
			return true;
322
		}
323
		return false;
324
	}
325
326
	private void handleMessage(MqttPublish publishMessage)
327
			throws MqttException, Exception {
328
		final String methodName = "handleMessage";
329
		// If quisecing process any pending messages. 
330
		if (mqttCallback != null) {
331
			String destName = publishMessage.getTopicName();
332
333
			// @TRACE 713=call messageArrived key={0} topic={1}
334
			log.fine(className, methodName, "713", new Object[] { 
335
					new Integer(publishMessage.getMessageId()), destName });
336
			mqttCallback.messageArrived(destName, publishMessage.getMessage());
337
			if (publishMessage.getMessage().getQos() == 1) {
338
				this.clientComms.internalSend(new MqttPubAck(publishMessage),
339
						new MqttToken(clientComms.getClient().getClientId()));
340
			} else if (publishMessage.getMessage().getQos() == 2) {
341
				this.clientComms.deliveryComplete(publishMessage);
342
				MqttPubComp pubComp = new MqttPubComp(publishMessage);
343
				this.clientComms.internalSend(pubComp, new MqttToken(clientComms.getClient().getClientId()));
344
			}
345
		}
346
	}
347
348
	public void asyncOperationComplete(MqttToken token) {
349
		final String methodName = "asyncOperationComplete";
350
351
		if (running) {
352
			// invoke callbacks on callback thread
353
			completeQueue.addElement(token);
354
			synchronized (workAvailable) {
355
				// @TRACE 715=new workAvailable. key={0}
356
				log.fine(className, methodName, "715", new Object[] { token.internalTok.getKey() });
357
				workAvailable.notifyAll();
358
			}
359
		} else {
360
			// invoke async callback on invokers thread
361
			try {
362
				handleActionComplete(token);
363
			} catch (Throwable ex) {
364
				// Users code could throw an Error or Exception e.g. in the case
365
				// of class NoClassDefFoundError
366
				// @TRACE 719=callback threw ex:
367
				log.fine(className, methodName, "719", null, ex);
368
				
369
				// Shutdown likely already in progress but no harm to confirm
370
				System.err.println("problem in asyncopcomplete "+ex);
371
				ex.printStackTrace();
372
				clientComms.shutdownConnection(null, new MqttException(ex));
373
			}
374
375
		}
376
	}
377
378
	/**
379
	 * Returns the thread used by this callback.
380
	 */
381
	protected Thread getThread() {
382
		return callbackThread;
383
	}
384
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/CommsReceiver.java (+150 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal;
13
14
import java.io.IOException;
15
import java.io.InputStream;
16
17
import org.eclipse.paho.client.mqttv3.MqttException;
18
import org.eclipse.paho.client.mqttv3.MqttToken;
19
import org.eclipse.paho.client.mqttv3.internal.wire.MqttAck;
20
import org.eclipse.paho.client.mqttv3.internal.wire.MqttInputStream;
21
import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage;
22
import org.eclipse.paho.client.mqttv3.logging.Logger;
23
import org.eclipse.paho.client.mqttv3.logging.LoggerFactory;
24
25
/**
26
 * Receives MQTT packets from the server.
27
 */
28
public class CommsReceiver implements Runnable {
29
	private boolean running = false;
30
	private Object lifecycle = new Object();
31
	private ClientState clientState = null;
32
	private ClientComms clientComms = null;
33
	private MqttInputStream in;
34
	private CommsTokenStore tokenStore = null;
35
	private Thread recThread = null;
36
	
37
	private final static String className = CommsReceiver.class.getName();
38
	private Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className);
39
	
40
	public CommsReceiver(ClientComms clientComms, ClientState clientState,CommsTokenStore tokenStore, InputStream in) {
41
		this.in = new MqttInputStream(in);
42
		this.clientComms = clientComms;
43
		this.clientState = clientState;
44
		this.tokenStore = tokenStore;
45
		log.setResourceName(clientComms.getClient().getClientId());
46
	}
47
	
48
	/**
49
	 * Starts up the Receiver's thread.
50
	 */
51
	public void start(String threadName) {
52
		final String methodName = "start";
53
		//@TRACE 855=starting
54
		log.fine(className,methodName, "855");
55
		synchronized (lifecycle) {
56
			if (running == false) {
57
				running = true;
58
				recThread = new Thread(this, threadName);
59
				recThread.start();
60
			}
61
		}
62
	}
63
64
	/**
65
	 * Stops the Receiver's thread.  This call will block.
66
	 */
67
	public void stop() {
68
		final String methodName = "stop";
69
		synchronized (lifecycle) {
70
			//@TRACE 850=stopping
71
			log.fine(className,methodName, "850");
72
			if (running) {
73
				running = false;
74
				if (!Thread.currentThread().equals(recThread)) {
75
					try {
76
						// Wait for the thread to finish.
77
						recThread.join();
78
					}
79
					catch (InterruptedException ex) {
80
					}
81
				}
82
			}
83
		}
84
		recThread = null;
85
		//@TRACE 851=stopped
86
		log.fine(className,methodName,"851");
87
	}
88
	
89
	/**
90
	 * Run loop to receive messages from the server.
91
	 */
92
	public void run() {
93
		final String methodName = "run";
94
		MqttToken token = null;
95
		
96
		while (running && (in != null)) {
97
			try {
98
				//@TRACE 852=network read message
99
				log.fine(className,methodName,"852");
100
				MqttWireMessage message = in.readMqttWireMessage();
101
				
102
				if (message instanceof MqttAck) {
103
					token = tokenStore.getToken(message);
104
					if (token!=null) {
105
						synchronized (token) {
106
							// Ensure the notify processing is done under a lock on the token
107
							// This ensures that the send processing can complete  before the 
108
							// receive processing starts! ( request and ack and ack processing
109
							// can occur before request processing is complete if not!
110
							clientState.notifyReceivedAck((MqttAck)message);
111
						}
112
					} else {
113
						// It its an ack and there is no token then something is not right.
114
						// An ack should always have a token assoicated with it.
115
						throw new MqttException(MqttException.REASON_CODE_UNEXPECTED_ERROR);
116
					}
117
				} else {
118
					// A new message has arrived
119
					clientState.notifyReceivedMsg(message);
120
				}
121
			}
122
			catch (MqttException ex) {
123
				//@TRACE 856=Stopping, MQttException
124
				log.fine(className,methodName,"856",null,ex);
125
				running = false;
126
				// Token maybe null but that is handled in shutdown
127
				clientComms.shutdownConnection(token, ex);
128
			} 
129
			catch (IOException ioe) {
130
				//@TRACE 853=Stopping due to IOException
131
				log.fine(className,methodName,"853");
132
133
				running = false;
134
				// An EOFException could be raised if the broker processes the 
135
				// DISCONNECT and ends the socket before we complete. As such,
136
				// only shutdown the connection if we're not already shutting down.
137
				if (!clientComms.isDisconnecting()) {
138
					clientComms.shutdownConnection(token, new MqttException(MqttException.REASON_CODE_CONNECTION_LOST, ioe));
139
				} // else {
140
			}
141
		}
142
		
143
		//@TRACE 854=<
144
		log.fine(className,methodName,"854");
145
	}
146
	
147
	public boolean isRunning() {
148
		return running;
149
	}
150
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/CommsSender.java (+146 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal;
13
14
import java.io.OutputStream;
15
import org.eclipse.paho.client.mqttv3.MqttException;
16
import org.eclipse.paho.client.mqttv3.MqttToken;
17
import org.eclipse.paho.client.mqttv3.internal.wire.MqttAck;
18
import org.eclipse.paho.client.mqttv3.internal.wire.MqttOutputStream;
19
import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage;
20
import org.eclipse.paho.client.mqttv3.logging.Logger;
21
import org.eclipse.paho.client.mqttv3.logging.LoggerFactory;
22
23
24
public class CommsSender implements Runnable {
25
	/**
26
	 * Sends MQTT packets to the server on its own thread
27
	 */
28
	private boolean running 		= false;
29
	private Object lifecycle 		= new Object();
30
	private ClientState clientState = null;
31
	private MqttOutputStream out;
32
	private ClientComms clientComms = null;
33
	private CommsTokenStore tokenStore = null;
34
	private Thread 	sendThread		= null;
35
	
36
	private final static String className = CommsSender.class.getName();
37
	private Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT, className);
38
	
39
	public CommsSender(ClientComms clientComms, ClientState clientState, CommsTokenStore tokenStore, OutputStream out) {
40
		this.out = new MqttOutputStream(out);
41
		this.clientComms = clientComms;
42
		this.clientState = clientState;
43
		this.tokenStore = tokenStore;
44
		log.setResourceName(clientComms.getClient().getClientId());
45
	}
46
	
47
	/**
48
	 * Starts up the Sender thread.
49
	 */
50
	public void start(String threadName) {
51
		synchronized (lifecycle) {
52
			if (running == false) {
53
				running = true;
54
				sendThread = new Thread(this, threadName);
55
				sendThread.start();
56
			}
57
		}
58
	}
59
60
	/**
61
	 * Stops the Sender's thread.  This call will block.
62
	 */
63
	public void stop() {
64
		final String methodName = "stop";
65
		
66
		synchronized (lifecycle) {
67
			//@TRACE 800=stopping sender
68
			log.fine(className,methodName,"800");
69
			if (running) {
70
				running = false;
71
				if (!Thread.currentThread().equals(sendThread)) {
72
					try {
73
						// first notify get routine to finish
74
						clientState.notifyQueueLock();
75
						// Wait for the thread to finish.
76
						sendThread.join();
77
					}
78
					catch (InterruptedException ex) {
79
					}
80
				}
81
			}
82
			sendThread=null;
83
			//@TRACE 801=stopped
84
			log.fine(className,methodName,"801");
85
		}
86
	}
87
	
88
	public void run() {
89
		final String methodName = "run";
90
		MqttWireMessage message = null;
91
		while (running && (out != null)) {
92
			try {
93
				message = clientState.get();
94
				if (message != null) {
95
					//@TRACE 802=network send key={0} msg={1}
96
					log.fine(className,methodName,"802", new Object[] {message.getKey(),message});
97
98
					if (message instanceof MqttAck) {
99
						out.write(message);
100
						out.flush();
101
					} else {
102
						MqttToken token = tokenStore.getToken(message);
103
						// While quiescing the tokenstore can be cleared so need 
104
						// to check for null for the case where clear occurs
105
						// while trying to send a message.
106
						if (token != null) {
107
							synchronized (token) {
108
								out.write(message);
109
								out.flush();
110
								clientState.notifySent(message);
111
							}
112
						}
113
					}
114
				} else { // null message
115
					//@TRACE 803=get message returned null, stopping}
116
					log.fine(className,methodName,"803");
117
118
					running = false;
119
				}
120
			} catch (MqttException me) {
121
				handleRunException(message, me);
122
			} catch (Exception ex) {		
123
				handleRunException(message, ex);	
124
			}
125
		} // end while
126
		
127
		//@TRACE 805=<
128
		log.fine(className, methodName,"805");
129
130
	}
131
132
	private void handleRunException(MqttWireMessage message, Exception ex) {
133
		final String methodName = "handleRunException";
134
		//@TRACE 804=exception
135
		log.fine(className,methodName,"804",null, ex);
136
		MqttException mex;
137
		if ( !(ex instanceof MqttException)) {
138
			mex = new MqttException(MqttException.REASON_CODE_CONNECTION_LOST, ex);
139
		} else {
140
			mex = (MqttException)ex;
141
		}
142
143
		running = false;
144
		clientComms.shutdownConnection(null, mex);
145
	}
146
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/CommsTokenStore.java (+255 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal;
13
14
import java.util.Enumeration;
15
import java.util.Hashtable;
16
import java.util.Vector;
17
18
import org.eclipse.paho.client.mqttv3.MqttDeliveryToken;
19
import org.eclipse.paho.client.mqttv3.MqttException;
20
import org.eclipse.paho.client.mqttv3.MqttToken;
21
import org.eclipse.paho.client.mqttv3.internal.wire.MqttPublish;
22
import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage;
23
import org.eclipse.paho.client.mqttv3.logging.Logger;
24
import org.eclipse.paho.client.mqttv3.logging.LoggerFactory;
25
26
27
/**
28
 * Provides a "token" based system for storing and tracking actions across 
29
 * multiple threads. 
30
 * When a message is sent, a token is associated with the message
31
 * and saved using the {@link #saveToken(MqttToken, MqttWireMessage)} method. Anyone interested
32
 * in tacking the state can call one of the wait methods on the token or using 
33
 * the asynchronous listener callback method on the operation. 
34
 * The {@link CommsReceiver} class, on another thread, reads responses back from 
35
 * the network. It uses the response to find the relevant token, which it can then 
36
 * notify. 
37
 * 
38
 * Note:
39
 *   Ping, connect and disconnect do not have a unique message id as
40
 *   only one outstanding request of each type is allowed to be outstanding
41
 */
42
public class CommsTokenStore {
43
	/** Maps message-specific data (usually message IDs) to tokens */
44
	private Hashtable tokens;
45
	private String logContext;
46
	private MqttException closedResponse = null;
47
	
48
	final static String className = CommsTokenStore.class.getName();
49
	Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT, className);
50
51
	public CommsTokenStore(String logContext) {
52
		final String methodName = "<Init>";
53
54
		log.setResourceName(logContext);
55
		this.tokens = new Hashtable();
56
		this.logContext = logContext;
57
		//@TRACE 308=<>
58
		log.fine(className,methodName,"308");//,new Object[]{message});
59
60
	}
61
62
	/**
63
	 * Based on the message type that has just been received return the associated
64
	 * token from the token store or null if one does not exist.
65
	 * @param message whose token is to be returned 
66
	 * @return token for the requested message
67
	 */
68
	public MqttToken getToken(MqttWireMessage message) {
69
		String key = message.getKey(); 
70
		return (MqttToken)tokens.get(key);
71
	}
72
73
	public MqttToken getToken(String key) {
74
		return (MqttToken)tokens.get(key);
75
	}
76
77
	
78
	public MqttToken removeToken(MqttWireMessage message) {
79
		if (message != null) {
80
			return removeToken(message.getKey());
81
		}
82
		return null;
83
	}
84
	
85
	public MqttToken removeToken(String key) {
86
		final String methodName = "removeToken";
87
		//@TRACE 306=key={0}
88
		log.fine(className,methodName,"306",new Object[]{key});
89
90
		if (key != null) {
91
			synchronized(tokens) {
92
				MqttToken tok = (MqttToken)tokens.get(key);
93
				if (tok != null) {
94
					synchronized(tok) {
95
	
96
						return (MqttToken) tokens.remove(key);
97
					}
98
				}
99
			}
100
		}
101
		return null;
102
	}
103
		
104
	/**
105
	 * Restores a token after a client restart.  This method could be called
106
	 * for a SEND of CONFIRM, but either way, the original SEND is what's 
107
	 * needed to re-build the token.
108
	 */
109
	protected MqttDeliveryToken restoreToken(MqttPublish message) {
110
		final String methodName = "restoreToken";
111
		MqttDeliveryToken token;
112
		synchronized(tokens) {
113
			String key = new Integer(message.getMessageId()).toString();
114
			if (this.tokens.containsKey(key)) {
115
				token = (MqttDeliveryToken)this.tokens.get(key);
116
				//@TRACE 302=existing key={0} message={1} token={2}
117
				log.fine(className,methodName, "302",new Object[]{key, message,token});
118
			} else {
119
				token = new MqttDeliveryToken(logContext);
120
				token.internalTok.setKey(key);
121
				this.tokens.put(key, token);
122
				//@TRACE 303=creating new token key={0} message={1} token={2}
123
				log.fine(className,methodName,"303",new Object[]{key, message, token});
124
			}
125
		}
126
		return token;
127
	}
128
	
129
	// For outbound messages store the token in the token store 
130
	// For pubrel use the existing publish token 
131
	protected void saveToken(MqttToken token, MqttWireMessage message) throws MqttException {
132
		final String methodName = "saveToken";
133
134
		synchronized(tokens) {
135
			if (closedResponse == null) {
136
				String key = message.getKey();
137
				//@TRACE 300=key={0} message={1}
138
				log.fine(className,methodName,"300",new Object[]{key, message});
139
				
140
				saveToken(token,key);
141
			} else {
142
				throw closedResponse;
143
			}
144
		}
145
	}
146
	
147
	protected void saveToken(MqttToken token, String key) {
148
		final String methodName = "saveToken";
149
150
		synchronized(tokens) {
151
			//@TRACE 307=key={0} token={1}
152
			log.fine(className,methodName,"307",new Object[]{key,token.toString()});
153
			token.internalTok.setKey(key);
154
			this.tokens.put(key, token);
155
		}
156
	}
157
158
	protected void quiesce(MqttException quiesceResponse) {
159
		final String methodName = "quiesce";
160
161
		synchronized(tokens) {
162
			//@TRACE 309=resp={0}
163
			log.fine(className,methodName,"309",new Object[]{quiesceResponse});
164
165
			closedResponse = quiesceResponse;
166
		}
167
	}
168
	
169
	public void open() {
170
		final String methodName = "open";
171
172
		synchronized(tokens) {
173
			//@TRACE 310=>
174
			log.fine(className,methodName,"310");
175
176
			closedResponse = null;
177
		}
178
	}
179
180
	public MqttDeliveryToken[] getOutstandingDelTokens() {
181
		final String methodName = "getOutstandingDelTokens";
182
183
		synchronized(tokens) {
184
			//@TRACE 311=>
185
			log.fine(className,methodName,"311");
186
187
			Vector list = new Vector();
188
			Enumeration enumeration = tokens.elements();
189
			MqttToken token;
190
			while(enumeration.hasMoreElements()) {
191
				token = (MqttToken)enumeration.nextElement();
192
				if (token != null 
193
					&& token instanceof MqttDeliveryToken 
194
					&& !token.internalTok.isNotified()) {
195
					
196
					list.addElement(token);
197
				}
198
			}
199
	
200
			MqttDeliveryToken[] result = new MqttDeliveryToken[list.size()];
201
			return (MqttDeliveryToken[]) list.toArray(result);
202
		}
203
	}
204
	
205
	public Vector getOutstandingTokens() {
206
		final String methodName = "getOutstandingTokens";
207
208
		synchronized(tokens) {
209
			//@TRACE 312=>
210
			log.fine(className,methodName,"312");
211
212
			Vector list = new Vector();
213
			Enumeration enumeration = tokens.elements();
214
			MqttToken token;
215
			while(enumeration.hasMoreElements()) {
216
				token = (MqttToken)enumeration.nextElement();
217
				if (token != null) {
218
						list.addElement(token);
219
				}
220
			}
221
			return list;
222
		}
223
	}
224
225
	/**
226
	 * Empties the token store without notifying any of the tokens.
227
	 */
228
	public void clear() {
229
		final String methodName = "clear";
230
		//@TRACE 305=> {0} tokens
231
		log.fine(className, methodName, "305", new Object[] {new Integer(tokens.size())});
232
		synchronized(tokens) {
233
			tokens.clear();
234
		}
235
	}
236
	
237
	public int count() {
238
		synchronized(tokens) {
239
			return tokens.size();
240
		}
241
	}
242
	public String toString() {
243
		String lineSep = System.getProperty("line.separator","\n");
244
		StringBuffer toks = new StringBuffer();
245
		synchronized(tokens) {
246
			Enumeration enumeration = tokens.elements();
247
			MqttToken token;
248
			while(enumeration.hasMoreElements()) {
249
				token = (MqttToken)enumeration.nextElement();
250
					toks.append("{"+token.internalTok+"}"+lineSep);
251
			}
252
			return toks.toString();
253
		}
254
	}
255
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/DestinationProvider.java (+27 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal;
13
14
import org.eclipse.paho.client.mqttv3.MqttTopic;
15
16
/**
17
 * This interface exists to act as a common type for
18
 * MqttClient and MqttMIDPClient so they can be passed to
19
 * ClientComms without either client class need to know
20
 * about the other.
21
 * Specifically, this allows the MIDP client to work
22
 * without the non-MIDP MqttClient/MqttConnectOptions
23
 * classes being present.
24
 */
25
public interface DestinationProvider {
26
	public MqttTopic getTopic(String topic);
27
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/ExceptionHelper.java (+52 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal;
13
14
import org.eclipse.paho.client.mqttv3.MqttException;
15
import org.eclipse.paho.client.mqttv3.MqttSecurityException;
16
17
/**
18
 * Utility class to help create exceptions of the correct type.
19
 */
20
public class ExceptionHelper {
21
	public static MqttException createMqttException(int reasonCode) {
22
		if ((reasonCode == MqttException.REASON_CODE_FAILED_AUTHENTICATION) || 
23
			(reasonCode == MqttException.REASON_CODE_NOT_AUTHORIZED)) {
24
			return new MqttSecurityException(reasonCode);
25
		}
26
		
27
		return new MqttException(reasonCode);
28
	}
29
30
	public static MqttException createMqttException(Throwable cause) {
31
		if (cause.getClass().getName().equals("java.security.GeneralSecurityException")) {
32
			return new MqttSecurityException(cause);
33
		}
34
		return new MqttException(cause);
35
	}
36
	
37
	/**
38
	 * Returns whether or not the specified class is available to the current
39
	 * class loader.  This is used to protect the code against using Java SE
40
	 * APIs on Java ME.
41
	 */
42
	public static boolean isClassAvailable(String className) {
43
		boolean result = false;
44
		try {
45
			Class.forName(className);
46
			result = true;
47
		}
48
		catch (ClassNotFoundException ex) {
49
		}
50
		return result;
51
	}
52
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/FileLock.java (+92 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal;
13
/**
14
 * FileLock - used to obtain a lock that can be used to prevent other MQTT clients
15
 * using the same persistent store. If the lock is already held then an exception
16
 * is thrown. 
17
 * 
18
 * Some Java runtimes such as JME MIDP do not support file locking or even 
19
 * the Java classes that support locking.  The class is coded to both compile 
20
 * and work on all Java runtimes.  In Java runtimes that do not support 
21
 * locking it will look as though a lock has been obtained but in reality
22
 * no lock has been obtained. 
23
 */
24
import java.io.File;
25
import java.io.IOException;
26
import java.io.RandomAccessFile;
27
import java.lang.reflect.Method;
28
29
public class FileLock {
30
	private File lockFile;
31
	private RandomAccessFile file;
32
	private Object fileLock;
33
	
34
	/**
35
	 * Creates an NIO FileLock on the specified file if on a suitable Java runtime. 
36
	 * @param clientDir the a File of the directory to contain the lock file. 
37
	 * @param lockFilename name of the the file to lock
38
	 * @throws Exception if the lock could not be obtained for any reason
39
	 */
40
	public FileLock(File clientDir, String lockFilename) throws Exception {
41
		// Create a file to obtain a lock on. 
42
		lockFile = new File(clientDir,lockFilename);
43
		if (ExceptionHelper.isClassAvailable("java.nio.channels.FileLock")) {
44
			try {
45
				this.file = new RandomAccessFile(lockFile,"rw");
46
				Method m = file.getClass().getMethod("getChannel",new Class[]{});
47
				Object channel = m.invoke(file,new Object[]{});
48
				m = channel.getClass().getMethod("tryLock",new Class[]{});
49
				this.fileLock = m.invoke(channel, new Object[]{});
50
			} catch(NoSuchMethodException nsme) {
51
				this.fileLock = null;
52
			} catch(IllegalArgumentException iae) {
53
				this.fileLock = null;
54
			} catch(IllegalAccessException iae) {
55
				this.fileLock = null;
56
			}
57
			if (fileLock == null) {
58
				// Lock not obtained
59
				release();
60
				throw new Exception("Problem obtaining file lock");
61
			}
62
		}
63
	}
64
	
65
	/**
66
	 * Releases the lock.
67
	 */
68
	public void release() {
69
		try {
70
			if (fileLock != null) {
71
				Method m = fileLock.getClass().getMethod("release",new Class[]{});
72
				m.invoke(fileLock, new Object[]{});
73
				fileLock =  null;
74
			}
75
		} catch (Exception e) {
76
			// Ignore exceptions
77
		}
78
		if (file != null) {
79
			try {
80
				file.close();
81
			} catch (IOException e) {
82
			}
83
			file = null;
84
		}
85
86
		if (lockFile != null && lockFile.exists()) {
87
			lockFile.delete();
88
		}
89
		lockFile = null;
90
	}
91
	
92
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/LocalNetworkModule.java (+87 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal;
13
14
import java.io.IOException;
15
import java.io.InputStream;
16
import java.io.OutputStream;
17
import java.lang.reflect.Method;
18
19
import org.eclipse.paho.client.mqttv3.MqttException;
20
21
22
/**
23
 * Special comms class that allows an MQTT client to use a non TCP / optimised 
24
 * mechanism to talk to an MQTT server when running in the same JRE instance as the 
25
 * MQTT server.  
26
 *
27
 * This class checks for the existence of the optimised comms adatper class i.e. the one
28
 * that provides the optimised communication mechanism.  If not available the request
29
 * to connect using the optimised mechanism is rejected.  
30
 *  
31
 * The only known server that implements this is the microbroker:- an MQTT server that 
32
 * ships with a number of IBM products.
33
 */
34
public class LocalNetworkModule implements NetworkModule {
35
	private Class LocalListener;
36
	private String brokerName;
37
	private Object localAdapter;
38
	
39
	public LocalNetworkModule(String brokerName) {
40
		this.brokerName = brokerName;
41
	}
42
43
	public void start() throws IOException, MqttException{
44
		if (!ExceptionHelper.isClassAvailable("com.ibm.mqttdirect.modules.local.bindings.LocalListener")) {
45
			throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_SERVER_CONNECT_ERROR);
46
		}
47
		try {
48
			LocalListener = Class.forName("com.ibm.mqttdirect.modules.local.bindings.LocalListener");
49
			Method connect_m = LocalListener.getMethod("connect", new Class[]{ java.lang.String.class });
50
			localAdapter = connect_m.invoke(null,new Object[]{ brokerName });
51
		} catch(Exception e) {
52
		}
53
		if(localAdapter == null) {
54
			throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_SERVER_CONNECT_ERROR);
55
		}
56
	}
57
58
	public InputStream getInputStream() throws IOException {
59
		InputStream stream = null;
60
		try {
61
			Method m = LocalListener.getMethod("getClientInputStream",new Class[]{});
62
			stream = (InputStream)m.invoke(this.localAdapter,new Object[]{});
63
		} catch(Exception e) {
64
		}
65
		return stream;
66
	}
67
	
68
	public OutputStream getOutputStream() throws IOException {
69
		OutputStream stream = null;
70
		try {
71
			Method m = LocalListener.getMethod("getClientOutputStream",new Class[]{});
72
			stream = (OutputStream)m.invoke(this.localAdapter,new Object[]{});
73
		} catch(Exception e) {
74
		}
75
		return stream;
76
	}
77
78
	public void stop() throws IOException {
79
		if (localAdapter != null) {
80
			try {
81
				Method m = LocalListener.getMethod("close",new Class[]{});
82
				m.invoke(this.localAdapter,new Object[]{});
83
			} catch(Exception e) {
84
			}
85
		}
86
	}
87
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/MessageCatalog.java (+42 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal;
13
14
/**
15
 * Catalog of human readable error messages.
16
 */
17
public abstract class MessageCatalog {
18
	private static MessageCatalog INSTANCE = null;
19
20
	public static final String getMessage(int id) {
21
		if (INSTANCE == null) {
22
			if (ExceptionHelper.isClassAvailable("java.util.ResourceBundle")) {
23
				try {
24
					// Hide this class reference behind reflection so that the class does not need to
25
					// be present when compiled on midp
26
					INSTANCE = (MessageCatalog)Class.forName("org.eclipse.paho.client.mqttv3.internal.ResourceBundleCatalog").newInstance();
27
				} catch (Exception e) {
28
					return "";
29
				}
30
			} else if (ExceptionHelper.isClassAvailable("org.eclipse.paho.client.mqttv3.internal.MIDPCatalog")){
31
				try {
32
					INSTANCE = (MessageCatalog)Class.forName("org.eclipse.paho.client.mqttv3.internal.MIDPCatalog").newInstance();
33
				} catch (Exception e) {
34
					return "";
35
				}
36
			}
37
		}
38
		return INSTANCE.getLocalizedMessage(id);
39
	}
40
	
41
	protected abstract String getLocalizedMessage(int id);
42
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/MqttPersistentData.java (+94 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal;
13
14
import org.eclipse.paho.client.mqttv3.MqttPersistable;
15
16
public class MqttPersistentData implements MqttPersistable {
17
	// Message key
18
	private String    key  = null;
19
20
	// Message header
21
	private byte[] header  = null;
22
	private int    hOffset = 0;
23
	private int    hLength = 0;
24
25
	// Message payload
26
	private byte[] payload = null;
27
	private int    pOffset = 0;
28
	private int    pLength = 0;
29
30
	/**
31
	 * Construct a data object to pass across the MQTT client persistence
32
	 * interface.<BR>
33
	 * When this Object is passed to the persistence implementation the key is
34
	 * used by the client to identify the persisted data to which further
35
	 * update or deletion requests are targeted.<BR>
36
	 * When this Object is created for returning to the client when it is
37
	 * recovering its state from persistence the key is not required to be set.
38
	 * The client can determine the key from the data. 
39
	 * @param key     The key which identifies this data
40
	 * @param header  The message header
41
	 * @param hOffset The start offset of the header bytes in header.
42
	 * @param hLength The length of the header in the header bytes array.
43
	 * @param payload The message payload
44
	 * @param pOffset The start offset of the payload bytes in payload.
45
	 * @param pLength The length of the payload in the payload bytes array
46
	 * when persisting the message.
47
	 */
48
	public MqttPersistentData( String key,
49
			byte[] header,
50
			int    hOffset,
51
			int    hLength,
52
			byte[] payload,
53
			int    pOffset,
54
			int    pLength) {
55
		this.key     = key;
56
		this.header  = header;
57
		this.hOffset = hOffset;
58
		this.hLength = hLength;
59
		this.payload = payload;
60
		this.pOffset = pOffset;
61
		this.pLength = pLength;
62
	}
63
64
	public String getKey() {
65
		return key;
66
	}
67
68
	public byte[] getHeaderBytes() {
69
		return header;
70
	}
71
72
	public int getHeaderLength() {
73
		return hLength;
74
	}
75
76
	public int getHeaderOffset() {
77
		return hOffset;
78
	}
79
80
	public byte[] getPayloadBytes() {
81
		return payload;
82
	}
83
84
	public int getPayloadLength() {
85
		if ( payload == null ) {
86
			return 0;
87
		}
88
		return pLength;
89
	}
90
91
	public int getPayloadOffset() {
92
		return pOffset;
93
	}
94
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/NetworkModule.java (+29 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal;
13
14
import java.io.IOException;
15
import java.io.InputStream;
16
import java.io.OutputStream;
17
18
import org.eclipse.paho.client.mqttv3.MqttException;
19
20
21
public interface NetworkModule {
22
	public void start() throws IOException, MqttException;
23
	
24
	public InputStream getInputStream() throws IOException;
25
	
26
	public OutputStream getOutputStream() throws IOException;
27
	
28
	public void stop() throws IOException;
29
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/ResourceBundleCatalog.java (+32 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal;
13
14
import java.util.MissingResourceException;
15
import java.util.ResourceBundle;
16
17
public class ResourceBundleCatalog extends MessageCatalog {
18
	
19
	private ResourceBundle bundle;
20
	
21
	public ResourceBundleCatalog() throws MissingResourceException {
22
		bundle = ResourceBundle.getBundle("org.eclipse.paho.client.mqttv3.internal.nls.messages");
23
	}
24
25
	protected String getLocalizedMessage(int id) {
26
		try {
27
			return bundle.getString(Integer.toString(id));
28
		} catch(MissingResourceException mre) {
29
			return "MqttException";
30
		}
31
	}
32
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/SSLNetworkModule.java (+88 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal;
13
14
import java.io.IOException;
15
16
import javax.net.ssl.SSLSocket;
17
import javax.net.ssl.SSLSocketFactory;
18
19
import org.eclipse.paho.client.mqttv3.MqttException;
20
import org.eclipse.paho.client.mqttv3.logging.Logger;
21
import org.eclipse.paho.client.mqttv3.logging.LoggerFactory;
22
23
/**
24
 * A network module for connecting over SSL.
25
 */
26
public class SSLNetworkModule extends TCPNetworkModule {
27
	private String[] enabledCiphers;
28
	private int handshakeTimeoutSecs;
29
30
	final static String className = SSLNetworkModule.class.getName();
31
	Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className);
32
33
	/**
34
	 * Constructs a new SSLNetworkModule using the specified host and
35
	 * port.  The supplied SSLSocketFactory is used to supply the network
36
	 * socket.
37
	 */
38
	public SSLNetworkModule(SSLSocketFactory factory, String host, int port, String resourceContext) {
39
		super(factory, host, port, resourceContext);
40
		log.setResourceName(resourceContext);
41
	}
42
43
	/**
44
	 * Returns the enabled cipher suites.
45
	 */
46
	public String[] getEnabledCiphers() {
47
		return enabledCiphers;
48
	}
49
50
	/**
51
	 * Sets the enabled cipher suites on the underlying network socket.
52
	 */
53
	public void setEnabledCiphers(String[] enabledCiphers) {
54
		final String methodName = "setEnabledCiphers";
55
		this.enabledCiphers = enabledCiphers;
56
		if ((socket != null) && (enabledCiphers != null)) {
57
			if (log.isLoggable(Logger.FINE)) {
58
				String ciphers = "";
59
				for (int i=0;i<enabledCiphers.length;i++) {
60
					if (i>0) {
61
						ciphers+=",";
62
					}
63
					ciphers+=enabledCiphers[i];
64
				}
65
				//@TRACE 260=setEnabledCiphers ciphers={0}
66
				log.fine(className,methodName,"260",new Object[]{ciphers});
67
			}
68
			((SSLSocket) socket).setEnabledCipherSuites(enabledCiphers);
69
		}
70
	}
71
	
72
	public void setSSLhandshakeTimeout(int timeout) {
73
		this.handshakeTimeoutSecs = timeout;
74
	}
75
	
76
	public void start() throws IOException, MqttException {
77
		super.start();
78
		setEnabledCiphers(enabledCiphers);
79
		int soTimeout = socket.getSoTimeout();
80
		if ( soTimeout == 0 ) {
81
			// RTC 765: Set a timeout to avoid the SSL handshake being blocked indefinitely
82
			socket.setSoTimeout(this.handshakeTimeoutSecs*1000);
83
		}
84
		((SSLSocket)socket).startHandshake();
85
		// reset timeout to default value
86
		socket.setSoTimeout(soTimeout);   
87
	}
88
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/TCPNetworkModule.java (+103 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal;
13
14
import java.io.IOException;
15
import java.io.InputStream;
16
import java.io.OutputStream;
17
import java.net.ConnectException;
18
import java.net.InetSocketAddress;
19
import java.net.Socket;
20
import java.net.SocketAddress;
21
22
import javax.net.SocketFactory;
23
24
import org.eclipse.paho.client.mqttv3.MqttException;
25
import org.eclipse.paho.client.mqttv3.logging.Logger;
26
import org.eclipse.paho.client.mqttv3.logging.LoggerFactory;
27
28
/**
29
 * A network module for connecting over TCP. 
30
 */
31
public class TCPNetworkModule implements NetworkModule {
32
	protected Socket socket;
33
	private SocketFactory factory;
34
	private String host;
35
	private int port;
36
	private int conTimeout;
37
	
38
	final static String className = TCPNetworkModule.class.getName();
39
	Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className);
40
	
41
	/**
42
	 * Constructs a new TCPNetworkModule using the specified host and
43
	 * port.  The supplied SocketFactory is used to supply the network
44
	 * socket.
45
	 */
46
	public TCPNetworkModule(SocketFactory factory, String host, int port, String resourceContext) {
47
		log.setResourceName(resourceContext);
48
		this.factory = factory;
49
		this.host = host;
50
		this.port = port;
51
		
52
	}
53
54
	/**
55
	 * Starts the module, by creating a TCP socket to the server.
56
	 */
57
	public void start() throws IOException, MqttException {
58
		final String methodName = "start";
59
		try {
60
//			InetAddress localAddr = InetAddress.getLocalHost();
61
//			socket = factory.createSocket(host, port, localAddr, 0);
62
			// @TRACE 252=connect to host {0} port {1} timeout {2}
63
			log.fine(className,methodName, "252", new Object[] {host, new Integer(port), new Long(conTimeout*1000)});
64
			SocketAddress sockaddr = new InetSocketAddress(host, port);
65
			socket = factory.createSocket();
66
			socket.connect(sockaddr, conTimeout*1000);
67
		
68
			// SetTcpNoDelay was originally set ot true disabling Nagle's algorithm. 
69
			// This should not be required.
70
//			socket.setTcpNoDelay(true);	// TCP_NODELAY on, which means we do not use Nagle's algorithm
71
		}
72
		catch (ConnectException ex) {
73
			//@TRACE 250=Failed to create TCP socket
74
			log.fine(className,methodName,"250",null,ex);
75
			throw new MqttException(MqttException.REASON_CODE_SERVER_CONNECT_ERROR, ex);
76
		}
77
	}
78
79
	public InputStream getInputStream() throws IOException {
80
		return socket.getInputStream();
81
	}
82
	
83
	public OutputStream getOutputStream() throws IOException {
84
		return socket.getOutputStream();
85
	}
86
87
	/**
88
	 * Stops the module, by closing the TCP socket.
89
	 */
90
	public void stop() throws IOException {
91
		if (socket != null) {
92
			socket.close();
93
		}
94
	}
95
	
96
	/**
97
	 * Set the maximum time to wait for a socket to be established
98
	 * @param timeout
99
	 */
100
	public void setConnectTimeout(int timeout) {
101
		this.conTimeout = timeout;
102
	}
103
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/Token.java (+348 lines)
Added Link Here
1
package org.eclipse.paho.client.mqttv3.internal;
2
3
import org.eclipse.paho.client.mqttv3.IMqttActionListener;
4
import org.eclipse.paho.client.mqttv3.IMqttAsyncClient;
5
import org.eclipse.paho.client.mqttv3.MqttException;
6
import org.eclipse.paho.client.mqttv3.MqttMessage;
7
import org.eclipse.paho.client.mqttv3.internal.wire.MqttAck;
8
import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage;
9
import org.eclipse.paho.client.mqttv3.logging.Logger;
10
import org.eclipse.paho.client.mqttv3.logging.LoggerFactory;
11
12
public class Token {
13
	volatile private boolean completed = false;
14
	private boolean pendingComplete = false;
15
	private boolean sent = false;
16
	
17
	private Object responseLock = new Object();
18
	private Object sentLock = new Object();
19
	
20
	protected MqttMessage message = null; 
21
	private MqttWireMessage response = null;
22
	private MqttException exception = null;
23
	private String[] topics = null;
24
	
25
	private String key;
26
	
27
	private IMqttAsyncClient client = null;
28
	private IMqttActionListener callback = null;
29
	
30
	private Object userContext = null;
31
	
32
	public int messageID = 0;
33
	public boolean notified = false;
34
	
35
	public Token(String logContext) {
36
		log.setResourceName(logContext);
37
	}
38
	
39
	public int getMessageID() {
40
		return messageID;
41
	}
42
43
	public void setMessageID(int messageID) {
44
		this.messageID = messageID;
45
	}
46
47
	final static String className = Token.class.getName();
48
	Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className);
49
	
50
	public boolean checkResult() throws MqttException {
51
		if ( getException() != null)  {
52
			throw getException();
53
		}
54
		return true;
55
	}
56
57
	public MqttException getException() {
58
		return exception;
59
	}
60
61
	public boolean isComplete() {
62
		return completed;
63
	}
64
65
	protected boolean isCompletePending() {
66
		return pendingComplete;
67
	}
68
69
	protected boolean isInUse() {
70
		return (getClient() != null && !isComplete());
71
	}
72
73
	public void setActionCallback(IMqttActionListener listener) {
74
		this.callback  = listener;
75
76
	}
77
	public IMqttActionListener getActionCallback() {
78
		return callback;
79
	}
80
81
	public void waitForCompletion() throws MqttException {
82
		waitForCompletion(-1);
83
	}
84
85
	public void waitForCompletion(long timeout) throws MqttException {
86
		final String methodName = "waitForCompletion";
87
		//@TRACE 407=key={0} wait max={1} token={2}
88
		log.fine(className,methodName, "407",new Object[]{getKey(), new Long(timeout), this});
89
90
		MqttWireMessage resp = waitForResponse(timeout);
91
		if (resp == null && !completed) {
92
			//@TRACE 406=key={0} timed out token={1}
93
			log.fine(className,methodName, "406",new Object[]{getKey(), this});
94
			throw new MqttException(MqttException.REASON_CODE_CLIENT_TIMEOUT);
95
		}
96
		checkResult();
97
	}
98
	
99
	/**
100
	 * Waits for the message delivery to complete, but doesn't throw an exception
101
	 * in the case of a NACK.  It does still throw an exception if something else
102
	 * goes wrong (e.g. an IOException).  This is used for packets like CONNECT, 
103
	 * which have useful information in the ACK that needs to be accessed.
104
	 */
105
	protected MqttWireMessage waitForResponse() throws MqttException {
106
		return waitForResponse(-1);
107
	}
108
	
109
	protected MqttWireMessage waitForResponse(long timeout) throws MqttException {
110
		final String methodName = "waitForResponse";
111
		synchronized (responseLock) {
112
			//@TRACE 400=>key={0} timeout={1} sent={2} completed={3} hasException={4} response={5} token={6}
113
			log.fine(className, methodName, "400",new Object[]{getKey(), new Long(timeout),new Boolean(sent),new Boolean(completed),(exception==null)?"false":"true",response,this},exception);
114
115
			if (!this.completed) {
116
				if (this.exception == null) {
117
					try {
118
						//@TRACE 408=key={0} wait max={1}
119
						log.fine(className,methodName,"408",new Object[] {getKey(),new Long(timeout)});
120
	
121
						if (timeout == -1) {
122
							responseLock.wait();
123
						} else {
124
							responseLock.wait(timeout);
125
						}
126
					} catch (InterruptedException e) {
127
						exception = new MqttException(e);
128
					}
129
				}
130
				if (!this.completed) {
131
					if (this.exception != null) {
132
						//@TRACE 401=failed with exception
133
						log.fine(className,methodName,"401",null,exception);
134
						throw exception;
135
					}
136
				}
137
			}
138
		}
139
		//@TRACE 402=key={0} response={1}
140
		log.fine(className,methodName, "402",new Object[]{getKey(), this.response});
141
		return this.response;
142
	}
143
	
144
	/**
145
	 * Mark the token as complete and ready for users to be notified.
146
	 * @param msg response message. Optional - there are no response messages for some flows
147
	 * @param ex if there was a problem store the exception in the token.
148
	 */
149
	protected void markComplete(MqttWireMessage msg, MqttException ex) {
150
		final String methodName = "markComplete";
151
		//@TRACE 404=>key={0} response={1} excep={2}
152
		log.fine(className,methodName,"404",new Object[]{getKey(),msg,ex});
153
		
154
		synchronized(responseLock) {
155
			// ACK means that everything was OK, so mark the message for garbage collection.
156
			if (msg instanceof MqttAck) {
157
				this.message = null;
158
			}
159
			this.pendingComplete = true;
160
			this.response = msg;
161
			this.exception = ex;
162
		}
163
	}
164
	/**
165
	 * Notifies this token that a response message (an ACK or NACK) has been
166
	 * received.
167
	 */
168
		protected void notifyComplete() {
169
			final String methodName = "notifyComplete";
170
			//@TRACE 411=>key={0} response={1} excep={2}
171
			log.fine(className,methodName,"404",new Object[]{getKey(),this.response, this.exception});
172
173
			synchronized (responseLock) {
174
				// If pending complete is set then normally the token can be marked
175
				// as complete and users notified. An abnormal error may have 
176
				// caused the client to shutdown beween pending complete being set
177
				// and notifying the user.  In this case - the action must be failed.
178
				if (exception == null && pendingComplete) {
179
					completed = true;
180
					pendingComplete = false;
181
				} else {
182
					pendingComplete = false;
183
				}
184
				
185
				responseLock.notifyAll();
186
			}
187
			synchronized (sentLock) {
188
				sent=true;	
189
				sentLock.notifyAll();
190
			}
191
		}
192
	
193
//	/**
194
//	 * Notifies this token that an exception has occurred.  This is only
195
//	 * used for things like IOException, and not for MQTT NACKs.
196
//	 */
197
//	protected void notifyException() {
198
//		final String methodName = "notifyException";
199
//		//@TRACE 405=token={0} excep={1}
200
//		log.fine(className,methodName, "405",new Object[]{this,this.exception});
201
//		synchronized (responseLock) {
202
//			responseLock.notifyAll();
203
//		}
204
//		synchronized (sentLock) {
205
//			sentLock.notifyAll();
206
//		}
207
//	}
208
209
	public void waitUntilSent() throws MqttException {
210
		final String methodName = "waitUntilSent";
211
		synchronized (sentLock) {
212
			synchronized (responseLock) {
213
				if (this.exception != null) {
214
					throw this.exception;
215
				}
216
			}
217
			if (!sent) {
218
				try {
219
					//@TRACE 409=wait key={0}
220
					log.fine(className,methodName, "409",new Object[]{getKey()});
221
222
					sentLock.wait();
223
				} catch (InterruptedException e) {
224
				}
225
			}
226
			
227
			if (!sent) {
228
				if (this.exception == null) {
229
					throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_UNEXPECTED_ERROR);
230
				}
231
				throw this.exception;
232
			}
233
		}
234
	}
235
	
236
	/**
237
	 * Notifies this token that the associated message has been sent
238
	 * (i.e. written to the TCP/IP socket).
239
	 */
240
	protected void notifySent() {
241
		final String methodName = "notifySent";
242
		//@TRACE 403=> key={0}
243
		log.fine(className, methodName, "403",new Object[]{getKey()});
244
		synchronized (responseLock) {
245
			this.response = null;
246
			this.completed = false;
247
		}
248
		synchronized (sentLock) {
249
			sent = true;
250
			sentLock.notifyAll();
251
		}
252
	}
253
	
254
	public IMqttAsyncClient getClient() {
255
		return client;
256
	}
257
	
258
	protected void setClient(IMqttAsyncClient client) {
259
		this.client = client;
260
	}
261
262
	public void reset() throws MqttException {
263
		final String methodName = "reset";
264
		if (isInUse() ) {
265
			// Token is already in use - cannot reset 
266
			throw new MqttException(MqttException.REASON_CODE_TOKEN_INUSE);
267
		}
268
		//@TRACE 410=> key={0}
269
		log.fine(className, methodName, "410",new Object[]{getKey()});
270
		
271
		client = null;
272
		completed = false;
273
		response = null;
274
		sent = false;
275
		exception = null;
276
		userContext = null;
277
	}
278
279
	public MqttMessage getMessage() {
280
		return message;
281
	}
282
	
283
	public MqttWireMessage getWireMessage() {
284
		return response;
285
	}
286
287
	
288
	public void setMessage(MqttMessage msg) {
289
		this.message = msg;
290
	}
291
	
292
	public String[] getTopics() {
293
		return topics;
294
	}
295
	
296
	public void setTopics(String[] topics) {
297
		this.topics = topics;
298
	}
299
	
300
	public Object getUserContext() {
301
		return userContext;
302
	}
303
304
	public void setUserContext(Object userContext) {
305
		this.userContext = userContext;	
306
	}
307
308
	public void setKey(String key) {
309
		this.key = key;
310
	}
311
312
	public String getKey() {
313
		return key;
314
	}
315
316
	public void setException(MqttException exception) {
317
		synchronized(responseLock) {
318
			this.exception = exception;
319
		}
320
	}
321
322
	public boolean isNotified() {
323
		return notified;
324
	}
325
326
	public void setNotified(boolean notified) {
327
		this.notified = notified;
328
	}
329
330
	public String toString() {
331
		StringBuffer tok = new StringBuffer();
332
		tok.append("key="+getKey());
333
		tok.append(" ,topics=");
334
		if (getTopics() != null) {
335
			for (int i=0; i<getTopics().length; i++) {
336
				tok.append(getTopics()[i]+", ");
337
			} 
338
		}
339
		tok.append(" ,usercontext="+getUserContext());
340
		tok.append(" ,isComplete="+isComplete());
341
		tok.append(" ,isNotified="+isNotified());
342
		tok.append(" ,exception="+getException());
343
		tok.append(" ,actioncallback="+getActionCallback());
344
345
		return tok.toString();
346
	}
347
348
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/logcat.properties (+132 lines)
Added Link Here
1
0=MQTT Catalog
2
200=internalSend key={0} message={1} token={2}
3
213=fail: token in use: key={0} message={1} token={2}
4
208=failed: not connected
5
224=failed: not disconnected
6
214=state=CONNECTING
7
207=connect failed: not disconnected {0}
8
215=state=CONNECTED
9
204=connect failed: rc={0}
10
216=state=DISCONNECTING
11
217=state=DISCONNECTED
12
222=>
13
223=failed: in closed state
14
211=failed: already disconnected
15
219=failed: already disconnecting
16
210=failed: called on callback thread
17
218=state=DISCONNECTING
18
220=>
19
212=connect failed: unexpected exception
20
209=connect failed: unexpected exception
21
221=>
22
603=clearState
23
602=key={0} exception
24
601=key={0} message={1}
25
600=>
26
604=inbound QoS 2 publish key={0} message={1}
27
605=outbound QoS 2 pubrel key={0} message={1}
28
606=outbound QoS 2 completed key={0} message={1}
29
607=outbound QoS 2 publish key={0} message={1}
30
608=outbound QoS 1 publish key={0} message={1}
31
609=removing orphaned pubrel key={0}
32
610=QoS 2 publish key={0}
33
611=QoS 2 pubrel key={0}
34
612=QoS 1 publish key={0}
35
613= sending {0} msgs at max inflight window
36
628=pending publish key={0} qos={1} message={2}
37
615=pending send key={0} message {1}
38
618=key={0} QoS={1}
39
620=ping needed. keepAlive={0} lastOutboundActivity={1} lastInboundActivity={2}
40
619=Timed out as no activity, keepAlive={0} lastOutboundActivity={1} lastInboundActivity={2}
41
644=nothing to send, wait for {0} ms
42
621=no outstanding flows and not connected
43
617=+1 inflightpubrels={0}
44
623=+1 actualInFlight={0}
45
622=inflight window full
46
625=key={0}
47
646=-1 actualInFlight={0}
48
626=quiescing={0} actualInFlight={1} pendingFlows={2} inFlightPubRels={3} callbackQuiesce={4} tokens={5}
49
627=received key={0} message={1}
50
651=received key={0} message={1}
51
629=received key={0} token={1} message={2}
52
650=removed Qos 1 publish. key={0}
53
645=removed QoS 2 publish/pubrel. key={0}, -1 inFlightPubRels={1}
54
648=key{0}, msg={1}, excep={2}
55
649=key={0},excep={1}
56
631=connected
57
632=reason {0}
58
633=disconnected
59
637=timeout={0}
60
639=wait for outstanding: actualInFlight={0} pendingFlows={1} inFlightPubRels={2} tokens={3}
61
640=finished
62
638=notifying queueLock holders
63
641=remove publish from persistence. key={0}
64
700=stopping
65
701=notify workAvailable and wait for run
66
703=stopped
67
704=wait for workAvailable
68
706=notify spaceAvailable
69
714=callback threw exception
70
705=callback and notify for key={0}
71
708=call connectionLost
72
720=exception from connectionLost {0}
73
716=call onSuccess key={0}
74
717=call onFailure key {0}
75
709=wait for spaceAvailable
76
710=new msg avail, notify workAvailable
77
711=quiesce notify spaceAvailable
78
713=call messageArrived key={0} topic={1}
79
715=new workAvailable. key={0}
80
719=callback threw ex:
81
855=starting
82
850=stopping
83
851=stopped
84
852=network read message
85
856=Stopping, MQttException
86
853=Stopping due to IOException
87
854=<
88
800=stopping sender
89
801=stopped
90
802=network send key={0} msg={1}
91
803=get message returned null, stopping}
92
805=<
93
804=exception
94
308=<>
95
306=key={0}
96
302=existing key={0} message={1} token={2}
97
303=creating new token key={0} message={1} token={2}
98
300=key={0} message={1}
99
307=key={0} token={1}
100
309=resp={0}
101
310=>
102
311=>
103
312=>
104
305=> {0} tokens
105
260=setEnabledCiphers ciphers={0}
106
252=connect to host {0} port {1} timeout {2}
107
250=Failed to create TCP socket
108
407=key={0} wait max={1} token={2}
109
406=key={0} timed out token={1}
110
400=>key={0} timeout={1} sent={2} completed={3} hasException={4} response={5} token={6}
111
408=key={0} wait max={1}
112
401=failed with exception
113
402=key={0} response={1}
114
404=>key={0} response={1} excep={2}
115
411=>key={0} response={1} excep={2}
116
409=wait key={0}
117
403=> key={0}
118
410=> key={0}
119
101=<init> ClientID={0} ServerURI={1} PersistenceType={2}
120
115=URI={0}
121
103=cleanSession={0} connectionTimeout={1} TimekeepAlive={2} userName={3} password={4} will={5} userContext={6} callback={7}
122
104=> quiesceTimeout={0} userContext={1} callback={2}
123
105=< exception
124
108=<
125
106=Subscribe topic={0} userContext={1} callback={2}
126
109=<
127
107=Unsubscribe topic={0} userContext={1} callback={2}
128
110=<
129
111=< topic={0} message={1}userContext={1} callback={2}
130
112=<
131
113=<
132
114=>
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages.properties (+35 lines)
Added Link Here
1
#/* 
2
# * Copyright (c) 2009, 2012 IBM Corp.
3
# *
4
# * All rights reserved. This program and the accompanying materials
5
# * are made available under the terms of the Eclipse Public License v1.0
6
# * which accompanies this distribution, and is available at
7
# * http://www.eclipse.org/legal/epl-v10.html
8
# *
9
# * Contributors:
10
# *    Dave Locke - initial API and implementation and/or initial documentation
11
# */
12
# NLS_MESSAGEFORMAT_VAR
13
# NLS_ENCODING=UNICODE
14
1=Invalid protocol version
15
2=Invalid client ID
16
3=Broker unavailable
17
4=Bad user name or password
18
5=Not authorized to connect
19
6=Unexpected error
20
32000=Timed out waiting for a response from the server
21
32100=Client is connected
22
32101=Client is disconnected
23
32102=Client is currently disconnecting
24
32103=Unable to connect to server
25
32104=Client is not connected
26
32105=The specified SocketFactory type does not match the broker URI
27
32106=SSL configuration error
28
32107=Disconnecting is not allowed from a callback method
29
32108=Unrecognized packet
30
32109=Connection lost
31
32110=Connect already in progress
32
32111=Client is closed
33
32200=Persistence already in use
34
32201=Token already in use
35
32202=Too many publishes in progress
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_cs.properties (+30 lines)
Added Link Here
1
#/* 
2
# * Copyright (c) 2009, 2012 IBM Corp.
3
# *
4
# * All rights reserved. This program and the accompanying materials
5
# * are made available under the terms of the Eclipse Public License v1.0
6
# * which accompanies this distribution, and is available at
7
# * http://www.eclipse.org/legal/epl-v10.html
8
# *
9
# * Contributors:
10
# *    Dave Locke - initial API and implementation and/or initial documentation
11
# */
12
# NLS_MESSAGEFORMAT_VAR
13
# NLS_ENCODING=UNICODE
14
1=Neplatn\u00e1 verze protokolu
15
2=Neplatn\u00e9 ID klienta
16
3=Nedostupn\u00fd zprost\u0159edkovatel
17
4=Chybn\u00e9 jm\u00e9no u\u017eivatele nebo heslo
18
5=Chyb\u00ed autorizace pro p\u0159ipojen\u00ed
19
32000=Vypr\u0161en\u00ed \u010dasov\u00e9ho limitu pro odpov\u011b\u010f ze serveru
20
32100=Klient je ji\u017e p\u0159ipojen
21
32101=Klient je ji\u017e odpojen
22
32102=Klient se aktu\u00e1ln\u011b odpojuje
23
32103=Nelze se p\u0159ipojit k serveru
24
32104=Klient nen\u00ed p\u0159ipojen
25
32105=Ur\u010den\u00fd typ polo\u017eky SocketFactory neodpov\u00edd\u00e1 identifik\u00e1toru URI zprost\u0159edkovatele.
26
32106=Chyba konfigurace zabezpe\u010den\u00ed SSL
27
32107=Z metody zp\u011btn\u00e9ho vol\u00e1n\u00ed nen\u00ed povoleno odpojen\u00ed
28
32108=Nerozpoznan\u00fd paket
29
32109=P\u0159ipojen\u00ed bylo ztraceno.
30
32200=Perzistence je ji\u017e pou\u017e\u00edv\u00e1na.
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_de.properties (+30 lines)
Added Link Here
1
#/* 
2
# * Copyright (c) 2009, 2012 IBM Corp.
3
# *
4
# * All rights reserved. This program and the accompanying materials
5
# * are made available under the terms of the Eclipse Public License v1.0
6
# * which accompanies this distribution, and is available at
7
# * http://www.eclipse.org/legal/epl-v10.html
8
# *
9
# * Contributors:
10
# *    Dave Locke - initial API and implementation and/or initial documentation
11
# */
12
# NLS_MESSAGEFORMAT_VAR
13
# NLS_ENCODING=UNICODE
14
1=Protokollversion ung\u00fcltig
15
2=Client-ID ung\u00fcltig
16
3=Broker nicht verf\u00fcgbar
17
4=Benutzername oder Kennwort falsch
18
5=Keine Berechtigung f\u00fcr Verbindung
19
32000=Zeitlimit\u00fcberschreitung beim Warten auf eine Antwort vom Server
20
32100=Verbindung zu Client besteht bereits
21
32101=Verbindung zu Client ist bereits getrennt
22
32102=Verbindung zu Client wird derzeit getrennt
23
32103=Verbindung zu Server kann nicht hergestellt werden
24
32104=Keine Verbindung zu Client
25
32105=Der angegebene Socket-Factorytyp entspricht nicht der Broker-URI
26
32106=SSL-Konfigurationsfehler
27
32107=Trennung einer Verbindung \u00fcber eine Callback-Methode ist nicht zul\u00e4ssig
28
32108=Paket nicht erkannt
29
32109=Verbindung wurde getrennt
30
32200=Persistenz wird bereits verwendet
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_es.properties (+30 lines)
Added Link Here
1
#/* 
2
# * Copyright (c) 2009, 2012 IBM Corp.
3
# *
4
# * All rights reserved. This program and the accompanying materials
5
# * are made available under the terms of the Eclipse Public License v1.0
6
# * which accompanies this distribution, and is available at
7
# * http://www.eclipse.org/legal/epl-v10.html
8
# *
9
# * Contributors:
10
# *    Dave Locke - initial API and implementation and/or initial documentation
11
# */
12
# NLS_MESSAGEFORMAT_VAR
13
# NLS_ENCODING=UNICODE
14
1=Versi\u00f3n de protocolo incorrecta
15
2=Identificador de cliente incorrecto
16
3=Intermediario no disponible
17
4=Nombre de usuario o contrase\u00f1a incorrecto
18
5=No autorizado a conectarse
19
32000=Tiempo de espera excedido al esperar una respuesta del servidor
20
32100=Cliente ya conectado
21
32101=Cliente ya desconectado
22
32102=El cliente se est\u00e1 desconectando
23
32103=No es posible conectarse al servidor
24
32104=El cliente no est\u00e1 conectado
25
32105=El tipo SocketFactory especificado no coincide con el URI del intermediario
26
32106=Error de configuraci\u00f3n SSL
27
32107=No se permite la desconexi\u00f3n desde un m\u00e9todo de devoluci\u00f3n de llamada
28
32108=Paquete no reconocido
29
32109=Se ha perdido la conexi\u00f3n
30
32200=La persistencia ya se est\u00e1 utilizando
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_fr.properties (+30 lines)
Added Link Here
1
#/* 
2
# * Copyright (c) 2009, 2012 IBM Corp.
3
# *
4
# * All rights reserved. This program and the accompanying materials
5
# * are made available under the terms of the Eclipse Public License v1.0
6
# * which accompanies this distribution, and is available at
7
# * http://www.eclipse.org/legal/epl-v10.html
8
# *
9
# * Contributors:
10
# *    Dave Locke - initial API and implementation and/or initial documentation
11
# */
12
# NLS_MESSAGEFORMAT_VAR
13
# NLS_ENCODING=UNICODE
14
1=Version de protocole incorrecte
15
2=ID client incorrect
16
3=Courtier indisponible
17
4=Nom d'utilisateur ou mot de passe incorrect
18
5=L'utilisateur n'est pas autoris\u00e9 \u00e0 se connecter
19
32000=Expiration du d\u00e9lai d'attente d'une r\u00e9ponse du serveur
20
32100=Client d\u00e9j\u00e0 connect\u00e9
21
32101=Client d\u00e9j\u00e0 d\u00e9connect\u00e9
22
32102=Client en cours de d\u00e9connexion
23
32103=Impossible de se connecter au serveur
24
32104=Client non connect\u00e9
25
32105=Le type SocketFactory sp\u00e9cifi\u00e9 ne correspond pas \u00e0 l'URI de courtier
26
32106=Erreur de configuration SSL
27
32107=D\u00e9connexion non autoris\u00e9e pour une m\u00e9thode de rappel
28
32108=Paquet non reconnu
29
32109=Connexion perdue
30
32200=La persistance est d\u00e9j\u00e0 en cours d'utilisation
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_hu.properties (+30 lines)
Added Link Here
1
#/* 
2
# * Copyright (c) 2009, 2012 IBM Corp.
3
# *
4
# * All rights reserved. This program and the accompanying materials
5
# * are made available under the terms of the Eclipse Public License v1.0
6
# * which accompanies this distribution, and is available at
7
# * http://www.eclipse.org/legal/epl-v10.html
8
# *
9
# * Contributors:
10
# *    Dave Locke - initial API and implementation and/or initial documentation
11
# */
12
# NLS_MESSAGEFORMAT_VAR
13
# NLS_ENCODING=UNICODE
14
1=\u00c9rv\u00e9nytelen protokoll v\u00e1ltozat
15
2=\u00c9rv\u00e9nytelen \u00fcgyf\u00e9lazonos\u00edt\u00f3
16
3=K\u00f6zvet\u00edt\u0151 nem el\u00e9rhet\u0151
17
4=Rossz felhaszn\u00e1l\u00f3i n\u00e9v vagy jelsz\u00f3
18
5=Nem jogosult csatlakozni
19
32000=T\u00fall\u00e9pte a megengedett id\u0151t a kiszolg\u00e1l\u00f3 v\u00e1lasz\u00e1ra v\u00e1rva
20
32100=Az \u00fcgyf\u00e9l m\u00e1r csatlakozik
21
32101=Az \u00fcgyf\u00e9l m\u00e1r sz\u00e9tkapcsolt
22
32102=Az \u00fcgyf\u00e9l \u00e9pp megszak\u00edtja a kapcsolatot
23
32103=Nem lehet kapcsol\u00f3dni a kiszolg\u00e1l\u00f3hoz
24
32104=Az \u00fcgyf\u00e9l nincs csatlakoztatva
25
32105=A megadott SocketFactory t\u00edpus nem illeszkedik a k\u00f6zvet\u00edt\u0151 URI azonos\u00edt\u00f3hoz
26
32106=SSL konfigur\u00e1ci\u00f3s hiba
27
32107=A megszak\u00edt\u00e1s visszah\u00edv\u00e1s met\u00f3dusb\u00f3l nem enged\u00e9lyezett
28
32108=Ismeretlen csomag
29
32109=Kapcsolat elveszett
30
32200=A megmarad\u00f3 \u00e1llapot m\u00e1r haszn\u00e1latban van
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_it.properties (+30 lines)
Added Link Here
1
#/* 
2
# * Copyright (c) 2009, 2012 IBM Corp.
3
# *
4
# * All rights reserved. This program and the accompanying materials
5
# * are made available under the terms of the Eclipse Public License v1.0
6
# * which accompanies this distribution, and is available at
7
# * http://www.eclipse.org/legal/epl-v10.html
8
# *
9
# * Contributors:
10
# *    Dave Locke - initial API and implementation and/or initial documentation
11
# */
12
# NLS_MESSAGEFORMAT_VAR
13
# NLS_ENCODING=UNICODE
14
1=Versione di protocollo non valida
15
2=ID client non valido
16
3=Broker non disponibile
17
4=Nome utente o password non validi
18
5=Non autorizzato per la connessione
19
32000=Scaduto in attesa di una risposta dal server
20
32100=Client gi\u00e0 connesso
21
32101=Client gi\u00e0 disconnesso
22
32102=Client in fase di disconnessione
23
32103=Impossibile effettuare la connessione al server
24
32104=Client non connesso
25
32105=Il tipo SocketFactory specificato non corrisponde all'URI del broker
26
32106=Errore di configurazione SSL
27
32107=Disconnessione non consentita da un metodo callback
28
32108=Pacchetto non riconosciuto
29
32109=Connessione persa
30
32200=Persistenza gi\u00e0 in uso
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_ja.properties (+30 lines)
Added Link Here
1
#/* 
2
# * Copyright (c) 2009, 2012 IBM Corp.
3
# *
4
# * All rights reserved. This program and the accompanying materials
5
# * are made available under the terms of the Eclipse Public License v1.0
6
# * which accompanies this distribution, and is available at
7
# * http://www.eclipse.org/legal/epl-v10.html
8
# *
9
# * Contributors:
10
# *    Dave Locke - initial API and implementation and/or initial documentation
11
# */
12
# NLS_MESSAGEFORMAT_VAR
13
# NLS_ENCODING=UNICODE
14
1=\u7121\u52b9\u306a\u30d7\u30ed\u30c8\u30b3\u30eb\u30fb\u30d0\u30fc\u30b8\u30e7\u30f3\u3067\u3059
15
2=\u7121\u52b9\u306a\u30af\u30e9\u30a4\u30a2\u30f3\u30c8 ID \u3067\u3059
16
3=\u30d6\u30ed\u30fc\u30ab\u30fc\u304c\u4f7f\u7528\u4e0d\u53ef\u3067\u3059
17
4=\u9593\u9055\u3063\u305f\u30e6\u30fc\u30b6\u30fc\u540d\u307e\u305f\u306f\u30d1\u30b9\u30ef\u30fc\u30c9\u3067\u3059
18
5=\u63a5\u7d9a\u3059\u308b\u6a29\u9650\u304c\u3042\u308a\u307e\u305b\u3093
19
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
20
32100=\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306f\u65e2\u306b\u63a5\u7d9a\u6e08\u307f\u3067\u3059
21
32101=\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306f\u65e2\u306b\u5207\u65ad\u6e08\u307f\u3067\u3059
22
32102=\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306f\u73fe\u5728\u5207\u65ad\u4e2d\u3067\u3059
23
32103=\u30b5\u30fc\u30d0\u30fc\u306b\u63a5\u7d9a\u3067\u304d\u307e\u305b\u3093
24
32104=\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306f\u63a5\u7d9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093
25
32105=\u6307\u5b9a\u3055\u308c\u305f SocketFactory \u30bf\u30a4\u30d7\u306f\u30d6\u30ed\u30fc\u30ab\u30fc URI \u3068\u4e00\u81f4\u3057\u307e\u305b\u3093
26
32106=SSL \u69cb\u6210\u30a8\u30e9\u30fc\u3067\u3059
27
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
28
32108=\u8b58\u5225\u3055\u308c\u3066\u3044\u306a\u3044\u30d1\u30b1\u30c3\u30c8\u3067\u3059
29
32109=\u63a5\u7d9a\u55aa\u5931
30
32200=\u30d1\u30fc\u30b7\u30b9\u30bf\u30f3\u30b9\u306f\u3059\u3067\u306b\u4f7f\u7528\u4e2d\u3067\u3059\u3002
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_ko.properties (+30 lines)
Added Link Here
1
#/* 
2
# * Copyright (c) 2009, 2012 IBM Corp.
3
# *
4
# * All rights reserved. This program and the accompanying materials
5
# * are made available under the terms of the Eclipse Public License v1.0
6
# * which accompanies this distribution, and is available at
7
# * http://www.eclipse.org/legal/epl-v10.html
8
# *
9
# * Contributors:
10
# *    Dave Locke - initial API and implementation and/or initial documentation
11
# */
12
# NLS_MESSAGEFORMAT_VAR
13
# NLS_ENCODING=UNICODE
14
1=\uc62c\ubc14\ub974\uc9c0 \uc54a\uc740 \ud504\ub85c\ud1a0\ucf5c \ubc84\uc804
15
2=\uc62c\ubc14\ub974\uc9c0 \uc54a\uc740 \ud074\ub77c\uc774\uc5b8\ud2b8 ID
16
3=\ube0c\ub85c\ucee4 \uc0ac\uc6a9 \ubd88\uac00\ub2a5
17
4=\uc798\ubabb\ub41c \uc0ac\uc6a9\uc790 \uc774\ub984 \ub610\ub294 \ube44\ubc00\ubc88\ud638
18
5=\uc5f0\uacb0\ud560 \uc218 \uc788\ub294 \uad8c\ud55c\uc774 \ubd80\uc5ec\ub418\uc9c0 \uc54a\uc74c
19
32000=\uc11c\ubc84\uc5d0\uc11c \uc751\ub2f5\uc744 \uae30\ub2e4\ub9ac\ub294 \uc911 \uc81c\ud55c\uc2dc\uac04 \ucd08\uacfc
20
32100=\ud074\ub77c\uc774\uc5b8\ud2b8\uac00 \uc774\ubbf8 \uc5f0\uacb0\ub428
21
32101=\ud074\ub77c\uc774\uc5b8\ud2b8\uac00 \uc774\ubbf8 \uc5f0\uacb0\uc774 \ub04a\uae40
22
32102=\ud604\uc7ac \ud074\ub77c\uc774\uc5b8\ud2b8\uac00 \uc5f0\uacb0\uc744 \ub04a\ub294 \uc911
23
32103=\uc11c\ubc84\uc5d0 \uc5f0\uacb0\ud560 \uc218 \uc5c6\uc74c
24
32104=\ud074\ub77c\uc774\uc5b8\ud2b8\uac00 \uc5f0\uacb0\ub418\uc9c0 \uc54a\uc74c
25
32105=\uc9c0\uc815\ub41c SocketFactory \uc720\ud615\uc774 \ube0c\ub85c\ucee4 URI\uc640 \uc77c\uce58\ud558\uc9c0 \uc54a\uc74c
26
32106=SSL \uad6c\uc131 \uc624\ub958
27
32107=\ucf5c\ubc31 \uba54\uc18c\ub4dc\ub85c\ubd80\ud130 \uc5f0\uacb0\uc744 \ub04a\ub294 \uac83\uc774 \ud5c8\uc6a9\ub418\uc9c0 \uc54a\uc74c
28
32108=\uc778\uc2dd\ub418\uc9c0 \uc54a\uc740 \ud328\ud0b7
29
32109=\uc5f0\uacb0 \uc720\uc2e4
30
32200=\uc9c0\uc18d \ud30c\uc77c\uc744 \uc774\ubbf8 \uc0ac\uc6a9 \uc911
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_pl.properties (+30 lines)
Added Link Here
1
#/* 
2
# * Copyright (c) 2009, 2012 IBM Corp.
3
# *
4
# * All rights reserved. This program and the accompanying materials
5
# * are made available under the terms of the Eclipse Public License v1.0
6
# * which accompanies this distribution, and is available at
7
# * http://www.eclipse.org/legal/epl-v10.html
8
# *
9
# * Contributors:
10
# *    Dave Locke - initial API and implementation and/or initial documentation
11
# */
12
# NLS_MESSAGEFORMAT_VAR
13
# NLS_ENCODING=UNICODE
14
1=Niepoprawna wersja protoko\u0142u
15
2=Niepoprawny identyfikator klienta
16
3=Broker niedost\u0119pny
17
4=Niepoprawna nazwa u\u017cytkownika lub has\u0142o
18
5=Brak autoryzacji do nawi\u0105zania po\u0142\u0105czenia
19
32000=Przekroczono limit czasu oczekiwania na odpowied\u017a z serwera
20
32100=Po\u0142\u0105czenie z klientem zosta\u0142o ju\u017c nawi\u0105zane
21
32101=Klient ju\u017c si\u0119 roz\u0142\u0105czy\u0142
22
32102=Klient roz\u0142\u0105cza si\u0119
23
32103=Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia z serwerem
24
32104=Po\u0142\u0105czenie z klientem nie jest nawi\u0105zane
25
32105=Podany typ fabryki SocketFactory nie jest zgodny z identyfikatorem URI brokera
26
32106=B\u0142\u0105d konfiguracji protoko\u0142u SSL
27
32107=Roz\u0142\u0105czenie nie jest dozwolone w metodzie procedury zwrotnej
28
32108=Nierozpoznany pakiet
29
32109=Utracono po\u0142\u0105czenie
30
32200=Trwa\u0142o\u015b\u0107 jest ju\u017c w u\u017cyciu
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_pt_BR.properties (+30 lines)
Added Link Here
1
#/* 
2
# * Copyright (c) 2009, 2012 IBM Corp.
3
# *
4
# * All rights reserved. This program and the accompanying materials
5
# * are made available under the terms of the Eclipse Public License v1.0
6
# * which accompanies this distribution, and is available at
7
# * http://www.eclipse.org/legal/epl-v10.html
8
# *
9
# * Contributors:
10
# *    Dave Locke - initial API and implementation and/or initial documentation
11
# */
12
# NLS_MESSAGEFORMAT_VAR
13
# NLS_ENCODING=UNICODE
14
1=Vers\u00e3o de protocolo inv\u00e1lida
15
2=ID de cliente inv\u00e1lido
16
3=Broker indispon\u00edvel
17
4=Nome de usu\u00e1rio ou senha inv\u00e1lidos
18
5=N\u00e3o autorizado a conectar
19
32000=Tempo limite atingido ao aguardar por uma resposta do servidor
20
32100=Cliente j\u00e1 conectado
21
32101=Cliente j\u00e1 desconectado
22
32102=Cliente desconectando atualmente
23
32103=N\u00e3o \u00e9 poss\u00edvel se conectar ao servidor
24
32104=O cliente n\u00e3o est\u00e1 conectado
25
32105=O tipo SocketFactory especificado n\u00e3o corresponde ao URI do broker
26
32106=Erro de configura\u00e7\u00e3o de SSL
27
32107=A desconex\u00e3o n\u00e3o \u00e9 permitida a partir de um m\u00e9todo de retorno de chamada
28
32108=Pacote n\u00e3o reconhecido
29
32109=Conex\u00e3o perdida
30
32200=Persist\u00eancia j\u00e1 em uso
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_ru.properties (+30 lines)
Added Link Here
1
#/* 
2
# * Copyright (c) 2009, 2012 IBM Corp.
3
# *
4
# * All rights reserved. This program and the accompanying materials
5
# * are made available under the terms of the Eclipse Public License v1.0
6
# * which accompanies this distribution, and is available at
7
# * http://www.eclipse.org/legal/epl-v10.html
8
# *
9
# * Contributors:
10
# *    Dave Locke - initial API and implementation and/or initial documentation
11
# */
12
# NLS_MESSAGEFORMAT_VAR
13
# NLS_ENCODING=UNICODE
14
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
15
2=\u041d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u0418\u0414 \u043a\u043b\u0438\u0435\u043d\u0442\u0430
16
3=\u041f\u043e\u0441\u0440\u0435\u0434\u043d\u0438\u043a \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d
17
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
18
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
19
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
20
32100=\u041a\u043b\u0438\u0435\u043d\u0442 \u0443\u0436\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d
21
32101=\u041a\u043b\u0438\u0435\u043d\u0442 \u0443\u0436\u0435 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d
22
32102=\u041a\u043b\u0438\u0435\u043d\u0442 \u043e\u0442\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f
23
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
24
32104=\u041a\u043b\u0438\u0435\u043d\u0442 \u043d\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d
25
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
26
32106=\u041e\u0448\u0438\u0431\u043a\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 SSL
27
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
28
32108=\u041d\u0435\u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u043d\u043d\u044b\u0439 \u043f\u0430\u043a\u0435\u0442
29
32109=\u0421\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043f\u043e\u0442\u0435\u0440\u044f\u043d\u043e
30
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
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_zh_CN.properties (+30 lines)
Added Link Here
1
#/* 
2
# * Copyright (c) 2009, 2012 IBM Corp.
3
# *
4
# * All rights reserved. This program and the accompanying materials
5
# * are made available under the terms of the Eclipse Public License v1.0
6
# * which accompanies this distribution, and is available at
7
# * http://www.eclipse.org/legal/epl-v10.html
8
# *
9
# * Contributors:
10
# *    Dave Locke - initial API and implementation and/or initial documentation
11
# */
12
# NLS_MESSAGEFORMAT_VAR
13
# NLS_ENCODING=UNICODE
14
1=\u65e0\u6548\u534f\u8bae\u7248\u672c
15
2=\u65e0\u6548\u5ba2\u6237\u673a\u6807\u8bc6
16
3=\u4ee3\u7406\u7a0b\u5e8f\u4e0d\u53ef\u7528
17
4=\u9519\u8bef\u7684\u7528\u6237\u540d\u6216\u5bc6\u7801
18
5=\u65e0\u6743\u8fde\u63a5
19
32000=\u7b49\u5f85\u6765\u81ea\u670d\u52a1\u5668\u7684\u54cd\u5e94\u65f6\u8d85\u65f6
20
32100=\u5ba2\u6237\u673a\u5df2\u8fde\u63a5
21
32101=\u5ba2\u6237\u673a\u5df2\u65ad\u5f00\u8fde\u63a5
22
32102=\u5ba2\u6237\u673a\u6b63\u5728\u65ad\u5f00\u8fde\u63a5
23
32103=\u65e0\u6cd5\u8fde\u63a5\u81f3\u670d\u52a1\u5668
24
32104=\u5ba2\u6237\u673a\u672a\u8fde\u63a5
25
32105=\u6307\u5b9a\u7684 SocketFactory \u7c7b\u578b\u4e0e\u4ee3\u7406\u7a0b\u5e8f URI \u4e0d\u5339\u914d
26
32106=SSL \u914d\u7f6e\u9519\u8bef
27
32107=\u4e0d\u5141\u8bb8\u901a\u8fc7\u56de\u8c03\u65b9\u6cd5\u65ad\u5f00\u8fde\u63a5
28
32108=\u4e0d\u53ef\u8bc6\u522b\u7684\u5305
29
32109=\u5df2\u65ad\u5f00\u8fde\u63a5
30
32200=\u6301\u4e45\u6027\u5df2\u5728\u4f7f\u7528\u4e2d
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/nls/messages_zh_TW.properties (+30 lines)
Added Link Here
1
#/* 
2
# * Copyright (c) 2009, 2012 IBM Corp.
3
# *
4
# * All rights reserved. This program and the accompanying materials
5
# * are made available under the terms of the Eclipse Public License v1.0
6
# * which accompanies this distribution, and is available at
7
# * http://www.eclipse.org/legal/epl-v10.html
8
# *
9
# * Contributors:
10
# *    Dave Locke - initial API and implementation and/or initial documentation
11
# */
12
# NLS_MESSAGEFORMAT_VAR
13
# NLS_ENCODING=UNICODE
14
1=\u901a\u8a0a\u5354\u5b9a\u7248\u672c\u7121\u6548
15
2=\u7528\u6236\u7aef ID \u7121\u6548
16
3=\u5206\u914d\u7ba1\u7406\u7cfb\u7d71\u7121\u6cd5\u4f7f\u7528
17
4=\u4f7f\u7528\u8005\u540d\u7a31\u6216\u5bc6\u78bc\u4e0d\u7576
18
5=\u672a\u7372\u6388\u6b0a\u9023\u63a5
19
32000=\u7b49\u5f85\u4f3a\u670d\u5668\u7684\u56de\u61c9\u6642\u903e\u6642
20
32100=\u7528\u6236\u7aef\u5df2\u9023\u63a5
21
32101=\u7528\u6236\u7aef\u5df2\u4e2d\u65b7\u9023\u7dda
22
32102=\u7528\u6236\u7aef\u76ee\u524d\u6b63\u5728\u4e2d\u65b7\u9023\u7dda
23
32103=\u7121\u6cd5\u9023\u63a5\u5230\u4f3a\u670d\u5668
24
32104=\u7528\u6236\u7aef\u672a\u9023\u63a5
25
32105=\u6307\u5b9a\u7684 SocketFactory \u985e\u578b\u8207\u5206\u914d\u7ba1\u7406\u7cfb\u7d71 URI \u4e0d\u7b26
26
32106=SSL \u914d\u7f6e\u932f\u8aa4
27
32107=\u4e0d\u5bb9\u8a31\u8207\u56de\u547c\u65b9\u6cd5\u4e2d\u65b7\u9023\u7dda
28
32108=\u5c01\u5305\u7121\u6cd5\u8fa8\u8b58
29
32109=\u9023\u7dda\u907a\u5931
30
32200=\u6301\u7e8c\u6027\u5df2\u5728\u4f7f\u7528\u4e2d
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/security/SSLSocketFactoryFactory.java (+1352 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.security;
13
14
import java.io.FileInputStream;
15
import java.io.FileNotFoundException;
16
import java.io.IOException;
17
import java.security.KeyManagementException;
18
import java.security.KeyStore;
19
import java.security.KeyStoreException;
20
import java.security.NoSuchAlgorithmException;
21
import java.security.NoSuchProviderException;
22
import java.security.UnrecoverableKeyException;
23
import java.security.cert.CertificateException;
24
import java.util.Hashtable;
25
import java.util.Iterator;
26
import java.util.Properties;
27
import java.util.Set;
28
import java.util.Vector;
29
30
import javax.net.ssl.KeyManager;
31
import javax.net.ssl.KeyManagerFactory;
32
import javax.net.ssl.SSLContext;
33
import javax.net.ssl.SSLSocketFactory;
34
import javax.net.ssl.TrustManager;
35
import javax.net.ssl.TrustManagerFactory;
36
37
import org.eclipse.paho.client.mqttv3.MqttSecurityException;
38
//import org.eclipse.paho.client.mqttv3.internal.comms.MqttDirectException;
39
//import org.eclipse.paho.client.mqttv3.internal.comms.MqttSSLInitException;
40
import org.eclipse.paho.client.mqttv3.logging.Logger;
41
42
43
/**
44
 * An SSLSocketFactoryFactory provides a socket factory and a server socket
45
 * factory that then can be used to create SSL client sockets or SSL server
46
 * sockets.
47
 * <p>
48
 * The SSLSocketFactoryFactory is configured using IBM SSL properties, i.e.
49
 * properties of the format "com.ibm.ssl.propertyName", e.g.
50
 * "com.ibm.ssl.keyStore". The class supports multiple configurations, each
51
 * configuration is identified using a name or configuration ID. The
52
 * configuration ID with "null" is used as a default configuration. When a
53
 * socket factory is being created for a given configuration, properties of that
54
 * configuration are first picked. If a property is not defined there, then that
55
 * property is looked up in the default configuration. Finally, if a property
56
 * element is still not found, then the corresponding system property is
57
 * inspected, i.e. javax.net.ssl.keyStore. If the system property is not set
58
 * either, then the system's default value is used (if available) or an
59
 * exception is thrown.
60
 * <p>
61
 * The SSLSocketFacotryFactory can be reconfigured at any time. A
62
 * reconfiguration does not affect existing socket factories.
63
 * <p>
64
 * All properties share the same key space; i.e. the configuration ID is not
65
 * part of the property keys.
66
 * <p>
67
 * The methods should be called in the following order:
68
 * <ol>
69
 * <li><b>isSupportedOnJVM()</b>: to check whether this class is supported on
70
 * the runtime platform. Not all runtimes support SSL/TLS.</li>
71
 * <li><b>SSLSocketFactoryFactory()</b>: the constructor. Clients 
72
 * (in the same JVM) may share an SSLSocketFactoryFactory, or have one each.</li>
73
 * <li><b>initialize(properties, configID)</b>: to initialize this object with
74
 * the required SSL properties for a configuration. This may be called multiple
75
 * times, once for each required configuration.It may be called again to change the required SSL
76
 * properties for a particular configuration</li>
77
 * <li><b>getEnabledCipherSuites(configID)</b>: to later set the enabled
78
 * cipher suites on the socket [see below].</li>
79
 * </ol>
80
 * <ul>
81
 * <li><i>For an MQTT server:</i></li>
82
 * <ol>
83
 * <li><b>getKeyStore(configID)</b>: Optionally, to check that if there is no
84
 * keystore, then that all the enabled cipher suits are anonymous.</li>
85
 * <li><b>createServerSocketFactory(configID)</b>: to create an
86
 * SSLServerSocketFactory.</li>
87
 * <li><b>getClientAuthentication(configID)</b>: to later set on the
88
 * SSLServerSocket (itself created from the SSLServerSocketFactory) whether
89
 * client authentication is needed.</li>
90
 * </ol>
91
 * <li><i>For an MQTT client:</i></li>
92
 * <ol>
93
 * <li><b>createSocketFactory(configID)</b>: to create an SSLSocketFactory.</li>
94
 * </ol>
95
 * </ul>
96
 */
97
public class SSLSocketFactoryFactory {
98
	private final static String CLASS_NAME = "org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory";
99
	/**
100
	 * Property keys specific to the client).
101
	 */
102
	public final static String SSLPROTOCOL="com.ibm.ssl.protocol";
103
	public final static String JSSEPROVIDER="com.ibm.ssl.contextProvider";
104
	public final static String KEYSTORE="com.ibm.ssl.keyStore";
105
	public final static String KEYSTOREPWD="com.ibm.ssl.keyStorePassword";
106
	public final static String KEYSTORETYPE="com.ibm.ssl.keyStoreType";
107
	public final static String KEYSTOREPROVIDER="com.ibm.ssl.keyStoreProvider";
108
	public final static String KEYSTOREMGR="com.ibm.ssl.keyManager";
109
	public final static String TRUSTSTORE="com.ibm.ssl.trustStore";
110
	public final static String TRUSTSTOREPWD="com.ibm.ssl.trustStorePassword";
111
	public final static String TRUSTSTORETYPE="com.ibm.ssl.trustStoreType";
112
	public final static String TRUSTSTOREPROVIDER="com.ibm.ssl.trustStoreProvider";
113
	public final static String TRUSTSTOREMGR="com.ibm.ssl.trustManager";
114
	public final static String CIPHERSUITES="com.ibm.ssl.enabledCipherSuites";
115
	public final static String CLIENTAUTH="com.ibm.ssl.clientAuthentication";
116
	
117
	/**
118
	 * Property keys used for java system properties
119
	 */
120
	public final static String SYSKEYSTORE="javax.net.ssl.keyStore";
121
	public final static String SYSKEYSTORETYPE="javax.net.ssl.keyStoreType";
122
	public final static String SYSKEYSTOREPWD="javax.net.ssl.keyStorePassword";
123
	public final static String SYSTRUSTSTORE="javax.net.ssl.trustStore";
124
	public final static String SYSTRUSTSTORETYPE="javax.net.ssl.trustStoreType";
125
	public final static String SYSTRUSTSTOREPWD="javax.net.ssl.trustStorePassword";
126
	public final static String SYSKEYMGRALGO="ssl.KeyManagerFactory.algorithm";
127
	public final static String SYSTRUSTMGRALGO="ssl.TrustManagerFactory.algorithm";
128
	
129
130
	public final static String DEFAULT_PROTOCOL = "TLS";  // "SSL_TLS" is not supported by DesktopEE
131
	
132
	private final static String propertyKeys[] = { SSLPROTOCOL, JSSEPROVIDER,
133
			KEYSTORE, KEYSTOREPWD, KEYSTORETYPE, KEYSTOREPROVIDER, KEYSTOREMGR, 
134
			TRUSTSTORE, TRUSTSTOREPWD, TRUSTSTORETYPE, TRUSTSTOREPROVIDER, 
135
			TRUSTSTOREMGR, CIPHERSUITES, CLIENTAUTH};
136
137
	private Hashtable configs; // a hashtable that maps configIDs to properties.
138
139
	private Properties defaultProperties;
140
141
	private static final byte[] key = { (byte) 0x9d, (byte) 0xa7, (byte) 0xd9,
142
		(byte) 0x80, (byte) 0x05, (byte) 0xb8, (byte) 0x89, (byte) 0x9c };
143
144
	private static final String xorTag = "{xor}";
145
	
146
	private Logger logger = null;
147
148
149
	/**
150
	 * Not all of the JVM/Platforms support all of its
151
	 * security features. This method determines if is supported.
152
	 * 
153
	 * @return whether dependent classes can be instantiated on the current
154
	 *         JVM/platform.
155
	 * 
156
	 * @throws Error
157
	 *             if any unexpected error encountered whilst checking. Note
158
	 *             this should not be a ClassNotFoundException, which should
159
	 *             cause the method to return false.
160
	 */
161
	public static boolean isSupportedOnJVM() throws LinkageError, ExceptionInInitializerError {
162
		String requiredClassname = "javax.net.ssl.SSLServerSocketFactory";
163
		try {
164
			Class.forName(requiredClassname);
165
		} catch (ClassNotFoundException e) {
166
			return false;
167
		}
168
		return true;
169
	}
170
171
172
	/**
173
	 * Create new instance of class.
174
	 * Constructor used by clients.
175
	 */
176
	public SSLSocketFactoryFactory() {
177
		configs = new Hashtable();
178
	}
179
	
180
	/**
181
	 * Create new instance of class.
182
	 * Constructor used by the broker.
183
	 */
184
	public SSLSocketFactoryFactory(Logger logger) {
185
		this();
186
		this.logger = logger;
187
	}
188
189
	/**
190
	 * Checks whether a key belongs to the supported IBM SSL property keys.
191
	 * 
192
	 * @param key
193
	 * @return whether a key belongs to the supported IBM SSL property keys.
194
	 */
195
	private boolean keyValid(String key) {
196
		int i = 0;
197
		while (i < propertyKeys.length) {
198
			if (propertyKeys[i].equals(key)) {
199
				break;
200
			}
201
			++i;
202
		}
203
		return i < propertyKeys.length;
204
	}
205
206
	/**
207
	 * Checks whether the property keys belong to the supported IBM SSL property
208
	 * key set.
209
	 * 
210
	 * @param properties
211
	 * @throws IllegalArgumentException
212
	 *             if any of the properties is not a valid IBM SSL property key.
213
	 */
214
	private void checkPropertyKeys(Properties properties)
215
			throws IllegalArgumentException {
216
		Set keys = properties.keySet();
217
		Iterator i = keys.iterator();
218
		while (i.hasNext()) {
219
			String k = (String) i.next();
220
			if (!keyValid(k)) {
221
				throw new IllegalArgumentException(k + " is not a valid IBM SSL property key.");
222
			}
223
		}
224
	}
225
226
	/**
227
	 * Convert byte array to char array, where each char is constructed from two
228
	 * bytes.
229
	 * 
230
	 * @param b
231
	 *            byte array
232
	 * @return char array
233
	 */
234
	public static char[] toChar(byte[] b) {
235
		if(b==null) return null;
236
		char[] c= new char[b.length/2]; 
237
		int i=0; int j=0;
238
		while(i<b.length) {
239
			c[j++] = (char) ((b[i++] & 0xFF) + ((b[i++] & 0xFF)<<8));
240
		}
241
		return c;
242
	}
243
	
244
	/**
245
	 * Convert char array to byte array, where each char is split into two
246
	 * bytes.
247
	 * 
248
	 * @param c
249
	 *            char array
250
	 * @return byte array
251
	 */
252
	public static byte[] toByte(char[] c) {
253
		if(c==null) return null;
254
		byte[] b=new byte[c.length*2];
255
		int i=0; int j=0;
256
		while(j<c.length) {
257
			b[i++] = (byte) (c[j] & 0xFF);
258
			b[i++] = (byte) ((c[j++] >> 8)& 0xFF);
259
		}
260
		return b;
261
	}
262
	
263
	/**
264
	 * Obfuscates the password using a simple and not very secure XOR mechanism.
265
	 * This should not be used for cryptographical purpose, it's a simple
266
	 * scrambler to obfuscate clear-text passwords.
267
	 * 
268
	 * @see org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory#deObfuscate
269
	 * 
270
	 * @param password
271
	 *            The password to be encrypted, as a char[] array.
272
	 * @return An obfuscated password as a String.
273
	 */
274
	public static String obfuscate(char[] password) {
275
		if (password == null)
276
			return null;
277
		byte[] bytes = toByte(password);
278
		for (int i = 0; i < bytes.length; i++) {
279
			bytes[i] = (byte) ((bytes[i] ^ key[i % key.length]) & 0x00ff);
280
		}
281
		String encryptedValue = xorTag
282
				+ new String(SimpleBase64Encoder.encode(bytes));
283
		return encryptedValue;
284
	}
285
286
	/**
287
	 * The inverse operation of obfuscate: returns a cleartext password that was
288
	 * previously obfuscated using the XOR scrambler.
289
	 * 
290
	 * @see org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory#obfuscate
291
	 * 
292
	 * @param ePassword
293
	 *            An obfuscated password.
294
	 * @return An array of char, containing the clear text password.
295
	 */
296
	public static char[] deObfuscate(String ePassword) {
297
		if (ePassword == null)
298
			return null;
299
		byte[] bytes = null;
300
		try {
301
			bytes = SimpleBase64Encoder.decode(ePassword.substring(xorTag
302
					.length()));
303
		} catch (Exception e) {
304
			return null;
305
		}
306
307
		for (int i = 0; i < bytes.length; i++) {
308
			bytes[i] = (byte) ((bytes[i] ^ key[i % key.length]) & 0x00ff);
309
		}
310
		return toChar(bytes);
311
	}
312
313
	/**
314
	 * Converts an array of ciphers into a single String.
315
	 * 
316
	 * @param ciphers
317
	 *            The array of cipher names.
318
	 * @return A string containing the name of the ciphers, separated by comma.
319
	 */
320
	public static String packCipherSuites(String[] ciphers) {
321
		String cipherSet=null;
322
		if (ciphers != null) {
323
			StringBuffer buf = new StringBuffer();
324
			for (int i = 0; i < ciphers.length; i++) {
325
				buf.append(ciphers[i]);
326
				if (i < ciphers.length - 1) {
327
					buf.append(',');
328
				}
329
			}
330
			cipherSet = buf.toString();
331
		}
332
		return cipherSet;
333
	}
334
335
	/**
336
	 * Inverse operation of packCipherSuites: converts a string of cipher names
337
	 * into an array of cipher names
338
	 * 
339
	 * @param ciphers
340
	 *            A list of ciphers, separated by comma.
341
	 * @return An array of string, each string containing a single cipher name.
342
	 */
343
	public static String[] unpackCipherSuites(String ciphers) {
344
		// can't use split as split is not available on all java platforms.
345
		if(ciphers==null) return null;
346
		Vector c=new Vector();
347
		int i=ciphers.indexOf(',');
348
		int j=0;
349
		// handle all commas.
350
		while(i>-1) {
351
			// add stuff before and up to (but not including) the comma.
352
			c.add(ciphers.substring(j, i));
353
			j=i+1; // skip the comma.
354
			i=ciphers.indexOf(',',j);
355
		}
356
		// add last element after the comma or only element if no comma is present.
357
		c.add(ciphers.substring(j));
358
		String[] s = new String[c.size()];
359
		c.toArray(s);
360
		return s;
361
	}
362
363
	/**
364
	 * Obfuscate any key & trust store passwords within the given properties.
365
	 * 
366
	 * @see org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory#obfuscate
367
	 * 
368
	 * @param p
369
	 *            properties
370
	 */
371
	private void convertPassword(Properties p) {
372
		String pw = p.getProperty(KEYSTOREPWD);
373
		if (pw != null && !pw.startsWith(xorTag)) {
374
			String epw = obfuscate(pw.toCharArray());
375
			p.put(KEYSTOREPWD, epw);
376
		}
377
		pw = p.getProperty(TRUSTSTOREPWD);
378
		if (pw != null && !pw.startsWith(xorTag)) {
379
			String epw = obfuscate(pw.toCharArray());
380
			p.put(TRUSTSTOREPWD, epw);
381
		}
382
	}
383
384
	/**
385
	 * Returns the properties object for configuration configID or creates a new
386
	 * one if required.
387
	 * 
388
	 * @param configID
389
	 *            The configuration identifier for selecting a configuration or
390
	 *            null for the default configuration.
391
	 * @return the properties object for configuration configID
392
	 */
393
//	private Properties getOrCreate(String configID) {
394
//		Properties res = null;
395
//		if (configID == null) {
396
//			if (this.defaultProperties == null) {
397
//				this.defaultProperties = new Properties();
398
//			}
399
//			res = this.defaultProperties;
400
//		} else {
401
//			res = (Properties) this.configs.get(configID);
402
//			if (res == null) {
403
//				res = new Properties();
404
//				this.configs.put(configID, res);
405
//			}
406
//		}
407
//		return res;
408
//	}
409
410
	/**
411
	 * Initializes the SSLSocketFactoryFactory with the provided properties for
412
	 * the provided configuration.
413
	 * 
414
	 * @param props
415
	 *            A properties object containing IBM SSL properties that are
416
	 *            qualified by one or more configuration identifiers.
417
	 * @param configID
418
	 *            The configuration identifier for selecting a configuration or
419
	 *            null for the default configuration.
420
	 * @throws IllegalArgumentException
421
	 *             if any of the properties is not a valid IBM SSL property key.
422
	 */
423
	public void initialize(Properties props, String configID)
424
			throws IllegalArgumentException {
425
		checkPropertyKeys(props);
426
		// copy the properties.
427
		Properties p = new Properties();
428
		p.putAll(props);
429
		convertPassword(p);
430
		if (configID != null) {
431
			this.configs.put(configID, p);
432
		} else {
433
			this.defaultProperties = p;
434
		}
435
	}
436
437
	/**
438
	 * Merges the given IBM SSL properties into the existing configuration,
439
	 * overwriting existing properties. This method is used to selectively
440
	 * change properties for a given configuration. The method throws an
441
	 * IllegalArgumentException if any of the properties is not a valid IBM SSL
442
	 * property key.
443
	 * 
444
	 * @param props
445
	 *            A properties object containing IBM SSL properties
446
	 * @param configID
447
	 *            The configuration identifier for selecting a configuration or
448
	 *            null for the default configuration.
449
	 * @throws IllegalArgumentException
450
	 *             if any of the properties is not a valid IBM SSL property key.
451
	 */
452
	public void merge(Properties props, String configID)
453
			throws IllegalArgumentException {
454
		checkPropertyKeys(props);
455
		Properties p = this.defaultProperties;
456
		if (configID == null) {
457
			p = (Properties) this.configs.get(configID);
458
		}
459
		if (p == null) {
460
			p = new Properties();
461
		}
462
		convertPassword(props);
463
		p.putAll(props);
464
		if (configID != null) {
465
			this.configs.put(configID, p);
466
		} else {
467
			this.defaultProperties = p;
468
		}
469
470
	}
471
472
	/**
473
	 * Remove the configuration of a given configuration identifier.
474
	 * 
475
	 * @param configID
476
	 *            The configuration identifier for selecting a configuration or
477
	 *            null for the default configuration.
478
	 * @return true, if the configuation could be removed.
479
	 */
480
	public boolean remove(String configID) {
481
		boolean res = false;
482
		if (configID != null) {
483
			res = this.configs.remove(configID) != null;
484
		} else {
485
			if(null != this.defaultProperties) {
486
				res = true;
487
				this.defaultProperties = null;
488
			}
489
		}
490
		return res;
491
	}
492
493
	/**
494
	 * Returns the configuration of the SSLSocketFactoryFactory for a given
495
	 * configuration. Note that changes in the property are reflected in the
496
	 * SSLSocketFactoryFactory.
497
	 * 
498
	 * @param configID
499
	 *            The configuration identifier for selecting a configuration or
500
	 *            null for the default configuration.
501
	 * @return A property object containing the current configuration of the
502
	 *         SSLSocketFactoryFactory.  Note that it could be null.
503
	 */
504
	public Properties getConfiguration(String configID) {
505
		return (Properties) (configID == null ? this.defaultProperties
506
				: this.configs.get(configID));
507
	}
508
509
	/**
510
	 * @return Returns the set of configuration IDs that exist in the SSLSocketFactoryFactory.
511
	 */
512
//	public String[] getConfigurationIDs() {
513
//		Set s = this.configs.keySet();
514
//		String[] configs = new String[s.size()];
515
//		configs = (String[]) s.toArray(configs);
516
//		return configs;
517
//	}
518
519
	/**
520
	 * If the value is not null, then put it in the properties object using the
521
	 * key. If the value is null, then remove the entry in the properties object
522
	 * with the key.
523
	 * 
524
	 * @param p
525
	 * @param key
526
	 * @param value
527
	 */
528
//	private final void putOrRemove(Properties p, String key, String value) {
529
//		if (value == null) {
530
//			p.remove(key);
531
//		} else {
532
//			p.put(key, value);
533
//		}
534
//	}
535
536
	/**
537
	 * Sets the SSL protocol variant. If protocol is NULL then an existing value
538
	 * will be removed.
539
	 * 
540
	 * @param configID
541
	 *            The configuration identifier for selecting a configuration or
542
	 *            null for the default configuration.
543
	 * @param protocol
544
	 *            One of SSL, SSLv3, TLS, TLSv1, SSL_TLS
545
	 */
546
//	public void setSSLProtocol(String configID, String protocol) {
547
//		Properties p = getOrCreate(configID);
548
//		putOrRemove(p, SSLPROTOCOL, protocol);
549
//	}
550
551
	/**
552
	 * Sets the JSSE context provider. If provider is null, then an existing
553
	 * value will be removed.
554
	 * 
555
	 * @param configID
556
	 *            The configuration identifier for selecting a configuration or
557
	 *            null for the default configuration.
558
	 * @param provider
559
	 *            The JSSE provider. For example "IBMJSSE2" or "SunJSSE".
560
	 */
561
//	public void setJSSEProvider(String configID, String provider) {
562
//		Properties p = getOrCreate(configID);
563
//		putOrRemove(p, JSSEPROVIDER, provider);
564
//	}
565
566
	/**
567
	 * Sets the filename of the keyStore object. A null value is ignored.
568
	 * 
569
	 * @param configID
570
	 *            The configuration identifier for selecting a configuration or
571
	 *            null for the default configuration.
572
	 * @param keyStore
573
	 *            A filename that points to a valid keystore.
574
	 */
575
//	public void setKeyStore(String configID, String keyStore) {
576
//		if (keyStore == null)
577
//			return;
578
//		Properties p = getOrCreate(configID);
579
//		putOrRemove(p, KEYSTORE, keyStore);
580
//	}
581
582
	/**
583
	 * Sets the password that is used for the keystore. The password must be
584
	 * provided in plain text, but it will be stored internally in a scrambled
585
	 * XOR format.
586
	 * 
587
	 * @see org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory#obfuscate
588
	 * 
589
	 * @param configID
590
	 *            The configuration identifier for selecting a configuration or
591
	 *            null for the default configuration.
592
	 * @param password
593
	 *            The keystore password
594
	 */
595
//	public void setKeyStorePassword(String configID, char[] password) {
596
//		if (password == null)
597
//			return;
598
//		Properties p = getOrCreate(configID);
599
//		// convert password, using XOR-based scrambling.
600
//		String ePasswd = obfuscate(password);
601
//		for(int i=0;i<password.length;i++) {
602
//			password[i]=' ';
603
//		}
604
//		putOrRemove(p, KEYSTOREPWD, ePasswd);
605
//	}
606
607
	/**
608
	 * Sets the keystore provider. The corresponding provider must be installed
609
	 * in the system. Example values: "IBMJCE" or "IBMJCEFIPS".
610
	 * 
611
	 * @param configID
612
	 *            The configuration identifier for selecting a configuration or
613
	 *            null for the default configuration.
614
	 * @param provider
615
	 *            The name of a java cryptography extension
616
	 */
617
//	public void setKeyStoreProvider(String configID, String provider) {
618
//		Properties p = getOrCreate(configID);
619
//		putOrRemove(p, KEYSTOREPROVIDER, provider);
620
//	}
621
622
	/**
623
	 * Sets the keystore type. For example, PKCS12, JKS or JCEKS. The types that
624
	 * are supported depend on the keystore provider.
625
	 * 
626
	 * @param configID
627
	 *            The configuration identifier for selecting a configuration or
628
	 *            null for the default configuration.
629
	 * @param type
630
	 *            The keystore type
631
	 */
632
//	public void setKeyStoreType(String configID, String type) {
633
//		Properties p = getOrCreate(configID);
634
//		putOrRemove(p, KEYSTORETYPE, type);
635
//	}
636
637
	/**
638
	 * Sets a custom key manager and the algorithm that it uses. The keymanager
639
	 * is specified in the format "algorithm|provider", for example
640
	 * "IbmX509|IBMJSSE2". The provider might be empty, in which case the
641
	 * default provider is configured with the specified algorithm. The key
642
	 * manager must implement the javax.net.ssl.X509KeyManager interface.
643
	 * 
644
	 * @param configID
645
	 *            The configuration identifier for selecting a configuration or
646
	 *            null for the default configuration.
647
	 * @param keymanager
648
	 *            An algorithm, provider pair as secified above.
649
	 */
650
//	public void setCustomKeyManager(String configID, String keymanager) {
651
//		Properties p = getOrCreate(configID);
652
//		putOrRemove(p, CUSTOMKEYMGR, keymanager);
653
//	}
654
655
	/**
656
	 * Sets the filename of the truststore object.
657
	 * 
658
	 * @param configID
659
	 *            The configuration identifier for selecting a configuration or
660
	 *            null for the default configuration.
661
	 * @param trustStore
662
	 *            A filename that points to a valid truststore.
663
	 */
664
//	public void setTrustStore(String configID, String trustStore) {
665
//		Properties p = getOrCreate(configID);
666
//		putOrRemove(p, TRUSTSTORE, trustStore);
667
//	}
668
669
	/**
670
	 * Sets the password that is used for the truststore. The password must be
671
	 * provided in plain text, but it will be stored internally in a scrambled
672
	 * XOR format.
673
	 * 
674
	 * @see org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory#obfuscate
675
	 * 
676
	 * @param configID
677
	 *            The configuration identifier for selecting a configuration or
678
	 *            null for the default configuration.
679
	 * @param password
680
	 *            The truststore password.
681
	 */
682
//	public void setTrustStorePassword(String configID, char[] password) {
683
//		Properties p = getOrCreate(configID);
684
//		// convert password, using XOR-based scrambling.
685
//		String ePasswd = obfuscate(password);
686
//		for(int i=0;i<password.length;i++) {
687
//			password[i]=' ';
688
//		}
689
//		putOrRemove(p, TRUSTSTOREPWD, ePasswd);
690
//	}
691
692
	/**
693
	 * Sets the truststore provider. The corresponding provider must be
694
	 * installed in the system. Example values: "IBMJCE" or "IBMJCEFIPS".
695
	 * 
696
	 * @param configID
697
	 *            The configuration identifier for selecting a configuration or
698
	 *            null for the default configuration.
699
	 * @param provider
700
	 *            The name of a java cryptography extension.
701
	 */
702
//	public void setTrustStoreProvider(String configID, String provider) {
703
//		Properties p = getOrCreate(configID);
704
//		putOrRemove(p, TRUSTSTOREPROVIDER, provider);
705
//	}
706
707
	/**
708
	 * Sets the truststore type. For example, PKCS12, JKS or JCEKS. The types
709
	 * that are supported depend on the truststore provider.
710
	 * 
711
	 * @param configID
712
	 *            The configuration identifier for selecting a configuration or
713
	 *            null for the default configuration.
714
	 * @param type
715
	 *            The truststore type.
716
	 */
717
//	public void setTrustStoreType(String configID, String type) {
718
//		Properties p = getOrCreate(configID);
719
//		putOrRemove(p, TRUSTSTORETYPE, type);
720
//	}
721
722
	/**
723
	 * Sets a custom trust managers and the algorithm that it uses. The
724
	 * trustmanager is specified in the format "algorithm|provider", for example
725
	 * "IbmX509|IBMJSSE2". The provider might be empty, in which case the
726
	 * default provider is configured with the specified algorithm. The trust
727
	 * manager must implement the javax.net.ssl.X509TrustManager interface.
728
	 * 
729
	 * @param configID
730
	 *            The configuration identifier for selecting a configuration or
731
	 *            null for the default configuration.
732
	 * @param trustmanager
733
	 *            An algorithm, provider pair as secified above.
734
	 */
735
//	public void setCustomTrustManager(String configID, String trustmanager) {
736
//		Properties p = getOrCreate(configID);
737
//		putOrRemove(p, CUSTOMTRUSTMGR, trustmanager);
738
//	}
739
740
	/**
741
	 * Sets the list of enabled ciphers. For a list of acceptable values, see
742
	 * the documentation of the underlying JSSE.
743
	 * 
744
	 * @param configID
745
	 *            The configuration identifier for selecting a configuration or
746
	 *            null for the default configuration.
747
	 * @param ciphers
748
	 *            An array of cipher suite names such as
749
	 *            SSL_RSA_WITH_AES_128_CBC_SHA.
750
	 */
751
//	public void setEnabledCipherSuites(String configID, String[] ciphers) {
752
//		if (ciphers == null)
753
//			return;
754
//		Properties p = getOrCreate(configID);
755
//		String cipherSet = packCipherSuites(ciphers);
756
//		putOrRemove(p, CIPHERSUITES, cipherSet);
757
//	}
758
759
	/**
760
	 * Specifies whether the client is required to provide a valid certificate
761
	 * to the client during SSL negotiation.
762
	 * 
763
	 * @param configID
764
	 *            The configuration identifier for selecting a configuration or
765
	 *            null for the default configuration.
766
	 * @param clientAuth
767
	 *            true, if clients are required to authenticate, false
768
	 *            otherwise.
769
	 */
770
//	public void setClientAuthentication(String configID, boolean clientAuth) {
771
//		Properties p = getOrCreate(configID);
772
//		p.put(CLIENTAUTH, Boolean.toString(clientAuth));
773
//	}
774
775
	/**
776
	 * Returns the property of a given key or null if it doesn't exist. It first
777
	 * scans the indicated configuration, then the default configuration, then
778
	 * the system properties.
779
	 * 
780
	 * @param configID
781
	 *            The configuration identifier for selecting a configuration or
782
	 *            null for the default configuration.
783
	 * @param ibmKey
784
	 * @param sysProperty
785
	 *            The key for the System property.
786
	 * @return the property of a given key or null if it doesn't exist.
787
	 */
788
	private String getProperty(String configID, String ibmKey, String sysProperty) {
789
		String res = null;
790
		res = getPropertyFromConfig(configID, ibmKey);
791
		if ( res != null ) {
792
			return res;
793
		}
794
		// scan system property, if it exists.
795
		if (sysProperty != null) {
796
			res = System.getProperty(sysProperty);
797
		}
798
		return res;
799
	}
800
801
	/**
802
	 * Returns the property of a given key or null if it doesn't exist. It first
803
	 * scans the indicated configuration, then the default configuration
804
	 * 
805
	 * @param configID
806
	 *            The configuration identifier for selecting a configuration or
807
	 *            null for the default configuration.
808
	 * @param ibmKey
809
	 * @return the property of a given key or null if it doesn't exist. It first
810
	 *         scans the indicated configuration, then the default configuration
811
	 */
812
	private String getPropertyFromConfig(String configID, String ibmKey) {
813
		String res = null;
814
		Properties p =null;
815
		if(configID!=null) {;
816
			p = (Properties) configs.get(configID);
817
		}
818
		if (p != null) {
819
			res = p.getProperty(ibmKey);
820
			if (res != null)
821
				return res;
822
		}
823
		// not found in config. try default properties.
824
		p = (Properties) this.defaultProperties;
825
		if (p != null) {
826
			res = p.getProperty(ibmKey);
827
			if (res != null)
828
				return res;
829
		}
830
		return res;
831
	}
832
833
	/**
834
	 * Gets the SSL protocol variant of the indicated configuration or the
835
	 * default configuration.
836
	 * 
837
	 * @param configID
838
	 *            The configuration identifier for selecting a configuration or
839
	 *            null for the default configuration.
840
	 * @return The SSL protocol variant.
841
	 */
842
	public String getSSLProtocol(String configID) {
843
		return getProperty(configID, SSLPROTOCOL, null);
844
	}
845
846
	/**
847
	 * Gets the JSSE provider of the indicated configuration
848
	 * 
849
	 * @param configID
850
	 *            The configuration identifier for selecting a configuration or
851
	 *            null for the default configuration.
852
	 * @return The JSSE provider.
853
	 */
854
	public String getJSSEProvider(String configID) {
855
		return getProperty(configID, JSSEPROVIDER, null);
856
	}
857
858
//	/**
859
//	 * Get the XPD Keystore if running on the XPD platform (otherwise null).
860
//	 * 
861
//	 * @return the XPD Keystore if running on the XPD platform (otherwise null).
862
//	 * @throws MqttDirectException
863
//	 */
864
//	private KeyStore getXPDKeystore() throws MqttDirectException {
865
//		KeyStore keyStore = null;
866
//		try {
867
//			Class secPlatClass = Class.forName("com.ibm.rcp.security.auth.SecurePlatform");
868
//			Method m = secPlatClass.getMethod("getKeyStore", null);
869
//			Object secPlat = m.invoke(null,null); // getKeyStore is static
870
//			m = secPlatClass.getMethod("isLoggedIn", null);
871
//			Boolean b = (Boolean) m.invoke(secPlat, null);
872
//			if (b.booleanValue()) {
873
//				// login to secure platform was done.
874
//				m = secPlatClass.getMethod("getKeyStore", null);
875
//				keyStore = (KeyStore) m.invoke(secPlat, null);
876
//			}
877
//		} catch (ClassNotFoundException e) {
878
//			/*
879
//			 * DEVELOPER NOTE: This is not an error. This means that we are not
880
//			 * running on XPD runtime and therefore we can not get XPD keystore.
881
//			 * [Next step for the caller, is try to get the keystore from System
882
//			 * properties (see getKeyStore() method).]
883
//			 */
884
//		} catch (IllegalAccessException e) {
885
//			Object[] inserts = { e.getLocalizedMessage() };
886
//			throw new MqttSSLInitException(3026, inserts, e);
887
//		} catch (SecurityException e) {
888
//			Object[] inserts = { e.getLocalizedMessage() };
889
//			throw new MqttSSLInitException(3026, inserts, e);
890
//		} catch (NoSuchMethodException e) {
891
//			Object[] inserts = { e.getLocalizedMessage() };
892
//			throw new MqttSSLInitException(3026, inserts, e);
893
//		} catch (IllegalArgumentException e) {
894
//			Object[] inserts = { e.getLocalizedMessage() };
895
//			throw new MqttSSLInitException(3026, inserts, e);
896
//		} catch (InvocationTargetException e) {
897
//			Object[] inserts = { e.getLocalizedMessage() };
898
//			throw new MqttSSLInitException(3026, inserts, e);
899
//		}
900
//		return keyStore;
901
//	}
902
903
	/**
904
	 * Gets the name of the keystore file that is used.
905
	 * 
906
	 * @param configID
907
	 *            The configuration identifier for selecting a configuration or
908
	 *            null for the default configuration.
909
	 * @return The name of the file that contains the keystore.
910
	 */
911
	public String getKeyStore(String configID) { //throws MqttDirectException {
912
		String ibmKey = KEYSTORE;
913
		String sysProperty = SYSKEYSTORE;
914
		
915
		String res = null;
916
		res = getPropertyFromConfig(configID, ibmKey);
917
		if ( res != null ) {
918
			return res;
919
		}
920
		
921
//		// check for the XPD keystore here
922
//		if ( ibmKey != null && ibmKey.equals(KEYSTORE) ) {
923
//			KeyStore keyStore = getXPDKeystore();
924
//			if (keyStore != null)
925
//				return res = "Lotus Expeditor";
926
//		}
927
		
928
		// scan system property, if it exists.
929
		if (sysProperty != null) {
930
			res = System.getProperty(sysProperty);
931
		}
932
		
933
		return res;
934
	}
935
936
	/**
937
	 * Gets the plain-text password that is used for the keystore.
938
	 * 
939
	 * @param configID
940
	 *            The configuration identifier for selecting a configuration or
941
	 *            null for the default configuration.
942
	 * @return The password in plain text.
943
	 */
944
	public char[] getKeyStorePassword(String configID) {
945
		String pw = getProperty(configID, KEYSTOREPWD, SYSKEYSTOREPWD);
946
		char[] r=null;
947
		if (pw!=null) {
948
			if (pw.startsWith(xorTag)) {
949
				r = deObfuscate(pw);
950
			} else {
951
				r = pw.toCharArray();
952
			}
953
		}
954
		return r;
955
	}
956
957
	/**
958
	 * Gets the type of keystore.
959
	 * 
960
	 * @param configID
961
	 *            The configuration identifier for selecting a configuration or
962
	 *            null for the default configuration.
963
	 * @return The keystore type.
964
	 */
965
	public String getKeyStoreType(String configID) {
966
		return getProperty(configID, KEYSTORETYPE, SYSKEYSTORETYPE);
967
	}
968
969
	/**
970
	 * Gets the keystore provider.
971
	 * 
972
	 * @param configID
973
	 *            The configuration identifier for selecting a configuration or
974
	 *            null for the default configuration.
975
	 * @return The name of the keystore provider.
976
	 */
977
	public String getKeyStoreProvider(String configID) {
978
		return getProperty(configID, KEYSTOREPROVIDER, null);
979
	}
980
981
	/**
982
	 * Gets the key manager algorithm that is used.
983
	 * 
984
	 * @param configID
985
	 *            The configuration identifier for selecting a configuration or
986
	 *            null for the default configuration.
987
	 * @return The key manager algorithm.
988
	 */
989
	public String getKeyManager(String configID) {
990
		return getProperty(configID, KEYSTOREMGR, SYSKEYMGRALGO);
991
	}
992
	
993
	/**
994
	 * Gets the name of the truststore file that is used.
995
	 * 
996
	 * @param configID
997
	 *            The configuration identifier for selecting a configuration or
998
	 *            null for the default configuration.
999
	 * @return The name of the file that contains the truststore.
1000
	 */
1001
	public String getTrustStore(String configID) {
1002
		return getProperty(configID, TRUSTSTORE, SYSTRUSTSTORE);
1003
	}
1004
1005
	/**
1006
	 * Gets the plain-text password that is used for the truststore.
1007
	 * 
1008
	 * @param configID
1009
	 *            The configuration identifier for selecting a configuration or
1010
	 *            null for the default configuration.
1011
	 * @return The password in plain text.
1012
	 */
1013
	public char[] getTrustStorePassword(String configID) {
1014
		String pw = getProperty(configID, TRUSTSTOREPWD, SYSTRUSTSTOREPWD);
1015
		char[] r=null;
1016
		if (pw!=null) {
1017
			if(pw.startsWith(xorTag)) {
1018
				r = deObfuscate(pw);
1019
			} else {
1020
				r = pw.toCharArray();
1021
			}
1022
		}
1023
		return r;
1024
	}
1025
1026
	/**
1027
	 * Gets the type of truststore.
1028
	 * 
1029
	 * @param configID
1030
	 *            The configuration identifier for selecting a configuration or
1031
	 *            null for the default configuration.
1032
	 * @return The truststore type.
1033
	 */
1034
	public String getTrustStoreType(String configID) {
1035
		return getProperty(configID, TRUSTSTORETYPE, null);
1036
	}
1037
1038
	/**
1039
	 * Gets the truststore provider.
1040
	 * 
1041
	 * @param configID
1042
	 *            The configuration identifier for selecting a configuration or
1043
	 *            null for the default configuration.
1044
	 * @return The name of the truststore provider.
1045
	 */
1046
	public String getTrustStoreProvider(String configID) {
1047
		return getProperty(configID, TRUSTSTOREPROVIDER, null);
1048
	}
1049
1050
	/**
1051
	 * Gets the trust manager algorithm that is used.
1052
	 * 
1053
	 * @param configID
1054
	 *            The configuration identifier for selecting a configuration or
1055
	 *            null for the default configuration.
1056
	 * @return The trust manager algorithm.
1057
	 */
1058
	public String getTrustManager(String configID) {
1059
		return getProperty(configID, TRUSTSTOREMGR, SYSTRUSTMGRALGO);
1060
	}
1061
	
1062
	/**
1063
	 * Returns an array with the enabled ciphers.
1064
	 * 
1065
	 * @param configID
1066
	 *            The configuration identifier for selecting a configuration or
1067
	 *            null for the default configuration.
1068
	 * @return an array with the enabled ciphers
1069
	 */
1070
	public String[] getEnabledCipherSuites(String configID) {
1071
		String ciphers = getProperty(configID, CIPHERSUITES, null);
1072
		String[] res = unpackCipherSuites(ciphers);
1073
		return res;
1074
	}
1075
1076
	/**
1077
	 * Returns whether client authentication is required.
1078
	 * 
1079
	 * @param configID
1080
	 *            The configuration identifier for selecting a configuration or
1081
	 *            null for the default configuration.
1082
	 * @return true, if clients are required to authenticate, false otherwise.
1083
	 */
1084
	public boolean getClientAuthentication(String configID) {
1085
		String auth = getProperty(configID, CLIENTAUTH, null);
1086
		boolean res = false;
1087
		if (auth != null) {
1088
			res = Boolean.valueOf(auth).booleanValue();
1089
		}
1090
		return res;
1091
	}
1092
1093
	/**
1094
	 * Initializes key- and truststore. Returns an SSL context factory. If no
1095
	 * SSLProtocol is already set, uses DEFAULT_PROTOCOL
1096
	 * 
1097
	 * @see org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory#DEFAULT_PROTOCOL
1098
	 * 
1099
	 * @param configID
1100
	 *            The configuration ID
1101
	 * @return An SSL context factory.
1102
	 * @throws MqttDirectException
1103
	 */
1104
	private SSLContext getSSLContext(String configID)
1105
			throws MqttSecurityException{
1106
		final String METHOD_NAME = "getSSLContext";
1107
		SSLContext ctx = null;
1108
		
1109
		String protocol = getSSLProtocol(configID);
1110
		if (protocol == null) {
1111
			protocol = DEFAULT_PROTOCOL;
1112
		}
1113
		if (logger != null) {
1114
			// 12000 "SSL initialization: configID = {0}, protocol = {1}"
1115
			logger.fine(CLASS_NAME, METHOD_NAME, "12000", new Object[] {configID!=null ? configID : "null (broker defaults)", 
1116
					protocol});
1117
		}
1118
		
1119
		String provider = getJSSEProvider(configID);
1120
		try {
1121
			if (provider == null) {
1122
				ctx = SSLContext.getInstance(protocol);
1123
			} else {
1124
				ctx = SSLContext.getInstance(protocol, provider);
1125
			}
1126
			if (logger != null) {
1127
				// 12001 "SSL initialization: configID = {0}, provider = {1}"
1128
				logger.fine(CLASS_NAME, METHOD_NAME, "12001", new Object[] {configID!=null ? configID : "null (broker defaults)", 
1129
						ctx.getProvider().getName()});
1130
			}
1131
			
1132
			String keyStoreName = getProperty(configID, KEYSTORE, null);
1133
			KeyStore keyStore=null;
1134
			KeyManagerFactory keyMgrFact=null;
1135
			KeyManager[] keyMgr=null;
1136
//			if(keyStoreName==null) {
1137
//				// try to instantiate XPD keyStore.
1138
//				keyStore=getXPDKeystore();
1139
//				if (logger != null) {
1140
//					if (keyStore == null) {
1141
//						// 12002 "SSL initialization: configID = {0}, XPD keystore not available"
1142
//						logger.fine(CLASS_NAME, METHOD_NAME, "12002", new Object[]{configID!=null ? configID : "null (broker defaults)"});
1143
//					} else {
1144
//						// 12003 "SSL initialization: configID = {0}, XPD keystore available"
1145
//						logger.fine(CLASS_NAME, METHOD_NAME, "12003", new Object[]{configID!=null ? configID : "null (broker defaults)"});
1146
//					}
1147
//				}
1148
//			}
1149
			
1150
			if(keyStore==null) {
1151
				if(keyStoreName==null) {
1152
					/*
1153
					 * No keystore in config, XPD keystore not available. Try to
1154
					 * get config from system properties.
1155
					 */
1156
					keyStoreName = getProperty(configID, KEYSTORE, SYSKEYSTORE);
1157
				}
1158
				if (logger != null) {
1159
					// 12004 "SSL initialization: configID = {0}, keystore = {1}"
1160
					logger.fine(CLASS_NAME, METHOD_NAME, "12004", new Object[]{configID!=null ? configID : "null (broker defaults)", 
1161
							keyStoreName!=null ? keyStoreName : "null"});
1162
				}
1163
				
1164
				char[] keyStorePwd=getKeyStorePassword(configID);
1165
				if (logger != null) {
1166
					// 12005 "SSL initialization: configID = {0}, keystore password = {1}"
1167
					logger.fine(CLASS_NAME, METHOD_NAME, "12005", new Object[]{configID!=null ? configID : "null (broker defaults)", 
1168
							keyStorePwd!=null ? obfuscate(keyStorePwd) : "null"});
1169
				}
1170
				
1171
				String keyStoreType=getKeyStoreType(configID);
1172
				if(keyStoreType==null) {
1173
					keyStoreType = KeyStore.getDefaultType();
1174
				}
1175
				if (logger != null) {
1176
					// 12006 "SSL initialization: configID = {0}, keystore type = {1}"
1177
					logger.fine(CLASS_NAME, METHOD_NAME, "12006", new Object[]{configID!=null ? configID : "null (broker defaults)", 
1178
							keyStoreType!=null ? keyStoreType : "null"});
1179
				}
1180
				
1181
				String keyMgrAlgo = KeyManagerFactory.getDefaultAlgorithm();
1182
				String keyMgrProvider = getKeyStoreProvider(configID);
1183
				String keyManager = getKeyManager(configID);
1184
				if (keyManager != null) {
1185
					keyMgrAlgo = keyManager;
1186
				}
1187
				
1188
				if(keyStoreName!=null && keyStoreType!=null  && keyMgrAlgo!=null) {
1189
					try {
1190
						keyStore=KeyStore.getInstance(keyStoreType);
1191
						keyStore.load(new FileInputStream(keyStoreName), keyStorePwd);
1192
						if(keyMgrProvider!=null) {
1193
							keyMgrFact = KeyManagerFactory.getInstance(keyMgrAlgo, keyMgrProvider);
1194
						} else {
1195
							keyMgrFact = KeyManagerFactory.getInstance(keyMgrAlgo);
1196
						}
1197
						if (logger != null) {
1198
							// 12010 "SSL initialization: configID = {0}, keystore manager algorithm = {1}"
1199
							logger.fine(CLASS_NAME, METHOD_NAME, "12010", new Object[]{configID!=null ? configID : "null (broker defaults)", 
1200
									keyMgrAlgo!=null ? keyMgrAlgo : "null"});
1201
							// 12009 "SSL initialization: configID = {0}, keystore manager provider = {1}"
1202
							logger.fine(CLASS_NAME, METHOD_NAME, "12009", new Object[]{configID!=null ? configID : "null (broker defaults)", 
1203
									keyMgrFact.getProvider().getName()});				
1204
						}
1205
						keyMgrFact.init(keyStore, keyStorePwd);
1206
						keyMgr=keyMgrFact.getKeyManagers();
1207
					} catch (KeyStoreException e) {
1208
						throw new MqttSecurityException(e);
1209
					} catch (CertificateException e) {
1210
						throw new MqttSecurityException(e);
1211
					} catch (FileNotFoundException e) {
1212
						throw new MqttSecurityException(e);
1213
					} catch (IOException e) {
1214
						throw new MqttSecurityException(e);
1215
					} catch (UnrecoverableKeyException e) {
1216
						throw new MqttSecurityException(e);
1217
					}
1218
				}
1219
			}
1220
			// keystore loaded, keymanagers instantiated if possible
1221
			// now the same for the truststore.
1222
			String trustStoreName = getTrustStore(configID);
1223
			if (logger != null) {
1224
				// 12011 "SSL initialization: configID = {0}, truststore = {1}"
1225
				logger.fine(CLASS_NAME, METHOD_NAME, "12011", new Object[]{configID!=null ? configID : "null (broker defaults)", 
1226
						trustStoreName!=null ? trustStoreName : "null"});
1227
			}
1228
			KeyStore trustStore=null;
1229
			TrustManagerFactory trustMgrFact=null;
1230
			TrustManager[] trustMgr=null;
1231
			char[] trustStorePwd=getTrustStorePassword(configID);
1232
			if (logger != null) {
1233
				// 12012 "SSL initialization: configID = {0}, truststore password = {1}"
1234
				logger.fine(CLASS_NAME, METHOD_NAME, "12012", new Object[]{configID!=null ? configID : "null (broker defaults)", 
1235
						trustStorePwd!=null ? obfuscate(trustStorePwd) : "null"});
1236
			}
1237
			String trustStoreType=getTrustStoreType(configID);
1238
			if(trustStoreType==null) {
1239
				trustStoreType = KeyStore.getDefaultType();
1240
			}
1241
			if (logger != null) {
1242
				// 12013 "SSL initialization: configID = {0}, truststore type = {1}"
1243
				logger.fine(CLASS_NAME, METHOD_NAME, "12013", new Object[]{configID!=null ? configID : "null (broker defaults)", 
1244
						trustStoreType!=null ? trustStoreType : "null"});
1245
			}
1246
			
1247
			String trustMgrAlgo = TrustManagerFactory.getDefaultAlgorithm();
1248
			String trustMgrProvider = getTrustStoreProvider(configID);
1249
			String trustManager = getTrustManager(configID);
1250
			if (trustManager != null) {
1251
				trustMgrAlgo = trustManager;
1252
			}
1253
					
1254
			if(trustStoreName!=null && trustStoreType!=null && trustMgrAlgo!=null) {
1255
				try {
1256
					trustStore=KeyStore.getInstance(trustStoreType);
1257
					trustStore.load(new FileInputStream(trustStoreName), trustStorePwd);
1258
					if(trustMgrProvider!=null) {
1259
						trustMgrFact = TrustManagerFactory.getInstance(trustMgrAlgo, trustMgrProvider);
1260
					} else {
1261
						trustMgrFact = TrustManagerFactory.getInstance(trustMgrAlgo);
1262
					}
1263
					if (logger != null) {
1264
						
1265
						// 12017 "SSL initialization: configID = {0}, truststore manager algorithm = {1}"
1266
						logger.fine(CLASS_NAME, METHOD_NAME, "12017", new Object[]{configID!=null ? configID : "null (broker defaults)", 
1267
								trustMgrAlgo!=null ? trustMgrAlgo : "null"});
1268
						
1269
						// 12016 "SSL initialization: configID = {0}, truststore manager provider = {1}"
1270
						logger.fine(CLASS_NAME, METHOD_NAME, "12016", new Object[]{configID!=null ? configID : "null (broker defaults)", 
1271
								trustMgrFact.getProvider().getName()});		
1272
					}
1273
					trustMgrFact.init(trustStore);
1274
					trustMgr=trustMgrFact.getTrustManagers();
1275
				} catch (KeyStoreException e) {
1276
					throw new MqttSecurityException(e);
1277
				} catch (CertificateException e) {
1278
					throw new MqttSecurityException(e);
1279
				} catch (FileNotFoundException e) {
1280
					throw new MqttSecurityException(e);
1281
				} catch (IOException e) {
1282
					throw new MqttSecurityException(e);
1283
				} 
1284
			}
1285
			// done.
1286
			ctx.init(keyMgr, trustMgr, null);
1287
		} catch (NoSuchAlgorithmException e) {
1288
			throw new MqttSecurityException(e);
1289
		} catch (NoSuchProviderException e) {
1290
			throw new MqttSecurityException(e);
1291
		} catch (KeyManagementException e) {
1292
			throw new MqttSecurityException(e);
1293
		}
1294
		return ctx;
1295
	}
1296
1297
//	/**
1298
//	 * Returns an SSL server socket factory for the given configuration. If no
1299
//	 * SSLProtocol is already set, uses DEFAULT_PROTOCOL. Throws
1300
//	 * IllegalArgumentException if the server socket factory could not be
1301
//	 * created due to underlying configuration problems.
1302
//	 * 
1303
//	 * @see org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory#DEFAULT_PROTOCOL
1304
//	 * 
1305
//	 * @param configID
1306
//	 *            The configuration identifier for selecting a configuration.
1307
//	 * @return An SSLServerSocketFactory
1308
//	 * @throws MqttDirectException
1309
//	 */
1310
//	public SSLServerSocketFactory createServerSocketFactory(String configID)
1311
//			throws MqttDirectException {
1312
//		final String METHOD_NAME = "createServerSocketFactory";
1313
//		SSLContext ctx = getSSLContext(configID);
1314
//		if (logger != null) {
1315
//			// 12018 "SSL initialization: configID = {0}, application-enabled cipher suites = {1}"
1316
//			logger.fine(CLASS_NAME, METHOD_NAME, "12018", new Object[]{configID!=null ? configID : "null (broker defaults)", 
1317
//					getEnabledCipherSuites(configID)!=null ? getProperty(configID, CIPHERSUITES, null) : "null (using platform-enabled cipher suites)"});
1318
//			
1319
//			// 12019 "SSL initialization: configID = {0}, client authentication = {1}"
1320
//			logger.fine(CLASS_NAME, METHOD_NAME, "12019", new Object[]{configID!=null ? configID : "null (broker defaults)", 
1321
//					new Boolean (getClientAuthentication(configID)).toString()});
1322
//		}
1323
//		
1324
//		return ctx.getServerSocketFactory();
1325
//	}
1326
1327
	/**
1328
	 * Returns an SSL socket factory for the given configuration. If no
1329
	 * SSLProtocol is already set, uses DEFAULT_PROTOCOL. Throws
1330
	 * IllegalArgumentException if the socket factory could not be created due
1331
	 * to underlying configuration problems.
1332
	 * 
1333
	 * @see org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory#DEFAULT_PROTOCOL
1334
	 * @param configID
1335
	 *            The configuration identifier for selecting a configuration.
1336
	 * @return An SSLSocketFactory
1337
	 * @throws MqttDirectException
1338
	 */
1339
	public SSLSocketFactory createSocketFactory(String configID) 
1340
			throws MqttSecurityException {
1341
		final String METHOD_NAME = "createSocketFactory";
1342
		SSLContext ctx = getSSLContext(configID);
1343
		if (logger != null) {
1344
			// 12020 "SSL initialization: configID = {0}, application-enabled cipher suites = {1}"
1345
			logger.fine(CLASS_NAME, METHOD_NAME, "12020", new Object[]{configID!=null ? configID : "null (broker defaults)", 
1346
					getEnabledCipherSuites(configID)!=null ? getProperty(configID, CIPHERSUITES, null) : "null (using platform-enabled cipher suites)"});
1347
		}
1348
			
1349
		return ctx.getSocketFactory();
1350
	}
1351
1352
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/security/SimpleBase64Encoder.java (+123 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.security;
13
14
public class SimpleBase64Encoder {
15
16
	// if this string is changed, then the decode method must also be adapted.
17
	private final static String PWDCHARS_STRING = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
18
	private final static char[] PWDCHARS_ARRAY = PWDCHARS_STRING.toCharArray();
19
20
	/**
21
	 * Encodes an array of byte into a string of printable ASCII characters
22
	 * using a base-64 encoding.
23
	 * @param bytes The array of bytes to e encoded
24
	 * @return The encoded array.
25
	 */
26
	public static String encode(byte[] bytes) {
27
		// Allocate a string buffer.
28
		int len = bytes.length;
29
		final StringBuffer encoded = new StringBuffer((len+2)/3*4);		
30
		int i=0;
31
		int j=len;
32
		while(j>=3){
33
			encoded.append(to64((((bytes[i] & 0xff) << 16)
34
				| (int) ((bytes[i+1] & 0xff) << 8) | (int) (bytes[i+2] & 0xff)),4));
35
			i+=3;
36
			j-=3;
37
		}
38
		// j==2 | j==1 | j==0
39
		if(j==2) {
40
			// there is a rest of 2 bytes. This encodes into 3 chars.
41
			encoded.append(to64(((bytes[i] &0xff)<<8) | ((bytes[i+1] & 0xff)),3));
42
		}
43
		if(j==1) {
44
			// there is a rest of 1 byte. This encodes into 1 char.
45
			encoded.append(to64(((bytes[i] & 0xff)),2));
46
		}
47
		return encoded.toString();
48
	}
49
50
	public static byte[] decode(String string) {
51
		byte[] encoded=string.getBytes();
52
		int len=encoded.length;
53
		byte[] decoded=new byte[len*3/4];
54
		int i=0;
55
		int j=len;
56
		int k=0;
57
		while(j>=4) {
58
			long d=from64(encoded, i, 4);
59
			j-=4;
60
			i+=4;
61
			for(int l=2;l>=0;l--) {
62
				decoded[k+l]=(byte) (d & 0xff);
63
				d=d >>8;
64
			}
65
			k+=3;
66
		}
67
		// j==3 | j==2 
68
		if(j==3) {
69
			long d=from64(encoded, i, 3);
70
			for(int l=1;l>=0;l--) {
71
				decoded[k+l]=(byte) (d & 0xff);
72
				d=d >>8;
73
			}			
74
		}
75
		if(j==2) {
76
			long d=from64(encoded, i, 2);
77
			decoded[k]=(byte) (d & 0xff);
78
		}			
79
		return decoded;
80
	}
81
82
	/* the core conding routine. Translates an input integer into
83
	 * a string of the given length.*/
84
	private final static String to64(long input, int size) {
85
		final StringBuffer result = new StringBuffer(size);
86
		while (size > 0) {
87
			size--;
88
			result.append(PWDCHARS_ARRAY[((int) (input & 0x3f))]);
89
			input = input >> 6;
90
		}
91
		return result.toString();
92
	}
93
94
	/*
95
	 * The reverse operation of to64
96
	 */
97
	private final static long from64(byte[] encoded, int idx, int size) {
98
		long res=0;
99
		int f=0;
100
		while(size>0) {
101
			size--;
102
			long r=0;
103
			// convert encoded[idx] back into a 6-bit value.
104
			byte d=encoded[idx++];
105
			if(d=='/') {
106
				r=1;
107
			}
108
			if(d>='0' && d<='9') {
109
				r=2+d-'0';
110
			}
111
			if(d>='A' && d<='Z') {
112
				r=12+d-'A';
113
			}
114
			if(d>='a' && d<='z') {
115
				r=38+d-'a';
116
			}
117
			res=res+((long)r << f);
118
			f+=6;
119
		}
120
		return res;
121
	}
122
123
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/CountingInputStream.java (+54 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import java.io.IOException;
15
import java.io.InputStream;
16
17
/**
18
 * An input stream that counts the bytes read from it.
19
 */
20
public class CountingInputStream extends InputStream {
21
	private InputStream in;
22
	private int counter;
23
24
	/**
25
	 * Constructs a new <code>CountingInputStream</code> wrapping the supplied
26
	 * input stream.
27
	 */
28
	public CountingInputStream(InputStream in) {
29
		this.in = in;
30
		this.counter = 0;
31
	}
32
	
33
	public int read() throws IOException {
34
		int i = in.read();
35
		if (i != -1) {
36
			counter++;
37
		}
38
		return i;
39
	}
40
41
	/**
42
	 * Returns the number of bytes read since the last reset.
43
	 */
44
	public int getCounter() {
45
		return counter;
46
	}
47
	
48
	/**
49
	 * Resets the counter to zero.
50
	 */
51
	public void resetCounter() {
52
		counter = 0;
53
	}
54
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttAck.java (+27 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
15
/**
16
 * Abstract super-class of all acknowledgement messages.
17
 */
18
public abstract class MqttAck extends MqttWireMessage {
19
	public MqttAck(byte type) {
20
		super(type);
21
	}
22
	
23
	protected byte getMessageInfo() {
24
		return 0;
25
	}
26
27
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttConnack.java (+55 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import java.io.ByteArrayInputStream;
15
import java.io.DataInputStream;
16
import java.io.IOException;
17
18
import org.eclipse.paho.client.mqttv3.MqttException;
19
20
21
/**
22
 * An on-the-wire representation of an MQTT CONNACK.
23
 */
24
public class MqttConnack extends MqttAck {
25
	private int returnCode;
26
	
27
	public MqttConnack(byte info, byte[] variableHeader) throws IOException {
28
		super(MqttWireMessage.MESSAGE_TYPE_CONNACK);
29
		ByteArrayInputStream bais = new ByteArrayInputStream(variableHeader);
30
		DataInputStream dis = new DataInputStream(bais);
31
		dis.readByte();
32
		returnCode = dis.readUnsignedByte();
33
		dis.close();
34
	}
35
	
36
	public int getReturnCode() {
37
		return returnCode;
38
	}
39
40
	protected byte[] getVariableHeader() throws MqttException {
41
		// Not needed, as the client never encodes a CONNACK
42
		return new byte[0];
43
	}
44
	
45
	/**
46
	 * Returns whether or not this message needs to include a message ID.
47
	 */
48
	public boolean isMessageIdRequired() {
49
		return false;
50
	}
51
	
52
	public String getKey() {
53
		return new String("Con");
54
	}
55
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttConnect.java (+131 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import java.io.ByteArrayOutputStream;
15
import java.io.DataOutputStream;
16
import java.io.IOException;
17
18
import org.eclipse.paho.client.mqttv3.MqttException;
19
import org.eclipse.paho.client.mqttv3.MqttMessage;
20
21
/**
22
 * An on-the-wire representation of an MQTT CONNECT message.
23
 */
24
public class MqttConnect extends MqttWireMessage {
25
	private String clientId;
26
	private boolean cleanSession;
27
	private MqttMessage willMessage;
28
	private String userName;
29
	private char[] password;
30
	private int keepAliveInterval;
31
	private String willDestination;
32
	public static String KEY="Con";
33
	
34
	
35
	public MqttConnect(String clientId,
36
			boolean cleanSession,
37
			int keepAliveInterval,
38
			String userName,
39
			char[] password,
40
			MqttMessage willMessage,
41
			String willDestination) {
42
		super(MqttWireMessage.MESSAGE_TYPE_CONNECT);
43
		this.clientId = clientId;
44
		this.cleanSession = cleanSession;
45
		this.keepAliveInterval = keepAliveInterval;
46
		this.userName = userName;
47
		this.password = password;
48
		this.willMessage = willMessage;
49
		this.willDestination = willDestination;
50
	}
51
	
52
	protected byte getMessageInfo() {
53
		return (byte) 0;
54
	}
55
56
	public boolean isCleanSession() {
57
		return cleanSession;
58
	}
59
	
60
	protected byte[] getVariableHeader() throws MqttException {
61
		try {
62
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
63
			DataOutputStream dos = new DataOutputStream(baos);
64
			this.encodeUTF8(dos,"MQIsdp");			
65
			dos.write(3);
66
			byte connectFlags = 0;
67
			
68
			if (cleanSession) {
69
				connectFlags |= 0x02;
70
			}
71
			
72
			if (willMessage != null ) {
73
				connectFlags |= 0x04;
74
				connectFlags |= (willMessage.getQos()<<3);
75
				if (willMessage.isRetained()) {
76
					connectFlags |= 0x20;
77
				}
78
			}
79
			
80
			if (userName != null) {
81
				connectFlags |= 0x80;
82
				if (password != null) {
83
					connectFlags |= 0x40;
84
				}
85
			}
86
			dos.write(connectFlags);
87
			dos.writeShort(keepAliveInterval);
88
			dos.flush();
89
			return baos.toByteArray();
90
		} catch(IOException ioe) {
91
			throw new MqttException(ioe);
92
		}
93
	}
94
	
95
	public byte[] getPayload() throws MqttException {
96
		try {
97
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
98
			DataOutputStream dos = new DataOutputStream(baos);
99
			this.encodeUTF8(dos,clientId);
100
			
101
			if (willMessage != null) {
102
				this.encodeUTF8(dos,willDestination);
103
				dos.writeShort(willMessage.getPayload().length);
104
				dos.write(willMessage.getPayload());
105
			}
106
			
107
			if (userName != null) {
108
				this.encodeUTF8(dos,userName);
109
				if (password != null) {
110
					this.encodeUTF8(dos,new String(password));
111
				}
112
			}
113
			dos.flush();
114
			return baos.toByteArray();
115
		}
116
		catch (IOException ex) {
117
			throw new MqttException(ex);
118
		}
119
	}
120
	
121
	/**
122
	 * Returns whether or not this message needs to include a message ID.
123
	 */
124
	public boolean isMessageIdRequired() {
125
		return false;
126
	}
127
	
128
	public String getKey() {
129
		return new String(KEY);
130
	}
131
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttDisconnect.java (+44 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import org.eclipse.paho.client.mqttv3.MqttException;
15
16
/**
17
 * An on-the-wire representation of an MQTT DISCONNECT message.
18
 */
19
public class MqttDisconnect extends MqttWireMessage {
20
	public static String KEY="Disc";
21
	
22
	public MqttDisconnect() {
23
		super(MqttWireMessage.MESSAGE_TYPE_DISCONNECT);
24
	}
25
	
26
	protected byte getMessageInfo() {
27
		return (byte) 0;
28
	}
29
30
	protected byte[] getVariableHeader() throws MqttException {
31
		return new byte[0];
32
	}
33
34
	/**
35
	 * Returns whether or not this message needs to include a message ID.
36
	 */
37
	public boolean isMessageIdRequired() {
38
		return false;
39
	}
40
	
41
	public String getKey() {
42
		return new String(KEY);
43
	}
44
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttInputStream.java (+69 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import java.io.ByteArrayOutputStream;
15
import java.io.DataInputStream;
16
import java.io.IOException;
17
import java.io.InputStream;
18
19
import org.eclipse.paho.client.mqttv3.MqttException;
20
import org.eclipse.paho.client.mqttv3.internal.ExceptionHelper;
21
22
23
/**
24
 * An <code>MqttInputStream</code> lets applications read instances of
25
 * <code>MqttWireMessage</code>. 
26
 */
27
public class MqttInputStream extends InputStream {
28
	private DataInputStream in;
29
30
	public MqttInputStream(InputStream in) {
31
		this.in = new DataInputStream(in);
32
	}
33
	
34
	public int read() throws IOException {
35
		return in.read();
36
	}
37
	
38
	public int available() throws IOException {
39
		return in.available();
40
	}
41
	
42
	public void close() throws IOException {
43
		in.close();
44
	}
45
	
46
	/**
47
	 * Reads an <code>MqttWireMessage</code> from the stream.
48
	 */
49
	public MqttWireMessage readMqttWireMessage() throws IOException, MqttException {
50
		ByteArrayOutputStream bais = new ByteArrayOutputStream();
51
		byte first = in.readByte();
52
		byte type = (byte) ((first >>> 4) & 0x0F);
53
		if ((type < MqttWireMessage.MESSAGE_TYPE_CONNECT) ||
54
			(type > MqttWireMessage.MESSAGE_TYPE_DISCONNECT)) {
55
			// Invalid MQTT message type...
56
			throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_INVALID_MESSAGE);
57
		}
58
		long remLen = MqttWireMessage.readMBI(in).getValue();
59
		bais.write(first);
60
		// bit silly, we decode it then encode it
61
		bais.write(MqttWireMessage.encodeMBI(remLen));
62
		byte[] packet = new byte[(int)(bais.size()+remLen)];
63
		in.readFully(packet,bais.size(),packet.length - bais.size());
64
		byte[] header = bais.toByteArray();
65
		System.arraycopy(header,0,packet,0, header.length);
66
		MqttWireMessage message = MqttWireMessage.createWireMessage(packet);
67
		return message;
68
	}
69
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttOutputStream.java (+64 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import java.io.BufferedOutputStream;
15
import java.io.IOException;
16
import java.io.OutputStream;
17
18
import org.eclipse.paho.client.mqttv3.MqttException;
19
20
21
/**
22
 * An <code>MqttOutputStream</code> lets applications write instances of
23
 * <code>MqttWireMessage</code>. 
24
 */
25
public class MqttOutputStream extends OutputStream {
26
	private BufferedOutputStream out;
27
	
28
	public MqttOutputStream(OutputStream out) {
29
		this.out = new BufferedOutputStream(out);
30
	}
31
	
32
	public void close() throws IOException {
33
		out.close();
34
	}
35
	
36
	public void flush() throws IOException {
37
		out.flush();
38
	}
39
	
40
	public void write(byte[] b) throws IOException {
41
		out.write(b);
42
	}
43
	
44
	public void write(byte[] b, int off, int len) throws IOException {
45
		out.write(b, off, len);
46
	}
47
	
48
	public void write(int b) throws IOException {
49
		out.write(b);
50
	}
51
52
	/**
53
	 * Writes an <code>MqttWireMessage</code> to the stream.
54
	 */
55
	public void write(MqttWireMessage message) throws IOException, MqttException {
56
		byte[] bytes = message.getHeader();
57
		byte[] pl = message.getPayload();
58
//		out.write(message.getHeader());
59
//		out.write(message.getPayload());
60
		out.write(bytes,0,bytes.length);
61
		out.write(pl,0,pl.length);
62
	}
63
}
64
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPersistableWireMessage.java (+63 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import org.eclipse.paho.client.mqttv3.MqttException;
15
import org.eclipse.paho.client.mqttv3.MqttPersistable;
16
import org.eclipse.paho.client.mqttv3.MqttPersistenceException;
17
18
public abstract class MqttPersistableWireMessage extends MqttWireMessage
19
		implements MqttPersistable {
20
	
21
	public MqttPersistableWireMessage(byte type) {
22
		super(type);
23
	}
24
	
25
	public byte[] getHeaderBytes() throws MqttPersistenceException {
26
		try {
27
			return getHeader();
28
		}
29
		catch (MqttException ex) {
30
			throw new MqttPersistenceException(ex.getCause());
31
		}
32
	}
33
	
34
	public int getHeaderLength() throws MqttPersistenceException {
35
		return getHeaderBytes().length;
36
	}
37
38
	public int getHeaderOffset() throws MqttPersistenceException{
39
		return 0;
40
	}
41
42
//	public String getKey() throws MqttPersistenceException {
43
//		return new Integer(getMessageId()).toString();
44
//	}
45
46
	public byte[] getPayloadBytes() throws MqttPersistenceException {
47
		try {
48
			return getPayload();
49
		}
50
		catch (MqttException ex) {
51
			throw new MqttPersistenceException(ex.getCause());
52
		}
53
	}
54
	
55
	public int getPayloadLength() throws MqttPersistenceException {
56
		return 0;
57
	}
58
59
	public int getPayloadOffset() throws MqttPersistenceException {
60
		return 0;
61
	}
62
63
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPingReq.java (+44 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import org.eclipse.paho.client.mqttv3.MqttException;
15
16
/**
17
 * An on-the-wire representation of an MQTT PINGREQ message.
18
 */
19
public class MqttPingReq extends MqttWireMessage {
20
	public MqttPingReq() {
21
		super(MqttWireMessage.MESSAGE_TYPE_PINGREQ);
22
	}
23
	
24
	/**
25
	 * Returns <code>false</code> as message IDs are not required for MQTT
26
	 * PINGREQ messages.
27
	 */
28
	public boolean isMessageIdRequired() {
29
		return false;
30
	}
31
32
	protected byte[] getVariableHeader() throws MqttException {
33
		return new byte[0];
34
	}
35
	
36
	protected byte getMessageInfo() {
37
		return 0;
38
	}
39
	
40
	public String getKey() {
41
		return new String("Ping");
42
	}
43
}
44
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPingResp.java (+40 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import org.eclipse.paho.client.mqttv3.MqttException;
15
16
17
/**
18
 * An on-the-wire representation of an MQTT PINGRESP.
19
 */
20
public class MqttPingResp extends MqttAck {
21
	public MqttPingResp(byte info, byte[] variableHeader) {
22
		super(MqttWireMessage.MESSAGE_TYPE_PINGRESP);
23
	}
24
	
25
	protected byte[] getVariableHeader() throws MqttException {
26
		// Not needed, as the client never encodes a PINGRESP
27
		return new byte[0];
28
	}
29
	
30
	/**
31
	 * Returns whether or not this message needs to include a message ID.
32
	 */
33
	public boolean isMessageIdRequired() {
34
		return false;
35
	}
36
	
37
	public String getKey() {
38
		return new String("Ping");
39
	}
40
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubAck.java (+42 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import java.io.ByteArrayInputStream;
15
import java.io.DataInputStream;
16
import java.io.IOException;
17
18
import org.eclipse.paho.client.mqttv3.MqttException;
19
20
21
22
/**
23
 * An on-the-wire representation of an MQTT PUBACK message.
24
 */
25
public class MqttPubAck extends MqttAck {
26
	public MqttPubAck(byte info, byte[] data) throws IOException {
27
		super(MqttWireMessage.MESSAGE_TYPE_PUBACK);
28
		ByteArrayInputStream bais = new ByteArrayInputStream(data);
29
		DataInputStream dis = new DataInputStream(bais);
30
		msgId = dis.readUnsignedShort();
31
		dis.close();
32
	}
33
	
34
	public MqttPubAck(MqttPublish publish) {
35
		super(MqttWireMessage.MESSAGE_TYPE_PUBACK);
36
		msgId = publish.getMessageId();
37
	}
38
	
39
	protected byte[] getVariableHeader() throws MqttException {
40
		return encodeMessageId();
41
	}
42
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubComp.java (+47 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import java.io.ByteArrayInputStream;
15
import java.io.DataInputStream;
16
import java.io.IOException;
17
18
import org.eclipse.paho.client.mqttv3.MqttException;
19
20
21
22
/**
23
 * An on-the-wire representation of an MQTT PUBCOMP message.
24
 */
25
public class MqttPubComp extends MqttAck {
26
	public MqttPubComp(byte info, byte[] data) throws IOException {
27
		super(MqttWireMessage.MESSAGE_TYPE_PUBCOMP);
28
		ByteArrayInputStream bais = new ByteArrayInputStream(data);
29
		DataInputStream dis = new DataInputStream(bais);
30
		msgId = dis.readUnsignedShort();
31
		dis.close();
32
	}
33
	
34
	public MqttPubComp(MqttPublish publish) {
35
		super(MqttWireMessage.MESSAGE_TYPE_PUBCOMP);
36
		this.msgId = publish.getMessageId();
37
	}
38
	
39
	public MqttPubComp(int msgId) {
40
		super(MqttWireMessage.MESSAGE_TYPE_PUBCOMP);
41
		this.msgId = msgId;
42
	}
43
	
44
	protected byte[] getVariableHeader() throws MqttException {
45
		return encodeMessageId();
46
	}
47
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubRec.java (+42 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import java.io.ByteArrayInputStream;
15
import java.io.DataInputStream;
16
import java.io.IOException;
17
18
import org.eclipse.paho.client.mqttv3.MqttException;
19
20
21
22
/**
23
 * An on-the-wire representation of an MQTT PUBREC message.
24
 */
25
public class MqttPubRec extends MqttAck {
26
	public MqttPubRec(byte info, byte[] data) throws IOException {
27
		super(MqttWireMessage.MESSAGE_TYPE_PUBREC);
28
		ByteArrayInputStream bais = new ByteArrayInputStream(data);
29
		DataInputStream dis = new DataInputStream(bais);
30
		msgId = dis.readUnsignedShort();
31
		dis.close();
32
	}
33
	
34
	public MqttPubRec(MqttPublish publish) {
35
		super(MqttWireMessage.MESSAGE_TYPE_PUBREC);
36
		msgId = publish.getMessageId();
37
	}
38
	
39
	protected byte[] getVariableHeader() throws MqttException {
40
		return encodeMessageId();
41
	}
42
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubRel.java (+56 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import java.io.ByteArrayInputStream;
15
import java.io.DataInputStream;
16
import java.io.IOException;
17
18
import org.eclipse.paho.client.mqttv3.MqttException;
19
20
/**
21
 * An on-the-wire representation of an MQTT PUBREL message.
22
 */
23
public class MqttPubRel extends MqttPersistableWireMessage {
24
25
	/**
26
	 * Createa a pubrel message based on a pubrec
27
	 * @param pubRec
28
	 */
29
	public MqttPubRel(MqttPubRec pubRec) {
30
		super(MqttWireMessage.MESSAGE_TYPE_PUBREL);
31
		this.setMessageId(pubRec.getMessageId());
32
	}
33
	
34
	/**
35
	 * Creates a pubrel based on a pubrel set of bytes read fro the network
36
	 * @param info
37
	 * @param data
38
	 * @throws IOException
39
	 */
40
	public MqttPubRel(byte info, byte[] data) throws IOException {
41
		super(MqttWireMessage.MESSAGE_TYPE_PUBREL);
42
		ByteArrayInputStream bais = new ByteArrayInputStream(data);
43
		DataInputStream dis = new DataInputStream(bais);
44
		msgId = dis.readUnsignedShort();
45
		dis.close();
46
	}
47
48
	protected byte[] getVariableHeader() throws MqttException {
49
		return encodeMessageId();
50
	}
51
	
52
	protected byte getMessageInfo() {
53
		return (byte)( 2 | (this.duplicate?8:0));
54
	}
55
56
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPublish.java (+136 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import java.io.ByteArrayInputStream;
15
import java.io.ByteArrayOutputStream;
16
import java.io.DataInputStream;
17
import java.io.DataOutputStream;
18
import java.io.IOException;
19
20
import org.eclipse.paho.client.mqttv3.MqttException;
21
import org.eclipse.paho.client.mqttv3.MqttMessage;
22
23
24
/**
25
 * An on-the-wire representation of an MQTT SEND message.
26
 */
27
public class MqttPublish extends MqttPersistableWireMessage {
28
	
29
	private MqttMessage message;
30
	private String topicName;
31
	
32
	private byte[] encodedPayload = null;
33
	
34
	public MqttPublish(String name, MqttMessage message) {
35
		super(MqttWireMessage.MESSAGE_TYPE_PUBLISH);
36
		this.topicName = name;
37
		this.message = message;
38
	}
39
	
40
	/**
41
	 * Constructs a new MqttPublish object.
42
	 * @param info the message info byte
43
	 * @param data the variable header and payload bytes
44
	 */
45
	public MqttPublish(byte info, byte[] data) throws MqttException, IOException {
46
		super(MqttWireMessage.MESSAGE_TYPE_PUBLISH);
47
		this.message = new MqttReceivedMessage();
48
		message.setQos((info >> 1) & 0x03);
49
		if ((info & 0x01) == 0x01) {
50
			message.setRetained(true);
51
		}
52
		if ((info & 0x08) == 0x08) {
53
			((MqttReceivedMessage) message).setDuplicate(true);
54
		}
55
		
56
		ByteArrayInputStream bais = new ByteArrayInputStream(data);
57
		CountingInputStream counter = new CountingInputStream(bais);
58
		DataInputStream dis = new DataInputStream(counter);
59
		topicName = this.decodeUTF8(dis);
60
		if (message.getQos() > 0) {
61
			msgId = dis.readUnsignedShort();
62
		}
63
		byte[] payload = new byte[data.length-counter.getCounter()];
64
		dis.readFully(payload);
65
		dis.close();
66
		message.setPayload(payload);
67
	}
68
	
69
	protected byte getMessageInfo() {
70
		byte info = (byte) (message.getQos() << 1);
71
		if (message.isRetained()) {
72
			info |= 0x01;
73
		}
74
		if (message.isDuplicate() || duplicate ) {
75
			info |= 0x08;
76
		}
77
		
78
		return info;
79
	}
80
	
81
	public String getTopicName() {
82
		return topicName;
83
	}
84
	
85
	public MqttMessage getMessage() {
86
		return message;
87
	}
88
	
89
	protected static byte[] encodePayload(MqttMessage message) {
90
		return message.getPayload();
91
	}
92
93
	public byte[] getPayload() throws MqttException {
94
		if (encodedPayload == null) {
95
			encodedPayload = encodePayload(message);
96
		}
97
		return encodedPayload;
98
	}
99
100
	public int getPayloadLength() {
101
		int length = 0;
102
		try {
103
			length = getPayload().length;
104
		} catch(MqttException me) {
105
		}
106
		return length;
107
	}
108
	
109
	public void setMessageId(int msgId) {
110
		super.setMessageId(msgId);
111
		if (message instanceof MqttReceivedMessage) {
112
			((MqttReceivedMessage)message).setMessageId(msgId);
113
		}
114
	}
115
	
116
	protected byte[] getVariableHeader() throws MqttException {
117
		try {
118
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
119
			DataOutputStream dos = new DataOutputStream(baos);
120
			this.encodeUTF8(dos, topicName);
121
			if (message.getQos() > 0) {
122
				dos.writeShort(msgId);
123
			}
124
			dos.flush();
125
			return baos.toByteArray();
126
		}
127
		catch (IOException ex) {
128
			throw new MqttException(ex);
129
		}
130
	}
131
	
132
	public boolean isMessageIdRequired() {
133
		// all publishes require a message ID as it's used as the key to the token store
134
		return true;
135
	}
136
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttReceivedMessage.java (+33 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import org.eclipse.paho.client.mqttv3.MqttMessage;
15
16
public class MqttReceivedMessage extends MqttMessage {
17
	
18
	private int messageId;
19
	
20
	public void setMessageId(int msgId) {
21
		this.messageId = msgId;
22
	}
23
24
	public int getMessageId() {
25
		return messageId;
26
	}
27
	
28
	// This method exists here to get around the protected visibility of the
29
	// super class method.
30
	public void setDuplicate(boolean value) {
31
		super.setDuplicate(value);
32
	}
33
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttSuback.java (+47 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import java.io.ByteArrayInputStream;
15
import java.io.DataInputStream;
16
import java.io.IOException;
17
18
import org.eclipse.paho.client.mqttv3.MqttException;
19
20
21
/**
22
 * An on-the-wire representation of an MQTT SUBACK.
23
 */
24
public class MqttSuback extends MqttAck {
25
	private int[] grantedQos;		// Not currently made available to anyone. 
26
	
27
	public MqttSuback(byte info, byte[] data) throws IOException {
28
		super(MqttWireMessage.MESSAGE_TYPE_SUBACK);
29
		ByteArrayInputStream bais = new ByteArrayInputStream(data);
30
		DataInputStream dis = new DataInputStream(bais);
31
		msgId = dis.readUnsignedShort();
32
		int index = 0;
33
		grantedQos = new int[data.length-2];
34
		int qos = dis.read();
35
		while (qos != -1) {
36
			grantedQos[index] = qos;
37
			index++;
38
			qos = dis.read();
39
		}
40
		dis.close();
41
	}
42
	
43
	protected byte[] getVariableHeader() throws MqttException {
44
		// Not needed, as the client never encodes a SUBACK
45
		return new byte[0];
46
	}
47
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttSubscribe.java (+83 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import java.io.ByteArrayOutputStream;
15
import java.io.DataOutputStream;
16
import java.io.IOException;
17
18
import org.eclipse.paho.client.mqttv3.MqttException;
19
import org.eclipse.paho.client.mqttv3.MqttMessage;
20
21
22
/**
23
 * An on-the-wire representation of an MQTT SUBSCRIBE message.
24
 */
25
public class MqttSubscribe extends MqttWireMessage {
26
	private String[] names;
27
	private int[] qos;
28
29
	/**
30
	 * Constructor for on an on hte wire MQTT subscribe message
31
	 * @param names - one or more topics to subscribe to 
32
	 * @param qos - the max QOS that each each topic will be subscribed at 
33
	 */
34
	public MqttSubscribe(String[] names, int[] qos) {
35
		super(MqttWireMessage.MESSAGE_TYPE_SUBSCRIBE);
36
		this.names = names;
37
		this.qos = qos;
38
		
39
		if (names.length != qos.length) {
40
		throw new IllegalArgumentException();
41
		}
42
		
43
		for (int i=0;i<qos.length;i++) {
44
			MqttMessage.validateQos(qos[i]);
45
		}
46
	}
47
	
48
	protected byte getMessageInfo() {
49
		return (byte)(2 | (this.duplicate?8:0));
50
	}
51
	
52
	protected byte[] getVariableHeader() throws MqttException {
53
		try {
54
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
55
			DataOutputStream dos = new DataOutputStream(baos);
56
			dos.writeShort(msgId);
57
			dos.flush();
58
			return baos.toByteArray();
59
		}
60
		catch (IOException ex) {
61
			throw new MqttException(ex);
62
		}
63
	}
64
	
65
	public byte[] getPayload() throws MqttException {
66
		try {
67
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
68
			DataOutputStream dos = new DataOutputStream(baos);
69
			for (int i=0; i<names.length; i++) {
70
				this.encodeUTF8(dos,names[i]);
71
				dos.writeByte(qos[i]);
72
			}
73
			return baos.toByteArray();
74
		}
75
		catch (IOException ex) {
76
			throw new MqttException(ex);
77
		}
78
	}
79
	
80
	public boolean isRetryable() {
81
		return true;
82
	}
83
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttUnsubAck.java (+38 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import java.io.ByteArrayInputStream;
15
import java.io.DataInputStream;
16
import java.io.IOException;
17
18
import org.eclipse.paho.client.mqttv3.MqttException;
19
20
21
/**
22
 * An on-the-wire representation of an MQTT UNSUBACK.
23
 */
24
public class MqttUnsubAck extends MqttAck {
25
	
26
	public MqttUnsubAck(byte info, byte[] data) throws IOException {
27
		super(MqttWireMessage.MESSAGE_TYPE_UNSUBACK);
28
		ByteArrayInputStream bais = new ByteArrayInputStream(data);
29
		DataInputStream dis = new DataInputStream(bais);
30
		msgId = dis.readUnsignedShort();
31
		dis.close();
32
	}
33
	
34
	protected byte[] getVariableHeader() throws MqttException {
35
		// Not needed, as the client never encodes an UNSUBACK
36
		return new byte[0];
37
	}
38
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttUnsubscribe.java (+64 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import java.io.ByteArrayOutputStream;
15
import java.io.DataOutputStream;
16
import java.io.IOException;
17
18
import org.eclipse.paho.client.mqttv3.MqttException;
19
20
21
/**
22
 * An on-the-wire representation of an MQTT UNSUBSCRIBE message.
23
 */
24
public class MqttUnsubscribe extends MqttWireMessage {
25
	
26
	private String[] names;
27
28
	/**
29
	 * Constructs an MqttUnsubscribe
30
	 */
31
	public MqttUnsubscribe(String[] names) {
32
		super(MqttWireMessage.MESSAGE_TYPE_UNSUBSCRIBE);
33
		this.names = names;
34
	}
35
	
36
	protected byte getMessageInfo() {
37
		return (byte)( 2 | (this.duplicate?8:0));
38
	}
39
	
40
	protected byte[] getVariableHeader() throws MqttException {
41
		try {
42
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
43
			DataOutputStream dos = new DataOutputStream(baos);
44
			dos.writeShort(msgId);
45
			dos.flush();
46
			return baos.toByteArray();
47
		}
48
		catch (IOException ex) {
49
			throw new MqttException(ex);
50
		}
51
	}
52
53
	public byte[] getPayload() throws MqttException {
54
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
55
			DataOutputStream dos = new DataOutputStream(baos);
56
			for (int i=0; i<names.length; i++) {
57
				this.encodeUTF8(dos, names[i]);
58
			}
59
			return baos.toByteArray();
60
	}
61
	public boolean isRetryable() {
62
		return true;
63
	}
64
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttWireMessage.java (+329 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import java.io.ByteArrayInputStream;
15
import java.io.ByteArrayOutputStream;
16
import java.io.DataInputStream;
17
import java.io.DataOutputStream;
18
import java.io.IOException;
19
import java.io.InputStream;
20
import java.io.UnsupportedEncodingException;
21
22
import org.eclipse.paho.client.mqttv3.MqttException;
23
import org.eclipse.paho.client.mqttv3.MqttPersistable;
24
import org.eclipse.paho.client.mqttv3.internal.ExceptionHelper;
25
26
27
/**
28
 * An on-the-wire representation of an MQTT message.
29
 */
30
public abstract class MqttWireMessage {
31
	protected static final String STRING_ENCODING = "UTF-8";
32
	
33
	public static final byte MESSAGE_TYPE_CONNECT = 1;
34
	public static final byte MESSAGE_TYPE_CONNACK = 2;
35
	public static final byte MESSAGE_TYPE_PUBLISH = 3;
36
	public static final byte MESSAGE_TYPE_PUBACK = 4;
37
	public static final byte MESSAGE_TYPE_PUBREC = 5;
38
	public static final byte MESSAGE_TYPE_PUBREL = 6;
39
	public static final byte MESSAGE_TYPE_PUBCOMP = 7;
40
	public static final byte MESSAGE_TYPE_SUBSCRIBE = 8;
41
	public static final byte MESSAGE_TYPE_SUBACK = 9;
42
	public static final byte MESSAGE_TYPE_UNSUBSCRIBE = 10;
43
	public static final byte MESSAGE_TYPE_UNSUBACK = 11;
44
	public static final byte MESSAGE_TYPE_PINGREQ = 12;
45
	public static final byte MESSAGE_TYPE_PINGRESP = 13;
46
	public static final byte MESSAGE_TYPE_DISCONNECT = 14;
47
	
48
	/** The type of the message (e.g. CONNECT, PUBLISH, PUBACK) */
49
	private byte type;
50
	/** The MQTT message ID */
51
	protected int msgId;
52
	
53
	protected boolean duplicate = false;
54
	
55
	private byte[] encodedHeader = null;
56
	
57
	public MqttWireMessage(byte type) {
58
		this.type = type;
59
		// Use zero as the default message ID.  Can't use -1, as that is serialized
60
		// as 65535, which would be a valid ID.
61
		this.msgId = 0;
62
	}
63
	
64
	/**
65
	 * Sub-classes should override this to encode the message info.
66
	 * Only the least-significant four bits will be used.
67
	 */
68
	abstract protected byte getMessageInfo();
69
	
70
	/**
71
	 * Sub-classes should override this method to supply the payload bytes.
72
	 */
73
	public byte[] getPayload() throws MqttException {
74
		return new byte[0];
75
	}
76
	
77
	/**
78
	 * Returns the type of the message.
79
	 */
80
	public byte getType() {
81
		return type;
82
	}
83
	
84
	/**
85
	 * Returns the MQTT message ID.
86
	 */
87
	public int getMessageId() {
88
		return msgId;
89
	}
90
	
91
	/**
92
	 * Sets the MQTT message ID.
93
	 */
94
	public void setMessageId(int msgId) {
95
		this.msgId = msgId;
96
	}
97
	
98
	/** 
99
	 * Returns a key associated with the message. For most message types
100
	 * this will be unique. For connect, disconnect and ping only one 
101
	 * message of this type is allowed so a fixed key will be returned
102
	 * @return key a key associated with the message
103
	 */
104
	public String getKey() {
105
		return new Integer(getMessageId()).toString();
106
	}
107
	
108
	public byte[] getHeader() throws MqttException {
109
		if (encodedHeader == null) {
110
			try {
111
				int first = ((getType() & 0x0f) << 4) ^ (getMessageInfo() & 0x0f);
112
				byte[] varHeader = getVariableHeader();
113
				int remLen = varHeader.length + getPayload().length;
114
115
				ByteArrayOutputStream baos = new ByteArrayOutputStream();
116
				DataOutputStream dos = new DataOutputStream(baos);
117
				dos.writeByte(first);
118
				dos.write(encodeMBI(remLen));
119
				dos.write(varHeader);
120
				dos.flush();
121
				encodedHeader = baos.toByteArray();
122
			} catch(IOException ioe) {
123
				throw new MqttException(ioe);
124
			}
125
		}
126
		return encodedHeader;
127
	}
128
	
129
	protected abstract byte[] getVariableHeader() throws MqttException;
130
131
132
	/**
133
	 * Returns whether or not this message needs to include a message ID.
134
	 */
135
	public boolean isMessageIdRequired() {
136
		return true;
137
	}
138
	
139
	public static MqttWireMessage createWireMessage(MqttPersistable data) throws MqttException {
140
		byte[] payload = data.getPayloadBytes();
141
		// The persistable interface allows a message to be restored entirely in the header array
142
		// Need to treat these two arrays as a single array of bytes and use the decoding
143
		// logic to identify the true header/payload split
144
		if (payload == null) {
145
			payload = new byte[0];
146
		}
147
		MultiByteArrayInputStream mbais = new MultiByteArrayInputStream(
148
				data.getHeaderBytes(),
149
				data.getHeaderOffset(),
150
				data.getHeaderLength(),
151
				payload,
152
				data.getPayloadOffset(),
153
				data.getPayloadLength());
154
		return createWireMessage(mbais);
155
	}
156
	
157
	public static MqttWireMessage createWireMessage(byte[] bytes) throws MqttException {
158
		ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
159
		return createWireMessage(bais);
160
	}
161
162
	private static MqttWireMessage createWireMessage(InputStream inputStream) throws MqttException {
163
		try {
164
			CountingInputStream counter = new CountingInputStream(inputStream);
165
			DataInputStream in = new DataInputStream(counter);
166
			int first = in.readUnsignedByte();
167
			byte type = (byte) (first >> 4);
168
			byte info = (byte) (first &= 0x0f);
169
			long remLen = readMBI(in).getValue();
170
			long totalToRead = counter.getCounter() + remLen;
171
172
			MqttWireMessage result;
173
			long remainder = totalToRead - counter.getCounter();
174
			byte[] data = new byte[0];
175
			// The remaining bytes must be the payload...
176
			if (remainder > 0) {
177
				data = new byte[(int) remainder];
178
				in.readFully(data, 0, data.length);
179
			}
180
				
181
			if (type == MqttWireMessage.MESSAGE_TYPE_PUBLISH) {
182
				result = new MqttPublish(info, data);
183
			}
184
			else if (type == MqttWireMessage.MESSAGE_TYPE_PUBACK) {
185
				result = new MqttPubAck(info, data);
186
			}
187
			else if (type == MqttWireMessage.MESSAGE_TYPE_PUBCOMP) {
188
				result = new MqttPubComp(info, data);
189
			}
190
			else if (type == MqttWireMessage.MESSAGE_TYPE_CONNACK) {
191
				result = new MqttConnack(info, data);
192
			}
193
			else if (type == MqttWireMessage.MESSAGE_TYPE_PINGRESP) {
194
				result = new MqttPingResp(info, data);
195
			}
196
			else if (type == MqttWireMessage.MESSAGE_TYPE_SUBACK) {
197
				result = new MqttSuback(info, data);
198
			}
199
			else if (type == MqttWireMessage.MESSAGE_TYPE_UNSUBACK) {
200
				result = new MqttUnsubAck(info, data);
201
			}
202
			else if (type == MqttWireMessage.MESSAGE_TYPE_PUBREL) {
203
				result = new MqttPubRel(info, data);
204
			}
205
			else if (type == MqttWireMessage.MESSAGE_TYPE_PUBREC) {
206
				result = new MqttPubRec(info, data);
207
			}
208
			else {
209
				throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_UNEXPECTED_ERROR);
210
			}
211
			return result;
212
		} catch(IOException io) {
213
			throw new MqttException(io);
214
		}
215
	}
216
		
217
	protected static byte[] encodeMBI( long number) {
218
		int numBytes = 0;
219
		long no = number;
220
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
221
		// Encode the remaining length fields in the four bytes
222
		do {
223
			byte digit = (byte)(no % 128);
224
			no = no / 128;
225
			if (no > 0) {
226
				digit |= 0x80;
227
			}
228
			bos.write(digit);
229
			numBytes++;
230
		} while ( (no > 0) && (numBytes<4) );
231
		
232
		return bos.toByteArray();
233
	}
234
	
235
	/**
236
	 * Decodes an MQTT Multi-Byte Integer from the given stream.
237
	 */
238
	protected static MultiByteInteger readMBI(DataInputStream in) throws IOException {
239
		byte digit;
240
		long msgLength = 0;
241
		int multiplier = 1;
242
		int count = 0;
243
		
244
		do {
245
			digit = in.readByte();
246
			count++;
247
			msgLength += ((digit & 0x7F) * multiplier);
248
			multiplier *= 128;
249
		} while ((digit & 0x80) != 0);
250
		
251
		return new MultiByteInteger(msgLength, count);
252
	}
253
	
254
	protected byte[] encodeMessageId() throws MqttException {
255
		try {
256
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
257
			DataOutputStream dos = new DataOutputStream(baos);
258
			dos.writeShort(msgId);
259
			dos.flush();
260
			return baos.toByteArray();
261
		}
262
		catch (IOException ex) {
263
			throw new MqttException(ex);
264
		}
265
	}
266
	
267
	public boolean isRetryable() {
268
		return false;
269
	}
270
	
271
	public void setDuplicate(boolean duplicate) {
272
		this.duplicate = duplicate;
273
	}
274
275
	/**
276
	 * Encodes a String given into UTF-8, before writing this to the DataOutputStream the length of the
277
	 * encoded string is encoded into two bytes and then written to the DataOutputStream. @link{DataOutputStream#writeUFT(String)}
278
	 * should be no longer used. @link{DataOutputStream#writeUFT(String)} does not correctly encode UTF-16 surrogate characters.
279
	 * 
280
	 * @param dos The stream to write the encoded UTF-8 String to.
281
	 * @param stringToEncode The String to be encoded 
282
	 * @throws MqttException Thrown when an error occurs with either the encoding or writing the data to the stream
283
	 */
284
	protected void encodeUTF8(DataOutputStream dos, String stringToEncode) throws MqttException
285
	{
286
		try {
287
288
			byte[] encodedString = stringToEncode.getBytes("UTF-8");
289
			byte byte1 = (byte) ((encodedString.length >>> 8) & 0xFF);
290
			byte byte2 =  (byte) ((encodedString.length >>> 0) & 0xFF);  
291
			
292
293
			dos.write(byte1);
294
			dos.write(byte2);
295
			dos.write(encodedString);
296
		}
297
		catch(UnsupportedEncodingException ex)
298
		{
299
			throw new MqttException(ex);
300
		} catch (IOException ex) {
301
			throw new MqttException(ex);
302
		}
303
	}
304
	
305
	/**
306
	 * Decodes a UTF-8 string from the DataInputStream provided. @link(DataInoutStream#readUTF()) should be no longer used, because  @link(DataInoutStream#readUTF()) 
307
	 * does not decode UTF-16 surrogate characters correctly.
308
	 * 
309
	 * @param input The input stream from which to read the encoded string
310
	 * @return a decoded String from the DataInputStream
311
	 * @throws MqttException thrown when an error occurs with either reading from the stream or
312
	 * decoding the encoded string.
313
	 */
314
	protected String decodeUTF8(DataInputStream input) throws MqttException
315
	{
316
		int encodedLength;
317
		try {
318
			encodedLength = input.readUnsignedShort();
319
320
			byte[] encodedString = new byte[encodedLength];
321
				input.readFully(encodedString);
322
323
			return new String(encodedString, "UTF-8");
324
		} catch (IOException ex) {
325
			throw new MqttException(ex);
326
		}
327
	}
328
329
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MultiByteArrayInputStream.java (+52 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import java.io.IOException;
15
import java.io.InputStream;
16
17
public class MultiByteArrayInputStream extends InputStream {
18
19
	private byte[] bytesA;
20
	private int offsetA;
21
	private int lengthA;
22
	private byte[] bytesB;
23
	private int offsetB;
24
	private int lengthB;
25
	
26
	private int pos = 0;
27
	
28
	public MultiByteArrayInputStream(byte[] bytesA, int offsetA, int lengthA, byte[] bytesB, int offsetB, int lengthB) {
29
		this.bytesA = bytesA;
30
		this.bytesB = bytesB;
31
		this.offsetA = offsetA;
32
		this.offsetB = offsetB;
33
		this.lengthA = lengthA;
34
		this.lengthB = lengthB;
35
	}
36
	public int read() throws IOException {
37
		int result = -1;
38
		if (pos<lengthA) {
39
			result = bytesA[offsetA+pos];
40
		} else if (pos<lengthA+lengthB) {
41
			result = bytesB[offsetB+pos-lengthA];
42
		} else {
43
			return -1;
44
		}
45
		if (result < 0) {
46
			result += 256;
47
		}
48
		pos++;
49
		return result;
50
	}
51
52
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MultiByteInteger.java (+44 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
/**
15
 * Represents a Multi-Byte Integer (MBI), as defined by the MQTT V3
16
 * specification.
17
 */
18
public class MultiByteInteger {
19
	private long value;
20
	private int length;
21
	
22
	public MultiByteInteger(long value) {
23
		this(value, -1);
24
	}
25
	
26
	public MultiByteInteger(long value, int length) {
27
		this.value = value;
28
		this.length = length;
29
	}
30
	
31
	/**
32
	 * Returns the number of bytes read when decoding this MBI.
33
	 */
34
	public int getEncodedLength() {
35
		return length;
36
	}
37
38
	/**
39
	 * Returns the value of this MBI.
40
	 */
41
	public long getValue() {
42
		return value;
43
	}
44
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/logging/JSR47Logger.java (+272 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.logging;
13
import java.text.MessageFormat;
14
import java.util.MissingResourceException;
15
import java.util.ResourceBundle;
16
import java.util.logging.Handler;
17
import java.util.logging.LogRecord;
18
import java.util.logging.MemoryHandler;
19
20
/**
21
 * Implementation of the the logger interface that uses java.uti.logging
22
 * 
23
 * A Logger that utilises Java's built in logging facility - java.util.logging.
24
 * <p>A sample java.util.logging properties file - jsr47min.properties is provided that demonstrates
25
 * how to run with a memory based trace facility that runs with minimal performance 
26
 * overhead. The memory buffer can be dumped when a log/trace record is written matching 
27
 * the MemoryHandlers trigger level or when the push method is invoked on the MemoryHandler. 
28
 * {@link org.eclipse.paho.client.mqttv3.util.Debug Debug} provides method to make it easy
29
 * to dump the memory buffer as well as other useful debug info. 
30
 */
31
public class JSR47Logger implements Logger {
32
	private java.util.logging.Logger	julLogger			= null;
33
	private ResourceBundle				logMessageCatalog	= null;
34
	private ResourceBundle				traceMessageCatalog	= null;
35
	private String						catalogID			= null;
36
	private String						resourceName		= null;
37
	private String						loggerName			= null;
38
39
	/**
40
	 * 
41
	 * @param logMsgCatalog  The resource bundle associated with this logger
42
	 * @param loggerID			The suffix for the loggerName (will be appeneded to org.eclipse.paho.client.mqttv3
43
	 * @param resourceContext	A context for the logger e.g. clientID or appName...
44
	 */
45
	public void initialise(ResourceBundle logMsgCatalog, String loggerID, String resourceContext ) { 
46
		this.traceMessageCatalog = logMessageCatalog;
47
		this.resourceName = resourceContext;
48
//		loggerName = "org.eclipse.paho.client.mqttv3." + ((null == loggerID || 0 == loggerID.length()) ? "internal" : loggerID);
49
		loggerName = loggerID;
50
		this.julLogger = java.util.logging.Logger.getLogger(loggerName);
51
		this.logMessageCatalog = logMsgCatalog;
52
		this.traceMessageCatalog = logMsgCatalog;
53
		this.catalogID = logMessageCatalog.getString("0");
54
		
55
	}
56
	
57
	public void setResourceName(String logContext) {
58
		this.resourceName = logContext;
59
	}
60
61
	public boolean isLoggable(int level) {
62
		return julLogger.isLoggable(mapJULLevel(level)); // || InternalTracer.isLoggable(level);
63
	}
64
65
	public void severe(String sourceClass, String sourceMethod, String msg) {
66
		log(SEVERE, sourceClass, sourceMethod, msg, null, null);
67
	}
68
69
	public void severe(String sourceClass, String sourceMethod, String msg, Object[] inserts) {
70
		log(SEVERE, sourceClass, sourceMethod, msg, inserts, null);
71
	}
72
73
	public void severe(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown) {
74
		log(SEVERE, sourceClass, sourceMethod, msg, inserts, thrown);
75
	}
76
77
	public void warning(String sourceClass, String sourceMethod, String msg) {
78
		log(WARNING, sourceClass, sourceMethod, msg, null, null);
79
	}
80
81
	public void warning(String sourceClass, String sourceMethod, String msg, Object[] inserts) {
82
		log(WARNING, sourceClass, sourceMethod, msg, inserts, null);
83
	}
84
85
	public void warning(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown) {
86
		log(WARNING, sourceClass, sourceMethod, msg, inserts, thrown);
87
	}
88
89
	public void info(String sourceClass, String sourceMethod, String msg) {
90
		log(INFO, sourceClass, sourceMethod, msg, null, null);
91
	}
92
93
	public void info(String sourceClass, String sourceMethod, String msg, Object[] inserts) {
94
		log(INFO, sourceClass, sourceMethod, msg, inserts, null);
95
	}
96
97
	public void info(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown) {
98
		log(INFO, sourceClass, sourceMethod, msg, inserts, thrown);
99
	}
100
101
	public void config(String sourceClass, String sourceMethod, String msg) {
102
		log(CONFIG, sourceClass, sourceMethod, msg, null, null);
103
	}
104
105
	public void config(String sourceClass, String sourceMethod, String msg, Object[] inserts) {
106
		log(CONFIG, sourceClass, sourceMethod, msg, inserts, null);
107
	}
108
109
	public void config(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown) {
110
		log(CONFIG, sourceClass, sourceMethod, msg, inserts, thrown);
111
	}
112
113
	public void log(int level, String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown) {
114
//		InternalTracer.log(this.catalogID, level, sourceClass, sourceMethod, msg, inserts, thrown);
115
		java.util.logging.Level julLevel = mapJULLevel(level);
116
		if (julLogger.isLoggable(julLevel)) {
117
			logToJsr47(julLevel, sourceClass, sourceMethod, this.catalogID, this.logMessageCatalog, msg, inserts, thrown);
118
		}
119
	}
120
121
//	public void setTrace(Trace trace) {
122
//		InternalTracer.setTrace(trace);
123
//	}
124
125
	public void fine(String sourceClass, String sourceMethod, String msg) {
126
		trace(FINE, sourceClass, sourceMethod, msg, null, null);
127
	}
128
129
	public void fine(String sourceClass, String sourceMethod, String msg, Object[] inserts) {
130
		trace(FINE, sourceClass, sourceMethod, msg, inserts, null);
131
	}
132
	
133
	public void fine(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex) {
134
		trace(FINE, sourceClass, sourceMethod, msg, inserts, ex);
135
	}
136
137
	public void finer(String sourceClass, String sourceMethod, String msg) {
138
		trace(FINER, sourceClass, sourceMethod, msg, null, null);
139
	}
140
141
	public void finer(String sourceClass, String sourceMethod, String msg, Object[] inserts) {
142
		trace(FINER, sourceClass, sourceMethod, msg, inserts, null);
143
	}
144
	
145
	public void finer(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex) {
146
		trace(FINER, sourceClass, sourceMethod, msg, inserts, ex);
147
	}
148
149
	public void finest(String sourceClass, String sourceMethod, String msg) {
150
		trace(FINEST, sourceClass, sourceMethod, msg, null, null);
151
	}
152
153
	public void finest(String sourceClass, String sourceMethod, String msg, Object[] inserts) {
154
		trace(FINEST, sourceClass, sourceMethod, msg, inserts, null);
155
	}
156
	
157
	public void finest(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex) {
158
		trace(FINEST, sourceClass, sourceMethod, msg, inserts, ex);
159
	}
160
161
162
	public void trace(int level, String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex) {
163
		java.util.logging.Level julLevel = mapJULLevel(level);
164
		boolean isJULLoggable = julLogger.isLoggable(julLevel);
165
//		if (FINE == level || isJULLoggable || InternalTracer.isLoggable(level)) {
166
//			InternalTracer.traceForced(level, sourceClass, sourceMethod, msg, inserts);
167
//		}
168
		if (isJULLoggable) {
169
			logToJsr47(julLevel, sourceClass, sourceMethod, this.catalogID, this.traceMessageCatalog, msg, inserts, ex);
170
		}
171
	}
172
173
	
174
	private String getResourceMessage(ResourceBundle messageCatalog, String msg) {
175
		String message;
176
		try {
177
			message = messageCatalog.getString(msg);
178
		} catch (MissingResourceException e) {
179
			// This is acceptable, simply return the given msg string.
180
			message = msg;
181
		}
182
		return message;
183
	}
184
185
	private void logToJsr47(java.util.logging.Level julLevel, String sourceClass, String sourceMethod, String catalogName,
186
			ResourceBundle messageCatalog, String msg, Object[] inserts, Throwable thrown) {
187
//		LogRecord logRecord = new LogRecord(julLevel, msg);
188
		String formattedWithArgs = msg;
189
		if (msg.indexOf("=====")== -1) {
190
			formattedWithArgs = MessageFormat.format(getResourceMessage(messageCatalog, msg), inserts);
191
		}
192
		LogRecord logRecord = new LogRecord(julLevel, resourceName + ": " +formattedWithArgs);
193
		
194
		logRecord.setSourceClassName(sourceClass);
195
		logRecord.setSourceMethodName(sourceMethod);
196
		logRecord.setLoggerName(loggerName);
197
//		logRecord.setResourceBundleName(catalogName);
198
//		logRecord.setResourceBundle(messageCatalog);
199
//		if (null != inserts) {
200
//			logRecord.setParameters(inserts);
201
//		}
202
		if (null != thrown) {
203
			logRecord.setThrown(thrown);
204
		}
205
206
		julLogger.log(logRecord);
207
	}
208
209
	private java.util.logging.Level mapJULLevel(int level) {
210
		java.util.logging.Level julLevel = null;
211
212
		switch (level) {
213
			case SEVERE:
214
				julLevel = java.util.logging.Level.SEVERE;
215
				break;
216
			case WARNING:
217
				julLevel = java.util.logging.Level.WARNING;
218
				break;
219
			case INFO:
220
				julLevel = java.util.logging.Level.INFO;
221
				break;
222
			case CONFIG:
223
				julLevel = java.util.logging.Level.CONFIG;
224
				break;
225
			case FINE:
226
				julLevel = java.util.logging.Level.FINE;
227
				break;
228
			case FINER:
229
				julLevel = java.util.logging.Level.FINER;
230
				break;
231
			case FINEST:
232
				julLevel = java.util.logging.Level.FINEST;
233
				break;
234
		}
235
236
		return julLevel;
237
	}
238
239
	public String formatMessage(String msg, Object[] inserts) {
240
		String formatString;
241
		try {
242
			formatString = logMessageCatalog.getString(msg);
243
		} catch (MissingResourceException e) {
244
			formatString = msg;
245
		}
246
		return formatString;
247
	}
248
249
	public void dumpTrace() {
250
			dumpMemoryTrace47(julLogger);
251
	}
252
253
	protected static void dumpMemoryTrace47(java.util.logging.Logger logger) {
254
		MemoryHandler mHand = null;
255
256
		if (logger!= null) {
257
			Handler[] handlers = logger.getHandlers();
258
			
259
		    for (int i=0; i<handlers.length; i++) {
260
		      if (handlers[i] instanceof java.util.logging.MemoryHandler) {
261
		        synchronized (handlers[i]) {
262
		        	mHand = ((java.util.logging.MemoryHandler)handlers[i]);
263
		        	mHand.push();
264
		        	return;
265
		        } // synchronized (handler).
266
		      }      
267
		    } // for handlers...
268
		    dumpMemoryTrace47(logger.getParent());
269
		}
270
	}
271
	
272
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/logging/Logger.java (+572 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.logging;
13
14
import java.util.ResourceBundle;
15
16
/**
17
 * A Logger object is used to send log and trace messages to a platform
18
 * specific logging implementation. Loggers are named, using a hierarchical 
19
 * dot-separated name-space.
20
 * Logger names can be arbitrary strings, but they should normally be based on
21
 * the component or the package name of the logged component
22
 * 
23
 * Logger objects may be obtained by calls on one of the getLogger factory
24
 * methods. These will either create a new Logger or return a suitable existing
25
 * Logger.
26
 * 
27
 * <p>
28
 * The int levels define a set of standard logging levels that can be used to
29
 * control logging output. The logging levels are ordered and are specified by
30
 * ordered integers. Enabling logging at a given level also enables logging at
31
 * all higher levels.
32
 * <p>
33
 * Clients should use the the convenience methods such as severe() and fine() or
34
 * one of the predefined level constants such as Logger.SEVERE and Logger.FINE
35
 * with the appropriate log(int level...) or trace(int level...) methods.
36
 * <p>
37
 * The levels in descending order are:
38
 * <ul>
39
 * <li>SEVERE (log - highest value)</li>
40
 * <li>WARNING (log)</li>
41
 * <li>INFO (log)</li>
42
 * <li>CONFIG (log)</li>
43
 * <li>FINE (trace)</li>
44
 * <li>FINER (trace)</li>
45
 * <li>FINEST (trace - lowest value)</li>
46
 * </ul>
47
 * <p>
48
 */
49
public interface Logger {
50
	/**
51
	 * SEVERE is a message level indicating a serious failure.
52
	 * <p>
53
	 * In general SEVERE messages should describe events that are of
54
	 * considerable importance and which will prevent normal program execution.
55
	 * They should be reasonably intelligible to end users and to system
56
	 * administrators.
57
	 */
58
	public static final int	SEVERE	= 1;
59
	/**
60
	 * WARNING is a message level indicating a potential problem.
61
	 * <p>
62
	 * In general WARNING messages should describe events that will be of
63
	 * interest to end users or system managers, or which indicate potential
64
	 * problems.
65
	 */
66
	public static final int	WARNING	= 2;
67
	/**
68
	 * INFO is a message level for informational messages.
69
	 * <p>
70
	 * Typically INFO messages will be written to the console or its equivalent.
71
	 * So the INFO level should only be used for reasonably significant messages
72
	 * that will make sense to end users and system admins.
73
	 */
74
	public static final int	INFO	= 3;
75
	/**
76
	 * CONFIG is a message level for static configuration messages.
77
	 * <p>
78
	 * CONFIG messages are intended to provide a variety of static configuration
79
	 * information, to assist in debugging problems that may be associated with
80
	 * particular configurations. For example, CONFIG message might include the
81
	 * CPU type, the graphics depth, the GUI look-and-feel, etc.
82
	 */
83
	public static final int	CONFIG	= 4;
84
	/**
85
	 * FINE is a message level providing tracing information.
86
	 * <p>
87
	 * All of FINE, FINER, and FINEST are intended for relatively detailed
88
	 * tracing. The exact meaning of the three levels will vary between
89
	 * subsystems, but in general, FINEST should be used for the most voluminous
90
	 * detailed output, FINER for somewhat less detailed output, and FINE for
91
	 * the lowest volume (and most important) messages.
92
	 * <p>
93
	 * In general the FINE level should be used for information that will be
94
	 * broadly interesting to developers who do not have a specialized interest
95
	 * in the specific subsystem.
96
	 * <p>
97
	 * FINE messages might include things like minor (recoverable) failures.
98
	 * Issues indicating potential performance problems are also worth logging
99
	 * as FINE.
100
	 */
101
	public static final int	FINE	= 5;
102
	/**
103
	 * FINER indicates a fairly detailed tracing message. By default logging
104
	 * calls for entering, returning, or throwing an exception are traced at
105
	 * this level.
106
	 */
107
	public static final int	FINER	= 6;
108
	/**
109
	 * FINEST indicates a highly detailed tracing message.
110
	 */
111
	public static final int	FINEST	= 7;
112
	
113
	public void initialise(ResourceBundle messageCatalog, String loggerID, String resourceName);
114
	
115
	/**
116
	 * Set a name that can be used to provide context with each log record.
117
	 * This overrides the value passed in on initialise
118
	 */
119
	public void setResourceName(String logContext);
120
	
121
	/**
122
	 * Check if a message of the given level would actually be logged by this
123
	 * logger. This check is based on the Loggers effective level, which may be
124
	 * inherited from its parent.
125
	 * 
126
	 * @param level
127
	 *            a message logging level.
128
	 * @return true if the given message level is currently being logged.
129
	 */
130
	public boolean isLoggable(int level);
131
132
	/**
133
	 * Log a message, specifying source class and method, if the logger is
134
	 * currently enabled for the given message level.
135
	 * 
136
	 * @param sourceClass
137
	 *            Name of class that issued the logging request.
138
	 * @param sourceMethod
139
	 *            Name of method that issued the logging request.
140
	 * @param msg
141
	 *            The key in the message localization catalog for the message or
142
	 *            the actual message itself. During formatting, if the logger
143
	 *            has a mapping for the msg string, then the msg string is
144
	 *            replaced by the localized value. Otherwise the original msg
145
	 *            string is used.
146
	 */
147
	public void severe(String sourceClass, String sourceMethod, String msg);
148
149
	/**
150
	 * Log a message, specifying source class and method, with an array of
151
	 * object arguments, if the logger is currently enabled for the given
152
	 * message level.
153
	 * 
154
	 * @param sourceClass
155
	 *            Name of class that issued the logging request.
156
	 * @param sourceMethod
157
	 *            Name of method that issued the logging request.
158
	 * @param msg
159
	 *            The key in the message localization catalog for the message or
160
	 *            the actual message itself. During formatting, if the logger
161
	 *            has a mapping for the msg string, then the msg string is
162
	 *            replaced by the localized value. Otherwise the original msg
163
	 *            string is used. The formatter uses java.text.MessageFormat
164
	 *            style formatting to format parameters, so for example a format
165
	 *            string "{0} {1}" would format two inserts into the message.
166
	 * @param inserts
167
	 *            Array of parameters to the message.
168
	 */
169
	public void severe(String sourceClass, String sourceMethod, String msg, Object[] inserts);
170
171
	/**
172
	 * Log a message, specifying source class and method, with an array of
173
	 * object arguments and a throwable, if the logger is currently enabled for
174
	 * the given message level.
175
	 * 
176
	 * @param sourceClass
177
	 *            Name of class that issued the logging request.
178
	 * @param sourceMethod
179
	 *            Name of method that issued the logging request.
180
	 * @param msg
181
	 *            The key in the message localization catalog for the message or
182
	 *            the actual message itself. During formatting, if the logger
183
	 *            has a mapping for the msg string, then the msg string is
184
	 *            replaced by the localized value. Otherwise the original msg
185
	 *            string is used. The formatter uses java.text.MessageFormat
186
	 *            style formatting to format parameters, so for example a format
187
	 *            string "{0} {1}" would format two inserts into the message.
188
	 * @param inserts
189
	 *            Array of parameters to the message.
190
	 * @param thrown
191
	 *            Throwable associated with log message.
192
	 */
193
	public void severe(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown);
194
195
	/**
196
	 * Log a message, specifying source class and method, if the logger is
197
	 * currently enabled for the given message level.
198
	 * 
199
	 * @param sourceClass
200
	 *            Name of class that issued the logging request.
201
	 * @param sourceMethod
202
	 *            Name of method that issued the logging request.
203
	 * @param msg
204
	 *            The key in the message localization catalog for the message or
205
	 *            the actual message itself. During formatting, if the logger
206
	 *            has a mapping for the msg string, then the msg string is
207
	 *            replaced by the localized value. Otherwise the original msg
208
	 *            string is used.
209
	 */
210
	public void warning(String sourceClass, String sourceMethod, String msg);
211
212
	/**
213
	 * Log a message, specifying source class and method, with an array of
214
	 * object arguments, if the logger is currently enabled for the given
215
	 * message level.
216
	 * 
217
	 * @param sourceClass
218
	 *            Name of class that issued the logging request.
219
	 * @param sourceMethod
220
	 *            Name of method that issued the logging request.
221
	 * @param msg
222
	 *            The key in the message localization catalog for the message or
223
	 *            the actual message itself. During formatting, if the logger
224
	 *            has a mapping for the msg string, then the msg string is
225
	 *            replaced by the localized value. Otherwise the original msg
226
	 *            string is used. The formatter uses java.text.MessageFormat
227
	 *            style formatting to format parameters, so for example a format
228
	 *            string "{0} {1}" would format two inserts into the message.
229
	 * @param inserts
230
	 *            Array of parameters to the message.
231
	 */
232
	public void warning(String sourceClass, String sourceMethod, String msg, Object[] inserts);
233
234
	/**
235
	 * Log a message, specifying source class and method, with an array of
236
	 * object arguments and a throwable, if the logger is currently enabled for
237
	 * the given message level.
238
	 * 
239
	 * @param sourceClass
240
	 *            Name of class that issued the logging request.
241
	 * @param sourceMethod
242
	 *            Name of method that issued the logging request.
243
	 * @param msg
244
	 *            The key in the message localization catalog for the message or
245
	 *            the actual message itself. During formatting, if the logger
246
	 *            has a mapping for the msg string, then the msg string is
247
	 *            replaced by the localized value. Otherwise the original msg
248
	 *            string is used. The formatter uses java.text.MessageFormat
249
	 *            style formatting to format parameters, so for example a format
250
	 *            string "{0} {1}" would format two inserts into the message.
251
	 * @param inserts
252
	 *            Array of parameters to the message.
253
	 * @param thrown
254
	 *            Throwable associated with log message.
255
	 */
256
	public void warning(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown);
257
258
	/**
259
	 * Log a message, specifying source class and method, if the logger is
260
	 * currently enabled for the given message level.
261
	 * 
262
	 * @param sourceClass
263
	 *            Name of class that issued the logging request.
264
	 * @param sourceMethod
265
	 *            Name of method that issued the logging request.
266
	 * @param msg
267
	 *            The key in the message localization catalog for the message or
268
	 *            the actual message itself. During formatting, if the logger
269
	 *            has a mapping for the msg string, then the msg string is
270
	 *            replaced by the localized value. Otherwise the original msg
271
	 *            string is used.
272
	 */
273
	public void info(String sourceClass, String sourceMethod, String msg);
274
275
	/**
276
	 * Log a message, specifying source class and method, with an array of
277
	 * object arguments, if the logger is currently enabled for the given
278
	 * message level.
279
	 * 
280
	 * @param sourceClass
281
	 *            Name of class that issued the logging request.
282
	 * @param sourceMethod
283
	 *            Name of method that issued the logging request.
284
	 * @param msg
285
	 *            The key in the message localization catalog for the message or
286
	 *            the actual message itself. During formatting, if the logger
287
	 *            has a mapping for the msg string, then the msg string is
288
	 *            replaced by the localized value. Otherwise the original msg
289
	 *            string is used. The formatter uses java.text.MessageFormat
290
	 *            style formatting to format parameters, so for example a format
291
	 *            string "{0} {1}" would format two inserts into the message.
292
	 * @param inserts
293
	 *            Array of parameters to the message.
294
	 */
295
	public void info(String sourceClass, String sourceMethod, String msg, Object[] inserts);
296
297
	/**
298
	 * Log a message, specifying source class and method, with an array of
299
	 * object arguments and a throwable, if the logger is currently enabled for
300
	 * the given message level.
301
	 * 
302
	 * @param sourceClass
303
	 *            Name of class that issued the logging request.
304
	 * @param sourceMethod
305
	 *            Name of method that issued the logging request.
306
	 * @param msg
307
	 *            The key in the message localization catalog for the message or
308
	 *            the actual message itself. During formatting, if the logger
309
	 *            has a mapping for the msg string, then the msg string is
310
	 *            replaced by the localized value. Otherwise the original msg
311
	 *            string is used. The formatter uses java.text.MessageFormat
312
	 *            style formatting to format parameters, so for example a format
313
	 *            string "{0} {1}" would format two inserts into the message.
314
	 * @param inserts
315
	 *            Array of parameters to the message.
316
	 * @param thrown
317
	 *            Throwable associated with log message.
318
	 */
319
	public void info(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown);
320
321
	/**
322
	 * Log a message, specifying source class and method, if the logger is
323
	 * currently enabled for the given message level.
324
	 * 
325
	 * @param sourceClass
326
	 *            Name of class that issued the logging request.
327
	 * @param sourceMethod
328
	 *            Name of method that issued the logging request.
329
	 * @param msg
330
	 *            The key in the message localization catalog for the message or
331
	 *            the actual message itself. During formatting, if the logger
332
	 *            has a mapping for the msg string, then the msg string is
333
	 *            replaced by the localized value. Otherwise the original msg
334
	 *            string is used.
335
	 */
336
	public void config(String sourceClass, String sourceMethod, String msg);
337
338
	/**
339
	 * Log a message, specifying source class and method, with an array of
340
	 * object arguments, if the logger is currently enabled for the given
341
	 * message level.
342
	 * 
343
	 * @param sourceClass
344
	 *            Name of class that issued the logging request.
345
	 * @param sourceMethod
346
	 *            Name of method that issued the logging request.
347
	 * @param msg
348
	 *            The key in the message localization catalog for the message or
349
	 *            the actual message itself. During formatting, if the logger
350
	 *            has a mapping for the msg string, then the msg string is
351
	 *            replaced by the localized value. Otherwise the original msg
352
	 *            string is used. The formatter uses java.text.MessageFormat
353
	 *            style formatting to format parameters, so for example a format
354
	 *            string "{0} {1}" would format two inserts into the message.
355
	 * @param inserts
356
	 *            Array of parameters to the message.
357
	 */
358
	public void config(String sourceClass, String sourceMethod, String msg, Object[] inserts);
359
360
	/**
361
	 * Log a message, specifying source class and method, with an array of
362
	 * object arguments and a throwable, if the logger is currently enabled for
363
	 * the given message level.
364
	 * 
365
	 * @param sourceClass
366
	 *            Name of class that issued the logging request.
367
	 * @param sourceMethod
368
	 *            Name of method that issued the logging request.
369
	 * @param msg
370
	 *            The key in the message localization catalog for the message or
371
	 *            the actual message itself. During formatting, if the logger
372
	 *            has a mapping for the msg string, then the msg string is
373
	 *            replaced by the localized value. Otherwise the original msg
374
	 *            string is used. The formatter uses java.text.MessageFormat
375
	 *            style formatting to format parameters, so for example a format
376
	 *            string "{0} {1}" would format two inserts into the message.
377
	 * @param inserts
378
	 *            Array of parameters to the message.
379
	 * @param thrown
380
	 *            Throwable associated with log message.
381
	 */
382
	public void config(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown);
383
384
	/**
385
	 * Trace a message, specifying source class and method, if the logger is
386
	 * currently enabled for the given message level.
387
	 * 
388
	 * @param sourceClass
389
	 *            Name of class that issued the logging request.
390
	 * @param sourceMethod
391
	 *            Name of method that issued the logging request.
392
	 * @param msg
393
	 *            The key in the message catalog for the message or the actual
394
	 *            message itself. During formatting, if the logger has a mapping
395
	 *            for the msg string, then the msg string is replaced by the
396
	 *            value. Otherwise the original msg string is used.
397
	 */
398
	public void fine(String sourceClass, String sourceMethod, String msg);
399
400
	/**
401
	 * Trace a message, specifying source class and method, with an array of
402
	 * object arguments, if the logger is currently enabled for the given
403
	 * message level.
404
	 * 
405
	 * @param sourceClass
406
	 *            Name of class that issued the logging request.
407
	 * @param sourceMethod
408
	 *            Name of method that issued the logging request.
409
	 * @param msg
410
	 *            The key in the message catalog for the message or the actual
411
	 *            message itself. During formatting, if the logger has a mapping
412
	 *            for the msg string, then the msg string is replaced by the
413
	 *            value. Otherwise the original msg string is used. The
414
	 *            formatter uses java.text.MessageFormat style formatting to
415
	 *            format parameters, so for example a format string "{0} {1}"
416
	 *            would format two inserts into the message.
417
	 * @param inserts
418
	 *            Array of parameters to the message.
419
	 */
420
	public void fine(String sourceClass, String sourceMethod, String msg, Object[] inserts);
421
	
422
	public void fine(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex);
423
424
	/**
425
	 * Trace a message, specifying source class and method, if the logger is
426
	 * currently enabled for the given message level.
427
	 * 
428
	 * @param sourceClass
429
	 *            Name of class that issued the logging request.
430
	 * @param sourceMethod
431
	 *            Name of method that issued the logging request.
432
	 * @param msg
433
	 *            The key in the message catalog for the message or the actual
434
	 *            message itself. During formatting, if the logger has a mapping
435
	 *            for the msg string, then the msg string is replaced by the
436
	 *            value. Otherwise the original msg string is used.
437
	 */
438
	public void finer(String sourceClass, String sourceMethod, String msg);
439
440
	/**
441
	 * Trace a message, specifying source class and method, with an array of
442
	 * object arguments, if the logger is currently enabled for the given
443
	 * message level.
444
	 * 
445
	 * @param sourceClass
446
	 *            Name of class that issued the logging request.
447
	 * @param sourceMethod
448
	 *            Name of method that issued the logging request.
449
	 * @param msg
450
	 *            The key in the message catalog for the message or the actual
451
	 *            message itself. During formatting, if the logger has a mapping
452
	 *            for the msg string, then the msg string is replaced by the
453
	 *            value. Otherwise the original msg string is used. The
454
	 *            formatter uses java.text.MessageFormat style formatting to
455
	 *            format parameters, so for example a format string "{0} {1}"
456
	 *            would format two inserts into the message.
457
	 * @param inserts
458
	 *            Array of parameters to the message.
459
	 */
460
	public void finer(String sourceClass, String sourceMethod, String msg, Object[] inserts);
461
	
462
	public void finer(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex);
463
464
	/**
465
	 * Trace a message, specifying source class and method, if the logger is
466
	 * currently enabled for the given message level.
467
	 * 
468
	 * @param sourceClass
469
	 *            Name of class that issued the logging request.
470
	 * @param sourceMethod
471
	 *            Name of method that issued the logging request.
472
	 * @param msg
473
	 *            The key in the message catalog for the message or the actual
474
	 *            message itself. During formatting, if the logger has a mapping
475
	 *            for the msg string, then the msg string is replaced by the
476
	 *            value. Otherwise the original msg string is used.
477
	 */
478
	public void finest(String sourceClass, String sourceMethod, String msg);
479
480
	/**
481
	 * Trace a message, specifying source class and method, with an array of
482
	 * object arguments, if the logger is currently enabled for the given
483
	 * message level.
484
	 * 
485
	 * @param sourceClass
486
	 *            Name of class that issued the logging request.
487
	 * @param sourceMethod
488
	 *            Name of method that issued the logging request.
489
	 * @param msg
490
	 *            The key in the message catalog for the message or the actual
491
	 *            message itself. During formatting, if the logger has a mapping
492
	 *            for the msg string, then the msg string is replaced by the
493
	 *            value. Otherwise the original msg string is used. The
494
	 *            formatter uses java.text.MessageFormat style formatting to
495
	 *            format parameters, so for example a format string "{0} {1}"
496
	 *            would format two inserts into the message.
497
	 * @param inserts
498
	 *            Array of parameters to the message.
499
	 */
500
	public void finest(String sourceClass, String sourceMethod, String msg, Object[] inserts);
501
	
502
	public void finest(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex);
503
504
	/**
505
	 * Log a message, specifying source class and method, with an array of
506
	 * object arguments and a throwable, if the logger is currently enabled for
507
	 * the given message level.
508
	 * 
509
	 * @param level
510
	 *            One of the message level identifiers, e.g. SEVERE.
511
	 * @param sourceClass
512
	 *            Name of class that issued the logging request.
513
	 * @param sourceMethod
514
	 *            Name of method that issued the logging request.
515
	 * @param msg
516
	 *            The key in the message localization catalog for the message or
517
	 *            the actual message itself. During formatting, if the logger
518
	 *            has a mapping for the msg string, then the msg string is
519
	 *            replaced by the localized value. Otherwise the original msg
520
	 *            string is used. The formatter uses java.text.MessageFormat
521
	 *            style formatting to format parameters, so for example a format
522
	 *            string "{0} {1}" would format two inserts into the message.
523
	 * @param inserts
524
	 *            Array of parameters to the message, may be null.
525
	 * @param thrown
526
	 *            Throwable associated with log message.
527
	 */
528
	public void log(int level, String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown);
529
530
	/**
531
	 * Log a trace message, specifying source class and method, with an array of
532
	 * object arguments and a throwable, if the logger is currently enabled for
533
	 * the given message level.
534
	 * 
535
	 * @param level
536
	 *            One of the message level identifiers, e.g. SEVERE.
537
	 * @param sourceClass
538
	 *            Name of class that issued the logging request.
539
	 * @param sourceMethod
540
	 *            Name of method that issued the logging request.
541
	 * @param msg
542
	 *            The key in the message catalog for the message or the actual
543
	 *            message itself. During formatting, if the logger has a mapping
544
	 *            for the msg string, then the msg string is replaced by the
545
	 *            value. Otherwise the original msg string is used. The
546
	 *            formatter uses java.text.MessageFormat style formatting to
547
	 *            format parameters, so for example a format string "{0} {1}"
548
	 *            would format two inserts into the message.
549
	 * @param inserts
550
	 *            Array of parameters to the message, may be null.
551
	 */
552
	public void trace(int level, String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex);
553
554
	/**
555
	 * Format a log message without causing it to be written to the log.
556
	 * 
557
	 * @param msg
558
	 *            The key in the message localization catalog for the message or
559
	 *            the actual message itself. During formatting, if the logger
560
	 *            has a mapping for the msg string, then the msg string is
561
	 *            replaced by the localized value. Otherwise the original msg
562
	 *            string is used. The formatter uses java.text.MessageFormat
563
	 *            style formatting to format parameters, so for example a format
564
	 *            string "{0} {1}" would format two inserts into the message.
565
	 * @param inserts
566
	 *            Array of parameters to the message.
567
	 * @return The formatted message for the current locale.
568
	 */
569
	public String formatMessage(String msg, Object[] inserts);
570
	
571
	public void dumpTrace();
572
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/logging/LoggerFactory.java (+152 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.logging;
13
14
import java.lang.reflect.Method;
15
16
/**
17
 * LoggerFactory will create a logger instance ready for use by the caller. 
18
 * 
19
 * The default is to create a logger that utilises the Java's built in 
20
 * logging facility java.util.logging (JSR47).  It is possible to override
21
 * this for systems where JSR47 is not available or an alternative logging
22
 * facility is needed by using setLogger and passing the the class name of 
23
 * a logger that implements {@link Logger}
24
 */
25
import java.util.MissingResourceException;
26
import java.util.ResourceBundle;
27
/**
28
 * A factory that returns a logger for use by the MQTT client. 
29
 * 
30
 * The default log and trace facility uses Java's build in log facility:-
31
 * java.util.logging.  For systems where this is not available or where
32
 * an alternative logging framework is required the logging facility can be 
33
 * replaced using {@link org.eclipse.paho.client.mqttv3.logging.LoggerFactory#setLogger(String)}
34
 * which takes an implementation of the {@link org.eclipse.paho.client.mqttv3.logging.Logger}
35
 * interface.
36
 */
37
public class LoggerFactory {
38
	/**
39
	 * Default message catalog.
40
	 */
41
	public final static String MQTT_CLIENT_MSG_CAT = "org.eclipse.paho.client.mqttv3.internal.nls.logcat";
42
	private final static String className = LoggerFactory.class.getName();
43
	
44
	private static String overrideloggerClassName = null;
45
	/**
46
	 * Default logger that uses java.util.logging. 
47
	 */
48
	private static String jsr47LoggerClassName = "org.eclipse.paho.client.mqttv3.logging.JSR47Logger"; 
49
	
50
	/**
51
	 * Find or create a logger for a named package/class. 
52
	 * If a logger has already been created with the given name 
53
	 * it is returned. Otherwise a new logger is created. By default a logger
54
	 * that uses java.util.logging will be returned.
55
	 * 
56
	 * @param messageCatalogName the resource bundle containing the logging messages.
57
	 * @param loggerID  unique name to identify this logger.
58
	 * @return a suitable Logger.
59
	 * @throws Exception
60
	 */
61
	public static Logger getLogger(String messageCatalogName, String loggerID) {
62
		String loggerClassName = overrideloggerClassName;
63
		Logger logger = null;
64
		
65
		if (loggerClassName == null) {
66
			loggerClassName = jsr47LoggerClassName;
67
		}
68
//			logger = getJSR47Logger(ResourceBundle.getBundle(messageCatalogName), loggerID, null) ;
69
		logger = getLogger(loggerClassName, ResourceBundle.getBundle(messageCatalogName), loggerID, null) ;
70
//		}
71
72
		if (null == logger) {
73
			throw new MissingResourceException("Error locating the logging class", className, loggerID);
74
		}
75
76
		return logger;
77
	}
78
79
80
	/**
81
	 * Return an instance of a logger
82
	 * 
83
	 * @param the class name of the load to load
84
	 * @param messageCatalog  the resource bundle containing messages 
85
	 * @param loggerID  an identifier for the logger 
86
	 * @param resourceName a name or context to associate with this logger instance.  
87
	 * @return a ready for use logger
88
	 */
89
	private static Logger getLogger(String loggerClassName, ResourceBundle messageCatalog, String loggerID, String resourceName) { //, FFDC ffdc) {
90
		Logger logger  = null;
91
		Class logClass = null;
92
		
93
		try {
94
			logClass = Class.forName(loggerClassName);
95
		} catch (NoClassDefFoundError ncdfe) {
96
			return null;
97
		} catch (ClassNotFoundException cnfe) {
98
			return null;
99
		}
100
		if (null != logClass) {
101
			// Now instantiate the log
102
			try {
103
				logger = (Logger)logClass.newInstance();
104
			} catch (IllegalAccessException e) {
105
				return null;
106
			} catch (InstantiationException e) {
107
				return null;
108
			} catch (ExceptionInInitializerError e) {
109
				return null;
110
			} catch (SecurityException e) {
111
				return null;
112
			}
113
			logger.initialise(messageCatalog, loggerID, resourceName);
114
		}
115
116
		return logger;
117
	}
118
119
	/**
120
	 * When run in JSR47, this allows access to the properties in the logging.properties
121
	 * file.
122
	 * If not run in JSR47, or the property isn't set, returns null.
123
	 * @param name the property to return
124
	 * @return the property value, or null if it isn't set or JSR47 isn't being used
125
	 */
126
	public static String getLoggingProperty(String name) {
127
		String result = null;
128
		try {
129
			// Hide behind reflection as java.util.logging is guaranteed to be
130
			// available.
131
			Class logManagerClass = Class.forName("java.util.logging.LogManager");
132
			Method m1 = logManagerClass.getMethod("getLogManager", new Class[]{});
133
			Object logManagerInstance = m1.invoke(null, null);
134
			Method m2 = logManagerClass.getMethod("getProperty", new Class[]{String.class});
135
			result = (String)m2.invoke(logManagerInstance,new Object[]{name});
136
		} catch(Exception e) {
137
			// Any error, assume JSR47 isn't available and return null
138
			result = null;
139
		}
140
		return result;
141
	}
142
143
	/**
144
	 * Set the class name of the logger that the LoggerFactory will load
145
	 * If not set getLogger will attempt to create a logger 
146
	 * appropriate for the platform.
147
	 * @param loggerClassName - Logger implementation class name to use.
148
	 */
149
	public static void setLogger(String loggerClassName) {
150
		LoggerFactory.overrideloggerClassName = loggerClassName;
151
	}
152
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/logging/SimpleLogFormatter.java (+91 lines)
Added Link Here
1
package org.eclipse.paho.client.mqttv3.logging;
2
3
import java.io.PrintWriter;
4
import java.io.StringWriter;
5
import java.text.MessageFormat;
6
import java.util.Date;
7
import java.util.logging.Formatter;
8
import java.util.logging.LogRecord;
9
10
/**
11
 * SimpleLogFormatter prints a single line 
12
 * log record in human readable form.
13
 */
14
public class SimpleLogFormatter extends Formatter {
15
	
16
	final String ls = System.getProperty("line.separator");
17
    /**
18
     * Constructs a <code>SimpleFormatter</code> object.
19
     */
20
    public SimpleLogFormatter() {
21
        super();
22
    }
23
24
    /**
25
     * Format the logrecord as a single line with well defined columns.
26
     */
27
    public String format(LogRecord r) {
28
        StringBuffer sb = new StringBuffer();
29
        sb.append(r.getLevel().getName()+"\t");
30
        sb.append(MessageFormat.format("{0, date, yy-MM-dd} {0, time, kk:mm:ss.SSSS} ",
31
                new Object[] { new Date(r.getMillis()) })+"\t");
32
        String cnm = r.getSourceClassName();
33
        String cn="";
34
        if (cnm != null) {
35
	        int cnl = cnm.length();
36
	        if (cnl>20) {
37
	        	cn = r.getSourceClassName().substring(cnl-19);
38
	        } else {
39
	        	char sp[] = {' '};
40
	        	StringBuffer sb1= new StringBuffer().append(cnm);
41
	        	cn = sb1.append(sp,0, 1).toString();
42
	        }        
43
        }
44
        sb.append(cn+"\t").append(" ");
45
        sb.append(left(r.getSourceMethodName(),23,' ')+"\t");
46
        sb.append(r.getThreadID()+"\t"); 
47
        sb.append(formatMessage(r)).append(ls);
48
        if (null != r.getThrown()) {
49
            sb.append("Throwable occurred: "); 
50
            Throwable t = r.getThrown();
51
            PrintWriter pw = null;
52
            try {
53
                StringWriter sw = new StringWriter();
54
                pw = new PrintWriter(sw);
55
                t.printStackTrace(pw);
56
                sb.append(sw.toString());
57
            } finally {
58
                if (pw != null) {
59
                    try {
60
                        pw.close();
61
                    } catch (Exception e) {
62
                        // ignore
63
                    }
64
                }
65
            }
66
        }
67
        return sb.toString();
68
    }
69
    
70
	/**
71
	   * Left justify a string.
72
	   *
73
	   * @param s the string to justify
74
	   * @param width the field width to justify within
75
	   * @param fillChar the character to fill with
76
	   *
77
	   * @return the justified string.
78
	   */
79
	  public static String left(String s, int width, char fillChar) {
80
	    if (s.length() >= width) {
81
	      return s;
82
	    }
83
	    StringBuffer sb = new StringBuffer(width);
84
	    sb.append(s);
85
	    for (int i = width - s.length(); --i >= 0;) {
86
	      sb.append(fillChar);
87
	    }
88
	    return sb.toString();
89
	  }
90
91
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/logging/jsr47min.properties (+83 lines)
Added Link Here
1
# Properties file which configures the operation of the JDK logging facility.
2
#
3
# The configuration in this file is the suggesgted configuration
4
# for collecting trace for helping debug problems related to the
5
# Paho MQTT client.  It configures trace to be continuosly collected
6
# in memory with minimal impact on performance. 
7
# 
8
# When the push trigger (by default a Severe level message) or a 
9
# specific request is made to "push" the in memory trace then it 
10
# is "pushed" to the configured target handler. By default
11
# this is the standard java.util.logging.FileHandler. The Paho Debug 
12
# class can be used to push the memory trace to its target 
13
# 
14
# To enable trace either:
15
# - use this properties file as is and set the logging facility up 
16
#   to use it by configuring the util logging system property e.g.
17
#
18
# >java -Djava.util.logging.config.file=<location>\jsr47min.properties
19
#
20
# - This contents of this file can also be merged with another
21
#   java.util.logging config file to ensure provide wider logging 
22
#   and trace including Paho trace 
23
24
# Global logging properties.
25
# ------------------------------------------
26
# The set of handlers to be loaded upon startup.
27
# Comma-separated list of class names.
28
# - Root handlers are not enabled by default - just handlers on the Paho packages. 
29
#handlers=java.util.logging.MemoryHandler,java.util.logging.FileHandler, java.util.logging.ConsoleHandler
30
31
# Default global logging level.
32
# Loggers and Handlers may override this level
33
#.level=INFO
34
35
# Loggers
36
# ------------------------------------------
37
# A memoryhandler is attached to the paho packages
38
# and the level specified to collected all trace related
39
# to paho packages.  This will override any root/global
40
# level handlers if set.  
41
org.eclipse.paho.client.mqttv3.handlers=java.util.logging.MemoryHandler
42
org.eclipse.paho.client.mqttv3.level=ALL
43
# It is possible to set more granular trace on a per class basis e.g.
44
#org.eclipse.paho.client.mqttv3.internal.ClientComms.level=ALL
45
46
# Handlers
47
# -----------------------------------------
48
# Note: the target handler that is associated with the MemoryHandler is not a root handler 
49
# and hence not returned when getting the handlers from root. It appears accessing 
50
# target handler programatically is not possible as target is a private variable in 
51
# class MemoryHandler
52
java.util.logging.MemoryHandler.level=FINEST
53
java.util.logging.MemoryHandler.size=10000
54
java.util.logging.MemoryHandler.push=SEVERE
55
java.util.logging.MemoryHandler.target=java.util.logging.FileHandler
56
#java.util.logging.MemoryHandler.target=java.util.logging.ConsoleHandler
57
58
59
# --- FileHandler ---
60
# Override of global logging level
61
java.util.logging.FileHandler.level=ALL
62
63
# Naming style for the output file:
64
# (The output file is placed in the directory
65
# defined by the "user.home" System property.)
66
# See java.util.logging for more options 
67
java.util.logging.FileHandler.pattern=%h/paho%u.log
68
69
# Limiting size of output file in bytes:
70
java.util.logging.FileHandler.limit=200000
71
72
# Number of output files to cycle through, by appending an
73
# integer to the base file name:
74
java.util.logging.FileHandler.count=3
75
76
# Style of output (Simple or XML):
77
java.util.logging.FileHandler.formatter=org.eclipse.paho.client.mqttv3.logging.SimpleLogFormatter
78
79
# --- ConsoleHandler ---
80
# Override of global logging level
81
#java.util.logging.ConsoleHandler.level=INFO
82
#java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
83
#java.util.logging.ConsoleHandler.formatter=org.eclipse.paho.client.mqttv3.logging.SimpleLogFormatter
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/logging/package.html (+18 lines)
Added Link Here
1
<body>
2
Provides facilities to write and format log and trace to help debug problems.
3
4
<p>The default log and trace facility uses Java's build in log facility:-
5
java.util.logging.  For systems where this is not available or where
6
an alternative logging framework is required the logging facility can be 
7
replaced using {@link org.eclipse.paho.client.mqttv3.logging.LoggerFactory#setLogger(String)}
8
which takes an implementation of the {@link org.eclipse.paho.client.mqttv3.logging.Logger}
9
interface.
10
11
<p>A sample java.util.logging properties file - jsr47min.properties is provided that demonstrates
12
how to run with a memory based trace facility that runs with minimal performance 
13
overhead. The memory buffer can be dumped when a log/trace record is written matching 
14
the MemoryHandlers trigger level or when the push method is invoked on the MemoryHandler. 
15
{@link org.eclipse.paho.client.mqttv3.util.Debug Debug} provides method to make it easy
16
to dump the memory buffer as well as other useful debug info. 
17
18
</body>
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/package.html (+127 lines)
Added Link Here
1
<body>
2
Contains a programming interface enabling applications to communicate with an MQTT server
3
4
<p>
5
The MQ Telemetry Transport (MQTT) is a lightweight broker-based publish/subscribe 
6
messaging protocol designed to be open, simple, lightweight and easy to implement. 
7
These characteristics make it ideal for use in constrained environments, for example, 
8
but not limited to:
9
<ul>
10
  <li>Where the network is expensive, has low bandwidth or is unreliable such as mobile and vsat networks
11
  <li>When run on an embedded or mobile device with limited processor, memory or battery
12
</ul>
13
<p>Features of the protocol include:
14
<ul>
15
  <li>The publish/subscribe message pattern to provide one-to-many message 
16
  distribution and decoupling of applications
17
  <li>A messaging transport that is agnostic to the content of the payload
18
  <li>The use of TCP/IP to provide network connectivity
19
  <li>The use of SSL/TLS to provide network security and trust
20
  <li>Three qualities of service for message delivery which are maintained across 
21
  network, client and server breaks.
22
  <ul>
23
    <li>"At most once", where messages are delivered according to the best efforts 
24
     of the underlying TCP/IP network. Message loss or duplication can occur. 
25
     This level could be used, for example, with ambient sensor data where it 
26
     does not matter if an individual reading is lost as the next one will be published soon after.
27
    <li>"At least once", where messages are assured to arrive but duplicates may occur.
28
    <li>"Exactly once", where message are assured to arrive exactly once. This 
29
     level could be used, for example, with billing systems where duplicate or 
30
     lost messages could lead to incorrect charges being applied.
31
  </ul>
32
  The quality of service for message delivery is met even if the network connection 
33
  breaks, or the client or the server stop while a message is being delivered
34
  <li>A small transport overhead (the fixed-length header is just 2 bytes), and 
35
   protocol exchanges minimised to reduce network traffic
36
  <li>A mechanism to notify interested parties to an abnormal disconnection of
37
   a client using the Last Will and Testament feature
38
</ul>
39
40
<p>The basic means of operating the client is:</p>
41
<ol>
42
  <li>Create an instance of {@link org.eclipse.paho.client.mqttv3.MqttClient} or 
43
	{@link org.eclipse.paho.client.mqttv3.MqttAsyncClient}, providing
44
	the address of an MQTT server and a unique client identifier.</li>
45
  <li><code>connect</code> to the server</li>
46
  <li>Exchange messages with the server:
47
  <ul>
48
	<li><code>publish messages</code> to the server, 
49
	 via a <code>topic</code>.</li>
50
	<li><code>subscribe</code> to one more <code>topics</code>. The server will send any messages
51
	 it receives on those topics to the client. The client will be informed when a message 
52
	 arrives via a <code>callback</code> 
53
  </ul>
54
  <li><code>disconnect</code> from the server.</li>
55
</ol>
56
57
<p>The programming model and concepts like the protocol are small and easy to use. Key concepts 
58
to use when creating MQTT application include:
59
<ul>
60
  <li>Every client instance that connects to an MQTT server must have a unique client identifier.
61
    If a second instance of a client with the same ID connects to a server the first instance will be
62
    disconnected.
63
  <li>For message delivery to be reliable and withstand both abnormal network breaks and clients/server
64
    outages the client must use a persistent store to hold messages while they are being delivered. This is 
65
    the default case where a file based persistent store 
66
    {@link org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence MqttDefaultFilePersistence} is used. 
67
  <li>When connecting the {@link org.eclipse.paho.client.mqttv3.MqttConnectOptions#setCleanSession(boolean) cleansession}
68
   option has a big impact on the operation of the client. If set to false:
69
  <ul>
70
    <li>Message delivery will match the quality of service specified when the message was published even across
71
      failures of the network, client or server
72
    <li>The server will store messages for active subscriptions on behalf of the client when the client is not connected.
73
      The server will deliver these messages to the client the next time it connects.
74
  </ul>
75
  If set to true:
76
  <ul>
77
    <li>Any state stored on the client and server related to the client will be cleansed 
78
      before the connection is fully started.  Subscriptions from earlier sessions will be unsubscribed
79
      and any messages still in-flight from previous sessions will be deleted.
80
    <li>When the client disconnects either as the result of the application requesting a disconnect
81
      or a network failure, state related to the client will be cleansed just as at connect time. 
82
    <li>Messages will only be delivered to the quality of service requested at publish time if 
83
      the connection is maintained while the message is being delivered
84
  </ul>
85
  <li>When subscribing for messages the subscription can be for an absolute topic or a wildcarded topic.
86
  <li>When unsubscribing the topic to be unsubscribed must match one specified on an earlier subscribe.
87
  <li>There are two MQTT client libraries to choose from:
88
  <ol>
89
    <li>{@link org.eclipse.paho.client.mqttv3.IMqttAsyncClient MqttAsyncClient} which provides a non-blocking interface where
90
      methods return before the requested operation has completed. The completion of the operation
91
      can be monitored by in several ways:
92
    <ul>
93
      <li>Use the {@link org.eclipse.paho.client.mqttv3.IMqttToken#waitForCompletion waitForCompletion}
94
       call on the token returned from the operation. This will block 
95
        until the operation completes. 
96
      <li>Pass a {@link org.eclipse.paho.client.mqttv3.IMqttActionListener IMqttActionListener}
97
        to the operation. The listener will then be called back when the operation completes.
98
      <li>Set a {@link org.eclipse.paho.client.mqttv3.MqttCallback MqttCallback} on the client. It
99
        will be notified when a message arrive, a message have been delivered to the server and when the
100
        connection to the server is lost.
101
    </ul>
102
    <li>{@link org.eclipse.paho.client.mqttv3.IMqttClient MqttClient} where methods block until
103
      the operation has completed.
104
  </ol>
105
  <li>For both the blocking and non-blocking clients some operations are asynchronous.  This includes:
106
  <ul>
107
    <li>Notification that a new message has arrived:
108
      {@link org.eclipse.paho.client.mqttv3.MqttCallback#messageArrived messageArrived}.
109
    <li>Notification that the connection to the server has broken:
110
      {@link org.eclipse.paho.client.mqttv3.MqttCallback#connectionLost connectionLost}.
111
    <li>Notification that a message has been delivered to the server: 
112
      {@link org.eclipse.paho.client.mqttv3.MqttCallback#deliveryComplete deliveryComplete}.
113
  </ul>
114
  A client registers interest in these notifications by registering a 
115
  {@link org.eclipse.paho.client.mqttv3.MqttCallback MqttCallback} on the client
116
  <li>There are a number of programs that demonstrate the different modes of 
117
    writing MQTT applications
118
  <ul>
119
    <li>{@link org.eclipse.paho.sample.mqttv3app.Sample} uses the blocking client interface
120
    <li>{@link org.eclipse.paho.sample.mqttv3app.SampleAsyncCallBack} uses the asynchronous client with
121
      callbacks which are notified when an operation completes
122
    <li>{@link org.eclipse.paho.sample.mqttv3app.SampleAsyncWait} uses the asynchronous client and
123
      shows how to use the token returned from each operation to block until the operation completes.
124
  </ul>
125
</ul>
126
127
</body>
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/persist/MemoryPersistence.java (+89 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.persist;
13
14
import java.util.Enumeration;
15
import java.util.Hashtable;
16
17
import org.eclipse.paho.client.mqttv3.MqttClientPersistence;
18
import org.eclipse.paho.client.mqttv3.MqttPersistable;
19
import org.eclipse.paho.client.mqttv3.MqttPersistenceException;
20
21
/**
22
 * Persistence that uses memory
23
 * 
24
 * In cases where reliability is not required across client or device 
25
 * restarts memory this memory peristence can be used. In cases where
26
 * reliability is required like when clean session is set to false
27
 * then a non-volatile form of persistence should be used. 
28
 * 
29
 */
30
public class MemoryPersistence implements MqttClientPersistence {
31
32
	private Hashtable data;
33
	
34
	/* (non-Javadoc)
35
	 * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#close()
36
	 */
37
	public void close() throws MqttPersistenceException {
38
		data.clear();
39
	}
40
41
	/* (non-Javadoc)
42
	 * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#keys()
43
	 */
44
	public Enumeration keys() throws MqttPersistenceException {
45
		return data.keys();
46
	}
47
48
	/* (non-Javadoc)
49
	 * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#get(java.lang.String)
50
	 */
51
	public MqttPersistable get(String key) throws MqttPersistenceException {
52
		return (MqttPersistable)data.get(key);
53
	}
54
55
	/* (non-Javadoc)
56
	 * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#open(java.lang.String, java.lang.String)
57
	 */
58
	public void open(String clientId, String serverURI) throws MqttPersistenceException {
59
		this.data = new Hashtable();
60
	}
61
62
	/* (non-Javadoc)
63
	 * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#put(java.lang.String, org.eclipse.paho.client.mqttv3.MqttPersistable)
64
	 */
65
	public void put(String key, MqttPersistable persistable) throws MqttPersistenceException {
66
		data.put(key, persistable);
67
	}
68
69
	/* (non-Javadoc)
70
	 * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#remove(java.lang.String)
71
	 */
72
	public void remove(String key) throws MqttPersistenceException {
73
		data.remove(key);
74
	}
75
76
	/* (non-Javadoc)
77
	 * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#clear()
78
	 */
79
	public void clear() throws MqttPersistenceException {
80
		data.clear();
81
	}
82
83
	/* (non-Javadoc)
84
	 * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#containsKey(java.lang.String)
85
	 */
86
	public boolean containsKey(String key) throws MqttPersistenceException {
87
		return data.containsKey(key);
88
	}
89
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/persist/MqttDefaultFilePersistence.java (+288 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.persist;
13
14
import java.io.File;
15
import java.io.FileFilter;
16
import java.io.FileInputStream;
17
import java.io.FileOutputStream;
18
import java.io.FilenameFilter;
19
import java.io.IOException;
20
import java.util.Enumeration;
21
import java.util.Vector;
22
23
import org.eclipse.paho.client.mqttv3.MqttClientPersistence;
24
import org.eclipse.paho.client.mqttv3.MqttPersistable;
25
import org.eclipse.paho.client.mqttv3.MqttPersistenceException;
26
import org.eclipse.paho.client.mqttv3.internal.FileLock;
27
import org.eclipse.paho.client.mqttv3.internal.MqttPersistentData;
28
29
/**
30
 * An implementation of the {@link MqttClientPersistence} interface that provides
31
 * file based persistence.
32
 * 
33
 * A directory is specified when the Persistence object is created. When the persistence
34
 * is then opened (see {@link #open(String, String)}), a sub-directory is made beneath the base
35
 * for this client ID and connection key. This allows one persistence base directory
36
 * to be shared by multiple clients.
37
 * 
38
 * The sub-directory's name is created from a concatenation of the client ID and connection key
39
 * with any instance of '/', '\\', ':' or ' ' removed.
40
 */
41
public class MqttDefaultFilePersistence implements MqttClientPersistence {
42
43
	private File dataDir;
44
	private File clientDir = null;
45
	private FileLock fileLock = null;
46
	private static final String MESSAGE_FILE_EXTENSION = ".msg";
47
	private static final String MESSAGE_BACKUP_FILE_EXTENSION = ".bup";
48
	private static final String LOCK_FILENAME = ".lck"; 
49
	
50
	private static final FilenameFilter FILE_FILTER = new FilenameFilter() { 
51
		public boolean accept(File dir, String name) { return name.endsWith(MESSAGE_FILE_EXTENSION); }
52
		};
53
	
54
	public MqttDefaultFilePersistence()  { //throws MqttPersistenceException {
55
		this(System.getProperty("user.dir"));
56
	}
57
	
58
	/**
59
	 * Create an file-based persistent data store within the specified directory.
60
	 * @param directory the directory to use.
61
	 */
62
	public MqttDefaultFilePersistence(String directory) { //throws MqttPersistenceException {
63
		dataDir = new File(directory);
64
	}
65
	
66
	public void open(String clientId, String theConnection) throws MqttPersistenceException {
67
		
68
		if (dataDir.exists() && !dataDir.isDirectory()) {
69
			throw new MqttPersistenceException();
70
		} else if (!dataDir.exists() ) {
71
			if (!dataDir.mkdirs()) {
72
				throw new MqttPersistenceException();
73
			}
74
		} 
75
		if (!dataDir.canWrite()) {
76
			throw new MqttPersistenceException();
77
		}
78
		
79
		
80
		StringBuffer keyBuffer = new StringBuffer();
81
		for (int i=0;i<clientId.length();i++) {
82
			char c = clientId.charAt(i);
83
			if (isSafeChar(c)) {
84
				keyBuffer.append(c);
85
			}
86
		}
87
		keyBuffer.append("-");
88
		for (int i=0;i<theConnection.length();i++) {
89
			char c = theConnection.charAt(i);
90
			if (isSafeChar(c)) {
91
				keyBuffer.append(c);
92
			}
93
		}
94
		String key = keyBuffer.toString();
95
96
		clientDir = new File(dataDir,key);
97
98
		if (!clientDir.exists()) {
99
			clientDir.mkdir();
100
		}
101
	
102
		try {
103
			fileLock = new FileLock(clientDir,LOCK_FILENAME);
104
		} catch (Exception e) {
105
			throw new MqttPersistenceException(MqttPersistenceException.REASON_CODE_PERSISTENCE_IN_USE);
106
		}
107
108
		// Scan the directory for .backup files. These will
109
		// still exist if the JVM exited during addMessage, before
110
		// the new message was written to disk and the backup removed.
111
		restoreBackups(clientDir);
112
		
113
	}
114
115
	/**
116
	 * Checks whether the persistence has been opened.
117
	 * @throws MqttPersistenceException if the persistence has not been opened.
118
	 */
119
	private void checkIsOpen() throws MqttPersistenceException {
120
		if (clientDir == null) {
121
			throw new MqttPersistenceException();
122
		}
123
	}
124
125
	public void close() throws MqttPersistenceException {
126
127
//		checkIsOpen();
128
		if (fileLock != null) {
129
			fileLock.release();
130
		}
131
132
		if (getFiles().length == 0) {
133
			clientDir.delete();
134
		}
135
		clientDir = null;
136
	}
137
138
	/**
139
	 * Writes the specified persistent data to the previously specified persistence directory.
140
	 * This method uses a safe overwrite policy to ensure IO errors do not lose messages.
141
	 * @param message
142
	 * @throws MqttPersistenceException
143
	 */
144
	public void put(String key, MqttPersistable message) throws MqttPersistenceException {
145
		checkIsOpen();
146
		File file = new File(clientDir, key+MESSAGE_FILE_EXTENSION);
147
		File backupFile = new File(clientDir, key+MESSAGE_FILE_EXTENSION+MESSAGE_BACKUP_FILE_EXTENSION);
148
		
149
		if (file.exists()) {
150
			// Backup the existing file so the overwrite can be rolled-back 
151
			boolean result = file.renameTo(backupFile);
152
			if (!result) {
153
				backupFile.delete();
154
				file.renameTo(backupFile);
155
			}
156
		}
157
		try {
158
			FileOutputStream fos = new FileOutputStream(file);
159
			fos.write(message.getHeaderBytes(), message.getHeaderOffset(), message.getHeaderLength());
160
			if (message.getPayloadBytes()!=null) {
161
				fos.write(message.getPayloadBytes(), message.getPayloadOffset(), message.getPayloadLength());
162
			}
163
			fos.getFD().sync();
164
			fos.close();
165
			if (backupFile.exists()) {
166
				// The write has completed successfully, delete the backup 
167
				backupFile.delete();
168
			}
169
		}
170
		catch (IOException ex) {
171
			throw new MqttPersistenceException(ex);
172
		} 
173
		finally {
174
			if (backupFile.exists()) {
175
				// The write has failed - restore the backup
176
				boolean result = backupFile.renameTo(file);
177
				if (!result) {
178
					file.delete();
179
					backupFile.renameTo(file);
180
				}
181
			}
182
		}
183
	}
184
185
	public MqttPersistable get(String key) throws MqttPersistenceException {
186
		checkIsOpen();
187
		MqttPersistable result;
188
		try {
189
			File file = new File(clientDir, key+MESSAGE_FILE_EXTENSION);
190
			FileInputStream fis = new FileInputStream(file);
191
			int size = fis.available();
192
			byte[] data = new byte[size];
193
			int read = 0;
194
			while (read<size) {
195
				read += fis.read(data,read,size-read);
196
			}
197
			fis.close();
198
			result = new MqttPersistentData(key, data, 0, data.length, null, 0, 0);
199
		} 
200
		catch(IOException ex) {
201
			throw new MqttPersistenceException(ex);
202
		}
203
		return result;
204
	}
205
206
207
	/**
208
	 * Deletes the data with the specified key from the previously specified persistence directory.
209
	 */
210
	public void remove(String key) throws MqttPersistenceException {
211
		checkIsOpen();
212
		File file = new File(clientDir, key+MESSAGE_FILE_EXTENSION);
213
		if (file.exists()) {
214
			file.delete();
215
		}
216
	}
217
	
218
	/**
219
	 * Returns all of the persistent data from the previously specified persistence directory.
220
	 * @return all of the persistent data from the persistence directory.
221
	 * @throws MqttPersistenceException
222
	 */
223
	public Enumeration keys() throws MqttPersistenceException {
224
		checkIsOpen();
225
		File[] files = getFiles();
226
		Vector result = new Vector(files.length);
227
		for (int i=0;i<files.length;i++) {
228
			String filename = files[i].getName();
229
			String key = filename.substring(0,filename.length()-MESSAGE_FILE_EXTENSION.length());
230
			result.addElement(key);
231
		}
232
		return result.elements();
233
	}
234
	
235
	private File[] getFiles() throws MqttPersistenceException {
236
		checkIsOpen();
237
		File[] files = clientDir.listFiles(FILE_FILTER);
238
		if (files == null) {
239
			throw new MqttPersistenceException();
240
		}
241
		return files;
242
	}
243
	
244
	private boolean isSafeChar(char c) {
245
		return Character.isJavaIdentifierPart(c) || c=='-';
246
	}
247
	
248
	/**
249
	 * Identifies any backup files in the specified directory and restores them
250
	 * to their original file. This will overwrite any existing file of the same
251
	 * name. This is safe as a stray backup file will only exist if a problem
252
	 * occured whilst writing to the original file.
253
	 * @param dir The directory in which to scan and restore backups
254
	 */
255
	private void restoreBackups(File dir) throws MqttPersistenceException {
256
		File[] files = dir.listFiles(new FileFilter() {
257
			public boolean accept(File f) {
258
				return f.getName().endsWith(MESSAGE_BACKUP_FILE_EXTENSION);
259
			}
260
		});
261
		if (files == null) {
262
			throw new MqttPersistenceException();
263
		}
264
265
		for (int i=0;i<files.length;i++) {
266
			File originalFile = new File(dir,files[i].getName().substring(0,files[i].getName().length()-MESSAGE_BACKUP_FILE_EXTENSION.length()));
267
			boolean result = files[i].renameTo(originalFile);
268
			if (!result) {
269
				originalFile.delete();
270
				files[i].renameTo(originalFile);
271
			}
272
		}
273
	}
274
275
	public boolean containsKey(String key) throws MqttPersistenceException {
276
		checkIsOpen();
277
		File file = new File(clientDir, key+MESSAGE_FILE_EXTENSION);
278
		return file.exists();
279
	}
280
281
	public void clear() throws MqttPersistenceException {
282
		checkIsOpen();
283
		File[] files = getFiles();
284
		for (int i=0; i<files.length; i++) {
285
			files[i].delete();
286
		}
287
	}
288
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/persist/package.html (+12 lines)
Added Link Here
1
<body>
2
Contains implementations of the MqttClientPersistence interface.
3
4
<p>
5
An MQTT client needs a persistence mechanism to store messages while they 
6
are in the process of being delivered. This package contains several 
7
implementations of the interface.  If a persistence class is not
8
specified on the constructor to an MQTT client, 
9
{@link org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence MqttDefaultFilePersistence} 
10
is used by default. 
11
12
</body>
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/util/Debug.java (+177 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.util;
13
14
import java.util.Enumeration;
15
import java.util.Properties;
16
17
import org.eclipse.paho.client.mqttv3.internal.ClientComms;
18
import org.eclipse.paho.client.mqttv3.logging.Logger;
19
import org.eclipse.paho.client.mqttv3.logging.LoggerFactory;
20
21
/**
22
 * Utility to help debug problems with the Paho MQTT client
23
 * Once initialised a call to dumpClientDebug will force any memory trace
24
 * together with pertinent client and system state to the main log facility.
25
 * 
26
 * No client wide lock is taken when the dump is progress. This means the 
27
 * set of client state may not be consistent as the client can still be 
28
 * processing work while the dump is in progress.
29
 */
30
public class Debug {
31
	
32
	final static String className = ClientComms.class.getName();
33
	Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className);
34
	
35
	static String separator = "==============";
36
	static String lineSep = System.getProperty("line.separator","\n");
37
	
38
	String clientID;
39
	ClientComms comms;
40
	
41
	/**
42
	 * Set the debug facility up for a specific client
43
	 * @param clientID  the ID of the client being debugged
44
	 * @param comms    the ClientComms object of the client being debugged
45
	 */
46
	public Debug(String clientID, ClientComms comms) {
47
		this.clientID = clientID;
48
		this.comms = comms;
49
		log.setResourceName(clientID);
50
	}
51
52
	/**
53
	 * Dump maximum debug info.
54
	 * This includes state specific to a client as well 
55
	 * as debug that is JVM wide like trace and system properties.
56
	 * All state is written as debug log entries. 
57
	 */
58
	public void dumpClientDebug() { 
59
		dumpClientComms();
60
		dumpConOptions();
61
		dumpClientState();
62
		dumpBaseDebug();
63
	}
64
	
65
	/**
66
	 * Dump of JVM wide debug info.
67
	 * This includes trace and system properties.
68
	 * Includes trace and system properties
69
	 */
70
	public void dumpBaseDebug() {
71
		dumpVersion();
72
		dumpSystemProperties();
73
		dumpMemoryTrace();
74
	}
75
76
	/**
77
	 * If memory trace is being used a request is made to push it 
78
	 * to the target handler.
79
	 */
80
	protected void dumpMemoryTrace() {
81
		log.dumpTrace();
82
	}
83
	
84
	/**
85
	 * Dump information that show the version of the MQTT client being used.
86
	 */
87
	protected void dumpVersion() {
88
		StringBuffer vInfo = new StringBuffer();
89
    	vInfo.append(lineSep+separator+" Version Info "+ separator+lineSep);
90
    	vInfo.append(left("Version",20,' ') + ":  "+ ClientComms.VERSION + lineSep);
91
    	vInfo.append(left("Build Level",20,' ') + ":  "+ ClientComms.BUILD_LEVEL + lineSep);
92
    	vInfo.append(separator+separator+separator+lineSep);
93
    	log.fine(className,"dumpVersion", vInfo.toString());
94
	}
95
96
	/**
97
	 * Dump the current set of system.properties to a log record
98
	 */
99
	public void dumpSystemProperties() {
100
		
101
	    Properties sysProps = System.getProperties();
102
    	log.fine(className,"dumpSystemProperties", dumpProperties(sysProps, "SystemProperties").toString());
103
	}
104
105
	/**
106
	 * Dump interesting variables from ClientState
107
	 */
108
	public void dumpClientState() {
109
		Properties props = null;
110
	    if (comms != null && comms.getClientState() != null ) {
111
	    	props = comms.getClientState().getDebug();
112
	    	log.fine(className,"dumpClientState", dumpProperties(props, clientID + " : ClientState").toString());
113
	    }
114
	}
115
116
	/**
117
	 * Dump interesting variables from ClientComms
118
	 */
119
	public void dumpClientComms() {
120
		Properties props = null;
121
	    if (comms != null) {
122
	    	props = comms.getDebug();
123
	    	log.fine(className,"dumpClientComms", dumpProperties(props, clientID + " : ClientComms").toString());
124
	    }
125
	}
126
	
127
	/**
128
	 * Dump Connection options
129
	 */
130
	public void dumpConOptions() {
131
		Properties props = null;
132
	    if (comms != null) {
133
	    	props = comms.getConOptions().getDebug();
134
	    	log.fine(className,"dumpConOptions", dumpProperties(props, clientID + " : Connect Options").toString());
135
	    }
136
	}
137
138
139
	/**
140
	 * Return a set of properties as a formatted string
141
	 */
142
	public static String dumpProperties(Properties props, String name) {
143
		
144
		StringBuffer propStr = new StringBuffer();
145
	    Enumeration propsE = props.propertyNames();
146
    	propStr.append(lineSep+separator+" "+name+" "+ separator+lineSep);
147
	    while (propsE.hasMoreElements()) {
148
	    	String key = (String)propsE.nextElement();
149
	    	propStr.append(left(key,28,' ') + ":  "+ props.get(key)+lineSep);
150
	    }
151
    	propStr.append(separator+separator+separator+lineSep);
152
153
    	return propStr.toString();
154
	}
155
	
156
	/**
157
	   * Left justify a string.
158
	   *
159
	   * @param s the string to justify
160
	   * @param width the field width to justify within
161
	   * @param fillChar the character to fill with
162
	   *
163
	   * @return the justified string.
164
	   */
165
	  public static String left(String s, int width, char fillChar) {
166
	    if (s.length() >= width) {
167
	      return s;
168
	    }
169
	    StringBuffer sb = new StringBuffer(width);
170
	    sb.append(s);
171
	    for (int i = width - s.length(); --i >= 0;) {
172
	      sb.append(fillChar);
173
	    }
174
	    return sb.toString();
175
	  }
176
	
177
}
(-)a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/util/package.html (+5 lines)
Added Link Here
1
<body>
2
Provides helpers and utilities.
3
4
5
</body>
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/IMqttActionListener.java (-29 lines)
Lines 1-29 Link Here
1
package org.eclipse.paho.client.mqttv3;
2
3
/**
4
 * Implementors of this interface will be notified when an asynchronous action completes.
5
 * 
6
 * <p>A listener is registered on an MqttToken and a token is associated
7
 * with an action like connect or publish. When used with tokens on the MqttAsyncClient 
8
 * the listener will be called back on the MQTT clients thread. The listener will be informed 
9
 * if the action succeeds or fails. It is important that the listener returns control quickly 
10
 * otherwise the operation of the MQTT client will be stalled.
11
 * </p>  
12
 */
13
public interface IMqttActionListener {
14
	/**
15
	 * This method is invoked when an action has completed successfully.  
16
	 * @param asyncActionToken associated with the action that has completed
17
	 */
18
	public void onSuccess(IMqttToken asyncActionToken );
19
	/**
20
	 * This method is invoked when an action fails.  
21
	 * If a client is disconnected while an action is in progress 
22
	 * onFailure will be called. For connections
23
	 * that use clean session set to false, any QOS 1 and 2 messages that 
24
	 * are in the process of being delivered will be delivered to the requested
25
	 * quality of service next time the client connects.  
26
	 * @param asyncActionToken associated with the action that has failed
27
	 */
28
	public void onFailure(IMqttToken asyncActionToken, Throwable exception);
29
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/IMqttAsyncClient.java (-713 lines)
Lines 1-713 Link Here
1
package org.eclipse.paho.client.mqttv3;
2
3
/**
4
 * Enables an application to communicate with an MQTT server using using non-blocking methods. 
5
 * <p>
6
 * It provides applications a simple programming interface to all features of the MQTT version 3.1
7
 * specification including: 
8
 * <ul>
9
 * <li>connect
10
 * <li>publish
11
 * <li>subscribe
12
 * <li>unsubscribe
13
 * <li>disconnect
14
 * </ul>
15
 * </p>
16
 * <p>
17
 * There are two styles of MQTT client, this one and {@link IMqttClient}.
18
 * <ul>
19
 * <li>IMqttAsyncClient provides a set of non blocking methods that return control to the
20
 * invoking application after initial validation of parameters and state. The main processing is
21
 * performed in the background so as not to block the application programs thread. This non 
22
 * blocking approach is handy when the application needs to carry on processing while the 
23
 * MQTT action takes place. For instance connecting to an MQTT server can take time, using 
24
 * the non blocking connect method allows an application to display a busy indicator while the
25
 * connect action takes place in the background. Non blocking methods are particularly useful 
26
 * in event oriented programs and graphical programs where invoking methods that take time 
27
 * to complete on the the main or GUI thread can cause problems. The non-blocking interface
28
 * can also be used in blocking form.</li>
29
 * <li>IMqttClient provides a set of methods that block and return control to the application
30
 * program once the MQTT action has completed. It is a thin layer that sits on top of  
31
 * IMqttAsyncClient implementation and is provided mainly for compatibility with earlier
32
 * versions of the MQTT client. In most circumstances it is recommended to use IMqttAsyncClient
33
 * based clients which allow an application to mix both non-blocking and blocking calls. </li>
34
 * </ul>
35
 * </p>
36
 * <p>
37
 * An application is not restricted to using one style, if an IMqttAsyncClient based client is used
38
 * as both blocking and non-blocking methods can be used in the same application. If an IMqttClient
39
 * based client is used then only blocking methods are available to the application.  
40
 * For more details on the blocking client see {@link IMqttClient}</p>
41
 * 
42
 * <p>There are two forms of non-blocking method:
43
 * <ol>
44
 *   <li>
45
 *     <code><pre>
46
 *     IMqttToken token = asyncClient.method(parms)
47
 *     </pre></code>
48
 *     <p>In this form the method returns a token that can be used to track the 
49
 *     progress of the action (method). The method provides a waitForCompletion() 
50
 *     method that once invoked will block until the action completes. Once 
51
 *     completed there are method on the token that can be used to check if the
52
 *     action completed successfully or not. For example
53
 * 	   to wait until a connect completes:
54
 *     <code><pre>
55
 *      IMqttToken conToken;
56
 *   	conToken = asyncClient.client.connect(conToken);
57
 *     ... do some work...
58
 *   	conToken.waitForCompletion();
59
 *     </pre></code>
60
 *	   /p>
61
 *     <p>To turn a method into a blocking invocation the following form can be used:
62
 *     <code><pre>
63
 *     IMqttToken token;
64
 *     token = asyncClient.method(parms).waitForCompletion();
65
 *     </pre></code>
66
67
 *   </li>
68
 *
69
 *   <li>
70
 *     <code><pre>
71
 *     IMqttToken token method(parms, Object userContext, IMqttActionListener callback)
72
 *     </pre></code>
73
 *     <p>In this form a callback is registered with the method. The callback will be 
74
 *     notified when the action succeeds or fails. The callback is invoked on the thread 
75
 *     managed by the MQTT client so it is important that processing is minimised in the 
76
 *     callback. If not the operation of the MQTT client will be inhibited. For example
77
 *      to be notified (called back) when a connect completes: 
78
 *     <code><pre>
79
 *     	IMqttToken conToken;	
80
 *	    conToken = asyncClient.connect("some context",new new MqttAsyncActionListener() {			
81
 *			public void onSuccess(IMqttToken asyncActionToken) {
82
 *				log("Connected");
83
 *			}
84
 *				
85
 *			public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
86
 *				log ("connect failed" +exception);
87
 *			}
88
 *		  });
89
 *	    An optional context object can be passed into the method which will then be made
90
 *      available in the callback. The context is stored by the MQTT client) in the token
91
 *      which is then returned to the invoker. The token is provided to the callback methods
92
 *      where the context can then be accessed. 
93
 *     </pre></code> 
94
 *     </p> 
95
 *   </li>
96
 * </ol>
97
 *   <p>To understand when the delivery of a message is complete either of the two methods above
98
 *   can be used to either wait on or be notified when the publish completes.  An alternative is to  
99
 *   use the {@link MqttCallback#deliveryComplete(IMqttDeliveryToken)} method which will
100
 *   also be notified when a message has been delivered to the requested quality of service.</p> 
101
 *   
102
 */
103
public interface IMqttAsyncClient {
104
	/**
105
	 * Connects to an MQTT server using the default options.  
106
	 * <p>The default options are specified in {@link MqttConnectOptions} class.
107
	 * </p> 
108
	 *  
109
	 * @throws MqttSecurityException  for security related problems
110
	 * @throws MqttException  for non security related problems 
111
	 * @return token used to track and wait for the connect to complete. The token
112
	 * will be passed to the callback methtods if a callback is set.
113
	 * @see #connect(MqttConnectOptions, Object, IMqttActionListener)
114
	 */
115
	public IMqttToken connect() throws MqttException, MqttSecurityException;
116
	
117
	/**
118
	 * Connects to an MQTT server using the provided connect options. 
119
	 * <p>The connection will be established using the options specified in the 
120
	 * {@link MqttConnectOptions} parameter. 
121
	 * </p> 
122
	 *  
123
	 * @param options a set of connection parameters that override the defaults.
124
	 * @throws MqttSecurityException  for security related problems
125
	 * @throws MqttException  for non security related problems 
126
	 * @return token used to track and wait for the connect to complete. The token
127
	 * will be passed to any callback that has been set.
128
	 * @see #connect(MqttConnectOptions, Object, IMqttActionListener)
129
	 */
130
	public IMqttToken connect(MqttConnectOptions options) throws MqttException, MqttSecurityException ;
131
	/**
132
	 * Connects to an MQTT server using the default options. 
133
	 * <p>The default options are specified in {@link MqttConnectOptions} class.
134
	 * </p> 
135
	 *  
136
	 * @param userContext optional object used to pass context to the callback. Use 
137
	 * null if not required.
138
	 * @param callback optional listener that will be notified when the connect completes. Use 
139
	 * null if not required.
140
	 * @throws MqttSecurityException  for security related problems
141
	 * @throws MqttException  for non security related problems 
142
	 * @return token used to track and wait for the connect to complete. The token
143
	 * will be passed to any callback that has been set.
144
	 * @see #connect(MqttConnectOptions, Object, IMqttActionListener)
145
	 */
146
	public IMqttToken connect(Object userContext, IMqttActionListener callback) throws MqttException, MqttSecurityException;
147
	
148
	
149
	/**
150
	 * Connects to an MQTT server using the specified options. 
151
	 * <p>The server to connect to is specified on the constructor.
152
	 * It is recommended to call {@link #setCallback(MqttCallback)} prior to
153
	 * connecting in order that messages destined for the client can be accepted
154
	 * as soon as the client is connected.
155
	 * </p> 
156
	 * <p>The method returns control before the connect completes. Completion can 
157
	 * be tracked by:
158
	 * <ul>
159
	 * <li>Waiting on the returned token {@link IMqttToken#waitForCompletion()} or</li>
160
	 * <li>Passing in a callback {@link IMqttActionListener}</li>
161
	 * </ul>
162
	 * </p> 
163
	 * 
164
	 * @param options a set of connection parameters that override the defaults. 
165
	 * @param userContext optional object for used to pass context to the callback. Use 
166
	 * null if not required.
167
	 * @param callback optional listener that will be notified when the connect completes. Use 
168
	 * null if not required.
169
	 * @return token used to track and wait for the connect to complete. The token
170
	 * will be passed to any callback that has been set. 
171
	 * @throws MqttSecurityException  for security related problems
172
	 * @throws MqttException  for non security related problems including communication errors
173
	 */
174
	public IMqttToken connect(MqttConnectOptions options, Object userContext, IMqttActionListener callback) throws MqttException, MqttSecurityException;
175
176
	/**
177
	 * Disconnects from the server. 
178
	 * <p>An attempt is made to quiesce the client allowing outstanding
179
	 * work to complete before disconnecting. It will wait
180
	 * for a maximum of 30 seconds for work to quiesce before disconnecting. 
181
 	 * This method must not be called from inside {@link MqttCallback} methods.
182
 	 * </p>
183
 	 * 
184
	 * @return token used to track and wait for disconnect to complete. The token
185
	 * will be passed to any callback that has been set.
186
	 * @throws MqttException for problems encountered while disconnecting 
187
	 * @see #disconnect(long, Object, IMqttActionListener)
188
	 */
189
	public IMqttToken disconnect( ) throws MqttException;
190
	
191
	/**
192
	 * Disconnects from the server. 
193
	 * <p>An attempt is made to quiesce the client allowing outstanding
194
	 * work to complete before disconnecting. It will wait
195
	 * for a maximum of the specified quiesce time  for work to complete before disconnecting. 
196
 	 * This method must not be called from inside {@link MqttCallback} methods.
197
 	 * </p>
198
	 * @param quiesceTimeout the amount of time in milliseconds to allow for 
199
	 * existing work to finish before disconnecting.  A value of zero or less 
200
	 * means the client will not quiesce.  
201
	 * @return token used to track and wait for disconnect to complete. The token
202
	 * will be passed to the callback methtods if a callback is set.
203
	 * @throws MqttException for problems encountered while disconnecting 
204
	 * @see #disconnect(long, Object, IMqttActionListener)
205
	 */
206
	public IMqttToken disconnect(long quiesceTimeout) throws MqttException;
207
	
208
	/**
209
	 * Disconnects from the server. 
210
	 * <p>An attempt is made to quiesce the client allowing outstanding
211
	 * work to complete before disconnecting. It will wait
212
	 * for a maximum of 30 seconds for work to quiesce before disconnecting. 
213
 	 * This method must not be called from inside {@link MqttCallback} methods.
214
 	 * </p>
215
 	 * 
216
 	 * @param userContext optional object used to pass context to the callback. Use 
217
	 * null if not required.
218
	 * @param callback optional listener that will be notified when the disconnect completes. Use 
219
	 * null if not required.
220
	 * @return token used to track and wait for the disconnect to complete. The token
221
	 * will be passed to any callback that has been set.
222
	 * @throws MqttException for problems encountered while disconnecting
223
	 * @see #disconnect(long, Object, IMqttActionListener)
224
	 */
225
	public IMqttToken disconnect( Object userContext, IMqttActionListener callback) throws MqttException;
226
227
	/**
228
	 * Disconnects from the server.
229
	 * <p>
230
	 * The client will wait for {@link MqttCallback} methods to 
231
	 * complete. It will then wait for up to the quiesce timeout to allow for
232
	 * work which has already been initiated to complete. For instance when a QOS 2
233
	 * message has started flowing to the server but the QOS 2 flow has not completed.It 
234
	 * prevents new messages being accepted and does not send any messages that have 
235
	 * been accepted but not yet started delivery across the network to the server. When 
236
	 * work has completed or after the quiesce timeout, the client will disconnect from 
237
	 * the server. If the cleansession flag was set to false and is set to false the 
238
	 * next time a connection is made QoS 1 and 2 messages that 
239
	 * were not previously delivered will be delivered.</p>
240
	 * <p>This method must not be called from inside {@link MqttCallback} methods.</p>
241
	 * <p>The method returns control before the disconnect completes. Completion can 
242
	 * be tracked by:
243
	 * <ul>
244
	 * <li>Waiting on the returned token {@link IMqttToken#waitForCompletion()} or</li>
245
	 * <li>Passing in a callback {@link IMqttActionListener}</li>
246
	 * </ul>
247
	 * </p> 
248
	 * 
249
	 * @param quiesceTimeout the amount of time in milliseconds to allow for 
250
	 * existing work to finish before disconnecting.  A value of zero or less 
251
	 * means the client will not quiesce.
252
 	 * @param userContext optional object used to pass context to the callback. Use 
253
	 * null if not required.
254
	 * @param callback optional listener that will be notified when the disconnect completes. Use 
255
	 * null if not required.
256
	 * @return token used to track and wait for the connect to complete. The token
257
	 * will be passed to any callback that has been set.
258
	 * @throws MqttException for problems encountered while disconnecting
259
	 */
260
	public IMqttToken disconnect(long quiesceTimeout, Object userContext, IMqttActionListener callback) throws MqttException;
261
	
262
263
	/**
264
	 * Determines if this client is currently connected to the server.
265
	 * 
266
	 * @return <code>true</code> if connected, <code>false</code> otherwise.
267
	 */
268
	public boolean isConnected();
269
270
	/**
271
	 * Returns the client ID used by this client. 
272
	 * <p>All clients connected to the
273
	 * same server or server farm must have a unique ID.
274
	 * </p> 
275
	 * 
276
	 * @return the client ID used by this client.
277
	 */
278
	public String getClientId();
279
280
	/**
281
	 * Returns the address of the server used by this client.
282
	 * <p>The format of the returned String is the same as that used on the constructor.
283
	 * </p>
284
	 * 
285
	 * @return the server's address, as a URI String.
286
	 * @see MqttAsyncClient#MqttAsyncClient(String, String)
287
	 */
288
	public String getServerURI();
289
290
	/**
291
	 * Publishes a message to a topic on the server
292
	 * <p>A convenience method, which will 
293
	 * create a new {@link MqttMessage} object with a byte array payload and the
294
	 * specified QoS, and then publish it. 
295
	 * </p>
296
	 *
297
	 * @param topic to deliver the message to, for example "finance/stock/ibm".
298
	 * @param payload the byte array to use as the payload
299
	 * @param qos the Quality of Service to deliver the message at. Valid values are 0, 1 or 2.
300
	 * @param retained whether or not this message should be retained by the server.
301
	 * @return token used to track and wait for the publish to complete. The token
302
	 * will be passed to any callback that has been set. 
303
	 * @throws MqttPersistenceException when a problem occurs storing the message
304
	 * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2.
305
	 * @throws MqttException for other errors encountered while publishing the message.
306
	 * For instance if too many messages are being processed. 
307
	 * @see #publish(String, MqttMessage, Object, IMqttActionListener)
308
	 * @see MqttMessage#setQos(int)
309
	 * @see MqttMessage#setRetained(boolean)
310
	 */
311
	public IMqttDeliveryToken publish(String topic, byte[] payload, int qos, 
312
			boolean retained ) throws MqttException, MqttPersistenceException;
313
	
314
	/**
315
	 * Publishes a message to a topic on the server
316
 	 * <p>A convenience method, which will 
317
	 * create a new {@link MqttMessage} object with a byte array payload and the
318
	 * specified QoS, and then publish it. 
319
	 * </p>
320
	 *
321
	 * @param topic  to deliver the message to, for example "finance/stock/ibm".
322
	 * @param payload the byte array to use as the payload
323
	 * @param qos the Quality of Service to deliver the message at.  Valid values are 0, 1 or 2.
324
	 * @param retained whether or not this message should be retained by the server.
325
	 * @param userContext optional object used to pass context to the callback. Use 
326
	 * null if not required.
327
	 * @param callback optional listener that will be notified when message delivery
328
	 * hsa completed to the requested quality of service
329
	 * @return token used to track and wait for the publish to complete. The token
330
	 * will be passed to any callback that has been set. 
331
	 * @throws MqttPersistenceException when a problem occurs storing the message
332
	 * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2.
333
	 * @throws MqttException for other errors encountered while publishing the message.
334
	 * For instance client not connected. 
335
	 * @see #publish(String, MqttMessage, Object, IMqttActionListener)
336
	 * @see MqttMessage#setQos(int)
337
	 * @see MqttMessage#setRetained(boolean)
338
	 */
339
	public IMqttDeliveryToken publish(String topic, byte[] payload, int qos, 
340
			boolean retained, Object userContext, IMqttActionListener callback ) throws MqttException, MqttPersistenceException;
341
342
	/**
343
	 * Publishes a message to a topic on the server
344
	 * Takes an {@link MqttMessage} message and delivers it to the server at the 
345
	 * requested quality of service.
346
	 *
347
	 * @param topic  to deliver the message to, for example "finance/stock/ibm".
348
	 * @param message to deliver to the server
349
	 * @return token used to track and wait for the publish to complete. The token
350
	 * will be passed to any callback that has been set. 
351
	 * @throws MqttPersistenceException when a problem occurs storing the message
352
	 * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2.
353
	 * @throws MqttException for other errors encountered while publishing the message.
354
	 * For instance client not connected. 
355
	 * @see #publish(String, MqttMessage, Object, IMqttActionListener)
356
	 */
357
	public IMqttDeliveryToken publish(String topic, MqttMessage message ) throws MqttException, MqttPersistenceException;
358
	
359
	/**
360
	 * Publishes a message to a topic on the server. 
361
	 * <p>
362
	 * Once this method has returned cleanly, the message has been accepted for publication by the
363
	 * client and will be delivered on a background thread. 
364
	 * In the event the connection fails or the client stops. Messages will be delivered to the 
365
	 * requested quality of service once the connection is re-established to the server on condition that: 
366
	 * <ul>
367
	 * <li>The connection is re-established with the same clientID
368
	 * <li>The original connection was made with (@link MqttConnectOptions#setCleanSession(boolean)} 
369
	 * set to false
370
	 * <li>The connection is re-established with (@link MqttConnectOptions#setCleanSession(boolean)} 
371
	 * set to false
372
	 * <liDepending when the failure occurs QOS 0 messages may not be delivered.
373
	 * </ul> 
374
	 * </p>
375
	 * 
376
	 * <p>When building an application,
377
	 * the design of the topic tree should take into account the following principles
378
	 * of topic name syntax and semantics:</p>
379
	 * 
380
	 * <ul>
381
	 * 	<li>A topic must be at least one character long.</li>
382
	 * 	<li>Topic names are case sensitive.  For example, <em>ACCOUNTS</em> and <em>Accounts</em> are
383
	 * 	two different topics.</li>
384
	 * 	<li>Topic names can include the space character.  For example, <em>Accounts
385
	 * 	payable</em> is a valid topic.</li>
386
	 * 	<li>A leading "/" creates a distinct topic.  For example, <em>/finance</em> is
387
	 * 	different from <em>finance</em>. <em>/finance</em> matches "+/+" and "/+", but
388
	 * 	not "+".</li>
389
	 * 	<li>Do not include the null character (Unicode<samp class="codeph"> \x0000</samp>) in
390
	 * 	any topic.</li>
391
	 * </ul>
392
	 * 
393
	 * <p>The following principles apply to the construction and content of a topic
394
	 * tree:</p>
395
	 * 
396
	 * <ul>
397
	 * 	<li>The length is limited to 64k but within that there are no limits to the
398
	 * 	number of levels in a topic tree.</li>
399
	 * 	<li>There can be any number of root nodes; that is, there can be any number
400
	 * 	of topic trees.</li>
401
	 * 	</ul>
402
	 * </p>
403
	 * <p>The method returns control before the publish completes. Completion can 
404
	 * be tracked by:
405
	 * <ul>
406
	 * <li>Setting an {@link IMqttAsyncClient#setCallback(MqttCallback)} where the 
407
	 * {@link MqttCallback#deliveryComplete(IMqttDeliveryToken)}
408
	 * method will be called.</li> 
409
	 * <li>Waiting on the returned token {@link MqttToken#waitForCompletion()} or</li>
410
	 * <li>Passing in a callback {@link IMqttActionListener} to this method</li> 
411
	 * </ul>
412
	 * </p> 
413
	 *
414
	 * @param topic  to deliver the message to, for example "finance/stock/ibm".
415
	 * @param message to deliver to the server
416
	 * @param userContext optional object used to pass context to the callback. Use 
417
	 * null if not required.
418
	 * @param callback optional listener that will be notified when message delivery
419
	 * has completed to the requested quality of service
420
	 * @return token used to track and wait for the publish to complete. The token
421
	 * will be passed to callback methtods if set. 
422
 	 * @throws MqttPersistenceException when a problem occurs storing the message
423
	 * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2.
424
	 * @throws MqttException for other errors encountered while publishing the message.
425
	 * For instance client not connected. 
426
	 * @see MqttMessage
427
	 */
428
	public IMqttDeliveryToken publish(String topic, MqttMessage message, 
429
			Object userContext, IMqttActionListener callback) throws MqttException, MqttPersistenceException;
430
	
431
	/**
432
	 * Subscribe to a topic, which may include wildcards.
433
	 * 
434
	 * @see #subscribe(String[], int[], Object, IMqttActionListener)
435
	 * 
436
	 * @param topicFilter the topic to subscribe to, which can include wildcards.
437
	 * @param qos the maximum quality of service at which to subscribe. Messages 
438
	 * published at a lower quality of service will be received at the published 
439
	 * QOS.  Messages published at a higher quality of service will be received using 
440
	 * the QOS specified on the subscribe.
441
	 * @return token used to track and wait for the subscribe to complete. The token
442
	 * will be passed to callback methtods if set. 
443
	 * @throws MqttException if there was an error registering the subscription.
444
	 */
445
	public IMqttToken subscribe(String topicFilter, int qos) throws MqttException;
446
447
	/**
448
	 * Subscribe to a topic, which may include wildcards.
449
	 * 
450
	 * @see #subscribe(String[], int[], Object, IMqttActionListener)
451
	 * 
452
	 * @param topicFilter the topic to subscribe to, which can include wildcards.
453
	 * @param qos the maximum quality of service at which to subscribe. Messages 
454
	 * published at a lower quality of service will be received at the published 
455
	 * QOS.  Messages published at a higher quality of service will be received using 
456
	 * the QOS specified on the subscribe.
457
	 * @param userContext optional object used to pass context to the callback. Use 
458
	 * null if not required.
459
	 * @param callback optional listener that will be notified when subscribe 
460
	 * has completed  
461
	 * @return token used to track and wait for the subscribe to complete. The token
462
	 * will be passed to callback methtods if set. 
463
	 * @throws MqttException if there was an error registering the subscription.
464
	 */
465
	public IMqttToken subscribe(String topicFilter, int qos, Object userContext, IMqttActionListener callback)
466
	throws MqttException;
467
468
	/**
469
	 * Subscribe to multiple topics, each of which may include wildcards.
470
	 * 
471
	 * <p>Provides an optimised way to subscribe to multiple topics compared to 
472
	 * subscribing to each one individually.</p> 
473
	 * 
474
	 * @see #subscribe(String[], int[], Object, IMqttActionListener)
475
	 * 
476
	 * @param topicFilters one or more topics to subscribe to, which can include wildcards
477
	 * @param qos the maximum quality of service at which to subscribe. Messages 
478
	 * published at a lower quality of service will be received at the published 
479
	 * QOS.  Messages published at a higher quality of service will be received using 
480
	 * the QOS specified on the subscribe.
481
	 * @return token used to track and wait for the subscribe to complete. The token
482
	 * will be passed to callback methtods if set. 
483
	 * @throws MqttException if there was an error registering the subscription.
484
	 */
485
	public IMqttToken subscribe(String[] topicFilters, int[] qos) throws MqttException;
486
487
	/**
488
	 * Subscribes to multiple topics, each of which may include wildcards.
489
 	 * <p>Provides an optimised way to subscribe to multiple topics compared to 
490
	 * subscribing to each one individually.</p> 
491
	 * <p>The {@link #setCallback(MqttCallback)} method 
492
	 * should be called before this method, otherwise any received messages 
493
	 * will be discarded.
494
	 * </p>
495
	 * <p>
496
	 * If (@link MqttConnectOptions#setCleanSession(boolean)} was set to true 
497
	 * when when connecting to the server then the subscription remains in place
498
	 * until either:
499
	 * <ul>
500
	 * <li>The client disconnects</li>
501
	 * <li>An unsubscribe method is called to un-subscribe the topic</li>
502
	 * </li>
503
	 * </p>
504
	 * <p>
505
	 * If (@link MqttConnectOptions#setCleanSession(boolean)} was set to false 
506
	 * when connecting to the server then the subscription remains in place
507
	 * until either:
508
	 * <ul>
509
	 * <li>An unsubscribe method is called to un-subscribe the topic</li>
510
	 * <li>The next time the client connects with CleanSession set to true</ul>
511
	 * </li>
512
	 * With CleanSession set to false the MQTT server will store messages on 
513
	 * behalf of the client when the client is not connected. The next time the 
514
	 * client connects with the <bold>same client ID</bold> the server will 
515
	 * deliver the stored messages to the client.
516
	 * </p>  
517
	 * 
518
	 * <p>The "topic filter" string used when subscribing
519
	 * may contain special characters, which allow you to subscribe to multiple topics
520
	 * at once.</p>
521
	 * <p>The topic level separator is used to introduce structure into the topic, and
522
	 * can therefore be specified within the topic for that purpose.  The multi-level
523
	 * wildcard and single-level wildcard can be used for subscriptions, but they
524
	 * cannot be used within a topic by the publisher of a message.
525
	 * <dl>
526
	 * 	<dt>Topic level separator</dt>
527
	 * 	<dd>The forward slash (/) is used to separate each level within
528
	 * 	a topic tree and provide a hierarchical structure to the topic space. The
529
	 * 	use of the topic level separator is significant when the two wildcard characters
530
	 * 	are encountered in topics specified by subscribers.</dd>
531
	 * 
532
	 * 	<dt>Multi-level wildcard</dt>
533
	 * 	<dd><p>The number sign (#) is a wildcard character that matches
534
	 * 	any number of levels within a topic. For example, if you subscribe to 
535
	 *  <span><span class="filepath">finance/stock/ibm/#</span></span>, you receive
536
	 * 	messages on these topics:
537
	 *  <pre>   finance/stock/ibm<br />   finance/stock/ibm/closingprice<br />   finance/stock/ibm/currentprice</pre>
538
	 *  </p>
539
	 *  <p>The multi-level wildcard
540
	 *  can represent zero or more levels. Therefore, <em>finance/#</em> can also match
541
	 * 	the singular <em>finance</em>, where <em>#</em> represents zero levels. The topic
542
	 * 	level separator is meaningless in this context, because there are no levels
543
	 * 	to separate.</p>
544
	 * 
545
	 * 	<p>The <span>multi-level</span> wildcard can
546
	 * 	be specified only on its own or next to the topic level separator character.
547
	 * 	Therefore, <em>#</em> and <em>finance/#</em> are both valid, but <em>finance#</em> is
548
	 * 	not valid. <span>The multi-level wildcard must be the last character
549
	 *  used within the topic tree. For example, <em>finance/#</em> is valid but 
550
	 *  <em>finance/#/closingprice</em> is 	not valid.</span></p></dd>
551
	 * 
552
	 * 	<dt>Single-level wildcard</dt>
553
	 * 	<dd><p>The plus sign (+) is a wildcard character that matches only one topic
554
	 * 	level. For example, <em>finance/stock/+</em> matches 
555
	 * <em>finance/stock/ibm</em> and <em>finance/stock/xyz</em>,
556
	 * 	but not <em>finance/stock/ibm/closingprice</em>. Also, because the single-level
557
	 * 	wildcard matches only a single level, <em>finance/+</em> does not match <em>finance</em>.</p>
558
	 * 	
559
	 * 	<p>Use
560
	 * 	the single-level wildcard at any level in the topic tree, and in conjunction
561
	 * 	with the multilevel wildcard. Specify the single-level wildcard next to the
562
	 * 	topic level separator, except when it is specified on its own. Therefore, 
563
	 *  <em>+</em> and <em>finance/+</em> are both valid, but <em>finance+</em> is 
564
	 *  not valid. <span>The single-level wildcard can be used at the end of the 
565
	 *  topic tree or within the topic tree.
566
	 * 	For example, <em>finance/+</em> and <em>finance/+/ibm</em> are both valid.</span></p>
567
	 * 	</dd>
568
	 * </dl>
569
	 * </p>
570
	 * <p>The method returns control before the subscribe completes. Completion can 
571
	 * be tracked by:
572
	 * <ul>
573
	 * <li>Waiting on the supplied token {@link MqttToken#waitForCompletion()} or</li>
574
	 * <li>Passing in a callback {@link IMqttActionListener} to this method</li>
575
	 * </ul>
576
	 * </p> 
577
	 * 
578
	 * @param topicFilters one or more topics to subscribe to, which can include wildcards
579
	 * @param qos the maximum quality of service to subscribe each topic at.Messages 
580
	 * published at a lower quality of service will be received at the published 
581
	 * QOS.  Messages published at a higher quality of service will be received using 
582
	 * the QOS specified on the subscribe.
583
	 * @param userContext optional object used to pass context to the callback. Use 
584
	 * null if not required.
585
	 * @param callback optional listener that will be notified when subscribe 
586
	 * has completed  
587
	 * @return token used to track and wait for the subscribe to complete. The token
588
	 * will be passed to callback methtods if set. 
589
	 * @throws MqttException if there was an error registering the subscription.
590
	 * @throws IllegalArgumentException if the two supplied arrays are not the same size.
591
	 */
592
	public IMqttToken subscribe(String[] topicFilters, int[] qos, Object userContext, IMqttActionListener callback)
593
			throws MqttException;
594
	
595
	/**
596
	 * Requests the server unsubscribe the client from a topic. 
597
	 * 
598
	 * @see #unsubscribe(String[], Object, IMqttActionListener)
599
 	 * @param topicFilter the topic to unsubscribe from. It must match a topicFilter
600
	 * specified on an earlier subscribe.
601
	 * @return token used to track and wait for the unsubscribe to complete. The token
602
	 * will be passed to callback methtods if set. 
603
	 * @throws MqttException if there was an error unregistering the subscription.
604
	 */
605
	public IMqttToken unsubscribe(String topicFilter) throws MqttException;
606
	
607
	/**
608
	 * Requests the server unsubscribe the client from one or more topics. 
609
	 * 
610
	 * @see #unsubscribe(String[], Object, IMqttActionListener)
611
	 * 
612
	 * @param topicFilters one or more topics to unsubscribe from. Each topicFilter
613
	 * must match one specified on an earlier subscribe.	 * 
614
	 * @return token used to track and wait for the unsubscribe to complete. The token
615
	 * will be passed to callback methtods if set. 
616
	 * @throws MqttException if there was an error unregistering the subscription.
617
	 */	
618
	public IMqttToken unsubscribe(String[] topicFilters) throws MqttException;
619
	
620
	/**
621
	 * Requests the server unsubscribe the client from a topics. 
622
	 * 
623
	 * @see #unsubscribe(String[], Object, IMqttActionListener)
624
	 * 
625
	 * @param topicFilter the topic to unsubscribe from. It must match a topicFilter
626
	 * specified on an earlier subscribe.
627
	 * @param userContext optional object used to pass context to the callback. Use 
628
	 * null if not required.
629
	 * @param callback optional listener that will be notified when unsubscribe 
630
	 * has completed  
631
	 * @return token used to track and wait for the unsubscribe to complete. The token
632
	 * will be passed to callback methtods if set. 
633
	 * @throws MqttException if there was an error unregistering the subscription.
634
	 */		
635
	public IMqttToken unsubscribe(String topicFilter, Object userContext, IMqttActionListener callback)
636
			throws MqttException;
637
	
638
	/**
639
	 * Requests the server unsubscribe the client from one or more topics
640
	 * <p>
641
	 * Unsubcribing is the opposite of subscribing. When the server receives the 
642
	 * unsubscribe request it looks to see if it can find a matching subscription for the
643
	 * client and then removes it. After this point the server will send no more
644
	 * messages to the client for this subscription.  
645
	 * </p>
646
	 * <p>The topic(s) specified on the unsubscribe must match the topic(s) 
647
	 * specified in the original subscribe request for the unsubscribe to succeed
648
	 * </p>
649
	 * <p>The method returns control before the unsubscribe completes. Completion can 
650
	 * be tracked by:
651
	 * <ul>
652
	 * <li>Waiting on the returned token {@link MqttToken#waitForCompletion()} or</li>
653
	 * <li>Passing in a callback {@link IMqttActionListener} to this method</li>
654
	 * </ul>
655
	 * </p> 
656
	 * 
657
	 * @param topicFilters one or more topics to unsubscribe from. Each topicFilter
658
	 * must match one specified on an earlier subscribe.
659
	 * @param userContext optional object used to pass context to the callback. Use 
660
	 * null if not required.
661
	 * @param callback optional listener that will be notified when unsubscribe 
662
	 * has completed  
663
	 * @return token used to track and wait for the unsubscribe to complete. The token
664
	 * will be passed to callback methtods if set. 
665
	 * @throws MqttException if there was an error unregistering the subscription.
666
	 */
667
	public IMqttToken unsubscribe(String[] topicFilters, Object userContext, IMqttActionListener callback)
668
			throws MqttException;
669
670
671
	/**
672
	 * Sets a callback listener to use for events that happen asynchronously. 
673
	 * <p>There are a number of events that the listener will be notified about.
674
	 * These include:
675
	 * <ul>
676
	 * <li>A new message has arrived and is ready to be processed</li>
677
	 * <li>The connection to the server has been lost</li> 
678
	 * <li>Delivery of a message to the server has completed</li>
679
	 * </ul>  
680
	 * </p>
681
	 * <p>Other events that track the progress of an individual operation such 
682
	 * as connect and subscribe can be tracked using the {@link MqttToken} returned from 
683
	 * each non-blocking method or using setting a {@link IMqttActionListener} on the 
684
	 * non-blocking method.<p>
685
	 * @see MqttCallback
686
	 * @param callback which will be invoked for certain asyncrhonous events
687
	 */
688
	public void setCallback(MqttCallback callback);
689
690
	/**
691
	 * Returns the delivery tokens for any outstanding publish operations.
692
	 * <p>If a client has been restarted and there are messages that were in the
693
	 * process of being delivered when the client stopped this method  
694
	 * returns a token for each in-flight message enabling the delivery to be tracked
695
	 * Alternately the {@link MqttCallback#deliveryComplete(IMqttDeliveryToken)} 
696
	 * callback can be used to track the delivery of outstanding messages.
697
	 * </p>
698
	 * <p>If a client connects with cleansession true then there will be no
699
	 * delivery tokens as the cleansession option deletes all earlier state.
700
	 * For state to be remembered the client must connect with cleansession
701
	 * set to false</P>
702
	 * @return zero or more delivery tokens 
703
	 */
704
	public IMqttDeliveryToken[] getPendingDeliveryTokens();
705
706
	/**
707
	 * Close the client
708
	 * Releases all resource associated with the client. After the client has 
709
	 * been closed it cannot be reused. For instance attempts to connect will fail. 
710
	 * @throws MqttException  if the client is not disconnected. 
711
	 */
712
	public void close() throws MqttException;
713
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/IMqttClient.java (-462 lines)
Lines 1-462 Link Here
1
package org.eclipse.paho.client.mqttv3;
2
3
/**
4
 * Enables an application to communicate with an MQTT server using using blocking methods. 
5
 * <p>
6
 * This interface allows applications to utilise all features of the MQTT version 3.1
7
 * specification including: 
8
 * <ul>
9
 * <li>connect
10
 * <li>publish
11
 * <li>subscribe
12
 * <li>unsubscribe
13
 * <li>disconnect
14
 * </ul>
15
 * </p>
16
 * <p>
17
 * There are two styles of MQTT client, this one and {@link IMqttAsyncClient}.
18
 * <ul>
19
 * <li>IMqttClient provides a set of methods that block and return control to the application
20
 * program once the MQTT action has completed.</li> 
21
 * <li>IMqttAsyncClient provides a set of non blocking methods that return control to the
22
 * invoking application after initial validation of parameters and state. The main processing is
23
 * performed in the background so as not to block the application programs thread. This non 
24
 * blocking approach is handy when the application wants to carry on processing while the 
25
 * MQTT action takes place. For instance connecting to an MQTT server can take time, using 
26
 * the non blocking connect method allows an application to display a busy indicator while the
27
 * connect action is occurring. Non blocking methods are particularly useful in event oriented 
28
 * programs and graphical programs where issuing methods that take time to complete on the the 
29
 * main or GUI thread can cause problems.</li>
30
 * </ul>
31
 * </p>
32
 * <p>
33
 * The non-blocking client can also be used in a blocking form by turning a non-blocking 
34
 * method into a blocking invocation using the following pettern:
35
 *     <code><pre>
36
 *     IMqttToken token;
37
 *     token = asyncClient.method(parms).waitForCompletion();
38
 *     </pre></code>
39
 * Using the non-blocking client allows an application to use a mixture of blocking and
40
 * non-blocking styles. Using the blocking client only allows an application to use one 
41
 * style. The blocking client provides compatibility with earlier versions 
42
 * of the MQTT client.</p>
43
 */
44
public interface IMqttClient { //extends IMqttAsyncClient {
45
	/**
46
	 * Connects to an MQTT server using the default options. 
47
	 * <p>The default options are specified in {@link MqttConnectOptions} class.
48
	 * </p>  
49
	 *  
50
	 * @throws MqttSecurityException when the server rejects the connect for security 
51
	 * reasons
52
	 * @throws MqttException  for non security related problems 
53
	 * @see #connect(MqttConnectOptions)
54
	 */
55
  public void connect() throws MqttSecurityException, MqttException;
56
57
	/**
58
	 * Connects to an MQTT server using the specified options. 
59
	 * <p>The server to connect to is specified on the constructor.
60
	 * It is recommended to call {@link #setCallback(MqttCallback)} prior to
61
	 * connecting in order that messages destined for the client can be accepted
62
	 * as soon as the client is connected.
63
	 * </p> 
64
	 * <p>This is a blocking method that returns once connect completes</p>	 
65
	 *  
66
	 * @param options a set of connection parameters that override the defaults. 
67
	 * @throws MqttSecurityException when the server rejects the connect for security 
68
	 * reasons
69
	 * @throws MqttException  for non security related problems including communication errors
70
	 */
71
  public void connect(MqttConnectOptions options) throws MqttSecurityException, MqttException;
72
73
	/**
74
	 * Disconnects from the server. 
75
	 * <p>An attempt is made to quiesce the client allowing outstanding
76
	 * work to complete before disconnecting. It will wait
77
	 * for a maximum of 30 seconds for work to quiesce before disconnecting. 
78
	 * This method must not be called from inside {@link MqttCallback} methods.
79
	 * </p>
80
	 * 
81
	 * @see #disconnect(long)
82
	 */
83
  public void disconnect() throws MqttException;
84
85
	/**
86
	 * Disconnects from the server.
87
	 * <p>
88
	 * The client will wait for all {@link MqttCallback} methods to 
89
	 * complete. It will then wait for up to the quiesce timeout to allow for
90
	 * work which has already been initiated to complete - for example, it will
91
	 * wait for the QoS 2 flows from earlier publications to complete. When work has 
92
	 * completed or after the quiesce timeout, the client will disconnect from 
93
	 * the server. If the cleansession flag was set to false and is set to false the 
94
	 * next time a connection is made QoS 1 and 2 messages that 
95
	 * were not previously delivered will be delivered.</p>
96
	 * 
97
	 * <p>This is a blocking method that returns once disconnect completes</p>	 
98
	 * 
99
	 * @param quiesceTimeout the amount of time in milliseconds to allow for 
100
	 * existing work to finish before disconnecting.  A value of zero or less 
101
	 * means the client will not quiesce.
102
	 * @throws MqttException if a problem is encountered while disconnecting
103
	 */
104
  public void disconnect(long quiesceTimeout) throws MqttException;
105
106
	/**
107
	 * Subscribe to a topic, which may include wildcards using a QOS of 1.
108
	 * 
109
	 * @see #subscribe(String[], int[])
110
	 * 
111
	 * @param topicFilter the topic to subscribe to, which can include wildcards.
112
	 * @throws MqttException if there was an error registering the subscription.
113
	 */
114
  public void subscribe(String topicFilter) throws MqttException, MqttSecurityException;
115
116
	/**
117
	 * Subscribes to a one or more topics, which may include wildcards using a QOS of 1.
118
	 * 
119
	 * @see #subscribe(String[], int[])
120
	 * 
121
	 * @param topicFilters the topic to subscribe to, which can include wildcards.
122
	 * @throws MqttException if there was an error registering the subscription.
123
	 */
124
  public void subscribe(String[] topicFilters) throws MqttException;
125
126
	/**
127
	 * Subscribe to a topic, which may include wildcards.
128
	 * 
129
	 * @see #subscribe(String[], int[])
130
	 * 
131
	 * @param topicFilter the topic to subscribe to, which can include wildcards.
132
	 * @param qos the maximum quality of service at which to subscribe. Messages 
133
	 * published at a lower quality of service will be received at the published 
134
	 * QOS.  Messages published at a higher quality of service will be received using 
135
	 * the QOS specified on the subscribe.  
136
	 * @throws MqttException if there was an error registering the subscription.
137
	 */
138
  public void subscribe(String topicFilter, int qos) throws MqttException;
139
140
	/**
141
	 * Subscribes to multiple topics, each of which may include wildcards.
142
	 * <p>The {@link #setCallback(MqttCallback)} method 
143
	 * should be called before this method, otherwise any received messages 
144
	 * will be discarded.
145
	 * </p>
146
	 * <p>
147
	 * If (@link MqttConnectOptions#setCleanSession(boolean)} was set to true 
148
	 * when when connecting to the server then the subscription remains in place
149
	 * until either:
150
	 * <ul>
151
	 * <li>The client disconnects</li>
152
	 * <li>An unsubscribe method is called to un-subscribe the topic</li>
153
	 * </li>
154
	 * </p>
155
	 * <p>
156
	 * If (@link MqttConnectOptions#setCleanSession(boolean)} was set to false 
157
	 * when when connecting to the server then the subscription remains in place
158
	 * until either:
159
	 * <ul>
160
	 * <li>An unsubscribe method is called to un-subscribe the topic</li>
161
	 * <li>The client connects with CleanSession set to true</ul>
162
	 * </li>
163
	 * With CleanSession set to false the MQTT server will store messages on 
164
	 * behalf of the client when the client is not connected. The next time the 
165
	 * client connects with the <bold>same client ID</bold> the server will 
166
	 * deliver the stored messages to the client.
167
	 * </p>  
168
	 * 
169
	 * <p>The "topic filter" string used when subscribing
170
	 * may contain special characters, which allow you to subscribe to multiple topics
171
	 * at once.</p>
172
	 * <p>The topic level separator is used to introduce structure into the topic, and
173
	 * can therefore be specified within the topic for that purpose.  The multi-level
174
	 * wildcard and single-level wildcard can be used for subscriptions, but they
175
	 * cannot be used within a topic by the publisher of a message.
176
	 * <dl>
177
	 * 	<dt>Topic level separator</dt>
178
	 * 	<dd>The forward slash (/) is used to separate each level within
179
	 * 	a topic tree and provide a hierarchical structure to the topic space. The
180
	 * 	use of the topic level separator is significant when the two wildcard characters
181
	 * 	are encountered in topics specified by subscribers.</dd>
182
	 * 
183
	 * 	<dt>Multi-level wildcard</dt>
184
	 * 	<dd><p>The number sign (#) is a wildcard character that matches
185
	 * 	any number of levels within a topic. For example, if you subscribe to 
186
	 *  <span><span class="filepath">finance/stock/ibm/#</span></span>, you receive
187
	 * 	messages on these topics:
188
	 *  <pre>   finance/stock/ibm<br />   finance/stock/ibm/closingprice<br />   finance/stock/ibm/currentprice</pre>
189
	 *  </p>
190
	 *  <p>The multi-level wildcard
191
	 *  can represent zero or more levels. Therefore, <em>finance/#</em> can also match
192
	 * 	the singular <em>finance</em>, where <em>#</em> represents zero levels. The topic
193
	 * 	level separator is meaningless in this context, because there are no levels
194
	 * 	to separate.</p>
195
	 * 
196
	 * 	<p>The <span>multi-level</span> wildcard can
197
	 * 	be specified only on its own or next to the topic level separator character.
198
	 * 	Therefore, <em>#</em> and <em>finance/#</em> are both valid, but <em>finance#</em> is
199
	 * 	not valid. <span>The multi-level wildcard must be the last character
200
	 *  used within the topic tree. For example, <em>finance/#</em> is valid but 
201
	 *  <em>finance/#/closingprice</em> is 	not valid.</span></p></dd>
202
	 * 
203
	 * 	<dt>Single-level wildcard</dt>
204
	 * 	<dd><p>The plus sign (+) is a wildcard character that matches only one topic
205
	 * 	level. For example, <em>finance/stock/+</em> matches 
206
	 * <em>finance/stock/ibm</em> and <em>finance/stock/xyz</em>,
207
	 * 	but not <em>finance/stock/ibm/closingprice</em>. Also, because the single-level
208
	 * 	wildcard matches only a single level, <em>finance/+</em> does not match <em>finance</em>.</p>
209
	 * 	
210
	 * 	<p>Use
211
	 * 	the single-level wildcard at any level in the topic tree, and in conjunction
212
	 * 	with the multilevel wildcard. Specify the single-level wildcard next to the
213
	 * 	topic level separator, except when it is specified on its own. Therefore, 
214
	 *  <em>+</em> and <em>finance/+</em> are both valid, but <em>finance+</em> is 
215
	 *  not valid. <span>The single-level wildcard can be used at the end of the 
216
	 *  topic tree or within the topic tree.
217
	 * 	For example, <em>finance/+</em> and <em>finance/+/ibm</em> are both valid.</span></p>
218
	 * 	</dd>
219
	 * </dl>
220
	 * </p>
221
	 * 
222
	 * <p>This is a blocking method that returns once subscribe completes</p>
223
	 *  
224
	 * @param topicFilters one or more topics to subscribe to, which can include wildcards.
225
	 * @param qos the maximum quality of service to subscribe each topic at.Messages 
226
	 * published at a lower quality of service will be received at the published 
227
	 * QOS.  Messages published at a higher quality of service will be received using 
228
	 * the QOS specified on the subscribe.  
229
	 * @throws MqttException if there was an error registering the subscription.
230
	 * @throws IllegalArgumentException if the two supplied arrays are not the same size.
231
	 */
232
  public void subscribe(String[] topicFilters, int[] qos) throws MqttException;
233
234
	/**
235
	 * Requests the server unsubscribe the client from a topic. 
236
	 * 
237
	 * @see #unsubscribe(String[])
238
	 * @param topicFilter the topic to unsubscribe from. It must match a topicFilter
239
	 * specified on the subscribe.
240
	 * @throws MqttException if there was an error unregistering the subscription.
241
	 */
242
  public void unsubscribe(String topicFilter) throws MqttException;
243
244
	/**
245
	 * Requests the server unsubscribe the client from one or more topics
246
	 * <p>
247
	 * Unsubcribing is the opposite of subscribing. When the server receives the 
248
	 * unsubscribe request it looks to see if it can find a subscription for the
249
	 * client and then removes it. After this point the server will send no more
250
	 * messages to the client for this subscription.  
251
	 * </p>
252
	 * <p>The topic(s) specified on the unsubscribe must match the topic(s) 
253
	 * specified in the original subscribe request for the subscribe to succeed
254
	 * </p>
255
	 * 
256
	 * <p>This is a blocking method that returns once unsubscribe completes</p>
257
	 * 
258
	 * @param topicFilters one or more topics to unsubscribe from. Each topicFilter
259
	 * must match one specified on a subscribe
260
	 * @throws MqttException if there was an error unregistering the subscription.
261
	 */
262
  public void unsubscribe(String[] topicFilters) throws MqttException;
263
264
  
265
	/**
266
	 * Publishes a message to a topic on the server and return once it is delivered
267
	 * <p>This is a convenience method, which will 
268
	 * create a new {@link MqttMessage} object with a byte array payload and the
269
	 * specified QoS, and then publish it.  All other values in the
270
	 * message will be set to the defaults.
271
	 * </p>
272
	 *
273
	 * @param topic  to deliver the message to, for example "finance/stock/ibm".
274
	 * @param payload the byte array to use as the payload
275
	 * @param qos the Quality of Service to deliver the message at.  Valid values are 0, 1 or 2.
276
	 * @param retained whether or not this message should be retained by the server.
277
	 * @throws MqttPersistenceException when a problem with storing the message
278
	 * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2.
279
	 * @throws MqttException for other errors encountered while publishing the message.
280
	 * For instance client not connected. 
281
	 * @see #publish(String, MqttMessage)
282
	 * @see MqttMessage#setQos(int)
283
	 * @see MqttMessage#setRetained(boolean)
284
	 */
285
	public void publish(String topic, byte[] payload, int qos, boolean retained) throws MqttException, MqttPersistenceException;
286
	
287
	/**
288
	 * Publishes a message to a topic on the server. 
289
	 * <p>
290
	 * Delivers a message to the server at the requested quality of service and returns control
291
	 * once the message has been delivered. In the event the connection fails or the client
292
	 * stops, any messages that are in the process of being delivered will be delivered once
293
	 * a connection is re-established to the server on condition that: 
294
	 * <ul>
295
	 * <li>The connection is re-established with the same clientID
296
	 * <li>The original connection was made with (@link MqttConnectOptions#setCleanSession(boolean)} 
297
	 * set to false
298
	 * <li>The connection is re-established with (@link MqttConnectOptions#setCleanSession(boolean)} 
299
	 * set to false
300
	 * </ul> 
301
	 * </p>
302
	 * <p>In the event that the connection breaks or the client stops it is still possible to determine
303
	 * when the delivery of the message completes. Prior to re-establishing the connection to the server:
304
	 * <ul>
305
	 * <li>Register a {@link #setCallback(MqttCallback)} callback on the client and the delivery complete
306
	 * callback will be notified once a delivery of a message completes 
307
	 * <li>or call {@link #getPendingDeliveryTokens()} which will return a token for each message that
308
	 * is in-flight.  The token can be used to wait for delivery to complete.  
309
	 * </ul>
310
	 * </p>
311
	 * 
312
	 * <p>When building an application,
313
	 * the design of the topic tree should take into account the following principles
314
	 * of topic name syntax and semantics:</p>
315
	 * 
316
	 * <ul>
317
	 * 	<li>A topic must be at least one character long.</li>
318
	 * 	<li>Topic names are case sensitive.  For example, <em>ACCOUNTS</em> and <em>Accounts</em> are
319
	 * 	two different topics.</li>
320
	 * 	<li>Topic names can include the space character.  For example, <em>Accounts
321
	 * 	payable</em> is a valid topic.</li>
322
	 * 	<li>A leading "/" creates a distinct topic.  For example, <em>/finance</em> is
323
	 * 	different from <em>finance</em>. <em>/finance</em> matches "+/+" and "/+", but
324
	 * 	not "+".</li>
325
	 * 	<li>Do not include the null character (Unicode<samp class="codeph"> \x0000</samp>) in
326
	 * 	any topic.</li>
327
	 * </ul>
328
	 * 
329
	 * <p>The following principles apply to the construction and content of a topic
330
	 * tree:</p>
331
	 * 
332
	 * <ul>
333
	 * 	<li>The length is limited to 64k but within that there are no limits to the
334
	 * 	number of levels in a topic tree.</li>
335
	 * 	<li>There can be any number of root nodes; that is, there can be any number
336
	 * 	of topic trees.</li>
337
	 * 	</ul>
338
	 * </p>
339
	 * 
340
	 * <p>This is a blocking method that returns once publish completes</p>	 * 
341
	 * 
342
	 * @param topic  to deliver the message to, for example "finance/stock/ibm".
343
	 * @param message to delivery to the server
344
 	 * @throws MqttPersistenceException when a problem with storing the message
345
	 * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2.
346
	 * @throws MqttException for other errors encountered while publishing the message.
347
	 * For instance client not connected. 
348
	 */
349
	public void publish(String topic, MqttMessage message) throws MqttException, MqttPersistenceException;
350
	
351
	/**
352
	 * Sets the callback listener to use for events that happen asynchronously. 
353
	 * <p>There are a number of events that listener will be notified about. These include
354
	 * <ul>
355
	 * <li>A new message has arrived and is ready to be processed</li>
356
	 * <li>The connection to the server has been lost</li> 
357
	 * <li>Delivery of a message to the server has completed.</li>
358
	 * </ul>  
359
	 * </p>
360
	 * <p>Other events that track the progress of an individual operation such 
361
	 * as connect and subscribe can be tracked using the {@link MqttToken} passed to the
362
	 * operation<p>
363
	 * @see MqttCallback
364
	 * @param callback the class to callback when for events related to the client
365
	 */
366
	public void setCallback(MqttCallback callback);
367
368
	/**
369
	 * Get a topic object which can be used to publish messages.
370
	 * <p>An alternative method that should be used in preference to this one when publishing a message is:
371
	 * <ul>
372
	 * <li>{@link MqttClient#publish(String, MqttMessage)} to publish a message in a blocking manner
373
	 * <li>or use publish methods on the non blocking client like {@link IMqttAsyncClient#publish(String, MqttMessage, Object, IMqttActionListener)}
374
	 * </ul>
375
	 * </p>
376
	 * <p>When building an application,
377
	 * the design of the topic tree should take into account the following principles
378
	 * of topic name syntax and semantics:</p>
379
	 * 
380
	 * <ul>
381
	 * 	<li>A topic must be at least one character long.</li>
382
	 * 	<li>Topic names are case sensitive.  For example, <em>ACCOUNTS</em> and <em>Accounts</em> are
383
	 * 	two different topics.</li>
384
	 * 	<li>Topic names can include the space character.  For example, <em>Accounts
385
	 * 	payable</em> is a valid topic.</li>
386
	 * 	<li>A leading "/" creates a distinct topic.  For example, <em>/finance</em> is
387
	 * 	different from <em>finance</em>. <em>/finance</em> matches "+/+" and "/+", but
388
	 * 	not "+".</li>
389
	 * 	<li>Do not include the null character (Unicode<samp class="codeph"> \x0000</samp>) in
390
	 * 	any topic.</li>
391
	 * </ul>
392
	 * 
393
	 * <p>The following principles apply to the construction and content of a topic
394
	 * tree:</p>
395
	 * 
396
	 * <ul>
397
	 * 	<li>The length is limited to 64k but within that there are no limits to the
398
	 * 	number of levels in a topic tree.</li>
399
	 * 	<li>There can be any number of root nodes; that is, there can be any number
400
	 * 	of topic trees.</li>
401
	 * 	</ul>
402
	 * </p>
403
	 *
404
	 * @param topic the topic to use, for example "finance/stock/ibm".
405
	 * @return an MqttTopic object, which can be used to publish messages to 
406
	 * the topic.
407
	 * @throws IllegalArgumentException if the topic contains a '+' or '#' 
408
	 * wildcard character.
409
	 */
410
	public MqttTopic getTopic(String topic);
411
	
412
	/**
413
	 * Determines if this client is currently connected to the server.
414
	 * 
415
	 * @return <code>true</code> if connected, <code>false</code> otherwise.
416
	 */
417
	public boolean isConnected();
418
419
	/**
420
	 * Returns the client ID used by this client. 
421
	 * <p>All clients connected to the
422
	 * same server or server farm must have a unique ID.
423
	 * </p> 
424
	 * 
425
	 * @return the client ID used by this client.
426
	 */
427
	public String getClientId();
428
429
	/**
430
	 * Returns the address of the server used by this client, as a URI.
431
	 * <p>The format is the same as specified on the constructor.
432
	 * </p>
433
	 * 
434
	 * @return the server's address, as a URI String.
435
	 * @see MqttAsyncClient#MqttAsyncClient(String, String)
436
	 */
437
	public String getServerURI();
438
439
	/**
440
	 * Returns the delivery tokens for any outstanding publish operations.
441
	 * <p>If a client has been restarted and there are messages that were in the
442
	 * process of being delivered when the client stopped this method will 
443
	 * return a token for each message enabling the delivery to be tracked
444
	 * Alternately the {@link MqttCallback#deliveryComplete(IMqttDeliveryToken)} 
445
	 * callback can be used to track the delivery of outstanding messages.
446
	 * </p>
447
	 * <p>If a client connects with cleansession true then there will be no
448
	 * delivery tokens as the cleansession option deletes all earlier state.
449
	 * For state to be remembered the client must connect with cleansession
450
	 * set to false</P>
451
	 * @return zero or more delivery tokens 
452
	 */
453
	public IMqttDeliveryToken[] getPendingDeliveryTokens();
454
	
455
	/**
456
	 * Close the client
457
	 * Releases all resource associated with the client. After the client has 
458
	 * been closed it cannot be reused. For instance attempts to connect will fail. 
459
	 * @throws MqttException  if the client is not disconnected. 
460
	 */
461
	public void close() throws MqttException;
462
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/IMqttDeliveryToken.java (-41 lines)
Lines 1-41 Link Here
1
package org.eclipse.paho.client.mqttv3;
2
/**
3
 * Provides a mechanism for tracking the delivery of a message
4
 * 
5
 * <p>A subclass of IMqttToken that allows the delivery of a message to be tracked. 
6
 * Unlike instances of IMqttToken delivery tokens can be used across connection
7
 * and client restarts.  This enables the delivery of a messages to be tracked 
8
 * after failures. There are two approaches
9
 * <ul> 
10
 * <li>A list of delivery tokens for in-flight messages can be obtained using 
11
 * {@link IMqttAsyncClient#getPendingDeliveryTokens()}.  The waitForCompletion
12
 * method can then be used to block until the delivery is complete.
13
 * <li>A {@link MqttCallback} can be set on the client. Once a message has been 
14
 * delivered the {@link MqttCallback#deliveryComplete(IMqttDeliveryToken)} method will
15
 * be called withe delivery token being passed as a parameter. 
16
 * </ul>
17
 * <p> 
18
 * An action is in progress until either:
19
 * <ul>
20
 * <li>isComplete() returns true or 
21
 * <li>getException() is not null. If a client shuts down before delivery is complete. 
22
 * an exception is returned.  As long as the Java Runtime is not stopped a delivery token
23
 * is valid across a connection disconnect and reconnect. In the event the client 
24
 * is shut down the getPendingDeliveryTokens method can be used once the client is 
25
 * restarted to obtain a list of delivery tokens for inflight messages.
26
 * </ul>
27
 * </p>
28
 * 
29
 */
30
31
public interface IMqttDeliveryToken extends IMqttToken {
32
	/**
33
	 * Returns the message associated with this token.
34
	 * <p>Until the message has been delivered, the message being delivered will
35
	 * be returned. Once the message has been delivered <code>null</code> will be 
36
	 * returned.
37
	 * @return the message associated with this token or null if already delivered.
38
	 * @throws MqttException if there was a problem completing retrieving the message
39
	 */
40
	public MqttMessage getMessage() throws MqttException;
41
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/IMqttToken.java (-140 lines)
Lines 1-140 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3;
13
14
15
/**
16
 * Provides a mechanism for tracking the completion of an asynchronous task.
17
 * 
18
 * <p>When using the asynchronous/non-blocking MQTT programming interface all 
19
 * methods /operations that take any time and in particular those that involve 
20
 * any network operation return control to the caller immediately. The operation
21
 * then proceeds to run in the background so as not to block the invoking thread. 
22
 * An IMqttToken is used to track the state of the operation. An application can use the 
23
 * token to wait for an operation to complete. A token is passed to callbacks 
24
 * once the operation completes and provides context linking it to the original 
25
 * request. A token is associated with a single operation.<p>
26
 * <p> 
27
 * An action is in progress until either:
28
 * <ul>
29
 * <li>isComplete() returns true or 
30
 * <li>getException() is not null.
31
 * </ul>
32
 * </p>
33
 * 
34
 */
35
public interface IMqttToken {
36
	
37
	/**
38
	 * Blocks the current thread until the action this token is associated with has
39
	 * completed.
40
	 * 
41
	 * @throws MqttException if there was a problem with the action associated with the token.
42
	 * @see #waitForCompletion(long)
43
	 */
44
	public void waitForCompletion() throws MqttException;
45
	
46
	/**
47
	 * Blocks the current thread until the action this token is associated with has
48
	 * completed. 
49
	 * <p>The timeout specifies the maximum time it will block for. If the action 
50
	 * completes before the timeout then control returns immediately, if not
51
	 * it will block until the timeout expires. </p>
52
	 * <p>If the action being tracked fails or the timeout expires an exception will 
53
	 * be thrown. In the event of a timeout the action may complete after timeout. 
54
	 * </p>
55
	 * 
56
	 * @param timeout the maximum amount of time to wait for, in milliseconds.
57
	 * @throws MqttException if there was a problem with the action associated with the token.
58
	 */
59
	public void waitForCompletion(long timeout) throws MqttException;
60
	
61
	/**
62
	 * Returns whether or not the action has finished.
63
	 * <p>True will be returned both in the case where the action finished successfully
64
	 * and in the case where it failed. If the action failed {@link #getException()} will 
65
	 * be non null. 
66
	 * </p>
67
	 */
68
	public boolean isComplete();
69
		
70
	/**
71
	 * Returns an exception providing more detail if an operation failed 
72
	 * <p>While an action in in progress and when an action completes successfully
73
	 * null will be returned. Certain errors like timeout or shutting down will not
74
	 * set the exception as the action has not failed or completed at that time
75
	 * </p>  
76
	 * @return exception may return an exception if the operation failed. Null will be 
77
	 * returned while action is in progress and if action completes successfully.
78
	 */
79
	public MqttException getException();
80
	
81
	/**
82
	 * Register a listener to be notified when an action completes.
83
	 * <p>Once a listener is registered it will be invoked when the action the token 
84
	 * is associated with either succeeds or fails. 
85
	 * </p>
86
	 * @param listener to be invoked once the action completes
87
	 */
88
	public void setActionCallback(IMqttActionListener listener);
89
	
90
	/**
91
	 * Return the async listener for this token. 
92
	 * @return listener that is set on the token or null if a listener is not registered.
93
	 */
94
	public IMqttActionListener getActionCallback();
95
	
96
	/**
97
	 * Returns the MQTT client that is responsible for processing the asynchronous
98
	 * action 
99
	 */
100
	public IMqttAsyncClient getClient();
101
	
102
	/**
103
	 * Returns the topic string(s) for the action being tracked by this
104
	 * token. If the action has not been initiated or the action has not
105
	 * topic associated with it such as connect then null will be returned.
106
	 * 
107
	 * @return the topic string(s) for the subscribe being tracked by this token or null
108
	 */
109
	public String[] getTopics();
110
	
111
	/**
112
	 * Store some context associated with an action. 
113
	 * <p>Allows the caller of an action to store some context that can be 
114
	 * accessed from within the ActionListener associated with the action. This 
115
	 * can be useful when the same ActionListener is associated with multiple 
116
	 * actions</p>
117
	 * @param userContext to associate with an action
118
	 */
119
	public void setUserContext(Object userContext);
120
	
121
	/**
122
	 * Retrieve the context associated with an action. 
123
	 * <p>Allows the ActionListener associated with an action to retrieve any context
124
	 * that was associated with the action when the action was invoked. If not 
125
	 * context was provided null is returned. </p>
126
 
127
	 * @return Object context associated with an action or null if there is none.
128
	 */
129
	public Object getUserContext();
130
	
131
	/**
132
	 * Returns the message ID of the message that is associated with the token.
133
	 * A message id of zero will be returned for tokens associated with
134
	 * connect, disconnect and ping operations as there can only ever
135
	 * be one of these outstanding at a time. For other operations
136
	 * the MQTT message id flowed over the network.  
137
	 */
138
	public int getMessageId();
139
140
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttAsyncClient.java (-747 lines)
Lines 1-747 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3;
13
14
import java.util.Hashtable;
15
import java.util.Properties;
16
17
import javax.net.SocketFactory;
18
import javax.net.ssl.SSLSocketFactory;
19
20
import org.eclipse.paho.client.mqttv3.internal.ClientComms;
21
import org.eclipse.paho.client.mqttv3.internal.ExceptionHelper;
22
import org.eclipse.paho.client.mqttv3.internal.LocalNetworkModule;
23
import org.eclipse.paho.client.mqttv3.internal.NetworkModule;
24
import org.eclipse.paho.client.mqttv3.internal.SSLNetworkModule;
25
import org.eclipse.paho.client.mqttv3.internal.TCPNetworkModule;
26
import org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory;
27
import org.eclipse.paho.client.mqttv3.internal.wire.MqttDisconnect;
28
import org.eclipse.paho.client.mqttv3.internal.wire.MqttPublish;
29
import org.eclipse.paho.client.mqttv3.internal.wire.MqttSubscribe;
30
import org.eclipse.paho.client.mqttv3.internal.wire.MqttUnsubscribe;
31
import org.eclipse.paho.client.mqttv3.logging.Logger;
32
import org.eclipse.paho.client.mqttv3.logging.LoggerFactory;
33
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
34
import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence;
35
import org.eclipse.paho.client.mqttv3.util.Debug;
36
37
/**
38
 * Lightweight client for talking to an MQTT server using non-blocking methods 
39
 * that allow an operation to run in the background. 
40
 * 
41
 * <p>This class implements the non-blocking {@link IMqttAsyncClient} client interface
42
 * allowing applications to initiate MQTT actions and then carry working while the 
43
 * MQTT action completes on a background thread.
44
 * This implementation is compatible with all Java SE runtimes from 1.4.2 and up. 
45
 * </p>  
46
 * <p>An application can connect to an MQTT server using:
47
 * <ul>
48
 * <li>A plain TCP socket
49
 * <li>An secure SSL/TLS socket
50
 * </ul>
51
 * </p>
52
 * <p>To enable messages to be delivered even across network and client restarts
53
 * messages need to be safely stored until the message has been delivered at the requested
54
 * quality of service. A pluggable persistence mechanism is provided to store the messages. 
55
 * </p>
56
 * <p>By default {@link MqttDefaultFilePersistence} is used to store messages to a file. 
57
 * If persistence is set to null then messages are stored in memory and hence can  be lost
58
 * if the client, Java runtime or device shuts down. 
59
 * </p>
60
 * <p>If connecting with {@link MqttConnectOptions#setCleanSession(boolean)} set to true it 
61
 * is safe to use memory persistence as all state it cleared when a client disconnects. If
62
 * connecting with cleansession set to false, to provide reliable message delivery 
63
 * then a persistent message store should be used such as the default one. 
64
 * </p>
65
 * <p>The message store interface is pluggable. Different stores can be used by implementing
66
 * the {@link MqttClientPersistence} interface and passing it to the clients constructor. 
67
 * </p>
68
 *  
69
 * @see IMqttAsyncClient   
70
 */
71
public class MqttAsyncClient implements IMqttAsyncClient { // DestinationProvider {
72
	
73
	private static final int URI_TYPE_TCP = 0;
74
	private static final int URI_TYPE_SSL = 1;
75
	private static final int URI_TYPE_LOCAL = 2;
76
	
77
	private String clientId;
78
	private String serverURI;
79
	private int serverURIType;
80
	protected ClientComms comms;
81
	private Hashtable topics;
82
	private MqttClientPersistence persistence;
83
	
84
	final static String className = MqttAsyncClient.class.getName();
85
	public Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className);
86
87
	/**
88
	 * Create an MqttAsyncClient that can be used to communicate with an MQTT server.
89
	 * <p> 
90
	 * The address of the server should be a URI, using a scheme of either 
91
	 * "tcp://" for a TCP connection or "ssl://" for a TCP connection secured by SSL/TLS. 
92
	 * For example:
93
	 * <ul>
94
	 * 	<li><code>tcp://localhost:1883</code></li>
95
	 * 	<li><code>ssl://localhost:8883</code></li>
96
	 * </ul> 
97
	 * If the port is not specified, it will
98
	 * default to 1883 for "tcp://" URIs, and 8883 for "ssl://" URIs.
99
	 * </p>
100
	 * <p>
101
	 * A client identified to connect to an MQTT server, it 
102
	 * must be unique across all clients connecting to the same
103
	 * server. A convenience method is provided to generate a random client id that
104
	 * should satisfy this criteria - {@link #generateClientId()}. As the client identifier
105
	 * is used by the server to identify a client when it reconnects, the client must use the
106
	 * same identifier between connections if durable subscriptions are used and reliable 
107
	 * delivery of messages is required.
108
	 * </p>
109
	 * <p>
110
	 * In Java SE, SSL can be configured in one of several ways, which the 
111
	 * client will use in the following order:
112
	 * </p>
113
	 * <ul>
114
	 * 	<li><strong>Supplying an <code>SSLSocketFactory</code></strong> - applications can
115
	 * use {@link MqttConnectOptions#setSocketFactory(SocketFactory)} to supply 
116
	 * a factory with the appropriate SSL settings.</li>
117
	 * 	<li><strong>SSL Properties</strong> - applications can supply SSL settings as a 
118
	 * simple Java Properties using {@link MqttConnectOptions#setSSLProperties(Properties)}.</li>
119
	 * 	<li><strong>Use JVM settings</strong> - There are a number of standard 
120
	 * Java system properties that can be used to configure key and trust stores.</li>
121
	 * </ul>
122
	 * 
123
	 * <p>In Java ME, the platform settings are used for SSL connections.</p>
124
	 *  
125
	 * <p>A default instance of {@link MqttDefaultFilePersistence} is used by
126
	 * the client. To specify a different persistence implementation, or to turn
127
	 * off persistence, use the {@link #MqttAsyncClient(String, String, MqttClientPersistence)} constructor.
128
	 *  
129
	 * @param serverURI the address of the server to connect to, specified as a URI
130
	 * @param clientId a client identifier that is unique on the server being connected to
131
	 * @throws IllegalArgumentException if the URI does not start with
132
	 * "tcp://", "ssl://" or "local://".
133
	 * @throws IllegalArgumentException if the clientId is null or is greater than 23 characters in length
134
	 * @throws MqttException if any other problem was encountered 
135
	 */
136
	public MqttAsyncClient(String serverURI, String clientId) throws MqttException {
137
		this(serverURI,clientId, new MqttDefaultFilePersistence());
138
	}
139
	
140
	/**
141
	 * Create an MqttAsyncClient that can be used to communicate with an MQTT server.
142
	 * <p> 
143
	 * The address of the server should be a URI, using a scheme of either 
144
	 * "tcp://" for a TCP connection or "ssl://" for a TCP connection secured by SSL/TLS. 
145
	 * For example:
146
	 * <ul>
147
	 * 	<li><code>tcp://localhost:1883</code></li>
148
	 * 	<li><code>ssl://localhost:8883</code></li>
149
	 * </ul> 
150
	 * If the port is not specified, it will
151
	 * default to 1883 for "tcp://" URIs, and 8883 for "ssl://" URIs.
152
	 * </p>
153
	 * <p>
154
	 * A client identified to connect to an MQTT server, it 
155
	 * must be unique across all clients connecting to the same
156
	 * server. A convenience method is provided to generate a random client id that
157
	 * should satisfy this criteria - {@link #generateClientId()}. As the client identifier
158
	 * is used by the server to identify a client when it reconnects, the client must use the
159
	 * same identifier between connections if durable subscriptions are used and reliable 
160
	 * delivery of messages is required.
161
	 * </p>
162
	 * <p>
163
	 * In Java SE, SSL can be configured in one of several ways, which the 
164
	 * client will use in the following order:
165
	 * </p>
166
	 * <ul>
167
	 * 	<li><strong>Supplying an <code>SSLSocketFactory</code></strong> - applications can
168
	 * use {@link MqttConnectOptions#setSocketFactory(SocketFactory)} to supply 
169
	 * a factory with the appropriate SSL settings.</li>
170
	 * 	<li><strong>SSL Properties</strong> - applications can supply SSL settings as a 
171
	 * simple Java Properties using {@link MqttConnectOptions#setSSLProperties(Properties)}.</li>
172
	 * 	<li><strong>Use JVM settings</strong> - There are a number of standard 
173
	 * Java system properties that can be used to configure key and trust stores.</li>
174
	 * </ul>
175
	 * 
176
	 * <p>In Java ME, the platform settings are used for SSL connections.</p>
177
	 * <p> 
178
	 * The persistence mechanism is used to enable reliable messaging. 
179
	 * For qualities of server (QoS) 1 or 2 to work, messages must be persisted
180
	 * to disk by both the client and the server.  If this is not done, then
181
	 * a failure in the client or server can result in lost messages. A pluggable 
182
	 * persistence mechanism is supported via the {@link MqttClientPersistence} 
183
	 * interface. A implementer of this interface that safely stores messages
184
	 * must be specified in order for delivery of messages to be reliable. In
185
	 * addition {@link MqttConnectOptions#setCleanSession(boolean)} must be set
186
	 * to false. In the event that only QoS 0 messages are sent or received or 
187
	 * cleansession is set to true then a safe store is not needed. 
188
	 * </p>
189
	 * <p>An implementation of file-based persistence is provided in 
190
	 * class {@link MqttDefaultFilePersistence} which will work in all Java SE based 
191
	 * systems. If no persistence is needed, the persistence parameter 
192
	 * can be explicitly set to <code>null</code>.</p>
193
	 * 
194
	 * @param serverURI the address of the server to connect to, specified as a URI
195
	 * @param clientId a client identifier that is unique on the server being connected to
196
 	 * @param persistence the persistence mechanism to use.
197
	 * @throws IllegalArgumentException if the URI does not start with
198
	 * "tcp://", "ssl://" or "local://".
199
	 * @throws IllegalArgumentException if the clientId is null or is greater than 23 characters in length
200
	 * @throws MqttException if any other problem was encountered 
201
	 */
202
	public MqttAsyncClient(String serverURI, String clientId, MqttClientPersistence persistence) throws MqttException {
203
		
204
		log.setResourceName(clientId);
205
		
206
		if (clientId == null || clientId.length() == 0 || clientId.length() > 23) {
207
			throw new IllegalArgumentException();
208
		}
209
		final String methodName = "MqttAsyncClient";
210
		
211
		this.serverURI = serverURI;
212
		this.serverURIType = validateURI(serverURI);
213
		this.clientId = clientId;
214
		
215
		this.persistence = persistence;
216
		if (this.persistence == null) {
217
			this.persistence = new MemoryPersistence();
218
		}
219
		
220
		// @TRACE 101=<init> ClientID={0} ServerURI={1} PersistenceType={2}  	
221
		log.fine(className,methodName,"101",new Object[]{clientId,serverURI,persistence});
222
223
		this.persistence.open(clientId, serverURI);
224
		this.comms = new ClientComms(this, this.persistence);
225
		this.persistence.close();
226
		this.topics = new Hashtable();
227
228
	}
229
	
230
	private int validateURI(String srvURI) {
231
		if (srvURI.startsWith("tcp://")) {
232
			return URI_TYPE_TCP;
233
		}
234
		else if (srvURI.startsWith("ssl://")) {
235
			return URI_TYPE_SSL;
236
		}
237
		else if (srvURI.startsWith("local://")) {
238
			return URI_TYPE_LOCAL;
239
		}
240
		else {
241
			throw new IllegalArgumentException();
242
		}
243
	}
244
	
245
	/**
246
	 * Factory method to create the correct network module, based on the
247
	 * supplied address URI.
248
	 * 
249
	 * @param address the URI for the server.
250
	 * @return a network module appropriate to the specified address.
251
	 */
252
	protected NetworkModule createNetworkModule(String address, MqttConnectOptions options) throws MqttException, MqttSecurityException {
253
		final String methodName = "createNetworkModule";
254
		// @TRACE 115=URI={0}
255
		log.fine(className,methodName, "115", new Object[] {address});
256
257
		NetworkModule netModule;
258
		String shortAddress;
259
		String host;
260
		int port;
261
		SocketFactory factory = options.getSocketFactory();
262
		switch (serverURIType) {
263
		case URI_TYPE_TCP:
264
			shortAddress = address.substring(6);
265
			host = getHostName(shortAddress);
266
			port = getPort(shortAddress, 1883);
267
			if (factory == null) {
268
				factory = SocketFactory.getDefault();
269
				options.setSocketFactory(factory);
270
			}
271
			else if (factory instanceof SSLSocketFactory) {
272
				throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_SOCKET_FACTORY_MISMATCH);
273
			}
274
			netModule = new TCPNetworkModule(factory, host, port, clientId);
275
			((TCPNetworkModule)netModule).setConnectTimeout(options.getConnectionTimeout());
276
			break;
277
		case URI_TYPE_SSL:
278
			shortAddress = address.substring(6);
279
			host = getHostName(shortAddress);
280
			port = getPort(shortAddress, 8883);
281
			SSLSocketFactoryFactory factoryFactory = null;
282
			if (factory == null) {
283
//				try {
284
					factoryFactory = new SSLSocketFactoryFactory();
285
					Properties sslClientProps = options.getSSLProperties();
286
					if (null != sslClientProps)
287
						factoryFactory.initialize(sslClientProps, null);
288
					factory = factoryFactory.createSocketFactory(null);
289
//				}
290
//				catch (MqttDirectException ex) {
291
//					throw ExceptionHelper.createMqttException(ex.getCause());
292
//				}
293
			}
294
			else if ((factory instanceof SSLSocketFactory) == false) {
295
				throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_SOCKET_FACTORY_MISMATCH);
296
			}
297
298
			// Create the network module...
299
			netModule = new SSLNetworkModule((SSLSocketFactory) factory, host, port, clientId);
300
			((SSLNetworkModule)netModule).setSSLhandshakeTimeout(options.getConnectionTimeout());
301
			// Ciphers suites need to be set, if they are available
302
			if (factoryFactory != null) {
303
				String[] enabledCiphers = factoryFactory.getEnabledCipherSuites(null);
304
				if (enabledCiphers != null) {
305
					((SSLNetworkModule) netModule).setEnabledCiphers(enabledCiphers);
306
				}
307
			}
308
			break;
309
		case URI_TYPE_LOCAL:
310
			netModule = new LocalNetworkModule(address.substring(8));
311
			break;
312
		default:
313
			// This shouldn't happen, as long as validateURI() has been called.
314
			netModule = null;
315
		}
316
		return netModule;
317
	}
318
	
319
	private int getPort(String uri, int defaultPort) {
320
		int port;
321
		int portIndex = uri.lastIndexOf(':');
322
		if (portIndex == -1) {
323
			port = defaultPort;
324
		}
325
		else {
326
			port = Integer.valueOf(uri.substring(portIndex + 1)).intValue();
327
		}
328
		return port;
329
	}
330
	
331
	private String getHostName(String uri) {
332
		int schemeIndex = uri.lastIndexOf('/');
333
		int portIndex = uri.lastIndexOf(':');
334
		if (portIndex == -1) {
335
			portIndex = uri.length();
336
		}
337
		return uri.substring(schemeIndex + 1, portIndex);
338
	}
339
	
340
	/* (non-Javadoc)
341
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#connect(java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
342
	 */
343
	public IMqttToken connect(Object userContext, IMqttActionListener callback) 
344
			throws MqttException, MqttSecurityException {
345
		return this.connect(new MqttConnectOptions(), userContext, callback);
346
	} 
347
	
348
	/* (non-Javadoc)
349
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#connect()
350
	 */
351
	public IMqttToken connect() throws MqttException, MqttSecurityException {
352
		return this.connect(null, null);
353
	}
354
	
355
	/* (non-Javadoc)
356
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#connect(org.eclipse.paho.client.mqttv3.MqttConnectOptions)
357
	 */
358
	public IMqttToken connect(MqttConnectOptions options) throws MqttException, MqttSecurityException {
359
		return this.connect(options, null,null);
360
	}
361
	
362
	/* (non-Javadoc)
363
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#connect(org.eclipse.paho.client.mqttv3.MqttConnectOptions, java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
364
	 */
365
	public IMqttToken connect(MqttConnectOptions options, Object userContext, IMqttActionListener callback) 
366
			throws MqttException, MqttSecurityException {
367
		final String methodName = "connect";
368
		if (comms.isConnected()) {
369
			throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_CONNECTED);
370
		}
371
		if (comms.isConnecting()) {
372
			throw new MqttException(MqttException.REASON_CODE_CONNECT_IN_PROGRESS);
373
		}
374
		if (comms.isDisconnecting()) {
375
			throw new MqttException(MqttException.REASON_CODE_CLIENT_DISCONNECTING);
376
		}
377
		if (comms.isClosed()) {
378
			throw new MqttException(MqttException.REASON_CODE_CLIENT_CLOSED);
379
		}
380
381
		// @TRACE 103=cleanSession={0} connectionTimeout={1} TimekeepAlive={2} userName={3} password={4} will={5} userContext={6} callback={7}
382
		log.fine(className,methodName, "103",
383
				new Object[]{ 
384
				new Boolean(options.isCleanSession()),
385
				new Integer(options.getConnectionTimeout()),
386
				new Integer(options.getKeepAliveInterval()),
387
				options.getUserName(),
388
				((null == options.getPassword())?"[null]":"[notnull]"),
389
				((null == options.getWillMessage())?"[null]":"[notnull]"),
390
				userContext,
391
				callback });
392
		comms.setNetworkModule(createNetworkModule(serverURI, options));
393
394
		this.persistence.open(clientId, serverURI);
395
396
		if (options.isCleanSession()) {
397
			persistence.clear();
398
		}
399
		
400
		MqttToken token = new MqttToken(getClientId());
401
		token.setActionCallback(callback);
402
		token.setUserContext(userContext);
403
		
404
		comms.connect(options, token);
405
		
406
		return token;
407
	}
408
409
	/* (non-Javadoc)
410
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnect(java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
411
	 */
412
	public IMqttToken disconnect( Object userContext, IMqttActionListener callback) throws MqttException {
413
		return this.disconnect(30000, userContext, callback);
414
	}
415
	
416
	/* (non-Javadoc)
417
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnect()
418
	 */
419
	public IMqttToken disconnect() throws MqttException {
420
		return this.disconnect(null, null);
421
	}
422
	
423
	/* (non-Javadoc)
424
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnect(long)
425
	 */
426
	public IMqttToken disconnect(long quiesceTimeout) throws MqttException {
427
		return this.disconnect(quiesceTimeout, null, null);
428
	}
429
	
430
	/* (non-Javadoc)
431
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnect(long, java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
432
	 */
433
	public IMqttToken disconnect(long quiesceTimeout, Object userContext, IMqttActionListener callback) throws MqttException {
434
		final String methodName = "disconnect";
435
		// @TRACE 104=> quiesceTimeout={0} userContext={1} callback={2}
436
		log.fine(className,methodName, "104",new Object[]{ new Long(quiesceTimeout), userContext, callback});
437
		
438
		MqttToken token = new MqttToken(getClientId());
439
		token.setActionCallback(callback);
440
		token.setUserContext(userContext);
441
442
		MqttDisconnect disconnect = new MqttDisconnect();
443
		try {
444
			comms.disconnect(disconnect, quiesceTimeout, token);
445
		}
446
		catch (MqttException ex) {
447
			//@TRACE 105=< exception
448
			log.fine(className,methodName,"105",null,ex);
449
			throw ex;
450
		}
451
		//@TRACE 108=<
452
		log.fine(className,methodName,"108");
453
		
454
		return token;
455
	}
456
457
	/* (non-Javadoc)
458
	 * @see IMqttAsyncClient#isConnected()
459
	 */
460
	public boolean isConnected() {
461
		return comms.isConnected();
462
	}
463
	
464
	/* (non-Javadoc)
465
	 * @see IMqttAsyncClient#getClientId()
466
	 */
467
	public String getClientId() {
468
		return clientId;
469
	}
470
	
471
	/* (non-Javadoc)
472
	 * @see IMqttAsyncClient#getServerURI()
473
	 */
474
	public String getServerURI() {
475
		return serverURI;
476
	}
477
		
478
	/**
479
	 * Get a topic object which can be used to publish messages.
480
	 * <p>There are two alternative methods that should be used in preference to this one when publishing a message:
481
	 * <ul>
482
	 * <li>{@link MqttAsyncClient#publish(String, MqttMessage, MqttDeliveryToken)} to publish a message in a non-blocking manner or 
483
	 * <li>{@link MqttClient#publishBlock(String, MqttMessage, MqttDeliveryToken)} to publish a message in a blocking manner
484
	 * </ul>
485
	 * </p>
486
	 * <p>When you build an application,
487
	 * the design of the topic tree should take into account the following principles
488
	 * of topic name syntax and semantics:</p>
489
	 * 
490
	 * <ul>
491
	 * 	<li>A topic must be at least one character long.</li>
492
	 * 	<li>Topic names are case sensitive.  For example, <em>ACCOUNTS</em> and <em>Accounts</em> are
493
	 * 	two different topics.</li>
494
	 * 	<li>Topic names can include the space character.  For example, <em>Accounts
495
	 * 	payable</em> is a valid topic.</li>
496
	 * 	<li>A leading "/" creates a distinct topic.  For example, <em>/finance</em> is
497
	 * 	different from <em>finance</em>. <em>/finance</em> matches "+/+" and "/+", but
498
	 * 	not "+".</li>
499
	 * 	<li>Do not include the null character (Unicode<samp class="codeph"> \x0000</samp>) in
500
	 * 	any topic.</li>
501
	 * </ul>
502
	 * 
503
	 * <p>The following principles apply to the construction and content of a topic
504
	 * tree:</p>
505
	 * 
506
	 * <ul>
507
	 * 	<li>The length is limited to 64k but within that there are no limits to the
508
	 * 	number of levels in a topic tree.</li>
509
	 * 	<li>There can be any number of root nodes; that is, there can be any number
510
	 * 	of topic trees.</li>
511
	 * 	</ul>
512
	 * </p>
513
	 *
514
	 * @param topic the topic to use, for example "finance/stock/ibm".
515
	 * @return an MqttTopic object, which can be used to publish messages to 
516
	 * the topic.
517
	 * @throws IllegalArgumentException if the topic contains a '+' or '#' 
518
	 * wildcard character.
519
	 */
520
	protected MqttTopic getTopic(String topic) {
521
		validateTopic(topic);
522
		
523
		MqttTopic result = (MqttTopic)topics.get(topic);
524
		if (result == null) {
525
			result = new MqttTopic(topic, comms);
526
			topics.put(topic,result);
527
		}
528
		return result;
529
	}
530
		
531
	/* (non-Javadoc)
532
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang.String, int, java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
533
	 */
534
	public IMqttToken subscribe(String topicFilter, int qos, Object userContext, IMqttActionListener callback) throws MqttException {
535
		return this.subscribe(new String[] {topicFilter}, new int[] {qos}, userContext, callback);
536
	}
537
	
538
	/* (non-Javadoc)
539
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang.String, int)
540
	 */
541
	public IMqttToken subscribe(String topicFilter, int qos) throws MqttException {
542
		return this.subscribe(new String[] {topicFilter}, new int[] {qos}, null, null);
543
	}
544
545
	/* (non-Javadoc)
546
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang.String[], int[])
547
	 */
548
	public IMqttToken subscribe(String[] topicFilters, int[] qos) throws MqttException {
549
		return this.subscribe(topicFilters, qos, null, null);
550
	}
551
552
	/* (non-Javadoc)
553
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang.String[], int[], java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
554
	 */
555
	public IMqttToken subscribe(String[] topicFilters, int[] qos, Object userContext, IMqttActionListener callback) throws MqttException {
556
		final String methodName = "subscribe";
557
558
		if (topicFilters.length != qos.length) {
559
			throw new IllegalArgumentException();
560
		}
561
		String subs = "";
562
		for (int i=0;i<topicFilters.length;i++) {
563
			if (i>0) {
564
				subs+=", ";
565
			}
566
			subs+=topicFilters[i]+":"+qos[i];
567
		}
568
		//@TRACE 106=Subscribe topic={0} userContext={1} callback={2}
569
		log.fine(className,methodName,"106",new Object[]{subs, userContext, callback});
570
		
571
		MqttToken token = new MqttToken(getClientId());
572
		token.setActionCallback(callback);
573
		token.setUserContext(userContext);
574
		token.internalTok.setTopics(topicFilters);
575
		
576
		MqttSubscribe register = new MqttSubscribe(topicFilters, qos);
577
578
		comms.sendNoWait(register, token);
579
		//@TRACE 109=<
580
		log.fine(className,methodName,"109");
581
		
582
		return token;
583
	}
584
585
	/* (non-Javadoc)
586
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#unsubscribe(java.lang.String, java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
587
	 */
588
	public IMqttToken unsubscribe(String topicFilter,  Object userContext, IMqttActionListener callback) throws MqttException {
589
		return unsubscribe(new String[] {topicFilter}, userContext, callback);
590
	}
591
	
592
	/* (non-Javadoc)
593
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#unsubscribe(java.lang.String)
594
	 */
595
	public IMqttToken unsubscribe(String topicFilter) throws MqttException {
596
		return unsubscribe(new String[] {topicFilter}, null, null);
597
	}
598
599
	/* (non-Javadoc)
600
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#unsubscribe(java.lang.String[])
601
	 */
602
	public IMqttToken unsubscribe(String[] topicFilters) throws MqttException {
603
		return unsubscribe(topicFilters, null, null);
604
	}
605
	
606
	/* (non-Javadoc)
607
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#unsubscribe(java.lang.String[], java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
608
	 */
609
	public IMqttToken unsubscribe(String[] topicFilters, Object userContext, IMqttActionListener callback) throws MqttException {
610
		final String methodName = "unsubscribe";
611
		String subs = "";
612
		for (int i=0;i<topicFilters.length;i++) {
613
			if (i>0) {
614
				subs+=", ";
615
			}
616
			subs+=topicFilters[i];
617
		}
618
		//@TRACE 107=Unsubscribe topic={0} userContext={1} callback={2}
619
		log.fine(className, methodName,"107",new Object[]{subs, userContext, callback});
620
621
		MqttToken token = new MqttToken(getClientId());
622
		token.setActionCallback(callback);
623
		token.setUserContext(userContext);
624
		token.internalTok.setTopics(topicFilters);
625
		
626
		MqttUnsubscribe unregister = new MqttUnsubscribe(topicFilters);
627
		
628
		comms.sendNoWait(unregister, token);
629
		//@TRACE 110=<
630
		log.fine(className,methodName,"110");
631
		
632
		return token;
633
	}
634
	
635
	/* (non-Javadoc)
636
	 * @see IMqttAsyncClient#setCallback(MqttCallback)
637
	 */
638
	public void setCallback(MqttCallback callback) {
639
		comms.setCallback(callback);
640
	}
641
	
642
	/**
643
	 * Returns a randomly generated client identifier based on the current user's login
644
	 * name and the system time.
645
	 * <p>When cleanSession is set to false, an application must ensure it uses the 
646
	 * same client identifier when it reconnects to the server to resume state and maintain
647
	 * assured message delivery.</p>
648
	 * @return a generated client identifier
649
	 * @see MqttConnectOptions#setCleanSession(boolean)
650
	 */
651
	public static String generateClientId() {
652
		return (System.getProperty("user.name") + "." + System.currentTimeMillis());
653
	}
654
	
655
	/* (non-Javadoc)
656
	 * @see IMqttAsyncClient#getPendingDeliveryTokens()
657
	 */
658
	public IMqttDeliveryToken[] getPendingDeliveryTokens() {
659
		return comms.getPendingDeliveryTokens();
660
	}
661
662
	/* (non-Javadoc)
663
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#publish(java.lang.String, byte[], int, boolean, java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
664
	 */
665
	public IMqttDeliveryToken publish(String topic, byte[] payload, int qos,
666
			boolean retained, Object userContext, IMqttActionListener callback) throws MqttException,
667
			MqttPersistenceException {
668
		MqttMessage message = new MqttMessage(payload);
669
		message.setQos(qos);
670
		message.setRetained(retained);
671
		return this.publish(topic, message, userContext, callback);
672
	}
673
	/* (non-Javadoc)
674
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#publish(java.lang.String, byte[], int, boolean)
675
	 */
676
	public IMqttDeliveryToken publish(String topic, byte[] payload, int qos,
677
			boolean retained) throws MqttException, MqttPersistenceException {
678
		return this.publish(topic, payload, qos, retained, null, null);
679
	}
680
681
	/* (non-Javadoc)
682
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#publish(java.lang.String, org.eclipse.paho.client.mqttv3.MqttMessage)
683
	 */
684
	public IMqttDeliveryToken publish(String topic, MqttMessage message) throws MqttException, 	MqttPersistenceException {
685
		return this.publish(topic, message, null, null);
686
	}
687
688
	/* (non-Javadoc)
689
	 * @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)
690
	 */
691
	public IMqttDeliveryToken publish(String topic, MqttMessage message, Object userContext, IMqttActionListener callback) throws MqttException,
692
			MqttPersistenceException {
693
		final String methodName = "publish";
694
		//@TRACE 111=< topic={0} message={1}userContext={1} callback={2}
695
		log.fine(className,methodName,"111", new Object[] {topic, userContext, callback});
696
697
		validateTopic(topic);
698
		
699
		MqttDeliveryToken token = new MqttDeliveryToken(getClientId());
700
		token.setActionCallback(callback);
701
		token.setUserContext(userContext);
702
		token.setMessage(message);
703
		token.internalTok.setTopics(new String[] {topic});
704
		
705
		MqttPublish pubMsg = new MqttPublish(topic, message);
706
		comms.sendNoWait(pubMsg, token);
707
		
708
		//@TRACE 112=<
709
		log.fine(className,methodName,"112");
710
		
711
		return token;
712
	}
713
	
714
	/* (non-Javadoc)
715
	 * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#close()
716
	 */
717
	public void close() throws MqttException {
718
		final String methodName = "close";
719
		//@TRACE 113=<
720
		log.fine(className,methodName,"113");
721
		comms.close();
722
		//@TRACE 114=>
723
		log.fine(className,methodName,"114");
724
725
	}
726
	
727
	/**
728
	 * Return a debug object that can be used to help solve problems.
729
	 */
730
	public Debug getDebug() {
731
		return new Debug(clientId,comms);
732
	}
733
	
734
	/**
735
	 * Checks a topic is valid when publishing a message
736
	 * <p>Checks the topic does not contain a wild card character.</p>
737
	 * @param topic to validate
738
	 * @throws IllegalArgumentException if the topic is not valid
739
	 */
740
	static public void validateTopic(String topic) {
741
		if ((topic.indexOf('#') == -1) && (topic.indexOf('+') == -1)) {
742
			return;
743
		}
744
		// The topic string does not comply with topic string rules. 
745
		throw new IllegalArgumentException();
746
	}
747
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttCallback.java (-75 lines)
Lines 1-75 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3;
13
14
15
/**
16
 * Enables an application to be notified when asynchronous 
17
 * events related to the client occur.
18
 * Classes implementing this interface
19
 * can be registered on both types of client: {@link IMqttClient#setCallback(MqttCallback)} 
20
 * and {@link IMqttAsyncClient#setCallback(MqttCallback)} 
21
 */
22
public interface MqttCallback {
23
	/**
24
	 * This method is called when the connection to the server is lost.
25
	 * 
26
	 * @param cause the reason behind the loss of connection.
27
	 */
28
	public void connectionLost(Throwable cause);
29
30
	/**
31
	 * This method is called when a message arrives from the server.
32
	 * 
33
	 * <p>
34
	 * This method is invoked synchronously by the MQTT client. An
35
	 * acknowledgement is not sent back to the server until this
36
	 * method returns cleanly.</p>
37
	 * <p>
38
	 * If an implementation of this method throws an <code>Exception</code>, then the
39
	 * client will be shut down.  When the client is next re-connected, any QoS
40
	 * 1 or 2 messages will be redelivered by the server.</p>
41
	 * <p>
42
	 * Any additional messages which arrive while an
43
	 * implementation of this method is running, will build up in memory, and
44
	 * will then back up on the network.</p>
45
	 * <p>
46
	 * If an application needs to persist data, then it
47
	 * should ensure the data is persisted prior to returning from this method, as
48
	 * after returning from this method, the message is considered to have been
49
	 * delivered, and will not be reproducable.</p>
50
	 * <p>
51
	 * It is possible to send a new message within an implementation of this callback
52
	 * (for example, a response to this message), but the implementation must not 
53
	 * disconnect the client, as it will be impossible to send an acknowledgement for
54
	 * the message being processed, and a deadlock will occur.</p>
55
	 * 
56
	 * @param topic name of the topic on the message was published to
57
	 * @param message the actual message.
58
	 * @throws Exception if a terminal error has occurred, and the client should be
59
	 * shut down.
60
	 */
61
	public void messageArrived(String topic, MqttMessage message) throws Exception;
62
	
63
	/**
64
	 * Called when delivery for a message has been completed, and all 
65
	 * acknowledgements have been received. For QOS 0 messages it is 
66
	 * called once the message has been handed to the network for
67
	 * delivery. For QOS 1 it is called when PUBACK is received and
68
	 * for QOS 2 when PUBCOMP is received. The token will be the same
69
	 * token as that returned when the message was published. 
70
	 * 
71
	 * @param token the delivery token associated with the message.
72
	 */
73
	public void deliveryComplete(IMqttDeliveryToken token);
74
	
75
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttClient.java (-376 lines)
Lines 1-376 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3;
13
14
import java.util.Properties;
15
import javax.net.SocketFactory;
16
17
import org.eclipse.paho.client.mqttv3.logging.Logger;
18
import org.eclipse.paho.client.mqttv3.logging.LoggerFactory;
19
import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence;
20
import org.eclipse.paho.client.mqttv3.util.Debug;
21
22
/**
23
 * Lightweight client for talking to an MQTT server using methods that block 
24
 * until an operation completes.  
25
 * 
26
 * <p>This class implements the blocking {@link IMqttClient} client interface where all 
27
 * actions block until they have completed (or timed out). 
28
 * This implementation is compatible with all Java SE runtimes from 1.4.2 and up.  
29
 * </p>  
30
 * <p>An application can connect to an MQTT server using:
31
 * <ul>
32
 * <li>A plain TCP socket
33
 * <li>An secure SSL/TLS socket
34
 * </ul>
35
 * </p>
36
 * <p>To enable messages to be delivered even across network and client restarts
37
 * messages need to be safely stored until the message has been delivered at the requested
38
 * quality of service. A pluggable persistence mechanism is provided to store the messages. 
39
 * </p>
40
 * <p>By default {@link MqttDefaultFilePersistence} is used to store messages to a file. 
41
 * If persistence is set to null then messages are stored in memory and hence can  be lost
42
 * if the client, Java runtime or device shuts down. 
43
 * </p>
44
 * <p>If connecting with {@link MqttConnectOptions#setCleanSession(boolean)} set to true it 
45
 * is safe to use memory persistence as all state it cleared when a client disconnects. If
46
 * connecting with cleansession set to false, to provide reliable message delivery 
47
 * then a persistent message store should be used such as the default one. </p>
48
 * <p>The message store interface is pluggable. Different stores can be used by implementing
49
 * the {@link MqttClientPersistence} interface and passing it to the clients constructor. 
50
 * </p>
51
 *  
52
 * @see IMqttClient   
53
 */
54
public class MqttClient implements IMqttClient { //), DestinationProvider {
55
	
56
	protected MqttAsyncClient aClient = null;  // Delegate implementation to MqttAshyncClient
57
	protected long timeToWait = -1;				// How long each method should wait for action to complete	
58
	
59
	final static String className = MqttClient.class.getName();
60
	public Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className);
61
	
62
	
63
	/**
64
	 * Create an MqttClient that can be used to communicate with an MQTT server.
65
	 * <p> 
66
	 * The address of the server should be a URI, using a scheme of either 
67
	 * "tcp://" for a TCP connection or "ssl://" for a TCP connection secured by SSL/TLS. 
68
	 * For example:
69
	 * <ul>
70
	 * 	<li><code>tcp://localhost:1883</code></li>
71
	 * 	<li><code>ssl://localhost:8883</code></li>
72
	 * </ul> 
73
	 * If the port is not specified, it will
74
	 * default to 1883 for "tcp://" URIs, and 8883 for "ssl://" URIs.
75
	 * </p>
76
	 * <p>
77
	 * A client identified to connect to an MQTT server, it 
78
	 * must be unique across all clients connecting to the same
79
	 * server. A convenience method is provided to generate a random client id that
80
	 * should satisfy this criteria - {@link #generateClientId()}. As the client identifier
81
	 * is used by the server to identify a client when it reconnects, the client must use the
82
	 * same identifier between connections if durable subscriptions are used and reliable 
83
	 * delivery of messages is required.
84
	 * </p>
85
	 * <p>
86
	 * In Java SE, SSL can be configured in one of several ways, which the 
87
	 * client will use in the following order:
88
	 * </p>
89
	 * <ul>
90
	 * 	<li><strong>Supplying an <code>SSLSocketFactory</code></strong> - applications can
91
	 * use {@link MqttConnectOptions#setSocketFactory(SocketFactory)} to supply 
92
	 * a factory with the appropriate SSL settings.</li>
93
	 * 	<li><strong>SSL Properties</strong> - applications can supply SSL settings as a 
94
	 * simple Java Properties using {@link MqttConnectOptions#setSSLProperties(Properties)}.</li>
95
	 * 	<li><strong>Use JVM settings</strong> - There are a number of standard 
96
	 * Java system properties that can be used to configure key and trust stores.</li>
97
	 * </ul>
98
	 * 
99
	 * <p>In Java ME, the platform settings are used for SSL connections.</p>
100
	 *  
101
	 * <p>A default instance of {@link MqttDefaultFilePersistence} is used by
102
	 * the client. To specify a different persistence implementation, or to turn
103
	 * off persistence, use the {@link #MqttClient(String, String, MqttClientPersistence)} constructor.
104
	 *  
105
	 * @param serverURI the address of the server to connect to, specified as a URI
106
	 * @param clientId a client identifier that is unique on the server being connected to
107
	 * @throws IllegalArgumentException if the URI does not start with
108
	 * "tcp://", "ssl://" or "local://".
109
	 * @throws IllegalArgumentException if the clientId is null or is greater than 23 characters in length
110
	 * @throws MqttException if any other problem was encountered 
111
	 */
112
	public MqttClient(String serverURI, String clientId) throws MqttException {
113
		this(serverURI,clientId, new MqttDefaultFilePersistence());
114
	}
115
	
116
	/**
117
	 * Create an MqttAsyncClient that can be used to communicate with an MQTT server.
118
	 * <p> 
119
	 * The address of the server should be a URI, using a scheme of either 
120
	 * "tcp://" for a TCP connection or "ssl://" for a TCP connection secured by SSL/TLS. 
121
	 * For example:
122
	 * <ul>
123
	 * 	<li><code>tcp://localhost:1883</code></li>
124
	 * 	<li><code>ssl://localhost:8883</code></li>
125
	 * </ul> 
126
	 * If the port is not specified, it will
127
	 * default to 1883 for "tcp://" URIs, and 8883 for "ssl://" URIs.
128
	 * </p>
129
	 * <p>
130
	 * A client identified to connect to an MQTT server, it 
131
	 * must be unique across all clients connecting to the same
132
	 * server. A convenience method is provided to generate a random client id that
133
	 * should satisfy this criteria - {@link #generateClientId()}. As the client identifier
134
	 * is used by the server to identify a client when it reconnects, the client must use the
135
	 * same identifier between connections if durable subscriptions are used and reliable 
136
	 * delivery of messages is required.
137
	 * </p>
138
	 * <p>
139
	 * In Java SE, SSL can be configured in one of several ways, which the 
140
	 * client will use in the following order:
141
	 * </p>
142
	 * <ul>
143
	 * 	<li><strong>Supplying an <code>SSLSocketFactory</code></strong> - applications can
144
	 * use {@link MqttConnectOptions#setSocketFactory(SocketFactory)} to supply 
145
	 * a factory with the appropriate SSL settings.</li>
146
	 * 	<li><strong>SSL Properties</strong> - applications can supply SSL settings as a 
147
	 * simple Java Properties using {@link MqttConnectOptions#setSSLProperties(Properties)}.</li>
148
	 * 	<li><strong>Use JVM settings</strong> - There are a number of standard 
149
	 * Java system properties that can be used to configure key and trust stores.</li>
150
	 * </ul>
151
	 * 
152
	 * <p>In Java ME, the platform settings are used for SSL connections.</p>
153
	 * <p> 
154
	 * The persistence mechanism is used to enable reliable messaging. 
155
	 * For qualities of server (QoS) 1 or 2 to work, messages must be persisted
156
	 * to disk by both the client and the server.  If this is not done, then
157
	 * a failure in the client or server can result in lost messages. A pluggable 
158
	 * persistence mechanism is supported via the {@link MqttClientPersistence} 
159
	 * interface. A implementer of this interface that safely stores messages
160
	 * must be specified in order for delivery of messages to be reliable. In
161
	 * addition {@link MqttConnectOptions#setCleanSession(boolean)} must be set
162
	 * to false. In the event that only QoS 0 messages are sent or received or 
163
	 * cleansession is set to true then a safe store is not needed. 
164
	 * </p>
165
	 * <p>An implementation of file-based persistence is provided in 
166
	 * class {@link MqttDefaultFilePersistence} which will work in all Java SE based 
167
	 * systems. If no persistence is needed, the persistence parameter 
168
	 * can be explicitly set to <code>null</code>.</p>
169
	 * 
170
	 * @param serverURI the address of the server to connect to, specified as a URI
171
	 * @param clientId a client identifier that is unique on the server being connected to
172
 	 * @param persistence the persistence mechanism to use.
173
	 * @throws IllegalArgumentException if the URI does not start with
174
	 * "tcp://", "ssl://" or "local://".
175
	 * @throws IllegalArgumentException if the clientId is null or is greater than 23 characters in length
176
	 * @throws MqttException if any other problem was encountered 
177
	 */
178
	public MqttClient(String serverURI, String clientId, MqttClientPersistence persistence) throws MqttException {
179
		aClient = new MqttAsyncClient(serverURI, clientId, persistence);
180
	}
181
182
	/*
183
	 * @see IMqttClient#connect()
184
	 */
185
	public void connect() throws MqttSecurityException, MqttException {
186
		this.connect(new MqttConnectOptions());
187
	}
188
	
189
	/*
190
	 * @see IMqttClient#connect(MqttConnectOptions)
191
	 */
192
	public void connect(MqttConnectOptions options) throws MqttSecurityException, MqttException {
193
		aClient.connect(options, null, null).waitForCompletion(getTimeToWait());
194
	}
195
196
	/*
197
	 * @see IMqttClient#disconnect()
198
	 */
199
	public void disconnect() throws MqttException {
200
		this.disconnect(30000);
201
	}
202
	
203
	/*
204
	 * @see IMqttClient#disconnect(long)
205
	 */
206
	public void disconnect(long quiesceTimeout) throws MqttException {
207
		aClient.disconnect(quiesceTimeout, null, null).waitForCompletion();
208
	}
209
210
	/* 
211
	 * @see IMqttClient#subscribe(String)
212
	 */
213
	public void subscribe(String topicFilter) throws MqttException {
214
		this.subscribe(new String[] {topicFilter}, new int[] {1});
215
	}
216
	
217
	/* 
218
	 * @see IMqttClient#subscribe(String[])
219
	 */
220
	public void subscribe(String[] topicFilters) throws MqttException {
221
		int[] qos = new int[topicFilters.length];
222
		for (int i=0; i<qos.length; i++) {
223
			qos[i] = 1; 
224
		}
225
		this.subscribe(topicFilters, qos);
226
	}
227
	
228
	/*
229
	 * @see IMqttClient#subscribe(String, int)
230
	 */
231
	public void subscribe(String topicFilter, int qos) throws MqttException {
232
		this.subscribe(new String[] {topicFilter}, new int[] {qos});
233
	}
234
235
	/*
236
	 * @see IMqttClient#subscribe(String[], int[])
237
	 */
238
	public void subscribe(String[] topicFilters, int[] qos) throws MqttException {
239
		aClient.subscribe(topicFilters, qos, null,null).waitForCompletion(getTimeToWait());
240
	}
241
242
	/*
243
	 * @see IMqttClient#unsubscribe(String)
244
	 */
245
	public void unsubscribe(String topicFilter) throws MqttException {
246
		unsubscribe(new String[] {topicFilter});
247
	}
248
	
249
	/*
250
	 * @see IMqttClient#unsubscribe(String[])
251
	 */
252
	public void unsubscribe(String[] topicFilters) throws MqttException {
253
		aClient.unsubscribe(topicFilters, null,null).waitForCompletion(getTimeToWait());
254
	}
255
	
256
	/*
257
	 * @see IMqttClient#publishBlock(String, byte[], int, boolean)
258
	 */
259
	public void publish(String topic, byte[] payload,int qos, boolean retained) throws MqttException,
260
			MqttPersistenceException {
261
		MqttMessage message = new MqttMessage(payload);
262
		message.setQos(qos);
263
		message.setRetained(retained);
264
		this.publish(topic, message);
265
	}
266
267
	/*
268
	 * @see IMqttClient#publishBlock(String, MqttMessage)
269
	 */
270
	public void publish(String topic, MqttMessage message) throws MqttException,
271
			MqttPersistenceException {
272
		aClient.publish(topic, message, null, null).waitForCompletion(getTimeToWait());
273
	}
274
	
275
	/**
276
	 * Set the maximum time to wait for an action to complete
277
	 * <p>Set the maximum time to wait for an action to complete before
278
	 * returning control to the invoking application. Control is returned
279
	 * when: 
280
	 * <ul>
281
	 * <li>the action completes 
282
	 * <li>or when the timeout if exceeded
283
	 * <li>or when the client is disconnect/shutdown
284
	 * <ul>
285
	 * The default value is -1 which means the action will not timeout.
286
	 * In the event of a timeout the action carries on running in the
287
	 * background until it completes. The timeout is used on methods that
288
	 * block while the action is in progress.
289
	 * </p>
290
	 * @param timeToWaitInMillis before the action times out. A value or 0 or -1 will wait until 
291
	 * the action finishes and not timeout. 
292
	 */
293
	public void setTimeToWait(long timeToWaitInMillis) throws IllegalArgumentException{
294
		if (timeToWait < -1) {
295
			throw new IllegalArgumentException();
296
		}
297
		this.timeToWait = timeToWaitInMillis;
298
	}
299
	
300
	/**
301
	 * Return the maximum time to wait for an action to complete. 
302
	 * @see MqttClient#setTimeToWait(long)
303
	 */
304
	public long getTimeToWait() {
305
		return this.timeToWait;
306
	}
307
308
	/* (non-Javadoc)
309
	 * @see org.eclipse.paho.client.mqttv3.IMqttClient#close()
310
	 */
311
	public void close() throws MqttException {
312
		aClient.close();
313
	}
314
315
	/* (non-Javadoc)
316
	 * @see org.eclipse.paho.client.mqttv3.IMqttClient#getClientId()
317
	 */
318
	public String getClientId() {
319
		return aClient.getClientId();
320
	}
321
322
	/* (non-Javadoc)
323
	 * @see org.eclipse.paho.client.mqttv3.IMqttClient#getPendingDeliveryTokens()
324
	 */
325
	public IMqttDeliveryToken[] getPendingDeliveryTokens() {
326
		return aClient.getPendingDeliveryTokens();
327
	}
328
329
	/* (non-Javadoc)
330
	 * @see org.eclipse.paho.client.mqttv3.IMqttClient#getServerURI()
331
	 */
332
	public String getServerURI() {
333
		return aClient.getServerURI();
334
	}
335
336
	/* (non-Javadoc)
337
	 * @see org.eclipse.paho.client.mqttv3.IMqttClient#getTopic(java.lang.String)
338
	 */
339
	public MqttTopic getTopic(String topic) {
340
		return aClient.getTopic(topic);
341
	}
342
343
	/* (non-Javadoc)
344
	 * @see org.eclipse.paho.client.mqttv3.IMqttClient#isConnected()
345
	 */
346
	public boolean isConnected() {
347
		return aClient.isConnected();
348
	}
349
350
	/* (non-Javadoc)
351
	 * @see org.eclipse.paho.client.mqttv3.IMqttClient#setCallback(org.eclipse.paho.client.mqttv3.MqttCallback)
352
	 */
353
	public void setCallback(MqttCallback callback) {
354
		aClient.setCallback(callback);
355
	}
356
	
357
	/**
358
	 * Returns a randomly generated client identifier based on the current user's login
359
	 * name and the system time.
360
	 * <p>When cleanSession is set to false, an application must ensure it uses the 
361
	 * same client identifier when it reconnects to the server to resume state and maintain
362
	 * assured message delivery.</p>
363
	 * @return a generated client identifier
364
	 * @see MqttConnectOptions#setCleanSession(boolean)
365
	 */
366
	public static String generateClientId() {
367
		return MqttAsyncClient.generateClientId();
368
	}
369
	
370
	/**
371
	 * Return a debug object that can be used to help solve problems.
372
	 */
373
	public Debug getDebug() {
374
		return (aClient.getDebug());
375
	}
376
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttClientPersistence.java (-92 lines)
Lines 1-92 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3;
13
14
import java.util.Enumeration;
15
16
/**
17
 * Represents a persistent data store, used to store outbound and inbound messages while they
18
 * are in flight, enabling delivery to the QOS specified. You can specify an implementation
19
 * of this interface using {@link MqttClient#MqttClient(String, String, MqttClientPersistence)},
20
 * which the {@link MqttClient} will use to persist QoS 1 and 2 messages.
21
 * <p>
22
 * If the methods defined throw the MqttPersistenceException then the state of the data persisted
23
 * should remain as prior to the method being called. For example, if {@link #put(String, MqttPersistable)}
24
 * throws an exception at any point then the data will be assumed to not be in the persistent store.
25
 * Similarly if {@link #remove(String)} throws an exception then the data will be
26
 * assumed to still be held in the persistent store.</p>
27
 * <p>
28
 * It is up to the persistence interface to log any exceptions or error information 
29
 * which may be required when diagnosing a persistence failure.</p>
30
 */
31
public interface MqttClientPersistence {
32
	/**
33
	 * Initialise the persistent store.
34
	 * If a persistent store exists for this client ID then open it, otherwise 
35
	 * create a new one. If the persistent store is already open then just return.
36
	 * An application may use the same client ID to connect to many different 
37
	 * servers, so the client ID in conjunction with the
38
	 * connection will uniquely identify the persistence store required.
39
	 * 
40
	 * @param clientId The client for which the persistent store should be opened.
41
	 * @param serverURI The connection string as specified when the MQTT client instance was created.
42
	 * @throws MqttPersistenceException if there was a problem opening the persistent store.
43
	 */
44
	public void open(String clientId, String serverURI) throws MqttPersistenceException;
45
46
	/**
47
	 * Close the persistent store that was previously opened.
48
	 * This will be called when a client application disconnects from the broker.
49
	 * @throws MqttPersistenceException 
50
	 */	
51
	public void close() throws MqttPersistenceException;
52
53
	/**
54
	 * Puts the specified data into the persistent store.
55
	 * @param key the key for the data, which will be used later to retrieve it.
56
	 * @param persistable the data to persist
57
	 * @throws MqttPersistenceException if there was a problem putting the data
58
	 * into the persistent store.
59
	 */
60
	public void put(String key, MqttPersistable persistable) throws MqttPersistenceException;
61
	
62
	/**
63
	 * Gets the specified data out of the persistent store.
64
	 * @param key the key for the data, which was used when originally saving it.
65
	 * @return the un-persisted data
66
	 * @throws MqttPersistenceException if there was a problem getting the data
67
	 * from the persistent store.
68
	 */
69
	public MqttPersistable get(String key) throws MqttPersistenceException;
70
	
71
	/**
72
	 * Remove the data for the specified key.
73
	 */
74
	public void remove(String key) throws MqttPersistenceException;
75
76
	/**
77
	 * Returns an Enumeration over the keys in this persistent data store.
78
	 * @return an enumeration of {@link String} objects.
79
	 */
80
	public Enumeration keys() throws MqttPersistenceException;
81
	
82
	/**
83
	 * Clears persistence, so that it no longer contains any persisted data.
84
	 */
85
	public void clear() throws MqttPersistenceException;
86
	
87
	/**
88
	 * Returns whether or not data is persisted using the specified key.
89
	 * @param key the key for data, which was used when originally saving it.
90
	 */
91
	public boolean containsKey(String key) throws MqttPersistenceException;
92
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttConnectOptions.java (-366 lines)
Lines 1-366 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3;
13
14
import java.util.Properties;
15
16
import javax.net.SocketFactory;
17
18
import org.eclipse.paho.client.mqttv3.util.Debug;
19
20
/**
21
 * Holds the set of options that control how the client connects to a server.
22
 */
23
public class MqttConnectOptions {
24
	/**
25
	 * The default keep alive interval in seconds if one is not specified
26
	 */
27
	public static final int KEEP_ALIVE_INTERVAL_DEFAULT = 60;
28
	/**
29
	 * The default connection timeout in seconds if one is not specified
30
	 */
31
	public static final int CONNECTION_TIMEOUT_DEFAULT = 30;
32
	/**
33
	 * The default clean session setting if one is not specified
34
	 */
35
	public static final boolean CLEAN_SESSION_DEFAULT = true;
36
	
37
	private int keepAliveInterval = KEEP_ALIVE_INTERVAL_DEFAULT;
38
	private String willDestination = null;
39
	private MqttMessage willMessage = null;
40
	private String userName;
41
	private char[] password;
42
	private SocketFactory socketFactory;
43
	private Properties sslClientProps = null;
44
	private boolean cleanSession = CLEAN_SESSION_DEFAULT;
45
	private int connectionTimeout = CONNECTION_TIMEOUT_DEFAULT;
46
	
47
	/**
48
	 * Constructs a new <code>MqttConnectOptions</code> object using the 
49
	 * default values.
50
	 * 
51
	 * The defaults are:
52
	 * <ul>
53
	 * <li>The keepalive interval is 60 seconds</li>
54
	 * <li>Clean Session is true</li>
55
	 * <li>The message delivery retry interval is 15 seconds</li>
56
	 * <li>The connection timeout period is 30 seconds</li> 
57
	 * <li>No Will message is set</li>
58
	 * <li>A standard SocketFactory is used</li>
59
	 * </ul>
60
	 * More information about these values can be found in the setter methods. 
61
	 */
62
	public MqttConnectOptions() {
63
	}
64
	
65
	/**
66
	 * Returns the password to use for the connection.
67
	 * @return the password to use for the connection.
68
	 */
69
	public char[] getPassword() {
70
		return password;
71
	}
72
73
	/**
74
	 * Sets the password to use for the connection.
75
	 */
76
	public void setPassword(char[] password) {
77
		this.password = password;
78
	}
79
80
	/**
81
	 * Returns the user name to use for the connection.
82
	 * @return the user name to use for the connection.
83
	 */
84
	public String getUserName() {
85
		return userName;
86
	}
87
88
	/**
89
	 * Sets the user name to use for the connection.
90
	 * @throws IllegalArgumentException if the user name is blank or only
91
	 * contains whitespace characters.
92
	 */
93
	public void setUserName(String userName) {
94
		if ((userName != null) && (userName.trim().equals(""))) {
95
			throw new IllegalArgumentException();
96
		}
97
		this.userName = userName;
98
	}
99
	
100
	/**
101
	 * Sets the "Last Will and Testament" (LWT) for the connection.
102
	 * In the event that this client unexpectedly loses its connection to the 
103
	 * server, the server will publish a message to itself using the supplied
104
	 * details.
105
	 * 
106
	 * @param topic the topic to publish to.
107
	 * @param payload the byte payload for the message.
108
	 * @param qos the quality of service to publish the message at (0, 1 or 2).
109
	 * @param retained whether or not the message should be retained.
110
	 */
111
	public void setWill(MqttTopic topic, byte[] payload, int qos, boolean retained) {
112
		String topicS = topic.getName();
113
		validateWill(topicS, payload);
114
		this.setWill(topicS, new MqttMessage(payload), qos, retained);
115
	}
116
	
117
	/**
118
	 * Sets the "Last Will and Testament" (LWT) for the connection.
119
	 * In the event that this client unexpectedly loses its connection to the 
120
	 * server, the server will publish a message to itself using the supplied
121
	 * details.
122
	 * 
123
	 * @param topic the topic to publish to.
124
	 * @param payload the byte payload for the message.
125
	 * @param qos the quality of service to publish the message at (0, 1 or 2).
126
	 * @param retained whether or not the message should be retained.
127
	 */
128
	public void setWill(String topic, byte[] payload, int qos, boolean retained) {
129
		validateWill(topic, payload);
130
		this.setWill(topic, new MqttMessage(payload), qos, retained);
131
	}
132
	
133
	
134
	/**
135
	 * Validates the will fields.
136
	 */
137
	private void validateWill(String dest, Object payload) {
138
		if ((dest == null) || (payload == null)) {
139
			throw new IllegalArgumentException();
140
		}
141
		MqttAsyncClient.validateTopic(dest);
142
	}
143
144
	/**
145
	 * Sets up the will information, based on the supplied parameters.
146
	 */
147
	protected void setWill(String topic, MqttMessage msg, int qos, boolean retained) {
148
		willDestination = topic;
149
		willMessage = msg;
150
		willMessage.setQos(qos);
151
		willMessage.setRetained(retained);
152
		// Prevent any more changes to the will message
153
		willMessage.setMutable(false);
154
	}
155
	
156
	/**
157
	 * Returns the "keep alive" interval.
158
	 * @see #setKeepAliveInterval(int)
159
	 * @return the keep alive interval.
160
	 */
161
	public int getKeepAliveInterval() {
162
		return keepAliveInterval;
163
	}
164
165
	/**
166
	 * Sets the "keep alive" interval.
167
	 * This value, measured in seconds, defines the maximum time interval 
168
	 * between messages sent or received. It enables the client to 
169
	 * detect if the server is no longer available, without 
170
	 * having to wait for the TCP/IP timeout. The client will ensure
171
	 * that at least one message travels across the network within each
172
	 * keep alive period.  In the absence of a data-related message during 
173
	 * the time period, the client sends a very small "ping" message, which
174
	 * the server will acknowledge.
175
	 * A value of 0 disables keepalive processing in the client. 
176
	 * <p>The default value is 60 seconds</p>
177
	 * 
178
	 * @param keepAliveInterval the interval, measured in seconds, must be >= 0.
179
	 */
180
	public void setKeepAliveInterval(int keepAliveInterval)throws IllegalArgumentException {
181
		if (keepAliveInterval <0 ) {
182
			throw new IllegalArgumentException();
183
		}
184
		this.keepAliveInterval = keepAliveInterval;
185
	}
186
	
187
	/**
188
	 * Returns the connection timeout value.
189
	 * @see #setConnectionTimeout(int)
190
	 * @return the connection timeout value.
191
	 */
192
	public int getConnectionTimeout() {
193
		return connectionTimeout;
194
	}
195
	
196
	/**
197
	 * Sets the connection timeout value.
198
	 * This value, measured in seconds, defines the maximum time interval
199
	 * the client will wait for the network connection to the MQTT server to be established. 
200
	 * The default timeout is 30 seconds.
201
	 * @param connectionTimeout the timeout value, measured in seconds.
202
	 */
203
	public void setConnectionTimeout(int connectionTimeout) {
204
		this.connectionTimeout = connectionTimeout;
205
	}
206
	
207
	/**
208
	 * Returns the socket factory that will be used when connecting, or
209
	 * <code>null</code> if one has not been set.
210
	 */
211
	public SocketFactory getSocketFactory() {
212
		return socketFactory;
213
	}
214
	
215
	/**
216
	 * Sets the <code>SocketFactory</code> to use.  This allows an application
217
	 * to apply its own policies around the creation of network sockets.  If
218
	 * using an SSL connection, an <code>SSLSocketFactory</code> can be used
219
	 * to supply application-specific security settings.
220
	 * @param socketFactory the factory to use.
221
	 */
222
	public void setSocketFactory(SocketFactory socketFactory) {
223
		this.socketFactory = socketFactory;
224
	}
225
226
	/**
227
	 * Returns the topic to be used for last will and testament (LWT).
228
	 * @return the MqttTopic to use, or <code>null</code> if LWT is not set.
229
	 * @see #setWill(MqttTopic, byte[], int, boolean)
230
	 */
231
	public String getWillDestination() {
232
		return willDestination;
233
	}
234
235
	/**
236
	 * Returns the message to be sent as last will and testament (LWT).
237
	 * The returned object is "read only".  Calling any "setter" methods on 
238
	 * the returned object will result in an 
239
	 * <code>IllegalStateException</code> being thrown.
240
	 * @return the message to use, or <code>null</code> if LWT is not set.
241
	 */
242
	public MqttMessage getWillMessage() {
243
		return willMessage;
244
	}
245
	
246
	/**
247
	 * Returns the SSL properties for the connection.
248
	 * @return the properties for the SSL connection
249
	 */
250
	public Properties getSSLProperties() {
251
		return sslClientProps;
252
	}
253
254
	/**
255
	 * Sets the SSL properties for the connection.  Note that these
256
	 * properties are only valid if an implementation of the Java
257
	 * Secure Socket Extensions (JSSE) is available.  These properties are
258
	 * <em>not</em> used if a SocketFactory has been set using
259
	 * {@link #setSocketFactory(SocketFactory)}.
260
	 * The following properties can be used:</p>
261
	 * <dl>
262
	 * <dt>com.ibm.ssl.protocol</dt>
263
   	 * <dd>One of: SSL, SSLv3, TLS, TLSv1, SSL_TLS.</dd>
264
	 * <dt>com.ibm.ssl.contextProvider
265
   	 * <dd>Underlying JSSE provider.  For example "IBMJSSE2" or "SunJSSE"</dd>
266
	 * 
267
	 * <dt>com.ibm.ssl.keyStore</dt>
268
   	 * <dd>The name of the file that contains the KeyStore object that you 
269
   	 * want the KeyManager to use. For example /mydir/etc/key.p12</dd>
270
	 * 
271
	 * <dt>com.ibm.ssl.keyStorePassword</dt>
272
   	 * <dd>The password for the KeyStore object that you want the KeyManager to use.  
273
   	 * The password can either be in plain-text, 
274
   	 * or may be obfuscated using the static method:
275
     * <code>com.ibm.micro.security.Password.obfuscate(char[] password)</code>.
276
   	 * This obfuscates the password using a simple and insecure XOR and Base64 
277
   	 * encoding mechanism. Note that this is only a simple scrambler to 
278
   	 * obfuscate clear-text passwords.</dd>
279
	 * 
280
	 * <dt>com.ibm.ssl.keyStoreType</dt>
281
   	 * <dd>Type of key store, for example "PKCS12", "JKS", or "JCEKS".</dd>
282
	 * 
283
	 * <dt>com.ibm.ssl.keyStoreProvider</dt>
284
   	 * <dd>Key store provider, for example "IBMJCE" or "IBMJCEFIPS".</dd>
285
	 * 
286
	 * <dt>com.ibm.ssl.trustStore</dt>
287
   	 * <dd>The name of the file that contains the KeyStore object that you 
288
   	 * want the TrustManager to use.</dd> 
289
	 * 
290
	 * <dt>com.ibm.ssl.trustStorePassword</dt>
291
   	 * <dd>The password for the TrustStore object that you want the 
292
   	 * TrustManager to use.  The password can either be in plain-text, 
293
   	 * or may be obfuscated using the static method:
294
     * <code>com.ibm.micro.security.Password.obfuscate(char[] password)</code>.
295
   	 * This obfuscates the password using a simple and insecure XOR and Base64 
296
   	 * encoding mechanism. Note that this is only a simple scrambler to 
297
   	 * obfuscate clear-text passwords.</dd>
298
	 * 
299
	 * <dt>com.ibm.ssl.trustStoreType</dt>
300
   	 * <dd>The type of KeyStore object that you want the default TrustManager to use.  
301
   	 * Same possible values as "keyStoreType".</dd>
302
	 * 
303
	 * <dt>com.ibm.ssl.trustStoreProvider</dt>
304
   	 * <dd>Trust store provider, for example "IBMJCE" or "IBMJCEFIPS".</dd>
305
	 * 
306
	 * <dt>com.ibm.ssl.enabledCipherSuites</dt>
307
	 * <dd>A list of which ciphers are enabled.  Values are dependent on the provider, 
308
	 * for example: SSL_RSA_WITH_AES_128_CBC_SHA;SSL_RSA_WITH_3DES_EDE_CBC_SHA.</dd>
309
	 * 
310
	 * <dt>com.ibm.ssl.keyManager</dt>
311
	 * <dd>Sets the algorithm that will be used to instantiate a KeyManagerFactory object 
312
	 * instead of using the default algorithm available in the platform. Example values: 
313
	 * "IbmX509" or "IBMJ9X509".
314
	 * </dd>
315
	 * 
316
	 * <dt>com.ibm.ssl.trustManager</dt>
317
	 * <dd>Sets the algorithm that will be used to instantiate a TrustManagerFactory object 
318
	 * instead of using the default algorithm available in the platform. Example values: 
319
	 * "PKIX" or "IBMJ9X509".
320
	 * </dd>
321
	 * </dl>
322
	 */
323
	public void setSSLProperties(Properties props) {
324
		this.sslClientProps = props;
325
	}
326
	
327
	/**
328
	 * Returns whether the server should remember state for the client across reconnects.
329
	 * @return the clean session flag
330
	 */
331
	public boolean isCleanSession() {
332
		return this.cleanSession;
333
	}
334
	
335
	/**
336
	 * Sets whether the server should remember state for the client across reconnects.
337
	 * This includes subscriptions and the state of any in-flight messages.
338
 	 */
339
	public void setCleanSession(boolean cleanSession) {
340
		this.cleanSession = cleanSession;
341
	}
342
	
343
	public Properties getDebug() {
344
		Properties p = new Properties();
345
		p.put("CleanSession", new Boolean(isCleanSession()));
346
		p.put("ConTimeout", new Integer(getConnectionTimeout()));
347
		p.put("KeepAliveInterval", new Integer(getKeepAliveInterval()));
348
		p.put("UserName", (getUserName()==null)?"null":getUserName());
349
		p.put("WillDestination", (getWillDestination()==null)?"null":getWillDestination());
350
		if (getSocketFactory()==null) {
351
			p.put("SocketFactory", "null");	
352
		} else {
353
			p.put("SocketFactory", getSocketFactory());
354
		}
355
		if (getSSLProperties()==null) {
356
			p.put("SSLProperties", "null");
357
		} else {
358
			p.put("SSLProperties", getSSLProperties());
359
		}
360
		return p;
361
	}
362
	
363
	public String toString() {
364
		return Debug.dumpProperties(getDebug(), "Connection options");
365
	}
366
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttDeliveryToken.java (-49 lines)
Lines 1-49 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3;
13
14
/**
15
 * Provides a mechanism to track the delivery progress of a message.
16
 * 
17
 * <p>
18
 * Used to track the the delivery progress of a message when a publish is 
19
 * executed in a non-blocking manner (run in the background)</p>
20
 *  
21
 * @see MqttToken
22
 */
23
public class MqttDeliveryToken extends MqttToken implements IMqttDeliveryToken {
24
		
25
	
26
	public MqttDeliveryToken() {
27
		super();
28
	}
29
	
30
	public MqttDeliveryToken(String logContext) {
31
		super(logContext);
32
	}
33
34
	/**
35
	 * Returns the message associated with this token.
36
	 * <p>Until the message has been delivered, the message being delivered will
37
	 * be returned. Once the message has been delivered <code>null</code> will be 
38
	 * returned.
39
	 * @return the message associated with this token or null if already delivered.
40
	 * @throws MqttException if there was a problem completing retrieving the message
41
	 */
42
	public MqttMessage getMessage() throws MqttException {
43
		return internalTok.getMessage();
44
	}
45
	
46
	protected void setMessage(MqttMessage msg) {
47
		internalTok.setMessage(msg);
48
	}
49
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttException.java (-214 lines)
Lines 1-214 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3;
13
14
import org.eclipse.paho.client.mqttv3.internal.MessageCatalog;
15
16
/**
17
 * Thrown if an error occurs communicating with the server.
18
 */
19
public class MqttException extends Exception {
20
	private static final long serialVersionUID = 300L;
21
	
22
	/** 
23
	 * Client encountered an exception.  Use the {@link #getCause()}
24
	 * method to get the underlying reason.
25
	 */
26
	public static final short REASON_CODE_CLIENT_EXCEPTION              = 0x00;
27
28
	// CONNACK return codes
29
	/** The protocol version requested is not supported by the server. */
30
	public static final short REASON_CODE_INVALID_PROTOCOL_VERSION		= 0x01;
31
	/** The server has rejected the supplied client ID */
32
	public static final short REASON_CODE_INVALID_CLIENT_ID      		= 0x02;
33
	/** The broker was not available to handle the request. */
34
	public static final short REASON_CODE_BROKER_UNAVAILABLE             = 0x03;
35
	/** Authentication with the server has failed, due to a bad user name or password. */
36
	public static final short REASON_CODE_FAILED_AUTHENTICATION			= 0x04; 
37
	/** Not authorized to perform the requested operation */
38
	public static final short REASON_CODE_NOT_AUTHORIZED				= 0x05;
39
40
	/** An unexpected error has occurred. */
41
	public static final short REASON_CODE_UNEXPECTED_ERROR				= 0x06;
42
	
43
	/** 
44
	 * Client timed out while waiting for a response from the server.
45
	 * The server is no longer responding to keep-alive messages.
46
	 */
47
	public static final short REASON_CODE_CLIENT_TIMEOUT                = 32000;
48
	/**
49
	 * Internal error, caused by no new message IDs being available.
50
	 */
51
	public static final short REASON_CODE_NO_MESSAGE_IDS_AVAILABLE      = 32001;
52
	
53
	/**
54
	 * The client is already connected.
55
	 */
56
	public static final short REASON_CODE_CLIENT_CONNECTED      = 32100;
57
	/**
58
	 * The client is already disconnected.
59
	 */
60
	public static final short REASON_CODE_CLIENT_ALREADY_DISCONNECTED   = 32101;
61
	/** 
62
	 * The client is currently disconnecting and cannot accept any new work.
63
	 * This can occur when waiting on a token, and then disconnecting the client.  
64
	 * If the message delivery does not complete within the quiesce timeout 
65
	 * period, then the waiting token will be notified with an exception.
66
	 */
67
	public static final short REASON_CODE_CLIENT_DISCONNECTING          = 32102;
68
	
69
	/** Unable to connect to server */
70
	public static final short REASON_CODE_SERVER_CONNECT_ERROR          = 32103;
71
72
	/** 
73
	 * The client is not connected to the server.  The {@link MqttClient#connect()}
74
	 * or {@link MqttClient#connect(MqttConnectOptions)} method must be called
75
	 * first.  It is also possible that the connection was lost - see 
76
	 * {@link MqttClient#setCallback(MqttCallback)} for a way to track lost
77
	 * connections.  
78
	 */
79
	public static final short REASON_CODE_CLIENT_NOT_CONNECTED          = 32104;
80
81
	/** 
82
	 * Server URI and supplied <code>SocketFactory</code> do not match.
83
	 * URIs beginning <code>tcp://</code> must use a <code>javax.net.SocketFactory</code>,
84
	 * and URIs beginning <code>ssl://</code> must use a <code>javax.net.ssl.SSLSocketFactory</code>.
85
	 */
86
	public static final short REASON_CODE_SOCKET_FACTORY_MISMATCH       = 32105;
87
	
88
	/**
89
	 * SSL configuration error.
90
	 */
91
	public static final short REASON_CODE_SSL_CONFIG_ERROR              = 32106;
92
93
	/** 
94
	 * Thrown when an attempt to call {@link MqttClient#disconnect()} has been 
95
	 * made from within a method on {@link MqttCallback}.  These methods are invoked
96
	 * by the client's thread, and must not be used to control disconnection.
97
	 * 
98
	 * @see MqttCallback#messageArrived(String, MqttMessage)
99
	 */
100
	public static final short REASON_CODE_CLIENT_DISCONNECT_PROHIBITED  = 32107;
101
102
	/** 
103
	 * Protocol error: the message was not recognized as a valid MQTT packet.
104
	 * Possible reasons for this include connecting to a non-MQTT server, or
105
	 * connecting to an SSL server port when the client isn't using SSL.
106
	 */
107
	public static final short REASON_CODE_INVALID_MESSAGE				= 32108;
108
109
	/**
110
	 * The client has been unexpectedly disconnected from the server. The {@link #getCause() cause}
111
	 * will provide more details. 
112
	 */
113
	public static final short REASON_CODE_CONNECTION_LOST               = 32109;
114
	
115
	/**
116
	 * A connect operation in already in progress, only one connect can happen
117
	 * at a time.
118
	 */
119
	public static final short REASON_CODE_CONNECT_IN_PROGRESS           = 32110;
120
	
121
	/**
122
	 * The client is closed - no operations are permitted on the client in this
123
	 * state.  New up a new client to continue.
124
	 */
125
	public static final short REASON_CODE_CLIENT_CLOSED		           = 32111;
126
	
127
	/**
128
	 * A request has been made to use a token that is already associated with
129
	 * another action.  If the action is complete the reset() can ve called on the
130
	 * token to allow it to be reused.  
131
	 */
132
	public static final short REASON_CODE_TOKEN_INUSE		           = 32201;
133
	
134
	/**
135
	 * A request has been made to send a message but the maximum number of inflight 
136
	 * messages has already been reached. Once one or more messages have been moved
137
	 * then new messages can be sent.   
138
	 */
139
	public static final short REASON_CODE_MAX_INFLIGHT    			= 32202;
140
141
	private int reasonCode;
142
	private Throwable cause;
143
	
144
	/**
145
	 * Constructs a new <code>MqttException</code> with the specified code
146
	 * as the underlying reason.
147
	 * @param reasonCode the reason code for the exception.
148
	 */
149
	public MqttException(int reasonCode) {
150
		super();
151
		this.reasonCode = reasonCode;
152
	}
153
	
154
	/**
155
	 * Constructs a new <code>MqttException</code> with the specified 
156
	 * <code>Throwable</code> as the underlying reason.
157
	 * @param cause the underlying cause of the exception.
158
	 */
159
	public MqttException(Throwable cause) {
160
		super();
161
		this.reasonCode = REASON_CODE_CLIENT_EXCEPTION;
162
		this.cause = cause;
163
	}
164
165
	/**
166
	 * Constructs a new <code>MqttException</code> with the specified 
167
	 * <code>Throwable</code> as the underlying reason.
168
	 * @param reason the reason code for the exception.
169
	 * @param cause the underlying cause of the exception.
170
	 */
171
	public MqttException(int reason, Throwable cause) {
172
		super();
173
		this.reasonCode = reason;
174
		this.cause = cause;
175
	}
176
177
	
178
	/**
179
	 * Returns the reason code for this exception.
180
	 * @return the code representing the reason for this exception.
181
	 */
182
	public int getReasonCode() {
183
		return reasonCode;
184
	}
185
	
186
	/**
187
	 * Returns the underlying cause of this exception, if available.
188
	 * @return the Throwable that was the root cause of this exception,
189
	 * which may be <code>null</code>.
190
	 */
191
	public Throwable getCause() {
192
		return cause;
193
	}
194
	
195
	/**
196
	 * Returns the detail message for this exception.
197
	 * @return the detail message, which may be <code>null</code>.
198
	 */
199
	public String getMessage() {
200
		return MessageCatalog.getMessage(reasonCode);
201
	}
202
	
203
	/**
204
	 * Returns a <code>String</code> representation of this exception.
205
	 * @return a <code>String</code> representation of this exception.
206
	 */
207
	public String toString() {
208
		String result = getMessage() + " (" + reasonCode + ")";
209
		if (cause != null) {
210
			result = result + " - " + cause.toString();
211
		}
212
		return result;
213
	}
214
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttMessage.java (-215 lines)
Lines 1-215 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3;
13
14
/**
15
 * An MQTT message holds the application payload and options 
16
 * specifying how the message is to be delivered  
17
 * The message includes a "payload" (the body of the message)
18
 * represented as a byte[].
19
 */
20
public class MqttMessage {
21
	
22
	private boolean mutable = true;
23
	private byte[] payload;
24
	private int qos = 1;
25
	private boolean retained = false;
26
	private boolean dup = false;
27
28
	/**
29
	 * Utility method to validate the supplied QoS value.
30
	 * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2.
31
	 */
32
	public static void validateQos(int qos) {
33
		if ((qos < 0) || (qos > 2)) {
34
			throw new IllegalArgumentException();
35
		}
36
	}
37
	
38
	/**
39
	 * Constructs a message with an empty payload, and all other values
40
	 * set to defaults.
41
	 * 
42
	 * The defaults are:
43
	 * <ul>
44
	 *    <li>Message QoS set to 1</li>
45
	 *    <li>Message will not be "retained" by the server</li>
46
	 * </ul>
47
	 */
48
	public MqttMessage() {
49
		setPayload(new byte[]{});
50
	}
51
52
	/**
53
	 * Constructs a message with the specified byte array as a payload,
54
	 * and all other values set to defaults.
55
	 */
56
	public MqttMessage(byte[] payload) {
57
		setPayload(payload);
58
	}
59
	
60
	/**
61
	 * Returns the payload as a byte array.
62
	 * 
63
	 * @return the payload as a byte array.
64
	 */
65
	public byte[] getPayload() {
66
		return payload;
67
	}
68
	
69
	/**
70
	 * Clears the payload, resetting it to be empty.
71
	 * @throws IllegalStateException if this message cannot be edited
72
	 */
73
	public void clearPayload() {
74
		checkMutable();
75
		this.payload = new byte[] {};
76
	}
77
	
78
	/**
79
	 * Sets the payload of this message to be the specified byte array.
80
	 * 
81
	 * @param payload the payload for this message.
82
	 * @throws IllegalStateException if this message cannot be edited
83
	 * @throws NullPointerException if no payload is provided
84
	 */
85
	public void setPayload(byte[] payload) {
86
		checkMutable();
87
		if (payload == null) {
88
			throw new NullPointerException();
89
		}
90
		this.payload = payload;
91
	}
92
	
93
	/**
94
	 * Returns whether or not this message should be/was retained by the server.
95
	 * For messages received from the server, this method returns whether or not
96
	 * the message was from a current publisher, or was "retained" by the server as
97
	 * the last message published on the topic. 
98
	 * 
99
	 * @return <code>true</code> if the message should be, or was, retained by
100
	 * the server.
101
	 * @see #setRetained(boolean)
102
	 */
103
	public boolean isRetained() {
104
		return retained;
105
	}
106
107
	/**
108
	 * Whether or not the publish message should be retained by the messaging engine.
109
	 * Sending a message with the retained set to <code>false</code> will clear the
110
	 * retained message from the server.  The default value is <code>false</code>
111
	 * 
112
	 * @param retained whether or not the messaging engine should retain the message.
113
	 * @throws IllegalStateException if this message cannot be edited
114
	 */
115
	public void setRetained(boolean retained) {
116
		checkMutable();
117
		this.retained = retained;
118
	}
119
120
	/**
121
	 * Returns the quality of service for this message.
122
	 * @return the quality of service to use, either 0, 1, or 2.
123
	 * @see #setQos(int)
124
	 */
125
	public int getQos() {
126
		return qos;
127
	}
128
129
	/**
130
	 * Sets the quality of service for this message.
131
	 * <ul>
132
	 * <li>Quality of service 0 - indicates that a message should 
133
	 * be delivered at most once (zero or one times).  The message will not be persisted to disk,
134
	 * and will not be acknowledged across the network.  This QoS is the fastest,
135
	 * but should only be used for messages which are not valuable - note that  
136
	 * if the server cannot process the message (for example, there
137
	 * is an authorization problem), then an 
138
	 * {@link MqttCallback#deliveryComplete(IMqttDeliveryToken)}. 
139
	 * Also known as "fire and forget".</li>
140
	 * 
141
	 * <li>Quality of service 1 - indicates that a message should 
142
	 * be delivered at least once (one or more times).  The message can only be delivered safely if
143
	 * it can be persisted, so the application must supply a means of
144
	 * persistence using <code>MqttConnectOptions</code>.
145
	 * If a persistence mechanism is not specified, the message will not be 
146
	 * delivered in the event of a client failure.
147
	 * The message will be acknowledged across the network.  
148
	 * This is the default QoS.</li>
149
	 * 
150
	 * <li>Quality of service 2 - indicates that a message should
151
	 * be delivered once.  The message will be persisted to disk, and will
152
	 * be subject to a two-phase acknowledgement across the network.
153
	 * The message can only be delivered safely if
154
	 * it can be persisted, so the application must supply a means of
155
	 * persistence using <code>MqttConnectOptions</code>.
156
	 * If a persistence mechanism is not specified, the message will not be 
157
	 * delivered in the event of a client failure.</li>
158
	 * 
159
	 * If persistence is not configured, QOS 1 and 2 messages will still be delivered
160
	 * in the event of a network or server problem as the client will hold state in memory.
161
	 * If the MQTT client is shutdown or fails and persistence is not configured then 
162
	 * delivery of QOS 1 and 2 messages can not be maintained as client side state will 
163
	 * be lost. 
164
	 * 
165
	 * @param qos the "quality of service" to use.  Set to 0, 1, 2.
166
	 * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2.
167
	 * @throws IllegalStateException if this message cannot be edited
168
	 */
169
	public void setQos(int qos) {
170
		checkMutable();
171
		validateQos(qos);
172
		this.qos = qos;
173
	}
174
175
	/**
176
	 * Returns a string representation of this messages payload.
177
	 * Makes an attempt to return the payload as a string. As the
178
	 * MQTT client has no control over the content of the payload 
179
	 * it may fail. 
180
	 * @return a string representation of this message.
181
	 */
182
	public String toString() {
183
		return new String(payload);
184
	}
185
	
186
	/**
187
	 * Sets the mutability of this object (whether or not its values can be
188
	 * changed.
189
	 * @param mutable <code>true</code> if the values can be changed,
190
	 * <code>false</code> to prevent them from being changed.
191
	 */
192
	protected void setMutable(boolean mutable) {
193
		this.mutable = mutable;
194
	}
195
	
196
	protected void checkMutable() throws IllegalStateException {
197
		if (!mutable) {
198
			throw new IllegalStateException();
199
		}
200
	}
201
	
202
	protected void setDuplicate(boolean dup) {
203
		this.dup = dup;
204
	}
205
	
206
	/**
207
	 * Returns whether or not this message might be a duplicate of one which has
208
	 * already been received.  This will only be set on messages received from
209
	 * the server.
210
	 * @return <code>true</code> if the message might be a duplicate.
211
	 */
212
	public boolean isDuplicate() {
213
		return this.dup;
214
	}
215
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttPersistable.java (-93 lines)
Lines 1-93 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3;
13
14
/**
15
 * Represents an object used to pass data to be persisted across the
16
 * {@link org.eclipse.paho.client.mqttv3.MqttClientPersistence MqttClientPersistence}
17
 * interface.
18
 * <p>
19
 * When data is passed across the interface the header and payload are 
20
 * separated, so that unnecessary message copies may be avoided.
21
 * For example, if a 10 MB payload was published it would be inefficient
22
 * to create a byte array a few bytes larger than 10 MB and copy the
23
 * MQTT message header and payload into a contiguous byte array.</p>
24
 * <p>
25
 * When the request to persist data is made a separate byte array and offset
26
 * is passed for the header and payload. Only the data between
27
 * offset and length need be persisted.
28
 * So for example, a message to be persisted consists of a header byte
29
 * array starting at offset 1 and length 4, plus a payload byte array
30
 * starting at offset 30 and length 40000. There are three ways in which
31
 * the persistence implementation may return data to the client on
32
 * recovery:
33
 * <ul>
34
 * <li>It could return the data as it was passed in
35
 * originally, with the same byte arrays and offsets.</li>
36
 * <li>It could safely just persist and return the bytes from the offset
37
 * for the specified length. For example, return a header byte array with
38
 * offset 0 and length 4, plus a payload byte array with offset 0 and length
39
 * 40000</li>
40
 * <li>It could return the header and payload as a contiguous byte array
41
 * with the header bytes preceeding the payload. The contiguous byte array
42
 * should be set as the header byte array, with the payload byte array being
43
 * null. For example, return a single byte array with offset 0 
44
 * and length 40004.
45
 * This is useful when recovering from a file where the header and payload
46
 * could be written as a contiguous stream of bytes.</li>
47
 * </ul>
48
 * </p>  
49
 */
50
public interface MqttPersistable {
51
52
	/**
53
	 * Returns the header bytes in an array.
54
	 * The bytes start at {@link #getHeaderOffset()}
55
	 * and continue for {@link #getHeaderLength()}.
56
	 * @return the header bytes. 
57
	 */
58
	public byte[] getHeaderBytes() throws MqttPersistenceException;
59
60
	/**
61
	 * Returns the length of the header.
62
	 * @return the header length
63
	 */
64
	public int getHeaderLength() throws MqttPersistenceException;
65
66
	/**
67
	 * Returns the offset of the header within the byte array returned by {@link #getHeaderBytes()}.
68
	 * @return the header offset.
69
	 * 
70
	 */
71
	public int getHeaderOffset() throws MqttPersistenceException;
72
73
	/**
74
	 * Returns the payload bytes in an array.
75
	 * The bytes start at {@link #getPayloadOffset()}
76
	 * and continue for {@link #getPayloadLength()}.
77
	 * @return the payload bytes.  
78
	 */
79
	public byte[] getPayloadBytes() throws MqttPersistenceException;
80
81
	/**
82
	 * Returns the length of the payload.
83
	 * @return the payload length.
84
	 */
85
	public int getPayloadLength() throws MqttPersistenceException;
86
87
	/**
88
	 * Returns the offset of the payload within the byte array returned by {@link #getPayloadBytes()}.
89
	 * @return the payload offset.
90
	 * 
91
	 */
92
	public int getPayloadOffset() throws MqttPersistenceException;
93
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttPersistenceException.java (-56 lines)
Lines 1-56 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3;
13
14
/**
15
 * This exception is thrown by the implementor of the persistence
16
 * interface if there is a problem reading or writing persistent data.
17
 */
18
public class MqttPersistenceException extends MqttException {
19
	private static final long serialVersionUID = 300L;
20
21
	/** Persistence is already being used by another client. */
22
	public static final short REASON_CODE_PERSISTENCE_IN_USE	= 32200;
23
	
24
	/**
25
	 * Constructs a new <code>MqttPersistenceException</code>
26
	 */
27
	public MqttPersistenceException() {
28
		super(REASON_CODE_CLIENT_EXCEPTION);
29
	}
30
	
31
	/**
32
	 * Constructs a new <code>MqttPersistenceException</code> with the specified code
33
	 * as the underlying reason.
34
	 * @param reasonCode the reason code for the exception.
35
	 */
36
	public MqttPersistenceException(int reasonCode) {
37
		super(reasonCode);
38
	}
39
	/**
40
	 * Constructs a new <code>MqttPersistenceException</code> with the specified 
41
	 * <code>Throwable</code> as the underlying reason.
42
	 * @param cause the underlying cause of the exception.
43
	 */
44
	public MqttPersistenceException(Throwable cause) {
45
		super(cause);
46
	}
47
	/**
48
	 * Constructs a new <code>MqttPersistenceException</code> with the specified 
49
	 * <code>Throwable</code> as the underlying reason.
50
	 * @param reason the reason code for the exception.
51
	 * @param cause the underlying cause of the exception.
52
	 */
53
	public MqttPersistenceException(int reason, Throwable cause) {
54
		super(reason, cause);
55
	}
56
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttSecurityException.java (-47 lines)
Lines 1-47 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3;
13
14
/**
15
 * Thrown when a client is not authorized to perform an operation, or
16
 * if there is a problem with the security configuration.
17
 */
18
public class MqttSecurityException extends MqttException {
19
	private static final long serialVersionUID = 300L;
20
21
	/**
22
	 * Constructs a new <code>MqttSecurityException</code> with the specified code
23
	 * as the underlying reason.
24
	 * @param reasonCode the reason code for the exception.
25
	 */
26
	public MqttSecurityException(int reasonCode) {
27
		super(reasonCode);
28
	}
29
30
	/**
31
	 * Constructs a new <code>MqttSecurityException</code> with the specified 
32
	 * <code>Throwable</code> as the underlying reason.
33
	 * @param cause the underlying cause of the exception.
34
	 */
35
	public MqttSecurityException(Throwable cause) {
36
		super(cause);
37
	}
38
	/**
39
	 * Constructs a new <code>MqttSecurityException</code> with the specified 
40
	 * code and <code>Throwable</code> as the underlying reason.
41
	 * @param reasonCode the reason code for the exception.
42
	 * @param cause the underlying cause of the exception.
43
	 */
44
	public MqttSecurityException(int reasonCode, Throwable cause) {
45
		super(reasonCode, cause);
46
	}
47
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttToken.java (-71 lines)
Lines 1-71 Link Here
1
package org.eclipse.paho.client.mqttv3;
2
3
import org.eclipse.paho.client.mqttv3.internal.Token;
4
5
/**
6
 *  Provides a mechanism for tracking the completion of an asynchronous action.
7
 *  <p>
8
 *  A token that implements the ImqttToken interface is returned from all non-blocking 
9
 *  method with the exception of publish. 
10
 *  </p>
11
 *  
12
 * @see IMqttToken
13
 */
14
15
public class MqttToken implements IMqttToken {
16
	/**
17
	 * A reference to the the class that provides most of the implementation of the 
18
	 * MqttToken.  MQTT application programs must not use the internal class.
19
	 */
20
	public Token internalTok = null;
21
		
22
	public MqttToken() {
23
	}
24
	
25
	public MqttToken(String logContext) {
26
		internalTok = new Token(logContext);
27
	}
28
	
29
	public MqttException getException() {
30
		return internalTok.getException();
31
	}
32
33
	public boolean isComplete() {
34
		return internalTok.isComplete();
35
	}
36
37
	public void setActionCallback(IMqttActionListener listener) {
38
		internalTok.setActionCallback(listener);
39
40
	}
41
	public IMqttActionListener getActionCallback() {
42
		return internalTok.getActionCallback();
43
	}
44
45
	public void waitForCompletion() throws MqttException {
46
		internalTok.waitForCompletion(-1);
47
	}
48
49
	public void waitForCompletion(long timeout) throws MqttException {
50
		internalTok.waitForCompletion(timeout);
51
	}
52
	
53
	public IMqttAsyncClient getClient() {
54
		return internalTok.getClient();
55
	}
56
	
57
	public String[] getTopics() {
58
		return internalTok.getTopics();
59
	}
60
61
	public Object getUserContext() {
62
		return internalTok.getUserContext();
63
	}
64
65
	public void setUserContext(Object userContext) {
66
		internalTok.setUserContext(userContext);	}
67
68
	public int getMessageId() {
69
		return internalTok.getMessageID();
70
	}
71
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/MqttTopic.java (-94 lines)
Lines 1-94 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3;
13
14
import org.eclipse.paho.client.mqttv3.internal.ClientComms;
15
import org.eclipse.paho.client.mqttv3.internal.wire.MqttPublish;
16
17
/**
18
 * Represents a topic destination, used for publish/subscribe messaging.
19
 */
20
public class MqttTopic {
21
	
22
	private ClientComms comms;
23
	private String name;
24
	
25
	public MqttTopic(String name, ClientComms comms) {
26
		this.comms = comms;
27
		this.name = name;
28
	}
29
	
30
	/**
31
	 * Publishes a message on the topic.  This is a convenience method, which will 
32
	 * create a new {@link MqttMessage} object with a byte array payload and the
33
	 * specified QoS, and then publish it.  All other values in the
34
	 * message will be set to the defaults. 
35
36
	 * @param payload the byte array to use as the payload
37
	 * @param qos the Quality of Service.  Valid values are 0, 1 or 2.
38
	 * @param retained whether or not this message should be retained by the server.
39
	 * @throws IllegalArgumentException if value of QoS is not 0, 1 or 2.
40
	 * @see #publish(MqttMessage)
41
	 * @see MqttMessage#setQos(int)
42
	 * @see MqttMessage#setRetained(boolean)
43
	 */
44
	public MqttDeliveryToken publish(byte[] payload, int qos, boolean retained) throws MqttException, MqttPersistenceException {
45
		MqttMessage message = new MqttMessage(payload);
46
		message.setQos(qos);
47
		message.setRetained(retained);
48
		return this.publish(message);
49
	}
50
	
51
	/**
52
	 * Publishes the specified message to this topic, but does not wait for delivery 
53
	 * of the message to complete. The returned {@link MqttDeliveryToken token} can be used
54
	 * to track the delivery status of the message.  Once this method has 
55
	 * returned cleanly, the message has been accepted for publication by the
56
	 * client. Message delivery will be completed in the background when a connection 
57
	 * is available.
58
	 * 
59
	 * @param message the message to publish
60
	 * @return an MqttDeliveryToken for tracking the delivery of the message
61
	 */
62
	public MqttDeliveryToken publish(MqttMessage message) throws MqttException, MqttPersistenceException {
63
		MqttDeliveryToken token = new MqttDeliveryToken(comms.getClient().getClientId());
64
		token.setMessage(message);
65
		comms.sendNoWait(createPublish(message), token);
66
		token.internalTok.waitUntilSent();
67
		return token;
68
	}
69
	
70
	/**
71
	 * Returns the name of the queue or topic.
72
	 * 
73
	 * @return the name of this destination.
74
	 */
75
	public String getName() {
76
		return name;
77
	}
78
	
79
	/**
80
	 * Create a PUBLISH packet from the specified message.
81
	 */
82
	private MqttPublish createPublish(MqttMessage message) {
83
		return new MqttPublish(this.getName(), message);
84
	}
85
	
86
	/**
87
	 * Returns a string representation of this topic.
88
	 * @return a string representation of this topic.
89
	 */
90
	public String toString() {
91
		return getName();
92
	}
93
	
94
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/ClientComms.java (-582 lines)
Lines 1-582 Link Here
1
/* 
2
* Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal;
13
14
import java.util.Enumeration;
15
import java.util.Properties;
16
import java.util.Vector;
17
18
import org.eclipse.paho.client.mqttv3.IMqttAsyncClient;
19
import org.eclipse.paho.client.mqttv3.MqttCallback;
20
import org.eclipse.paho.client.mqttv3.MqttClientPersistence;
21
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
22
import org.eclipse.paho.client.mqttv3.MqttDeliveryToken;
23
import org.eclipse.paho.client.mqttv3.MqttException;
24
import org.eclipse.paho.client.mqttv3.MqttPersistenceException;
25
import org.eclipse.paho.client.mqttv3.MqttToken;
26
import org.eclipse.paho.client.mqttv3.MqttTopic;
27
import org.eclipse.paho.client.mqttv3.internal.wire.MqttConnack;
28
import org.eclipse.paho.client.mqttv3.internal.wire.MqttConnect;
29
import org.eclipse.paho.client.mqttv3.internal.wire.MqttDisconnect;
30
import org.eclipse.paho.client.mqttv3.internal.wire.MqttPublish;
31
import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage;
32
import org.eclipse.paho.client.mqttv3.logging.Logger;
33
import org.eclipse.paho.client.mqttv3.logging.LoggerFactory;
34
35
/**
36
 * Handles client communications with the server.  Sends and receives MQTT V3
37
 * messages. 
38
 */
39
public class ClientComms { 
40
	public static String 		VERSION = "@@VERSION@@";
41
	public static String 		BUILD_LEVEL = "@@BUILDLEVEL@@";
42
	
43
	private IMqttAsyncClient 	client;
44
	NetworkModule 				networkModule;
45
	CommsReceiver 				receiver;
46
	CommsSender 				sender;
47
	CommsCallback 				callback;
48
	ClientState	 				clientState;
49
	MqttConnectOptions			conOptions;
50
	private MqttClientPersistence persistence;
51
	CommsTokenStore 			tokenStore;
52
	boolean 					stoppingComms = false;
53
	
54
	final static byte CONNECTED		=0;
55
	final static byte CONNECTING	=1;
56
	final static byte DISCONNECTING	=2;
57
	final static byte DISCONNECTED	=3;
58
	final static byte CLOSED		=4;
59
	
60
	private byte 	conState = DISCONNECTED;
61
	Object 			conLock = new Object();  	// Used to syncrhonize connection state
62
	private boolean closePending  = false;
63
	
64
	final static String className = ClientComms.class.getName();
65
	Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className);
66
67
	
68
	/**
69
	 * Creates a new ClientComms object, using the specified module to handle
70
	 * the network calls.
71
	 */
72
	public ClientComms(IMqttAsyncClient client, MqttClientPersistence persistence) throws MqttException {
73
		this.conState = DISCONNECTED;
74
		this.client 	= client;
75
		this.persistence = persistence;
76
		this.tokenStore = new CommsTokenStore(getClient().getClientId());
77
		this.callback 	= new CommsCallback(this);
78
		this.clientState = new ClientState(persistence, tokenStore, this.callback, this);
79
  
80
		callback.setClientState(clientState);
81
		log.setResourceName(getClient().getClientId());
82
	}
83
84
	/** 
85
	 * Sends a message to the server. Does not check if connected this validation must be done
86
	 * by invoking routines.
87
	 * @param message
88
	 * @param token
89
	 * @throws MqttException
90
	 */
91
	void internalSend(MqttWireMessage message, MqttToken token) throws MqttException {
92
		final String methodName = "internalSend";
93
		//@TRACE 200=internalSend key={0} message={1} token={2} 
94
		log.fine(className, methodName, "200", new Object[]{message.getKey(), message, token});
95
		
96
		if (token.getClient() == null ) {
97
			// Associate the client with the token - also marks it as in use.
98
			token.internalTok.setClient(getClient());
99
		} else {
100
			// Token is already in use - cannot reuse
101
			//@TRACE 213=fail: token in use: key={0} message={1} token={2} 
102
			log.fine(className, methodName, "213", new Object[]{message.getKey(), message, token});
103
104
			throw new MqttException(MqttException.REASON_CODE_TOKEN_INUSE);
105
		}
106
107
		try {
108
			// Persist if needed and send the message
109
			this.clientState.send(message, token);
110
		} catch(MqttException e) {
111
			if (message instanceof MqttPublish) {
112
				this.clientState.undo((MqttPublish)message);
113
			}
114
			throw e;
115
		}
116
	}
117
118
	/**
119
	 * Sends a message to the broker if in connected state, but only waits for the message to be 
120
	 * stored, before returning.
121
	 */
122
	public void sendNoWait(MqttWireMessage message, MqttToken token) throws MqttException {
123
		final String methodName = "sendNoWait";
124
		if (isConnected() || 
125
				(!isConnected() && message instanceof MqttConnect) ||
126
				(isDisconnecting() && message instanceof MqttDisconnect)) {
127
			this.internalSend(message, token);
128
		} else {
129
			//@TRACE 208=failed: not connected
130
			log.fine(className, methodName, "208");
131
			throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_NOT_CONNECTED);
132
		}
133
	}
134
	
135
	/**
136
	 * Tidy up
137
	 * - call each main class and let it tidy up e.g. releasing the token 
138
	 *   store which normally survives a disconnect. 
139
	 * @throws MqttException  if not disconnected
140
	 */
141
	public void close() throws MqttException {
142
		final String methodName = "close";
143
		synchronized (conLock) {
144
			if (!isClosed()) {
145
				// Must be disconnected before close can take place
146
				if (!isDisconnected()) {
147
					//@TRACE 224=failed: not disconnected
148
					log.fine(className, methodName, "224");
149
	
150
					if (isConnecting()) {
151
						throw new MqttException(MqttException.REASON_CODE_CONNECT_IN_PROGRESS);
152
					} else if (isConnected()) {
153
						throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_CONNECTED);
154
					} else if (isDisconnecting()) {
155
						closePending = true; 
156
						return;
157
					} 		
158
				}
159
				
160
				conState = CLOSED;
161
				
162
				// ShutdownConnection has already cleaned most things
163
				clientState.close();
164
				clientState = null;
165
				callback = null;
166
				persistence = null;
167
				sender = null;
168
				receiver = null;
169
				networkModule = null;
170
				conOptions = null;
171
				tokenStore = null;
172
			}
173
		}
174
	}
175
	
176
	/**
177
	 * Sends a connect message and waits for an ACK or NACK.
178
	 * Connecting is a special case which will also start up the 
179
	 * network connection, receive thread, and keep alive thread.
180
	 */
181
	public void connect(MqttConnectOptions options, MqttToken token) throws MqttException {
182
		final String methodName = "connect";
183
		synchronized (conLock) {
184
			if (isDisconnected() && !closePending) {
185
				//@TRACE 214=state=CONNECTING
186
				log.fine(className,methodName,"214");
187
				
188
				conState = CONNECTING;
189
				
190
				this.conOptions = options;
191
				
192
				MqttConnect connect = new MqttConnect(client.getClientId(),
193
						options.isCleanSession(),
194
						options.getKeepAliveInterval(),
195
						options.getUserName(),
196
						options.getPassword(),
197
						options.getWillMessage(),
198
						options.getWillDestination());
199
			
200
				this.clientState.setKeepAliveSecs(options.getKeepAliveInterval());
201
				this.clientState.setCleanSession(options.isCleanSession());
202
		
203
				tokenStore.open();
204
				ConnectBG conbg = new ConnectBG(this, token, connect);
205
				conbg.start();
206
			}
207
			else {
208
				// @TRACE 207=connect failed: not disconnected {0}
209
				log.fine(className,methodName,"207", new Object[] {new Byte(conState)});
210
				if (isClosed() || closePending) {
211
					throw new MqttException(MqttException.REASON_CODE_CLIENT_CLOSED);
212
				} else if (isConnecting()) {
213
					throw new MqttException(MqttException.REASON_CODE_CONNECT_IN_PROGRESS);
214
				} else if (isDisconnecting()) {
215
					throw new MqttException(MqttException.REASON_CODE_CLIENT_DISCONNECTING); 
216
				} else {
217
					throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_CONNECTED);
218
				}
219
			}
220
		}
221
	}
222
	
223
	public void connectComplete( MqttConnack cack, MqttException mex) throws MqttException {
224
		final String methodName = "connectComplete";
225
		int rc = cack.getReturnCode();
226
		synchronized (conLock) {
227
			if (rc == 0) {
228
				// We've successfully connected
229
				// @TRACE 215=state=CONNECTED
230
				log.fine(className,methodName,"215");
231
232
				conState = CONNECTED;
233
				return;
234
			} 
235
		}
236
237
		// @TRACE 204=connect failed: rc={0}
238
		log.fine(className,methodName,"204", new Object[]{new Integer(rc)});
239
		throw mex;
240
	}
241
242
	/**
243
	 * Shuts down the connection to the server.
244
	 * This may have been invoked as a result of a user calling disconnect or 
245
	 * an abnormal disconnection.  The method may be invoked multiple times
246
	 * in parallel as each thread when it receives an error uses this method
247
	 * to ensure that shutdown completes successfully. 
248
	 */
249
	public void shutdownConnection(MqttToken token, MqttException reason) {
250
		final String methodName = "shutdownConnection";
251
		boolean wasConnected;
252
		MqttToken endToken = null; 		//Token to notify after disconnect completes
253
		
254
		// This method could concurrently be invoked from many places only allow it
255
		// to run once.
256
		synchronized(conLock) {
257
			if (stoppingComms || closePending) {
258
				return;
259
			}
260
			stoppingComms = true;
261
			
262
			//@TRACE 216=state=DISCONNECTING
263
			log.fine(className,methodName,"216");
264
			
265
			wasConnected = (isConnected() || isDisconnecting());
266
			conState = DISCONNECTING;
267
		}
268
		
269
		// Update the token with the reason for shutdown if it 
270
		// is not already complete. 
271
		if (token != null && !token.isComplete()) {
272
			token.internalTok.setException(reason);
273
		}
274
		
275
		// Stop the thread that is used to call the user back
276
		// when actions complete
277
		if (callback!= null) {callback.stop(); }
278
279
		// Stop the network module, send and receive now not possible
280
		try { 
281
			if (networkModule != null) {networkModule.stop();}
282
		}catch(Exception ioe) {
283
			// Ignore as we are shutting down
284
		}
285
		
286
		// Stop the thread that handles inbound work from the network
287
		if (receiver != null) {receiver.stop();}
288
289
		// Stop any new tokens being saved by app and throwing an exception if they do
290
		tokenStore.quiesce(new MqttException(MqttException.REASON_CODE_CLIENT_DISCONNECTING));
291
		
292
		// Notify any outstanding tokens with the exception of 
293
		// con or discon which may be returned and will be notified at 
294
		// the end 
295
		endToken = handleOldTokens(token, reason);
296
297
		try {
298
			// Clean session handling and tidy up
299
			clientState.disconnected(reason);
300
		}catch(Exception ex) {
301
			// Ignore as we are shutting down
302
		}
303
304
		if (sender != null) { sender.stop(); }
305
		
306
		try {
307
			if (persistence != null) {persistence.close();}
308
		}catch(Exception ex) {
309
			// Ignore as we are shutting down
310
		}
311
		// All disconnect logic has been completed allowing the
312
		// client to be marked as disconnected. 
313
		synchronized(conLock) {
314
			//@TRACE 217=state=DISCONNECTED
315
			log.fine(className,methodName,"217");
316
317
			conState = DISCONNECTED;
318
			stoppingComms = false;
319
		}
320
		
321
		// Internal disconnect processing has completed.  If there
322
		// is a disconnect token or a connect in error notify
323
		// it now. This is done at the end to allow a new connect
324
		// to be processed and now throw a currently disconnecting error.
325
		// any outstanding tokens and unblock any waiters
326
		if (endToken != null & callback != null) {
327
			callback.asyncOperationComplete(endToken);
328
		}
329
 
330
		if (wasConnected && callback != null) {
331
			// Let the user know client has disconnected either normally or abnormally
332
			callback.connectionLost(reason);
333
		}
334
335
		// While disconnecting, close may have been requested - try it now
336
		synchronized(conLock) {
337
			if (closePending) {
338
				try {
339
					close();
340
				} catch (Exception e) { // ignore any errors as closing
341
				}
342
			}
343
		}
344
	}
345
346
	// Tidy up. There may be tokens outstanding as the client was 
347
	// not disconnected/quiseced cleanly! Work out what tokens still 
348
	// need to be notified and waiters unblocked. Store the 
349
	// disconnect or connect token to notify after disconnect is 
350
	// complete.
351
	private MqttToken handleOldTokens(MqttToken token, MqttException reason) {
352
		final String methodName = "handleOldTokens";
353
		//@TRACE 222=>
354
		log.fine(className,methodName,"222");
355
356
		MqttToken tokToNotifyLater = null;
357
		try {
358
			// First the token that was related to the disconnect / shutdown may 
359
			// not be in the token table - temporarily add it if not
360
			if (token != null) {
361
				if (tokenStore.getToken(token.internalTok.getKey())==null) {
362
					tokenStore.saveToken(token, token.internalTok.getKey());
363
				}
364
			}
365
366
			Vector toksToNot = clientState.resolveOldTokens(reason);
367
			Enumeration toksToNotE = toksToNot.elements();
368
			while(toksToNotE.hasMoreElements()) {
369
				MqttToken tok = (MqttToken)toksToNotE.nextElement();
370
				
371
				if (tok.internalTok.getKey().equals(MqttDisconnect.KEY) ||
372
						tok.internalTok.getKey().equals(MqttConnect.KEY)) {
373
					// Its con or discon so remember and notify @ end of disc routine
374
					tokToNotifyLater = tok;
375
				} else {
376
					// notify waiters and callbacks of outstanding tokens 
377
					// that a problem has occurred and disconnect is in 
378
					// progress
379
					callback.asyncOperationComplete(tok);
380
				}
381
			}
382
		}catch(Exception ex) {
383
			// Ignore as we are shutting down
384
		}
385
		return tokToNotifyLater;
386
	}
387
	
388
	public void disconnect(MqttDisconnect disconnect, long quiesceTimeout, MqttToken token) throws MqttException {
389
		final String methodName = "disconnect";
390
		synchronized (conLock){
391
			if (isClosed()) {
392
				//@TRACE 223=failed: in closed state
393
				log.fine(className,methodName,"223");
394
				throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_CLOSED);				
395
			} else if (isDisconnected()) {
396
				//@TRACE 211=failed: already disconnected
397
				log.fine(className,methodName,"211");
398
				throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_ALREADY_DISCONNECTED);
399
			} else if (isDisconnecting()) {
400
				//@TRACE 219=failed: already disconnecting
401
				log.fine(className,methodName,"219");
402
				throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_DISCONNECTING);
403
			} else if (Thread.currentThread() == callback.getThread()) {
404
				//@TRACE 210=failed: called on callback thread
405
				log.fine(className,methodName,"210");
406
				// Not allowed to call disconnect() from the callback, as it will deadlock.
407
				throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_DISCONNECT_PROHIBITED);
408
			} 
409
			
410
			//@TRACE 218=state=DISCONNECTING
411
			log.fine(className,methodName,"218");			
412
			conState = DISCONNECTING;
413
			DisconnectBG discbg = new DisconnectBG(disconnect,quiesceTimeout,token);
414
			discbg.start();
415
		}
416
	}
417
	
418
	public boolean isConnected() {
419
		return conState == CONNECTED;
420
	}
421
	
422
	public boolean isConnecting() {
423
		return conState == CONNECTING;
424
	}
425
	public boolean isDisconnected() {
426
		return conState == DISCONNECTED;
427
	}
428
	
429
	public boolean isDisconnecting() {
430
		return conState == DISCONNECTING;
431
	}
432
	public boolean isClosed() {
433
		return conState == CLOSED;
434
	}
435
436
	
437
	public void setCallback(MqttCallback mqttCallback) {
438
		this.callback.setCallback(mqttCallback);
439
	}
440
	
441
	protected MqttTopic getTopic(String topic) {
442
		return new MqttTopic(topic, this);
443
	}
444
	public void setNetworkModule(NetworkModule networkModule) {
445
		this.networkModule = networkModule;
446
	}
447
	public MqttDeliveryToken[] getPendingDeliveryTokens() {
448
		return tokenStore.getOutstandingDelTokens();
449
	}
450
	protected void deliveryComplete(MqttPublish msg) throws MqttPersistenceException {
451
		this.clientState.deliveryComplete(msg);
452
	}
453
454
	public IMqttAsyncClient getClient() {
455
		return client;
456
	}
457
	
458
	public long getKeepAlive() {
459
		return this.clientState.getKeepAlive();
460
	}
461
	
462
	public ClientState getClientState() {
463
		return clientState;
464
	}
465
	
466
	public MqttConnectOptions getConOptions() {
467
		return conOptions;
468
	}
469
	
470
	public Properties getDebug() {
471
		Properties props = new Properties();
472
		props.put("conState", new Integer(conState));
473
		props.put("serverURI", getClient().getServerURI());
474
		props.put("callback", callback);
475
		props.put("stoppingComms", new Boolean(stoppingComms));
476
		return props;
477
	}
478
479
480
481
	// Kick off the connect processing in the background so that it does not block. For instance
482
	// the socket could take time to create.
483
	private class ConnectBG implements Runnable {
484
		ClientComms 	clientComms = null;
485
		Thread 			cBg = null;
486
		MqttToken 		conToken;
487
		MqttConnect 	conPacket;
488
	
489
		ConnectBG(ClientComms cc, MqttToken cToken, MqttConnect cPacket) {
490
			clientComms = cc;
491
			conToken 	= cToken;
492
			conPacket 	= cPacket;
493
			cBg = new Thread(this, "MQTT Con: "+getClient().getClientId());
494
		}
495
		
496
		void start() {
497
			cBg.start();
498
		}
499
500
		public void run() {
501
			final String methodName = "connectBG:run";
502
			MqttException mqttEx = null;
503
			//@TRACE 220=>
504
			log.fine(className, methodName, "220");
505
506
			try {
507
				// Reset an exception on existing delivery tokens.
508
				// This will have been set if disconnect occured before delivery was 
509
				// fully processed. 
510
				MqttDeliveryToken[] toks = tokenStore.getOutstandingDelTokens();
511
				for (int i=0; i<toks.length; i++) {
512
					toks[i].internalTok.setException(null);
513
				}
514
				
515
				// Save the conncet token in tokenStore as failure can occur before send
516
				tokenStore.saveToken(conToken,conPacket);
517
				
518
				// Connect to the server at the network level e.g. TCP socket and then
519
				// start the background processing threads before sending the connect
520
				// packet.
521
				networkModule.start();
522
				receiver = new CommsReceiver(clientComms, clientState, tokenStore, networkModule.getInputStream());
523
				receiver.start("MQTT Rec: "+getClient().getClientId());
524
				sender = new CommsSender(clientComms, clientState, tokenStore, networkModule.getOutputStream());
525
				sender.start("MQTT Snd: "+getClient().getClientId());
526
				callback.start("MQTT Call: "+getClient().getClientId());
527
528
				internalSend(conPacket, conToken);
529
			} catch (MqttException ex) {
530
				//@TRACE 212=connect failed: unexpected exception
531
				log.fine(className, methodName, "212", null, ex);
532
				mqttEx = ex;	
533
			} catch (Exception ex) {
534
				//@TRACE 209=connect failed: unexpected exception
535
				log.fine(className, methodName, "209", null, ex);
536
				mqttEx =  ExceptionHelper.createMqttException(ex);
537
			}
538
			
539
			if (mqttEx != null) {
540
				shutdownConnection(conToken, mqttEx);
541
			}			
542
		}
543
	}
544
545
	// Kick off the disconnect processing in the background so that it does not block. For instance
546
	// the quiesce 	
547
	private class DisconnectBG implements Runnable {
548
		Thread dBg = null;
549
		MqttDisconnect disconnect;
550
		long quiesceTimeout;
551
		MqttToken token;
552
		
553
		DisconnectBG(MqttDisconnect disconnect, long quiesceTimeout, MqttToken token ) {
554
			this.disconnect = disconnect;
555
			this.quiesceTimeout = quiesceTimeout;
556
			this.token = token;
557
		}
558
		
559
		void start() {
560
			dBg = new Thread(this, "MQTT Disc: "+getClient().getClientId());
561
			dBg.start();
562
		}
563
		public void run() {
564
			final String methodName = "disconnectBG:run";
565
			//@TRACE 221=>
566
			log.fine(className, methodName, "221");
567
568
			// Allow current inbound and outbound work to complete
569
			clientState.quiesce(quiesceTimeout);
570
			try {
571
				internalSend(disconnect, token);
572
				token.internalTok.waitUntilSent();
573
			}
574
			catch (MqttException ex) {
575
			}
576
			finally {
577
				token.internalTok.markComplete(null, null);
578
				shutdownConnection(token, null);
579
			}
580
		}
581
	}
582
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/ClientDefaults.java (-16 lines)
Lines 1-16 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal;
13
14
public class ClientDefaults {
15
	public static final int MAX_MSG_SIZE = 1024 * 1024 * 256; // 256 MB
16
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/ClientState.java (-1146 lines)
Lines 1-1146 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal;
13
14
import java.io.EOFException;
15
import java.util.Enumeration;
16
import java.util.Hashtable;
17
import java.util.Properties;
18
import java.util.Vector;
19
20
import org.eclipse.paho.client.mqttv3.MqttClientPersistence;
21
import org.eclipse.paho.client.mqttv3.MqttDeliveryToken;
22
import org.eclipse.paho.client.mqttv3.MqttException;
23
import org.eclipse.paho.client.mqttv3.MqttMessage;
24
import org.eclipse.paho.client.mqttv3.MqttPersistable;
25
import org.eclipse.paho.client.mqttv3.MqttPersistenceException;
26
import org.eclipse.paho.client.mqttv3.MqttToken;
27
import org.eclipse.paho.client.mqttv3.internal.wire.MqttAck;
28
import org.eclipse.paho.client.mqttv3.internal.wire.MqttConnack;
29
import org.eclipse.paho.client.mqttv3.internal.wire.MqttConnect;
30
import org.eclipse.paho.client.mqttv3.internal.wire.MqttPingReq;
31
import org.eclipse.paho.client.mqttv3.internal.wire.MqttPingResp;
32
import org.eclipse.paho.client.mqttv3.internal.wire.MqttPubAck;
33
import org.eclipse.paho.client.mqttv3.internal.wire.MqttPubComp;
34
import org.eclipse.paho.client.mqttv3.internal.wire.MqttPubRec;
35
import org.eclipse.paho.client.mqttv3.internal.wire.MqttPubRel;
36
import org.eclipse.paho.client.mqttv3.internal.wire.MqttPublish;
37
import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage;
38
import org.eclipse.paho.client.mqttv3.logging.Logger;
39
import org.eclipse.paho.client.mqttv3.logging.LoggerFactory;
40
41
/**
42
 * The core of the client, which holds the state information for pending and
43
 * in-flight messages.
44
 * 
45
 * Messages that have been accepted for delivery are moved between several objects 
46
 * while being delivered. 
47
 * 
48
 * 1) When the client is not running messages are stored in a persistent store that 
49
 * implements the MqttClientPersistent Interface. The default is MqttDefaultFilePersistencew
50
 * which stores messages safely across failures and system restarts. If no persistence
51
 * is specified there is a fall back to MemoryPersistence which will maintain the messages
52
 * while the Mqtt client is instantiated. 
53
 * 
54
 * 2) When the client or specifically ClientState is instantiated the messages are 
55
 * read from the persistent store into:
56
 * - outboundqos2 hashtable if a qos 2 publish or pubrel
57
 * - outboundqos1 hashtable if a qos 1 publish
58
 * (see restoreState)
59
 * 
60
 * 3) On Connect, copy messages from the outbound hashtables to the pendingMessages or 
61
 * pendingFlows vector in messageid order.
62
 * - Initial message publish goes onto the pendingmessages buffer. 
63
 * - Pubrel goes onto the pendingflows buffer
64
 * (see restoreInflightMessages)
65
 * 
66
 * 4) Sender thread reads messages from the pendingflows and pendingmessages buffer
67
 * one at a time.  The message is removed from the pendingbuffer but remains on the 
68
 * outbound* hashtable.  The hashtable is the place where the full set of outstanding 
69
 * messages are stored in memory. (Persistence is only used at start up)
70
 *  
71
 * 5) Receiver thread - receives wire messages: 
72
 *  - if QOS 1 then remove from persistence and outboundqos1
73
 *  - if QOS 2 pubrec send pubrel. Updating the outboundqos2 entry with the pubrel
74
 *    and update persistence.
75
 *  - if QOS 2 pubcomp remove from persistence and outboundqos2  
76
 * 
77
 * Notes:
78
 * because of the multi threaded nature of the client it is vital that any changes to this
79
 * class take concurrency into account.  For instance as soon as a flow / message is put on 
80
 * the wire it is possible for the receiving thread to receive the ack and to be processing 
81
 * the response before the sending side has finished processing.  For instance a connect may
82
 * be sent, the conack received before the connect notify send has been processed! 
83
 * 
84
 */
85
public class ClientState {
86
	private static final String PERSISTENCE_SENT_PREFIX = "s-";
87
	private static final String PERSISTENCE_CONFIRMED_PREFIX = "sc-";
88
	private static final String PERSISTENCE_RECEIVED_PREFIX = "r-";
89
	
90
	private static final int MIN_MSG_ID = 1;		// Lowest possible MQTT message ID to use
91
	private static final int MAX_MSG_ID = 65535;	// Highest possible MQTT message ID to use
92
	private int nextMsgId = MIN_MSG_ID - 1;			// The next available message ID to use
93
	private Hashtable inUseMsgIds;					// Used to store a set of in-use message IDs
94
95
	volatile private Vector pendingMessages;
96
	volatile private Vector pendingFlows;
97
	
98
	private CommsTokenStore tokenStore;
99
	private ClientComms clientComms = null;
100
	private CommsCallback callback = null;
101
	private long keepAlive;
102
	private boolean cleanSession;
103
	private MqttClientPersistence persistence;
104
	
105
	private int maxInflight = 10;	
106
	private int actualInFlight = 0;
107
	private int inFlightPubRels = 0;
108
	
109
	private Object queueLock = new Object();
110
	private Object quiesceLock = new Object();
111
	private boolean quiescing = false;
112
	
113
	private long lastOutboundActivity = 0;
114
	private long lastInboundActivity = 0;
115
	private long lastPing = 0;
116
	private MqttWireMessage pingCommand;
117
	private boolean pingOutstanding = false;
118
119
	private boolean connected = false;
120
	
121
	private Hashtable outboundQoS2 = null;
122
	private Hashtable outboundQoS1 = null;
123
	private Hashtable inboundQoS2 = null;
124
	
125
	private final static String className = ClientState.class.getName();
126
	private Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className); 
127
128
	protected ClientState(MqttClientPersistence persistence, CommsTokenStore tokenStore, 
129
			CommsCallback callback, ClientComms clientComms) throws MqttException {
130
		
131
		log.setResourceName(clientComms.getClient().getClientId());
132
		log.finer(className, "<Init>", "" );
133
134
		inUseMsgIds = new Hashtable();
135
		pendingMessages = new Vector(this.maxInflight);
136
		pendingFlows = new Vector();
137
		outboundQoS2 = new Hashtable();
138
		outboundQoS1 = new Hashtable();
139
		inboundQoS2 = new Hashtable();
140
		pingCommand = new MqttPingReq();
141
		inFlightPubRels = 0;
142
		actualInFlight = 0;
143
		
144
		this.persistence = persistence;
145
		this.callback = callback;
146
		this.tokenStore = tokenStore;
147
		this.clientComms = clientComms;		
148
		
149
		restoreState();
150
	}
151
152
	protected void setKeepAliveSecs(long keepAliveSecs) {
153
		this.keepAlive = keepAliveSecs*1000;
154
	}
155
	protected long getKeepAlive() {
156
		return this.keepAlive;
157
	}
158
	protected void setCleanSession(boolean cleanSession) {
159
		this.cleanSession = cleanSession;
160
	}
161
	
162
	private String getSendPersistenceKey(MqttWireMessage message) {
163
		return PERSISTENCE_SENT_PREFIX + message.getMessageId();
164
	}
165
	
166
	private String getSendConfirmPersistenceKey(MqttWireMessage message) {
167
		return PERSISTENCE_CONFIRMED_PREFIX + message.getMessageId();
168
	}
169
	
170
	private String getReceivedPersistenceKey(MqttWireMessage message) {
171
		return PERSISTENCE_RECEIVED_PREFIX + message.getMessageId();
172
	}
173
	
174
	protected void clearState() throws MqttException {
175
		final String methodName = "clearState";
176
		//@TRACE 603=clearState
177
		log.fine(className, methodName,">");
178
179
		persistence.clear();
180
		inUseMsgIds.clear();
181
		pendingMessages.clear();
182
		pendingFlows.clear();
183
		outboundQoS2.clear();
184
		outboundQoS1.clear();
185
		inboundQoS2.clear();
186
		tokenStore.clear();
187
	}
188
	
189
	private MqttWireMessage restoreMessage(String key, MqttPersistable persistable) throws MqttException {
190
		final String methodName = "restoreMessage";
191
		MqttWireMessage message = null;
192
193
		try {
194
			message = MqttWireMessage.createWireMessage(persistable);
195
		}
196
		catch (MqttException ex) {
197
			//@TRACE 602=key={0} exception
198
			log.fine(className, methodName, "602", new Object[] {key}, ex);
199
			if (ex.getCause() instanceof EOFException) {
200
				// Premature end-of-file means that the message is corrupted
201
				if (key != null) {
202
					persistence.remove(key);
203
				}
204
			}
205
			else {
206
				throw ex;
207
			}
208
		}
209
		//@TRACE 601=key={0} message={1}
210
		log.fine(className, methodName, "601", new Object[]{key,message});
211
		return message;
212
	}
213
214
	/**
215
	 * Inserts a new message to the list, ensuring that list is ordered from lowest to highest in terms of the message id's.
216
	 * @param list the list to insert the message into
217
	 * @param newMsg the message to insert into the list
218
	 */
219
	private void insertInOrder(Vector list, MqttWireMessage newMsg) {
220
		int newMsgId = newMsg.getMessageId();
221
		for (int i = 0; i < list.size(); i++) {
222
			MqttWireMessage otherMsg = (MqttWireMessage) list.elementAt(i);
223
			int otherMsgId = otherMsg.getMessageId();
224
			if (otherMsgId > newMsgId) {
225
				list.insertElementAt(newMsg, i);
226
				return;
227
			}
228
		}
229
		list.addElement(newMsg);
230
	}
231
232
	/**
233
	 * Produces a new list with the messages properly ordered according to their message id's.
234
	 * @param list the list containing the messages to produce a new reordered list for 
235
	 * - this will not be modified or replaced, i.e., be read-only to this method
236
	 * @return a new reordered list
237
	 */
238
	private Vector reOrder(Vector list) {
239
240
		// here up the new list
241
		Vector newList = new Vector();
242
243
		if (list.size() == 0) {
244
			return newList; // nothing to reorder
245
		}
246
		
247
		int previousMsgId = 0;
248
		int largestGap = 0;
249
		int largestGapMsgIdPosInList = 0;
250
		for (int i = 0; i < list.size(); i++) {
251
			int currentMsgId = ((MqttWireMessage) list.elementAt(i)).getMessageId();
252
			if (currentMsgId - previousMsgId > largestGap) {
253
				largestGap = currentMsgId - previousMsgId;
254
				largestGapMsgIdPosInList = i;
255
			}
256
			previousMsgId = currentMsgId;
257
		}
258
		int lowestMsgId = ((MqttWireMessage) list.elementAt(0)).getMessageId();
259
		int highestMsgId = previousMsgId; // last in the sorted list
260
		
261
		// we need to check that the gap after highest msg id to the lowest msg id is not beaten
262
		if (MAX_MSG_ID - highestMsgId + lowestMsgId > largestGap) {
263
			largestGapMsgIdPosInList = 0;
264
		}
265
		
266
		// starting message has been located, let's start from this point on
267
		for (int i = largestGapMsgIdPosInList; i < list.size(); i++) {
268
			newList.addElement(list.elementAt(i));
269
		}
270
	
271
		// and any wrapping back to the beginning
272
		for (int i = 0; i < largestGapMsgIdPosInList; i++) {
273
			newList.addElement(list.elementAt(i));
274
		}
275
		
276
		return newList;
277
	}
278
	
279
	/**
280
	 * Restores the state information from persistence.
281
	 */
282
	protected void restoreState() throws MqttException {
283
		final String methodName = "restoreState";
284
		Enumeration messageKeys = persistence.keys();
285
		MqttPersistable persistable;
286
		String key;
287
		int highestMsgId = nextMsgId;
288
		Vector orphanedPubRels = new Vector();
289
		//@TRACE 600=>
290
		log.fine(className, methodName, "600");
291
		
292
		while (messageKeys.hasMoreElements()) {
293
			key = (String) messageKeys.nextElement();
294
			persistable = persistence.get(key);
295
			MqttWireMessage message = restoreMessage(key, persistable);
296
			if (message != null) {
297
				if (key.startsWith(PERSISTENCE_RECEIVED_PREFIX)) {
298
					//@TRACE 604=inbound QoS 2 publish key={0} message={1}
299
					log.fine(className,methodName,"604", new Object[]{key,message});
300
301
					// The inbound messages that we have persisted will be QoS 2 
302
					inboundQoS2.put(new Integer(message.getMessageId()),message);
303
				} else if (key.startsWith(PERSISTENCE_SENT_PREFIX)) {
304
					MqttPublish sendMessage = (MqttPublish) message;
305
					highestMsgId = Math.max(sendMessage.getMessageId(), highestMsgId);
306
					if (persistence.containsKey(getSendConfirmPersistenceKey(sendMessage))) {
307
						MqttPersistable persistedConfirm = persistence.get(getSendConfirmPersistenceKey(sendMessage));
308
						// QoS 2, and CONFIRM has already been sent...
309
						MqttPubRel confirmMessage = (MqttPubRel) restoreMessage(key, persistedConfirm);
310
						if (confirmMessage != null) {
311
							confirmMessage.setDuplicate(true);
312
							//@TRACE 605=outbound QoS 2 pubrel key={0} message={1}
313
							log.fine(className,methodName, "605", new Object[]{key,message});
314
315
							outboundQoS2.put(new Integer(confirmMessage.getMessageId()), confirmMessage);
316
						} else {
317
							//@TRACE 606=outbound QoS 2 completed key={0} message={1}
318
							log.fine(className,methodName, "606", new Object[]{key,message});
319
						}
320
					} else {
321
						// QoS 1 or 2, with no CONFIRM sent...
322
						// Put the SEND to the list of pending messages, ensuring message ID ordering...
323
						sendMessage.setDuplicate(true);
324
						if (sendMessage.getMessage().getQos() == 2) {
325
							//@TRACE 607=outbound QoS 2 publish key={0} message={1}
326
							log.fine(className,methodName, "607", new Object[]{key,message});
327
							
328
							outboundQoS2.put(new Integer(sendMessage.getMessageId()),sendMessage);
329
						} else {
330
							//@TRACE 608=outbound QoS 1 publish key={0} message={1}
331
							log.fine(className,methodName, "608", new Object[]{key,message});
332
333
							outboundQoS1.put(new Integer(sendMessage.getMessageId()),sendMessage);
334
						}
335
					}
336
					MqttDeliveryToken tok = tokenStore.restoreToken(sendMessage);
337
					tok.internalTok.setClient(clientComms.getClient());
338
					inUseMsgIds.put(new Integer(sendMessage.getMessageId()),new Integer(sendMessage.getMessageId()));
339
				}
340
				else if (key.startsWith(PERSISTENCE_CONFIRMED_PREFIX)) {
341
					MqttPubRel pubRelMessage = (MqttPubRel) message;
342
					if (!persistence.containsKey(getSendPersistenceKey(pubRelMessage))) {
343
						orphanedPubRels.addElement(key);
344
					}
345
				}
346
			}
347
		}
348
349
		messageKeys = orphanedPubRels.elements();
350
		while(messageKeys.hasMoreElements()) {
351
			key = (String) messageKeys.nextElement();
352
			//@TRACE 609=removing orphaned pubrel key={0}
353
			log.fine(className,methodName, "609", new Object[]{key});
354
355
			persistence.remove(key);
356
		}
357
		
358
		nextMsgId = highestMsgId;
359
	}
360
	
361
	private void restoreInflightMessages() {
362
		final String methodName = "restoreInflightMessages";
363
		pendingMessages = new Vector(this.maxInflight);
364
		pendingFlows = new Vector();
365
366
		Enumeration keys = outboundQoS2.keys();
367
		while (keys.hasMoreElements()) {
368
			Object key = keys.nextElement();
369
			Object msg = outboundQoS2.get(key);
370
			if (msg instanceof MqttPublish) {
371
				//@TRACE 610=QoS 2 publish key={0}
372
				log.fine(className,methodName, "610", new Object[]{key});
373
374
				insertInOrder(pendingMessages, (MqttPublish)msg);
375
			} else if (msg instanceof MqttPubRel) {
376
				//@TRACE 611=QoS 2 pubrel key={0}
377
				log.fine(className,methodName, "611", new Object[]{key});
378
379
				insertInOrder(pendingFlows, (MqttPubRel)msg);
380
			}
381
		}
382
		keys = outboundQoS1.keys();
383
		while (keys.hasMoreElements()) {
384
			Object key = keys.nextElement();
385
			MqttPublish msg = (MqttPublish)outboundQoS1.get(key);
386
			//@TRACE 612=QoS 1 publish key={0}
387
			log.fine(className,methodName, "612", new Object[]{key});
388
389
			insertInOrder(pendingMessages, msg);
390
		}
391
		
392
		this.pendingFlows = reOrder(pendingFlows);
393
		this.pendingMessages = reOrder(pendingMessages);
394
	}
395
	
396
	/**
397
	 * Submits a message for delivery. This method will block until there is
398
	 * room in the inFlightWindow for the message. The message is put into
399
	 * persistence before returning.
400
	 * 
401
	 * @param message  the message to send
402
	 * @param token the token that can be used to track delivery of the message
403
	 * @throws MqttException
404
	 */
405
	public void send(MqttWireMessage message, MqttToken token) throws MqttException {
406
		final String methodName = "send";
407
		if (message.isMessageIdRequired() && (message.getMessageId() == 0)) {
408
			message.setMessageId(getNextMessageId());
409
		}
410
		if (token != null ) {
411
			try {
412
				token.internalTok.setMessageID(message.getMessageId());
413
			} catch (Exception e) {
414
			}
415
		}
416
			
417
		if (message instanceof MqttPublish) {
418
			synchronized (queueLock) {
419
				if (actualInFlight >= this.maxInflight) {
420
					//@TRACE 613= sending {0} msgs at max inflight window
421
					log.fine(className, methodName, "613", new Object[]{new Integer(actualInFlight)});
422
423
					throw new MqttException(MqttException.REASON_CODE_MAX_INFLIGHT);
424
				}
425
				
426
				MqttMessage innerMessage = ((MqttPublish) message).getMessage();
427
				//@TRACE 628=pending publish key={0} qos={1} message={2}
428
				log.fine(className,methodName,"628", new Object[]{new Integer(message.getMessageId()), new Integer(innerMessage.getQos()), message});
429
430
				switch(innerMessage.getQos()) {
431
					case 2:
432
						outboundQoS2.put(new Integer(message.getMessageId()), message);
433
						persistence.put(getSendPersistenceKey(message), (MqttPublish) message);
434
						break;
435
					case 1:
436
						outboundQoS1.put(new Integer(message.getMessageId()), message);
437
						persistence.put(getSendPersistenceKey(message), (MqttPublish) message);
438
						break;
439
				}
440
				tokenStore.saveToken(token, message);
441
				pendingMessages.addElement(message);
442
				queueLock.notifyAll();
443
			}
444
		} else {
445
			//@TRACE 615=pending send key={0} message {1}
446
			log.fine(className,methodName,"615", new Object[]{new Integer(message.getMessageId()), message});
447
			
448
			if (message instanceof MqttConnect) {
449
				synchronized (queueLock) {
450
					// Add the connect action at the head of the pending queue ensuring it jumps
451
					// ahead of any of other pending actions.
452
					tokenStore.saveToken(token, message);
453
					pendingFlows.insertElementAt(message,0);
454
					queueLock.notifyAll();
455
				}
456
			} else {
457
				if (message instanceof MqttPingReq) {
458
					this.pingCommand = message;
459
				}
460
				else if (message instanceof MqttPubRel) {
461
					outboundQoS2.put(new Integer(message.getMessageId()), message);
462
					persistence.put(getSendConfirmPersistenceKey(message), (MqttPubRel) message);
463
				}
464
				else if (message instanceof MqttPubComp)  {
465
					persistence.remove(getReceivedPersistenceKey(message));
466
				}
467
				
468
				synchronized (queueLock) {
469
					if ( !(message instanceof MqttAck )) {
470
						tokenStore.saveToken(token, message);
471
					}
472
					pendingFlows.addElement(message);
473
					queueLock.notifyAll();
474
				}
475
			}
476
		}
477
	}
478
	
479
	/**
480
	 * This removes the MqttSend message from the outbound queue and persistence.
481
	 * @param message
482
	 * @throws MqttPersistenceException
483
	 */
484
	protected void undo(MqttPublish message) throws MqttPersistenceException {
485
		final String methodName = "undo";
486
		synchronized (queueLock) {
487
			//@TRACE 618=key={0} QoS={1} 
488
			log.fine(className,methodName,"618", new Object[]{new Integer(message.getMessageId()), new Integer(message.getMessage().getQos())});
489
			
490
			if (message.getMessage().getQos() == 1) {
491
				outboundQoS1.remove(new Integer(message.getMessageId()));
492
			} else {
493
				outboundQoS2.remove(new Integer(message.getMessageId()));
494
			}
495
			pendingMessages.removeElement(message);
496
			persistence.remove(getSendPersistenceKey(message));
497
			tokenStore.removeToken(message);
498
			checkQuiesceLock();
499
		}
500
	}
501
	
502
	/**
503
	 * Check and send a ping if needed and check for ping timeout.
504
	 * Need to send a ping if nothing has been sent or received  
505
	 * in the last keepalive interval. It is important to check for 
506
	 * both sent and received packets in order to catch the case where an 
507
	 * app is solely sending QOS 0 messages or receiving QOS 0 messages.
508
	 * QOS 0 message are not good enough for checking a connection is
509
	 * alive as they are one way messages.
510
	 * 
511
	 * If a ping has been sent but no data has been received in the 
512
	 * last keepalive interval then the connection is deamed to be broken. 
513
	 */
514
	private void checkForActivity() throws MqttException {
515
		final String methodName = "checkForActivity";
516
517
		if (connected && this.keepAlive > 0) {
518
			long time = System.currentTimeMillis();
519
		
520
			if (!pingOutstanding) {
521
				// Is a ping required? 
522
				if (time - lastOutboundActivity >= this.keepAlive ||
523
					time - lastInboundActivity >= this.keepAlive) {
524
525
					//@TRACE 620=ping needed. keepAlive={0} lastOutboundActivity={1} lastInboundActivity={2}
526
					log.fine(className,methodName,"620", new Object[]{new Long(this.keepAlive),new Long(lastOutboundActivity),new Long(lastInboundActivity)});
527
					
528
					pingOutstanding = true;
529
					lastPing = time;
530
					MqttToken token = new MqttToken(clientComms.getClient().getClientId());
531
					tokenStore.saveToken(token, pingCommand);
532
					pendingFlows.insertElementAt(pingCommand, 0);
533
				}
534
			} else if (time - lastPing >= this.keepAlive) {
535
				// A ping is outstanding but no packet has been received in KA so connection is deemed broken
536
				//@TRACE 619=Timed out as no activity, keepAlive={0} lastOutboundActivity={1} lastInboundActivity={2}
537
				log.severe(className,methodName,"619", new Object[]{new Long(this.keepAlive),new Long(lastOutboundActivity),new Long(lastInboundActivity)});
538
				
539
				// A ping has already been sent. At this point, assume that the
540
				// broker has hung and the TCP layer hasn't noticed.
541
				throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_TIMEOUT);
542
			}		
543
		}
544
	}
545
	
546
	/**
547
	 * This returns the next piece of work, ie message, for the CommsSender
548
	 * to send over the network.
549
	 * Calls to this method block until either:
550
	 *  - there is a message to be sent
551
	 *  - the keepAlive interval is exceeded, which triggers a ping message
552
	 *    to be returned
553
	 *  - {@link #disconnected(MqttException, boolean)} is called
554
	 * @return the next message to send, or null if the client is disconnected
555
	 */
556
	protected MqttWireMessage get() throws MqttException {
557
		final String methodName = "get";
558
		MqttWireMessage result = null;
559
560
		synchronized (queueLock) {
561
			while (result == null) {		
562
				if (pendingMessages.isEmpty() && pendingFlows.isEmpty()) {
563
					try {
564
						long ttw = getTimeUntilPing();
565
						//@TRACE 644=nothing to send, wait for {0} ms
566
						log.fine(className,methodName, "644", new Object[] {new Long(ttw)});						
567
 
568
						queueLock.wait(getTimeUntilPing());
569
					} catch (InterruptedException e) {
570
					}
571
				}
572
				
573
				// Handle the case where not connected. This should only be the case if: 
574
				// - in the process of disconnecting / shutting down
575
				// - in the process of connecting
576
				if (!connected && 
577
	 				(pendingFlows.isEmpty() || !((MqttWireMessage)pendingFlows.elementAt(0) instanceof MqttConnect))) {
578
					//@TRACE 621=no outstanding flows and not connected
579
					log.fine(className,methodName,"621");
580
581
					return null;
582
				}
583
584
				// Check if there is a need to send a ping to keep the session alive. 
585
				// Note this check is done before processing messages. If not done first
586
				// an app that only publishes QOS 0 messages will prevent keepalive processing
587
				// from functioning.
588
				checkForActivity();
589
				
590
				// Now process any queued flows or messages
591
				if (!pendingFlows.isEmpty()) {
592
					// Process the first "flow" in the queue
593
					result = (MqttWireMessage)pendingFlows.elementAt(0);
594
					pendingFlows.removeElementAt(0);
595
					if (result instanceof MqttPubRel) {
596
						inFlightPubRels++;
597
598
						//@TRACE 617=+1 inflightpubrels={0}
599
						log.fine(className,methodName,"617", new Object[]{new Integer(inFlightPubRels)});
600
					}
601
		
602
					checkQuiesceLock();
603
				} else if (!pendingMessages.isEmpty()) {
604
					// If the inflight window is full then messages are not 
605
					// processed until the inflight window has space. 
606
					if (actualInFlight < this.maxInflight) {
607
						// The in flight window is not full so process the 
608
						// first message in the queue
609
						result = (MqttWireMessage)pendingMessages.elementAt(0);
610
						pendingMessages.removeElementAt(0);
611
						actualInFlight++;
612
	
613
						//@TRACE 623=+1 actualInFlight={0}
614
						log.fine(className,methodName,"623",new Object[]{new Integer(actualInFlight)});
615
					} else {
616
						//@TRACE 622=inflight window full
617
						log.fine(className,methodName,"622");				
618
					}
619
				}			
620
			}
621
		}
622
		return result;
623
	}
624
	
625
	public void setKeepAliveInterval(long interval) {
626
		this.keepAlive = interval;
627
	}
628
	
629
	/**
630
	 * Deduce how long to to wait until a ping is required.
631
	 * 
632
	 * In order to keep the connection alive the server must see activity within 
633
	 * the keepalive interval. If the application is not sending / receiving
634
	 * any messages then the client will send a ping.  This method works out
635
	 * the next time that a ping must be sent in order for the server to 
636
	 * know the client is alive.
637
	 * @return  time before a ping needs to be sent to keep alive the connection
638
	 */
639
	long getTimeUntilPing() {
640
		long pingin = getKeepAlive();
641
		// If KA is zero which means just wait for work or 
642
		// if a ping is outstanding return the KA value
643
		if (connected && (getKeepAlive() > 0) && !pingOutstanding) {
644
		
645
			long time = System.currentTimeMillis();
646
			long timeSinceOut = (time-lastOutboundActivity);
647
			long timeSinceIn = (time-lastInboundActivity);
648
			
649
			if (timeSinceOut > timeSinceIn) {
650
				pingin = (getKeepAlive()-timeSinceOut);
651
			} else {
652
				pingin = (getKeepAlive()-timeSinceIn);
653
			}
654
			
655
			// Unlikely to be negative or zero but in the case it is return a 
656
			// small value > 0 to cause a ping to occur
657
			if (pingin <= 0) {
658
				pingin = 10;
659
			}
660
		}
661
		return (pingin);
662
	}
663
	
664
	/**
665
	 * Called by the CommsSender when a message has been sent
666
	 * @param message
667
	 */
668
	protected void notifySent(MqttWireMessage message) {
669
		final String methodName = "notifySent";
670
		
671
		this.lastOutboundActivity = System.currentTimeMillis();
672
		//@TRACE 625=key={0}
673
		log.fine(className,methodName,"625",new Object[]{message.getKey()});
674
		
675
		MqttToken token = tokenStore.getToken(message);
676
		token.internalTok.notifySent();
677
		if (message instanceof MqttPublish) {
678
			if (((MqttPublish)message).getMessage().getQos() == 0) {
679
				// once a QOS 0 message is sent we can clean up its records straight away as
680
				// we won't be hearing about it again
681
				token.internalTok.markComplete(null, null);
682
				callback.asyncOperationComplete(token);
683
				decrementInFlight();
684
				releaseMessageId(message.getMessageId());
685
				tokenStore.removeToken(message);
686
				checkQuiesceLock();
687
			}
688
		}
689
	}
690
691
	private void decrementInFlight() {
692
		final String methodName = "decrementInFlight";
693
		synchronized (queueLock) {
694
			actualInFlight--;
695
			//@TRACE 646=-1 actualInFlight={0}
696
			log.fine(className,methodName,"646",new Object[]{new Integer(actualInFlight)});
697
			
698
			if (!checkQuiesceLock()) {
699
				queueLock.notifyAll();
700
			}
701
		}
702
	}
703
	
704
	protected boolean checkQuiesceLock() {
705
		final String methodName = "checkQuiesceLock";
706
//		if (quiescing && actualInFlight == 0 && pendingFlows.size() == 0 && inFlightPubRels == 0 && callback.isQuiesced()) {
707
		int tokC = tokenStore.count();
708
		if (quiescing && tokC == 0 && pendingFlows.size() == 0 && callback.isQuiesced()) {
709
			//@TRACE 626=quiescing={0} actualInFlight={1} pendingFlows={2} inFlightPubRels={3} callbackQuiesce={4} tokens={5}
710
			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)});
711
			synchronized (quiesceLock) {
712
				quiesceLock.notifyAll();
713
			}
714
			return true;
715
		}
716
		return false;
717
	}
718
	
719
	/**
720
	 * Called by the CommsReceiver when an ack has arrived. 
721
	 * 
722
	 * @param message
723
	 * @throws MqttException
724
	 */
725
	protected void notifyReceivedAck(MqttAck ack) throws MqttException {
726
		final String methodName = "notifyReceivedAck";
727
		this.lastInboundActivity = System.currentTimeMillis();
728
729
		// @TRACE 627=received key={0} message={1}
730
		log.fine(className, methodName, "627", new Object[] {
731
				new Integer(ack.getMessageId()), ack });
732
733
		MqttToken token = tokenStore.getToken(ack);
734
		MqttException mex = null;
735
736
		if (ack instanceof MqttPubRec) {
737
			// Complete the QOS 2 flow. Unlike all other
738
			// flows, QOS is a 2 phase flow. The second phase sends a
739
			// pubrel - the operation is not complete until a pubcomp
740
			// is received
741
			MqttPubRel rel = new MqttPubRel((MqttPubRec) ack);
742
			this.send(rel, token);
743
		} else if (ack instanceof MqttPubAck || ack instanceof MqttPubComp) {
744
			// QoS 1 & 2 notify users of result before removing from
745
			// persistence
746
			notifyResult(ack, token, mex);
747
			// Do not remove publish / delivery token at this stage
748
			// do this when the persistence is removed later 
749
		} else if (ack instanceof MqttPingResp) {
750
			pingOutstanding = false;		
751
			notifyResult(ack, token, mex);
752
			tokenStore.removeToken(ack);
753
		} else if (ack instanceof MqttConnack) {
754
			int rc = ((MqttConnack) ack).getReturnCode();
755
			if (rc == 0) {
756
				synchronized (queueLock) {
757
					if (cleanSession) {
758
						clearState();
759
						// Add the connect token back in so that users can be  
760
						// notified when connect completes.
761
						tokenStore.saveToken(token,ack);
762
					}
763
					inFlightPubRels = 0;
764
					actualInFlight = 0;
765
					restoreInflightMessages();
766
					connected();
767
				}
768
			} else {
769
				mex = ExceptionHelper.createMqttException(rc);
770
				throw mex;
771
			}
772
773
			clientComms.connectComplete((MqttConnack) ack, mex);
774
			notifyResult(ack, token, mex);
775
			tokenStore.removeToken(ack);
776
777
			// Notify the sender thread that there maybe work for it to do now
778
			synchronized (queueLock) {
779
				queueLock.notifyAll();
780
			}
781
		} else {
782
			// Sub ack or unsuback
783
			notifyResult(ack, token, mex);
784
			releaseMessageId(ack.getMessageId());
785
			tokenStore.removeToken(ack);
786
		}
787
		
788
		checkQuiesceLock();
789
	}
790
791
	/**
792
	 * Called by the CommsReceiver when a message has been received.
793
	 * Handles inbound messages and other flows such as pubrel. 
794
	 * 
795
	 * @param message
796
	 * @throws MqttException
797
	 */
798
	protected void notifyReceivedMsg(MqttWireMessage message) throws MqttException {
799
		final String methodName = "notifyReceivedMsg";
800
		this.lastInboundActivity = System.currentTimeMillis();
801
802
		// @TRACE 651=received key={0} message={1}
803
		log.fine(className, methodName, "651", new Object[] {
804
				new Integer(message.getMessageId()), message });
805
		
806
		if (!quiescing) {
807
			if (message instanceof MqttPublish) {
808
				MqttPublish send = (MqttPublish) message;
809
				switch (send.getMessage().getQos()) {
810
				case 0:
811
				case 1:
812
					if (callback != null) {
813
						callback.messageArrived(send);
814
					}
815
					break;
816
				case 2:
817
					persistence.put(getReceivedPersistenceKey(message),
818
							(MqttPublish) message);
819
					inboundQoS2.put(new Integer(send.getMessageId()), send);
820
					this.send(new MqttPubRec(send), null);
821
				}
822
			} else if (message instanceof MqttPubRel) {
823
				MqttPublish sendMsg = (MqttPublish) inboundQoS2
824
						.get(new Integer(message.getMessageId()));
825
				if (sendMsg != null) {
826
					if (callback != null) {
827
						callback.messageArrived(sendMsg);
828
					}
829
				} else {
830
					// Original publish has already been delivered.
831
					MqttPubComp pubComp = new MqttPubComp(message
832
							.getMessageId());
833
					this.send(pubComp, null);
834
				}
835
			}
836
		}
837
	}
838
839
	
840
	/**
841
	 * Called when waiters and callbacks have processed the message. For
842
	 * messages where delivery is complete the message can be removed from
843
	 * persistence and counters adjusted accordingly. Also tidy up by removing
844
	 * token from store...
845
	 * 
846
	 * @param message
847
	 * @throws MqttException
848
	 */
849
	protected void notifyComplete(MqttToken token) throws MqttException {
850
		final String methodName = "notifyComplete";
851
852
		MqttWireMessage message = token.internalTok.getWireMessage();
853
854
		if (message != null && message instanceof MqttAck) {
855
			// @TRACE 629=received key={0} token={1} message={2}
856
			log.fine(className, methodName, "629", new Object[] {
857
					 new Integer(message.getMessageId()), token, message });
858
859
			MqttAck ack = (MqttAck) message;
860
861
			if (ack instanceof MqttPubAck) {
862
				// QoS 1 - user notified now remove from persistence...
863
				persistence.remove(getSendPersistenceKey(message));
864
				outboundQoS1.remove(new Integer(ack.getMessageId()));
865
				decrementInFlight();
866
				releaseMessageId(message.getMessageId());
867
				tokenStore.removeToken(message);
868
				// @TRACE 650=removed Qos 1 publish. key={0}
869
				log.fine(className, methodName, "650",
870
						new Object[] { new Integer(ack.getMessageId()) });
871
			} else if (ack instanceof MqttPubComp) {
872
				// QoS 2 - user notified now remove from persistence...
873
				persistence.remove(getSendPersistenceKey(message));
874
				persistence.remove(getSendConfirmPersistenceKey(message));
875
				outboundQoS2.remove(new Integer(ack.getMessageId()));
876
877
				inFlightPubRels--;
878
				decrementInFlight();
879
				releaseMessageId(message.getMessageId());
880
				tokenStore.removeToken(message);
881
882
				// @TRACE 645=removed QoS 2 publish/pubrel. key={0}, -1 inFlightPubRels={1}
883
				log.fine(className, methodName, "645", new Object[] {
884
						new Integer(ack.getMessageId()),
885
						new Integer(inFlightPubRels) });
886
			}
887
888
			checkQuiesceLock();
889
		}
890
	}
891
892
	protected void notifyResult(MqttWireMessage ack, MqttToken token, MqttException ex) {
893
		final String methodName = "notifyResult";
894
		// unblock any threads waiting on the token  
895
		token.internalTok.markComplete(ack, ex);
896
						
897
		// Let the user know an async operation has completed and then remove the token
898
		if (ack != null && ack instanceof MqttAck && !(ack instanceof MqttPubRec)) {
899
			//@TRACE 648=key{0}, msg={1}, excep={2}
900
			log.fine(className,methodName, "648", new Object [] {token.internalTok.getKey(), ack, ex});
901
			callback.asyncOperationComplete(token);
902
		}
903
		// There are cases where there is no ack as the operation failed before 
904
		// an ack was received 
905
		if (ack == null ) {
906
			//@TRACE 649=key={0},excep={1}
907
			log.fine(className,methodName, "649", new Object [] { token.internalTok.getKey(), ex});
908
			callback.asyncOperationComplete(token);
909
		}
910
	}
911
	
912
	/**
913
	 * Called when the client has successfully connected to the broker
914
	 */
915
	public void connected() {
916
		final String methodName = "connected";
917
		//@TRACE 631=connected
918
		log.fine(className, methodName, "631");
919
		this.connected = true;
920
	}
921
	
922
	/**
923
	 * 
924
	 * Called during shutdown to work out if there are any tokens still
925
	 * to be notified and waiters to be unblocked.  Notifying and unblocking 
926
	 * takes place after most shutdown processing has completed. The tokenstore
927
	 * is tidied up so it only contains outstanding delivery tokens which are
928
	 * valid after reconnect (if clean session is false)
929
	 * @param reason The root cause of the disconnection, or null if it is a clean disconnect
930
	 */
931
	public Vector resolveOldTokens(MqttException reason) {
932
		final String methodName = "resolveOldTokens";
933
		//@TRACE 632=reason {0}
934
		log.fine(className,methodName,"632", new Object[] {reason});
935
		
936
		// If any outstanding let the user know the reason why it is still
937
		// outstanding by putting the reason shutdown is occurring into the 
938
		// token. 
939
		MqttException shutReason = reason;
940
		if (reason == null) {
941
			shutReason = new MqttException(MqttException.REASON_CODE_CLIENT_DISCONNECTING);
942
		}
943
		
944
		// Set the token up so it is ready to be notified after disconnect
945
		// processing has completed. Do not 
946
		// remove the token from the store if it is a delivery token, it is 
947
		// valid after a reconnect. 
948
		Vector outT = tokenStore.getOutstandingTokens();
949
		Enumeration outTE = outT.elements();
950
		while (outTE.hasMoreElements()) {
951
			MqttToken tok = (MqttToken)outTE.nextElement();
952
			synchronized (tok) {
953
				if (!tok.isComplete() && !tok.internalTok.isCompletePending() && tok.getException() == null) {
954
					tok.internalTok.setException(shutReason);
955
				}
956
			}
957
			if (!(tok instanceof MqttDeliveryToken)) {
958
				// If not a delivery token it is not valid on 
959
				// restart so remove
960
				tokenStore.removeToken(tok.internalTok.getKey());
961
			}					
962
		}
963
		return outT;
964
	}
965
	
966
	/**
967
	 * Called when the client has been disconnected from the broker.
968
	 * @param reason The root cause of the disconnection, or null if it is a clean disconnect
969
	 */
970
	public void disconnected(MqttException reason) {
971
		final String methodName = "disconnected";
972
		//@TRACE 633=disconnected
973
		log.fine(className,methodName,"633", new Object[] {reason});		
974
975
		this.connected = false;
976
977
		try {
978
			if (cleanSession) {
979
				clearState();
980
			}
981
982
			pendingMessages.clear();
983
			pendingFlows.clear();
984
			// Reset pingOutstanding to allow reconnects to assume no previous ping.
985
		    pingOutstanding = false;
986
		    
987
		} catch (MqttException e) {
988
			// Ignore as we have disconnected at this point
989
		}
990
	}
991
	
992
	/**
993
	 * Releases a message ID back into the pool of available message IDs.
994
	 * If the supplied message ID is not in use, then nothing will happen.
995
	 * 
996
	 * @param msgId A message ID that can be freed up for re-use.
997
	 */
998
	private synchronized void releaseMessageId(int msgId) {
999
		inUseMsgIds.remove(new Integer(msgId));
1000
	}
1001
1002
	/**
1003
	 * Get the next MQTT message ID that is not already in use, and marks
1004
	 * it as now being in use.
1005
	 * 
1006
	 * @return the next MQTT message ID to use
1007
	 */
1008
	private synchronized int getNextMessageId() throws MqttException {
1009
		int startingMessageId = nextMsgId;
1010
		// Allow two complete passes of the message ID range. This gives
1011
		// any asynchronous releases a chance to occur
1012
		int loopCount = 0;
1013
	    do {
1014
	        nextMsgId++;
1015
	        if ( nextMsgId > MAX_MSG_ID ) {
1016
	            nextMsgId = MIN_MSG_ID;
1017
	        }
1018
	        if (nextMsgId == startingMessageId) {
1019
	        	loopCount++;
1020
	        	if (loopCount == 2) {
1021
	        		throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_NO_MESSAGE_IDS_AVAILABLE);
1022
	        	}
1023
	        }
1024
	    } while( inUseMsgIds.containsKey( new Integer(nextMsgId) ) );
1025
	    Integer id = new Integer(nextMsgId);
1026
	    inUseMsgIds.put(id, id);
1027
	    return nextMsgId;
1028
	}
1029
	
1030
	/**
1031
	 * Quiesce the client state, preventing any new messages getting sent,
1032
	 * and preventing the callback on any newly received messages.
1033
	 * After the timeout expires, delete any pending messages except for
1034
	 * outbound ACKs, and wait for those ACKs to complete.
1035
	 */
1036
	public void quiesce(long timeout) {
1037
		final String methodName = "quiesce";
1038
		// If the timeout is greater than zero t
1039
		if (timeout > 0 ) {
1040
			//@TRACE 637=timeout={0}
1041
			log.fine(className,methodName, "637",new Object[]{new Long(timeout)});
1042
			synchronized (queueLock) {
1043
				this.quiescing = true;
1044
			}
1045
			// We don't want to handle any new inbound messages
1046
			callback.quiesce();
1047
			notifyQueueLock();
1048
1049
			synchronized (quiesceLock) {
1050
				try {			
1051
					// If token count is not zero there is outbound work to process and 
1052
					// if pending flows is not zero there is outstanding work to complete and
1053
					// if call back is not quiseced there it needs to complete. 
1054
					int tokc = tokenStore.count();
1055
					if (tokc > 0 || pendingFlows.size() >0 || !callback.isQuiesced()) {
1056
						//@TRACE 639=wait for outstanding: actualInFlight={0} pendingFlows={1} inFlightPubRels={2} tokens={3}
1057
						log.fine(className, methodName,"639", new Object[]{new Integer(actualInFlight), new Integer(pendingFlows.size()), new Integer(inFlightPubRels), new Integer(tokc)});
1058
1059
						// wait for outstanding in flight messages to complete and
1060
						// any pending flows to complete
1061
						quiesceLock.wait(timeout);
1062
					}
1063
				}
1064
				catch (InterruptedException ex) {
1065
					// Don't care, as we're shutting down anyway
1066
				}
1067
			}
1068
			
1069
			// Quiesce time up or inflight messsages delivered.  Ensure pending delivery
1070
			// vectors are cleared ready for disconnect to be sent as the final flow.
1071
			synchronized (queueLock) {
1072
				pendingMessages.clear();
1073
				pendingFlows.clear();
1074
				quiescing = false;
1075
				actualInFlight = 0;
1076
			}
1077
			//@TRACE 640=finished
1078
			log.fine(className, methodName, "640");
1079
		}
1080
	}
1081
1082
	protected void notifyQueueLock() {
1083
		final String methodName = "notifyQueueLock";
1084
		synchronized (queueLock) {
1085
			//@TRACE 638=notifying queueLock holders
1086
			log.fine(className,methodName,"638");
1087
			queueLock.notifyAll();
1088
		}
1089
	}
1090
1091
	protected void deliveryComplete(MqttPublish message) throws MqttPersistenceException {
1092
		final String methodName = "deliveryComplete";
1093
1094
		//@TRACE 641=remove publish from persistence. key={0}
1095
		log.fine(className,methodName,"641", new Object[]{new Integer(message.getMessageId())});
1096
		
1097
		persistence.remove(getReceivedPersistenceKey(message));
1098
		inboundQoS2.remove(new Integer(message.getMessageId()));
1099
	}
1100
	
1101
	/**
1102
	 * Tidy up
1103
	 * - ensure that tokens are released as they are maintained over a 
1104
	 * disconnect / connect cycle. 
1105
	 */
1106
	protected void close() {
1107
		inUseMsgIds.clear();
1108
		pendingMessages.clear();
1109
		pendingFlows.clear();
1110
		outboundQoS2.clear();
1111
		outboundQoS1.clear();
1112
		inboundQoS2.clear();
1113
		tokenStore.clear();
1114
		inUseMsgIds = null;
1115
		pendingMessages = null;
1116
		pendingFlows = null;
1117
		outboundQoS2 = null;
1118
		outboundQoS1 = null;
1119
		inboundQoS2 = null;
1120
		tokenStore = null;
1121
		callback = null;
1122
		clientComms = null;
1123
		persistence = null;
1124
		pingCommand = null;	
1125
	}
1126
	
1127
	public Properties getDebug() {
1128
		Properties props = new Properties();
1129
		props.put("In use msgids", inUseMsgIds);
1130
		props.put("pendingMessages", pendingMessages);
1131
		props.put("pendingFlows", pendingFlows);
1132
		props.put("maxInflight", new Integer(maxInflight));
1133
		props.put("nextMsgID", new Integer(nextMsgId));
1134
		props.put("actualInFlight", new Integer(actualInFlight));
1135
		props.put("inFlightPubRels", new Integer(inFlightPubRels));
1136
		props.put("quiescing", new Boolean(quiescing));
1137
		props.put("pingoutstanding", new Boolean(pingOutstanding));
1138
		props.put("lastOutboundActivity", new Long(lastOutboundActivity));
1139
		props.put("lastInboundActivity", new Long(lastInboundActivity));
1140
		props.put("outboundQoS2", outboundQoS2);
1141
		props.put("outboundQoS1", outboundQoS1);
1142
		props.put("inboundQoS2", inboundQoS2);
1143
		props.put("tokens", tokenStore);
1144
		return props;
1145
	}
1146
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/CommsCallback.java (-384 lines)
Lines 1-384 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal;
13
14
import java.util.Vector;
15
16
import org.eclipse.paho.client.mqttv3.IMqttActionListener;
17
import org.eclipse.paho.client.mqttv3.MqttCallback;
18
import org.eclipse.paho.client.mqttv3.MqttDeliveryToken;
19
import org.eclipse.paho.client.mqttv3.MqttException;
20
import org.eclipse.paho.client.mqttv3.MqttToken;
21
import org.eclipse.paho.client.mqttv3.internal.wire.MqttPubAck;
22
import org.eclipse.paho.client.mqttv3.internal.wire.MqttPubComp;
23
import org.eclipse.paho.client.mqttv3.internal.wire.MqttPublish;
24
import org.eclipse.paho.client.mqttv3.logging.Logger;
25
import org.eclipse.paho.client.mqttv3.logging.LoggerFactory;
26
27
/**
28
 * Bridge between Receiver and the external API. This class gets called by
29
 * Receiver, and then converts the comms-centric MQTT message objects into ones
30
 * understood by the external API.
31
 */
32
public class CommsCallback implements Runnable {
33
	private static int INBOUND_QUEUE_SIZE = 10;
34
	private MqttCallback mqttCallback;
35
	private ClientComms clientComms;
36
	private Vector messageQueue;
37
	private Vector completeQueue;
38
	public boolean running = false;
39
	private boolean quiescing = false;
40
	private Object lifecycle = new Object();
41
	private Thread callbackThread;
42
	private Object workAvailable = new Object();
43
	private Object spaceAvailable = new Object();
44
	private ClientState clientState;
45
46
	final static String className = CommsCallback.class.getName();
47
	Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,	className);
48
49
	CommsCallback(ClientComms clientComms) {
50
		this.clientComms = clientComms;
51
		this.messageQueue = new Vector(INBOUND_QUEUE_SIZE);
52
		this.completeQueue = new Vector(INBOUND_QUEUE_SIZE);
53
		log.setResourceName(clientComms.getClient().getClientId());
54
	}
55
56
	public void setClientState(ClientState clientState) {
57
		this.clientState = clientState;
58
	}
59
60
	/**
61
	 * Starts up the Callback thread.
62
	 */
63
	public void start(String threadName) {
64
		synchronized (lifecycle) {
65
			if (running == false) {
66
				// Praparatory work before starting the background thread.
67
				// For safety ensure any old events are cleared.
68
				messageQueue.clear();
69
				completeQueue.clear();
70
71
				running = true;
72
				quiescing = false;
73
				callbackThread = new Thread(this, threadName);
74
				callbackThread.start();
75
			}
76
		}
77
	}
78
79
	/**
80
	 * Stops the callback thread. 
81
	 * This call will block until stop has completed.
82
	 */
83
	public void stop() {
84
		final String methodName = "stop";
85
		synchronized (lifecycle) {
86
			if (running) {
87
				// @TRACE 700=stopping
88
				log.fine(className, methodName, "700");
89
				running = false;
90
				if (!Thread.currentThread().equals(callbackThread)) {
91
					try {
92
						synchronized (workAvailable) {
93
							// @TRACE 701=notify workAvailable and wait for run
94
							// to finish
95
							log.fine(className, methodName, "701");
96
							workAvailable.notifyAll();
97
						}
98
						// Wait for the thread to finish.
99
						callbackThread.join();
100
					} catch (InterruptedException ex) {
101
					}
102
				}
103
			}
104
			callbackThread = null;
105
			// @TRACE 703=stopped
106
			log.fine(className, methodName, "703");
107
		}
108
	}
109
110
	public void setCallback(MqttCallback mqttCallback) {
111
		this.mqttCallback = mqttCallback;
112
	}
113
114
	public void run() {
115
		final String methodName = "run";
116
		while (running) {
117
			try {
118
				// If no work is currently available, then wait until there is some...
119
				try {
120
					synchronized (workAvailable) {
121
						if (running & messageQueue.isEmpty()
122
								&& completeQueue.isEmpty()) {
123
							// @TRACE 704=wait for workAvailable
124
							log.fine(className, methodName, "704");
125
							workAvailable.wait();
126
						}
127
					}
128
				} catch (InterruptedException e) {
129
				}
130
131
				if (running) {
132
					// Check for deliveryComplete callbacks...
133
					if (!completeQueue.isEmpty()) {
134
						// First call the delivery arrived callback if needed
135
						MqttToken token = (MqttToken) completeQueue.elementAt(0);
136
						handleActionComplete(token);
137
						completeQueue.removeElementAt(0);
138
					}
139
					
140
					// Check for messageArrived callbacks...
141
					if (!messageQueue.isEmpty()) {
142
						// Note, there is a window on connect where a publish
143
						// could arrive before we've
144
						// finished the connect logic.
145
						MqttPublish message = (MqttPublish) messageQueue
146
								.elementAt(0);
147
148
						handleMessage(message);
149
						messageQueue.removeElementAt(0);
150
					}
151
				}
152
153
				if (quiescing) {
154
					clientState.checkQuiesceLock();
155
				}
156
157
				synchronized (spaceAvailable) {
158
					// Notify the spaceAvailable lock, to say that there's now
159
					// some space on the queue...
160
161
					// @TRACE 706=notify spaceAvailable
162
					log.fine(className, methodName, "706");
163
					spaceAvailable.notifyAll();
164
				}
165
			} catch (Throwable ex) {
166
				// Users code could throw an Error or Exception e.g. in the case
167
				// of class NoClassDefFoundError
168
				// @TRACE 714=callback threw exception
169
				log.fine(className, methodName, "714", null, ex);
170
				running = false;
171
				clientComms.shutdownConnection(null, new MqttException(ex));
172
			}
173
		}
174
	}
175
176
	private void handleActionComplete(MqttToken token)
177
			throws MqttException {
178
		final String methodName = "handleActionComplete";
179
		synchronized (token) {
180
			// @TRACE 705=callback and notify for key={0}
181
			log.fine(className, methodName, "705",	new Object[] { token.internalTok.getKey() });
182
			
183
			// Unblock any waiters and if pending complete now set completed
184
			token.internalTok.notifyComplete();
185
			
186
 			if (!token.internalTok.isNotified()) {
187
 				// If a callback is registered and delivery has finished 
188
 				// call delivery complete callback. 
189
				if ( mqttCallback != null 
190
					&& token instanceof MqttDeliveryToken 
191
					&& token.isComplete()) {
192
						mqttCallback.deliveryComplete((MqttDeliveryToken) token);
193
				}
194
				// Now call async action completion callbacks
195
				fireActionEvent(token);
196
			}
197
			
198
			// Set notified so we don't tell the user again about this action.
199
			if ( token instanceof MqttDeliveryToken && token.isComplete()) {
200
				token.internalTok.setNotified(true);
201
			}
202
203
			if (token.isComplete()) {
204
				// Finish by doing any post processing such as delete 
205
				// from persistent store but only do so if the action
206
				// is complete
207
				clientState.notifyComplete(token);
208
			}
209
		}
210
	}
211
212
	/**
213
	 * This method is called when the connection to the server is lost. If there
214
	 * is no cause then it was a clean disconnect. The connectionLost callback
215
	 * will be invoked if registered and run on the thread that requested
216
	 * shutdown e.g. receiver or sender thread. If the request was a user
217
	 * initiated disconnect then the disconnect token will be notified.
218
	 * 
219
	 * @param cause  the reason behind the loss of connection.
220
	 */
221
	public void connectionLost(MqttException cause) {
222
		final String methodName = "connectionLost";
223
		// If there was a problem and a client callback has been set inform
224
		// the connection lost listener of the problem.
225
		try {
226
			if (mqttCallback != null && cause != null) {
227
				// @TRACE 708=call connectionLost
228
				log.fine(className, methodName, "708", new Object[] { cause });
229
				mqttCallback.connectionLost(cause);
230
			}
231
		} catch (java.lang.Throwable t) {
232
			// Just log the fact that a throwable has caught connection lost 
233
			// is called during shutdown processing so no need to do anything else
234
			// @TRACE 720=exception from connectionLost {0}
235
			log.fine(className, methodName, "720", new Object[] { t });
236
		}
237
	}
238
239
	/**
240
	 * An action has completed - if a completion listener has been set on the
241
	 * token then invoke it with the outcome of the action.
242
	 * 
243
	 * @param token
244
	 */
245
	public void fireActionEvent(MqttToken token) {
246
		final String methodName = "fireActionEvent";
247
248
		if (token != null) {
249
			IMqttActionListener asyncCB = token.getActionCallback();
250
			if (asyncCB != null) {
251
				if (token.getException() == null) {
252
					// @TRACE 716=call onSuccess key={0}
253
					log.fine(className, methodName, "716",
254
							new Object[] { token.internalTok.getKey() });
255
					asyncCB.onSuccess(token);
256
				} else {
257
					// @TRACE 717=call onFailure key {0}
258
					log.fine(className, methodName, "716",
259
							new Object[] { token.internalTok.getKey() });
260
					asyncCB.onFailure(token, token.getException());
261
				}
262
			}
263
		}
264
	}
265
266
	/**
267
	 * This method is called when a message arrives on a topic. Messages are
268
	 * only added to the queue for inbound messages if the client is not
269
	 * quiescing.
270
	 * 
271
	 * @param sendMessage
272
	 *            the MQTT SEND message.
273
	 */
274
	public void messageArrived(MqttPublish sendMessage) {
275
		final String methodName = "messageArrived";
276
		if (mqttCallback != null) {
277
			// If we already have enough messages queued up in memory, wait
278
			// until some more queue space becomes available. This helps 
279
			// the client protect itself from getting flooded by messages 
280
			// from the server.
281
			synchronized (spaceAvailable) {
282
				if (!quiescing && messageQueue.size() >= INBOUND_QUEUE_SIZE) {
283
					try {
284
						// @TRACE 709=wait for spaceAvailable
285
						log.fine(className, methodName, "709");
286
						spaceAvailable.wait();
287
					} catch (InterruptedException ex) {
288
					}
289
				}
290
			}
291
			if (!quiescing) {
292
				messageQueue.addElement(sendMessage);
293
				// Notify the CommsCallback thread that there's work to do...
294
				synchronized (workAvailable) {
295
					// @TRACE 710=new msg avail, notify workAvailable
296
					log.fine(className, methodName, "710");
297
					workAvailable.notifyAll();
298
				}
299
			}
300
		}
301
	}
302
303
	/**
304
	 * Let the call back thread quiesce. Prevent new inbound messages being
305
	 * added to the process queue and let existing work quiesce. (until the
306
	 * thread is told to shutdown).
307
	 */
308
	public void quiesce() {
309
		final String methodName = "quiesce";
310
		this.quiescing = true;
311
		synchronized (spaceAvailable) {
312
			// @TRACE 711=quiesce notify spaceAvailable
313
			log.fine(className, methodName, "711");
314
			// Unblock anything waiting for space...
315
			spaceAvailable.notifyAll();
316
		}
317
	}
318
319
	public boolean isQuiesced() {
320
		if (quiescing && completeQueue.size() == 0 && messageQueue.size() == 0) {
321
			return true;
322
		}
323
		return false;
324
	}
325
326
	private void handleMessage(MqttPublish publishMessage)
327
			throws MqttException, Exception {
328
		final String methodName = "handleMessage";
329
		// If quisecing process any pending messages. 
330
		if (mqttCallback != null) {
331
			String destName = publishMessage.getTopicName();
332
333
			// @TRACE 713=call messageArrived key={0} topic={1}
334
			log.fine(className, methodName, "713", new Object[] { 
335
					new Integer(publishMessage.getMessageId()), destName });
336
			mqttCallback.messageArrived(destName, publishMessage.getMessage());
337
			if (publishMessage.getMessage().getQos() == 1) {
338
				this.clientComms.internalSend(new MqttPubAck(publishMessage),
339
						new MqttToken(clientComms.getClient().getClientId()));
340
			} else if (publishMessage.getMessage().getQos() == 2) {
341
				this.clientComms.deliveryComplete(publishMessage);
342
				MqttPubComp pubComp = new MqttPubComp(publishMessage);
343
				this.clientComms.internalSend(pubComp, new MqttToken(clientComms.getClient().getClientId()));
344
			}
345
		}
346
	}
347
348
	public void asyncOperationComplete(MqttToken token) {
349
		final String methodName = "asyncOperationComplete";
350
351
		if (running) {
352
			// invoke callbacks on callback thread
353
			completeQueue.addElement(token);
354
			synchronized (workAvailable) {
355
				// @TRACE 715=new workAvailable. key={0}
356
				log.fine(className, methodName, "715", new Object[] { token.internalTok.getKey() });
357
				workAvailable.notifyAll();
358
			}
359
		} else {
360
			// invoke async callback on invokers thread
361
			try {
362
				handleActionComplete(token);
363
			} catch (Throwable ex) {
364
				// Users code could throw an Error or Exception e.g. in the case
365
				// of class NoClassDefFoundError
366
				// @TRACE 719=callback threw ex:
367
				log.fine(className, methodName, "719", null, ex);
368
				
369
				// Shutdown likely already in progress but no harm to confirm
370
				System.err.println("problem in asyncopcomplete "+ex);
371
				ex.printStackTrace();
372
				clientComms.shutdownConnection(null, new MqttException(ex));
373
			}
374
375
		}
376
	}
377
378
	/**
379
	 * Returns the thread used by this callback.
380
	 */
381
	protected Thread getThread() {
382
		return callbackThread;
383
	}
384
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/CommsReceiver.java (-150 lines)
Lines 1-150 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal;
13
14
import java.io.IOException;
15
import java.io.InputStream;
16
17
import org.eclipse.paho.client.mqttv3.MqttException;
18
import org.eclipse.paho.client.mqttv3.MqttToken;
19
import org.eclipse.paho.client.mqttv3.internal.wire.MqttAck;
20
import org.eclipse.paho.client.mqttv3.internal.wire.MqttInputStream;
21
import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage;
22
import org.eclipse.paho.client.mqttv3.logging.Logger;
23
import org.eclipse.paho.client.mqttv3.logging.LoggerFactory;
24
25
/**
26
 * Receives MQTT packets from the server.
27
 */
28
public class CommsReceiver implements Runnable {
29
	private boolean running = false;
30
	private Object lifecycle = new Object();
31
	private ClientState clientState = null;
32
	private ClientComms clientComms = null;
33
	private MqttInputStream in;
34
	private CommsTokenStore tokenStore = null;
35
	private Thread recThread = null;
36
	
37
	private final static String className = CommsReceiver.class.getName();
38
	private Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className);
39
	
40
	public CommsReceiver(ClientComms clientComms, ClientState clientState,CommsTokenStore tokenStore, InputStream in) {
41
		this.in = new MqttInputStream(in);
42
		this.clientComms = clientComms;
43
		this.clientState = clientState;
44
		this.tokenStore = tokenStore;
45
		log.setResourceName(clientComms.getClient().getClientId());
46
	}
47
	
48
	/**
49
	 * Starts up the Receiver's thread.
50
	 */
51
	public void start(String threadName) {
52
		final String methodName = "start";
53
		//@TRACE 855=starting
54
		log.fine(className,methodName, "855");
55
		synchronized (lifecycle) {
56
			if (running == false) {
57
				running = true;
58
				recThread = new Thread(this, threadName);
59
				recThread.start();
60
			}
61
		}
62
	}
63
64
	/**
65
	 * Stops the Receiver's thread.  This call will block.
66
	 */
67
	public void stop() {
68
		final String methodName = "stop";
69
		synchronized (lifecycle) {
70
			//@TRACE 850=stopping
71
			log.fine(className,methodName, "850");
72
			if (running) {
73
				running = false;
74
				if (!Thread.currentThread().equals(recThread)) {
75
					try {
76
						// Wait for the thread to finish.
77
						recThread.join();
78
					}
79
					catch (InterruptedException ex) {
80
					}
81
				}
82
			}
83
		}
84
		recThread = null;
85
		//@TRACE 851=stopped
86
		log.fine(className,methodName,"851");
87
	}
88
	
89
	/**
90
	 * Run loop to receive messages from the server.
91
	 */
92
	public void run() {
93
		final String methodName = "run";
94
		MqttToken token = null;
95
		
96
		while (running && (in != null)) {
97
			try {
98
				//@TRACE 852=network read message
99
				log.fine(className,methodName,"852");
100
				MqttWireMessage message = in.readMqttWireMessage();
101
				
102
				if (message instanceof MqttAck) {
103
					token = tokenStore.getToken(message);
104
					if (token!=null) {
105
						synchronized (token) {
106
							// Ensure the notify processing is done under a lock on the token
107
							// This ensures that the send processing can complete  before the 
108
							// receive processing starts! ( request and ack and ack processing
109
							// can occur before request processing is complete if not!
110
							clientState.notifyReceivedAck((MqttAck)message);
111
						}
112
					} else {
113
						// It its an ack and there is no token then something is not right.
114
						// An ack should always have a token assoicated with it.
115
						throw new MqttException(MqttException.REASON_CODE_UNEXPECTED_ERROR);
116
					}
117
				} else {
118
					// A new message has arrived
119
					clientState.notifyReceivedMsg(message);
120
				}
121
			}
122
			catch (MqttException ex) {
123
				//@TRACE 856=Stopping, MQttException
124
				log.fine(className,methodName,"856",null,ex);
125
				running = false;
126
				// Token maybe null but that is handled in shutdown
127
				clientComms.shutdownConnection(token, ex);
128
			} 
129
			catch (IOException ioe) {
130
				//@TRACE 853=Stopping due to IOException
131
				log.fine(className,methodName,"853");
132
133
				running = false;
134
				// An EOFException could be raised if the broker processes the 
135
				// DISCONNECT and ends the socket before we complete. As such,
136
				// only shutdown the connection if we're not already shutting down.
137
				if (!clientComms.isDisconnecting()) {
138
					clientComms.shutdownConnection(token, new MqttException(MqttException.REASON_CODE_CONNECTION_LOST, ioe));
139
				} // else {
140
			}
141
		}
142
		
143
		//@TRACE 854=<
144
		log.fine(className,methodName,"854");
145
	}
146
	
147
	public boolean isRunning() {
148
		return running;
149
	}
150
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/CommsSender.java (-146 lines)
Lines 1-146 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal;
13
14
import java.io.OutputStream;
15
import org.eclipse.paho.client.mqttv3.MqttException;
16
import org.eclipse.paho.client.mqttv3.MqttToken;
17
import org.eclipse.paho.client.mqttv3.internal.wire.MqttAck;
18
import org.eclipse.paho.client.mqttv3.internal.wire.MqttOutputStream;
19
import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage;
20
import org.eclipse.paho.client.mqttv3.logging.Logger;
21
import org.eclipse.paho.client.mqttv3.logging.LoggerFactory;
22
23
24
public class CommsSender implements Runnable {
25
	/**
26
	 * Sends MQTT packets to the server on its own thread
27
	 */
28
	private boolean running 		= false;
29
	private Object lifecycle 		= new Object();
30
	private ClientState clientState = null;
31
	private MqttOutputStream out;
32
	private ClientComms clientComms = null;
33
	private CommsTokenStore tokenStore = null;
34
	private Thread 	sendThread		= null;
35
	
36
	private final static String className = CommsSender.class.getName();
37
	private Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT, className);
38
	
39
	public CommsSender(ClientComms clientComms, ClientState clientState, CommsTokenStore tokenStore, OutputStream out) {
40
		this.out = new MqttOutputStream(out);
41
		this.clientComms = clientComms;
42
		this.clientState = clientState;
43
		this.tokenStore = tokenStore;
44
		log.setResourceName(clientComms.getClient().getClientId());
45
	}
46
	
47
	/**
48
	 * Starts up the Sender thread.
49
	 */
50
	public void start(String threadName) {
51
		synchronized (lifecycle) {
52
			if (running == false) {
53
				running = true;
54
				sendThread = new Thread(this, threadName);
55
				sendThread.start();
56
			}
57
		}
58
	}
59
60
	/**
61
	 * Stops the Sender's thread.  This call will block.
62
	 */
63
	public void stop() {
64
		final String methodName = "stop";
65
		
66
		synchronized (lifecycle) {
67
			//@TRACE 800=stopping sender
68
			log.fine(className,methodName,"800");
69
			if (running) {
70
				running = false;
71
				if (!Thread.currentThread().equals(sendThread)) {
72
					try {
73
						// first notify get routine to finish
74
						clientState.notifyQueueLock();
75
						// Wait for the thread to finish.
76
						sendThread.join();
77
					}
78
					catch (InterruptedException ex) {
79
					}
80
				}
81
			}
82
			sendThread=null;
83
			//@TRACE 801=stopped
84
			log.fine(className,methodName,"801");
85
		}
86
	}
87
	
88
	public void run() {
89
		final String methodName = "run";
90
		MqttWireMessage message = null;
91
		while (running && (out != null)) {
92
			try {
93
				message = clientState.get();
94
				if (message != null) {
95
					//@TRACE 802=network send key={0} msg={1}
96
					log.fine(className,methodName,"802", new Object[] {message.getKey(),message});
97
98
					if (message instanceof MqttAck) {
99
						out.write(message);
100
						out.flush();
101
					} else {
102
						MqttToken token = tokenStore.getToken(message);
103
						// While quiescing the tokenstore can be cleared so need 
104
						// to check for null for the case where clear occurs
105
						// while trying to send a message.
106
						if (token != null) {
107
							synchronized (token) {
108
								out.write(message);
109
								out.flush();
110
								clientState.notifySent(message);
111
							}
112
						}
113
					}
114
				} else { // null message
115
					//@TRACE 803=get message returned null, stopping}
116
					log.fine(className,methodName,"803");
117
118
					running = false;
119
				}
120
			} catch (MqttException me) {
121
				handleRunException(message, me);
122
			} catch (Exception ex) {		
123
				handleRunException(message, ex);	
124
			}
125
		} // end while
126
		
127
		//@TRACE 805=<
128
		log.fine(className, methodName,"805");
129
130
	}
131
132
	private void handleRunException(MqttWireMessage message, Exception ex) {
133
		final String methodName = "handleRunException";
134
		//@TRACE 804=exception
135
		log.fine(className,methodName,"804",null, ex);
136
		MqttException mex;
137
		if ( !(ex instanceof MqttException)) {
138
			mex = new MqttException(MqttException.REASON_CODE_CONNECTION_LOST, ex);
139
		} else {
140
			mex = (MqttException)ex;
141
		}
142
143
		running = false;
144
		clientComms.shutdownConnection(null, mex);
145
	}
146
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/CommsTokenStore.java (-255 lines)
Lines 1-255 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal;
13
14
import java.util.Enumeration;
15
import java.util.Hashtable;
16
import java.util.Vector;
17
18
import org.eclipse.paho.client.mqttv3.MqttDeliveryToken;
19
import org.eclipse.paho.client.mqttv3.MqttException;
20
import org.eclipse.paho.client.mqttv3.MqttToken;
21
import org.eclipse.paho.client.mqttv3.internal.wire.MqttPublish;
22
import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage;
23
import org.eclipse.paho.client.mqttv3.logging.Logger;
24
import org.eclipse.paho.client.mqttv3.logging.LoggerFactory;
25
26
27
/**
28
 * Provides a "token" based system for storing and tracking actions across 
29
 * multiple threads. 
30
 * When a message is sent, a token is associated with the message
31
 * and saved using the {@link #saveToken(MqttToken, MqttWireMessage)} method. Anyone interested
32
 * in tacking the state can call one of the wait methods on the token or using 
33
 * the asynchronous listener callback method on the operation. 
34
 * The {@link CommsReceiver} class, on another thread, reads responses back from 
35
 * the network. It uses the response to find the relevant token, which it can then 
36
 * notify. 
37
 * 
38
 * Note:
39
 *   Ping, connect and disconnect do not have a unique message id as
40
 *   only one outstanding request of each type is allowed to be outstanding
41
 */
42
public class CommsTokenStore {
43
	/** Maps message-specific data (usually message IDs) to tokens */
44
	private Hashtable tokens;
45
	private String logContext;
46
	private MqttException closedResponse = null;
47
	
48
	final static String className = CommsTokenStore.class.getName();
49
	Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT, className);
50
51
	public CommsTokenStore(String logContext) {
52
		final String methodName = "<Init>";
53
54
		log.setResourceName(logContext);
55
		this.tokens = new Hashtable();
56
		this.logContext = logContext;
57
		//@TRACE 308=<>
58
		log.fine(className,methodName,"308");//,new Object[]{message});
59
60
	}
61
62
	/**
63
	 * Based on the message type that has just been received return the associated
64
	 * token from the token store or null if one does not exist.
65
	 * @param message whose token is to be returned 
66
	 * @return token for the requested message
67
	 */
68
	public MqttToken getToken(MqttWireMessage message) {
69
		String key = message.getKey(); 
70
		return (MqttToken)tokens.get(key);
71
	}
72
73
	public MqttToken getToken(String key) {
74
		return (MqttToken)tokens.get(key);
75
	}
76
77
	
78
	public MqttToken removeToken(MqttWireMessage message) {
79
		if (message != null) {
80
			return removeToken(message.getKey());
81
		}
82
		return null;
83
	}
84
	
85
	public MqttToken removeToken(String key) {
86
		final String methodName = "removeToken";
87
		//@TRACE 306=key={0}
88
		log.fine(className,methodName,"306",new Object[]{key});
89
90
		if (key != null) {
91
			synchronized(tokens) {
92
				MqttToken tok = (MqttToken)tokens.get(key);
93
				if (tok != null) {
94
					synchronized(tok) {
95
	
96
						return (MqttToken) tokens.remove(key);
97
					}
98
				}
99
			}
100
		}
101
		return null;
102
	}
103
		
104
	/**
105
	 * Restores a token after a client restart.  This method could be called
106
	 * for a SEND of CONFIRM, but either way, the original SEND is what's 
107
	 * needed to re-build the token.
108
	 */
109
	protected MqttDeliveryToken restoreToken(MqttPublish message) {
110
		final String methodName = "restoreToken";
111
		MqttDeliveryToken token;
112
		synchronized(tokens) {
113
			String key = new Integer(message.getMessageId()).toString();
114
			if (this.tokens.containsKey(key)) {
115
				token = (MqttDeliveryToken)this.tokens.get(key);
116
				//@TRACE 302=existing key={0} message={1} token={2}
117
				log.fine(className,methodName, "302",new Object[]{key, message,token});
118
			} else {
119
				token = new MqttDeliveryToken(logContext);
120
				token.internalTok.setKey(key);
121
				this.tokens.put(key, token);
122
				//@TRACE 303=creating new token key={0} message={1} token={2}
123
				log.fine(className,methodName,"303",new Object[]{key, message, token});
124
			}
125
		}
126
		return token;
127
	}
128
	
129
	// For outbound messages store the token in the token store 
130
	// For pubrel use the existing publish token 
131
	protected void saveToken(MqttToken token, MqttWireMessage message) throws MqttException {
132
		final String methodName = "saveToken";
133
134
		synchronized(tokens) {
135
			if (closedResponse == null) {
136
				String key = message.getKey();
137
				//@TRACE 300=key={0} message={1}
138
				log.fine(className,methodName,"300",new Object[]{key, message});
139
				
140
				saveToken(token,key);
141
			} else {
142
				throw closedResponse;
143
			}
144
		}
145
	}
146
	
147
	protected void saveToken(MqttToken token, String key) {
148
		final String methodName = "saveToken";
149
150
		synchronized(tokens) {
151
			//@TRACE 307=key={0} token={1}
152
			log.fine(className,methodName,"307",new Object[]{key,token.toString()});
153
			token.internalTok.setKey(key);
154
			this.tokens.put(key, token);
155
		}
156
	}
157
158
	protected void quiesce(MqttException quiesceResponse) {
159
		final String methodName = "quiesce";
160
161
		synchronized(tokens) {
162
			//@TRACE 309=resp={0}
163
			log.fine(className,methodName,"309",new Object[]{quiesceResponse});
164
165
			closedResponse = quiesceResponse;
166
		}
167
	}
168
	
169
	public void open() {
170
		final String methodName = "open";
171
172
		synchronized(tokens) {
173
			//@TRACE 310=>
174
			log.fine(className,methodName,"310");
175
176
			closedResponse = null;
177
		}
178
	}
179
180
	public MqttDeliveryToken[] getOutstandingDelTokens() {
181
		final String methodName = "getOutstandingDelTokens";
182
183
		synchronized(tokens) {
184
			//@TRACE 311=>
185
			log.fine(className,methodName,"311");
186
187
			Vector list = new Vector();
188
			Enumeration enumeration = tokens.elements();
189
			MqttToken token;
190
			while(enumeration.hasMoreElements()) {
191
				token = (MqttToken)enumeration.nextElement();
192
				if (token != null 
193
					&& token instanceof MqttDeliveryToken 
194
					&& !token.internalTok.isNotified()) {
195
					
196
					list.addElement(token);
197
				}
198
			}
199
	
200
			MqttDeliveryToken[] result = new MqttDeliveryToken[list.size()];
201
			return (MqttDeliveryToken[]) list.toArray(result);
202
		}
203
	}
204
	
205
	public Vector getOutstandingTokens() {
206
		final String methodName = "getOutstandingTokens";
207
208
		synchronized(tokens) {
209
			//@TRACE 312=>
210
			log.fine(className,methodName,"312");
211
212
			Vector list = new Vector();
213
			Enumeration enumeration = tokens.elements();
214
			MqttToken token;
215
			while(enumeration.hasMoreElements()) {
216
				token = (MqttToken)enumeration.nextElement();
217
				if (token != null) {
218
						list.addElement(token);
219
				}
220
			}
221
			return list;
222
		}
223
	}
224
225
	/**
226
	 * Empties the token store without notifying any of the tokens.
227
	 */
228
	public void clear() {
229
		final String methodName = "clear";
230
		//@TRACE 305=> {0} tokens
231
		log.fine(className, methodName, "305", new Object[] {new Integer(tokens.size())});
232
		synchronized(tokens) {
233
			tokens.clear();
234
		}
235
	}
236
	
237
	public int count() {
238
		synchronized(tokens) {
239
			return tokens.size();
240
		}
241
	}
242
	public String toString() {
243
		String lineSep = System.getProperty("line.separator","\n");
244
		StringBuffer toks = new StringBuffer();
245
		synchronized(tokens) {
246
			Enumeration enumeration = tokens.elements();
247
			MqttToken token;
248
			while(enumeration.hasMoreElements()) {
249
				token = (MqttToken)enumeration.nextElement();
250
					toks.append("{"+token.internalTok+"}"+lineSep);
251
			}
252
			return toks.toString();
253
		}
254
	}
255
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/DestinationProvider.java (-27 lines)
Lines 1-27 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal;
13
14
import org.eclipse.paho.client.mqttv3.MqttTopic;
15
16
/**
17
 * This interface exists to act as a common type for
18
 * MqttClient and MqttMIDPClient so they can be passed to
19
 * ClientComms without either client class need to know
20
 * about the other.
21
 * Specifically, this allows the MIDP client to work
22
 * without the non-MIDP MqttClient/MqttConnectOptions
23
 * classes being present.
24
 */
25
public interface DestinationProvider {
26
	public MqttTopic getTopic(String topic);
27
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/ExceptionHelper.java (-52 lines)
Lines 1-52 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal;
13
14
import org.eclipse.paho.client.mqttv3.MqttException;
15
import org.eclipse.paho.client.mqttv3.MqttSecurityException;
16
17
/**
18
 * Utility class to help create exceptions of the correct type.
19
 */
20
public class ExceptionHelper {
21
	public static MqttException createMqttException(int reasonCode) {
22
		if ((reasonCode == MqttException.REASON_CODE_FAILED_AUTHENTICATION) || 
23
			(reasonCode == MqttException.REASON_CODE_NOT_AUTHORIZED)) {
24
			return new MqttSecurityException(reasonCode);
25
		}
26
		
27
		return new MqttException(reasonCode);
28
	}
29
30
	public static MqttException createMqttException(Throwable cause) {
31
		if (cause.getClass().getName().equals("java.security.GeneralSecurityException")) {
32
			return new MqttSecurityException(cause);
33
		}
34
		return new MqttException(cause);
35
	}
36
	
37
	/**
38
	 * Returns whether or not the specified class is available to the current
39
	 * class loader.  This is used to protect the code against using Java SE
40
	 * APIs on Java ME.
41
	 */
42
	public static boolean isClassAvailable(String className) {
43
		boolean result = false;
44
		try {
45
			Class.forName(className);
46
			result = true;
47
		}
48
		catch (ClassNotFoundException ex) {
49
		}
50
		return result;
51
	}
52
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/FileLock.java (-92 lines)
Lines 1-92 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal;
13
/**
14
 * FileLock - used to obtain a lock that can be used to prevent other MQTT clients
15
 * using the same persistent store. If the lock is already held then an exception
16
 * is thrown. 
17
 * 
18
 * Some Java runtimes such as JME MIDP do not support file locking or even 
19
 * the Java classes that support locking.  The class is coded to both compile 
20
 * and work on all Java runtimes.  In Java runtimes that do not support 
21
 * locking it will look as though a lock has been obtained but in reality
22
 * no lock has been obtained. 
23
 */
24
import java.io.File;
25
import java.io.IOException;
26
import java.io.RandomAccessFile;
27
import java.lang.reflect.Method;
28
29
public class FileLock {
30
	private File lockFile;
31
	private RandomAccessFile file;
32
	private Object fileLock;
33
	
34
	/**
35
	 * Creates an NIO FileLock on the specified file if on a suitable Java runtime. 
36
	 * @param clientDir the a File of the directory to contain the lock file. 
37
	 * @param lockFilename name of the the file to lock
38
	 * @throws Exception if the lock could not be obtained for any reason
39
	 */
40
	public FileLock(File clientDir, String lockFilename) throws Exception {
41
		// Create a file to obtain a lock on. 
42
		lockFile = new File(clientDir,lockFilename);
43
		if (ExceptionHelper.isClassAvailable("java.nio.channels.FileLock")) {
44
			try {
45
				this.file = new RandomAccessFile(lockFile,"rw");
46
				Method m = file.getClass().getMethod("getChannel",new Class[]{});
47
				Object channel = m.invoke(file,new Object[]{});
48
				m = channel.getClass().getMethod("tryLock",new Class[]{});
49
				this.fileLock = m.invoke(channel, new Object[]{});
50
			} catch(NoSuchMethodException nsme) {
51
				this.fileLock = null;
52
			} catch(IllegalArgumentException iae) {
53
				this.fileLock = null;
54
			} catch(IllegalAccessException iae) {
55
				this.fileLock = null;
56
			}
57
			if (fileLock == null) {
58
				// Lock not obtained
59
				release();
60
				throw new Exception("Problem obtaining file lock");
61
			}
62
		}
63
	}
64
	
65
	/**
66
	 * Releases the lock.
67
	 */
68
	public void release() {
69
		try {
70
			if (fileLock != null) {
71
				Method m = fileLock.getClass().getMethod("release",new Class[]{});
72
				m.invoke(fileLock, new Object[]{});
73
				fileLock =  null;
74
			}
75
		} catch (Exception e) {
76
			// Ignore exceptions
77
		}
78
		if (file != null) {
79
			try {
80
				file.close();
81
			} catch (IOException e) {
82
			}
83
			file = null;
84
		}
85
86
		if (lockFile != null && lockFile.exists()) {
87
			lockFile.delete();
88
		}
89
		lockFile = null;
90
	}
91
	
92
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/LocalNetworkModule.java (-87 lines)
Lines 1-87 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal;
13
14
import java.io.IOException;
15
import java.io.InputStream;
16
import java.io.OutputStream;
17
import java.lang.reflect.Method;
18
19
import org.eclipse.paho.client.mqttv3.MqttException;
20
21
22
/**
23
 * Special comms class that allows an MQTT client to use a non TCP / optimised 
24
 * mechanism to talk to an MQTT server when running in the same JRE instance as the 
25
 * MQTT server.  
26
 *
27
 * This class checks for the existence of the optimised comms adatper class i.e. the one
28
 * that provides the optimised communication mechanism.  If not available the request
29
 * to connect using the optimised mechanism is rejected.  
30
 *  
31
 * The only known server that implements this is the microbroker:- an MQTT server that 
32
 * ships with a number of IBM products.
33
 */
34
public class LocalNetworkModule implements NetworkModule {
35
	private Class LocalListener;
36
	private String brokerName;
37
	private Object localAdapter;
38
	
39
	public LocalNetworkModule(String brokerName) {
40
		this.brokerName = brokerName;
41
	}
42
43
	public void start() throws IOException, MqttException{
44
		if (!ExceptionHelper.isClassAvailable("com.ibm.mqttdirect.modules.local.bindings.LocalListener")) {
45
			throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_SERVER_CONNECT_ERROR);
46
		}
47
		try {
48
			LocalListener = Class.forName("com.ibm.mqttdirect.modules.local.bindings.LocalListener");
49
			Method connect_m = LocalListener.getMethod("connect", new Class[]{ java.lang.String.class });
50
			localAdapter = connect_m.invoke(null,new Object[]{ brokerName });
51
		} catch(Exception e) {
52
		}
53
		if(localAdapter == null) {
54
			throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_SERVER_CONNECT_ERROR);
55
		}
56
	}
57
58
	public InputStream getInputStream() throws IOException {
59
		InputStream stream = null;
60
		try {
61
			Method m = LocalListener.getMethod("getClientInputStream",new Class[]{});
62
			stream = (InputStream)m.invoke(this.localAdapter,new Object[]{});
63
		} catch(Exception e) {
64
		}
65
		return stream;
66
	}
67
	
68
	public OutputStream getOutputStream() throws IOException {
69
		OutputStream stream = null;
70
		try {
71
			Method m = LocalListener.getMethod("getClientOutputStream",new Class[]{});
72
			stream = (OutputStream)m.invoke(this.localAdapter,new Object[]{});
73
		} catch(Exception e) {
74
		}
75
		return stream;
76
	}
77
78
	public void stop() throws IOException {
79
		if (localAdapter != null) {
80
			try {
81
				Method m = LocalListener.getMethod("close",new Class[]{});
82
				m.invoke(this.localAdapter,new Object[]{});
83
			} catch(Exception e) {
84
			}
85
		}
86
	}
87
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/MessageCatalog.java (-42 lines)
Lines 1-42 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal;
13
14
/**
15
 * Catalog of human readable error messages.
16
 */
17
public abstract class MessageCatalog {
18
	private static MessageCatalog INSTANCE = null;
19
20
	public static final String getMessage(int id) {
21
		if (INSTANCE == null) {
22
			if (ExceptionHelper.isClassAvailable("java.util.ResourceBundle")) {
23
				try {
24
					// Hide this class reference behind reflection so that the class does not need to
25
					// be present when compiled on midp
26
					INSTANCE = (MessageCatalog)Class.forName("org.eclipse.paho.client.mqttv3.internal.ResourceBundleCatalog").newInstance();
27
				} catch (Exception e) {
28
					return "";
29
				}
30
			} else if (ExceptionHelper.isClassAvailable("org.eclipse.paho.client.mqttv3.internal.MIDPCatalog")){
31
				try {
32
					INSTANCE = (MessageCatalog)Class.forName("org.eclipse.paho.client.mqttv3.internal.MIDPCatalog").newInstance();
33
				} catch (Exception e) {
34
					return "";
35
				}
36
			}
37
		}
38
		return INSTANCE.getLocalizedMessage(id);
39
	}
40
	
41
	protected abstract String getLocalizedMessage(int id);
42
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/MqttPersistentData.java (-94 lines)
Lines 1-94 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal;
13
14
import org.eclipse.paho.client.mqttv3.MqttPersistable;
15
16
public class MqttPersistentData implements MqttPersistable {
17
	// Message key
18
	private String    key  = null;
19
20
	// Message header
21
	private byte[] header  = null;
22
	private int    hOffset = 0;
23
	private int    hLength = 0;
24
25
	// Message payload
26
	private byte[] payload = null;
27
	private int    pOffset = 0;
28
	private int    pLength = 0;
29
30
	/**
31
	 * Construct a data object to pass across the MQTT client persistence
32
	 * interface.<BR>
33
	 * When this Object is passed to the persistence implementation the key is
34
	 * used by the client to identify the persisted data to which further
35
	 * update or deletion requests are targeted.<BR>
36
	 * When this Object is created for returning to the client when it is
37
	 * recovering its state from persistence the key is not required to be set.
38
	 * The client can determine the key from the data. 
39
	 * @param key     The key which identifies this data
40
	 * @param header  The message header
41
	 * @param hOffset The start offset of the header bytes in header.
42
	 * @param hLength The length of the header in the header bytes array.
43
	 * @param payload The message payload
44
	 * @param pOffset The start offset of the payload bytes in payload.
45
	 * @param pLength The length of the payload in the payload bytes array
46
	 * when persisting the message.
47
	 */
48
	public MqttPersistentData( String key,
49
			byte[] header,
50
			int    hOffset,
51
			int    hLength,
52
			byte[] payload,
53
			int    pOffset,
54
			int    pLength) {
55
		this.key     = key;
56
		this.header  = header;
57
		this.hOffset = hOffset;
58
		this.hLength = hLength;
59
		this.payload = payload;
60
		this.pOffset = pOffset;
61
		this.pLength = pLength;
62
	}
63
64
	public String getKey() {
65
		return key;
66
	}
67
68
	public byte[] getHeaderBytes() {
69
		return header;
70
	}
71
72
	public int getHeaderLength() {
73
		return hLength;
74
	}
75
76
	public int getHeaderOffset() {
77
		return hOffset;
78
	}
79
80
	public byte[] getPayloadBytes() {
81
		return payload;
82
	}
83
84
	public int getPayloadLength() {
85
		if ( payload == null ) {
86
			return 0;
87
		}
88
		return pLength;
89
	}
90
91
	public int getPayloadOffset() {
92
		return pOffset;
93
	}
94
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/NetworkModule.java (-29 lines)
Lines 1-29 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal;
13
14
import java.io.IOException;
15
import java.io.InputStream;
16
import java.io.OutputStream;
17
18
import org.eclipse.paho.client.mqttv3.MqttException;
19
20
21
public interface NetworkModule {
22
	public void start() throws IOException, MqttException;
23
	
24
	public InputStream getInputStream() throws IOException;
25
	
26
	public OutputStream getOutputStream() throws IOException;
27
	
28
	public void stop() throws IOException;
29
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/ResourceBundleCatalog.java (-32 lines)
Lines 1-32 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal;
13
14
import java.util.MissingResourceException;
15
import java.util.ResourceBundle;
16
17
public class ResourceBundleCatalog extends MessageCatalog {
18
	
19
	private ResourceBundle bundle;
20
	
21
	public ResourceBundleCatalog() throws MissingResourceException {
22
		bundle = ResourceBundle.getBundle("org.eclipse.paho.client.mqttv3.internal.nls.messages");
23
	}
24
25
	protected String getLocalizedMessage(int id) {
26
		try {
27
			return bundle.getString(Integer.toString(id));
28
		} catch(MissingResourceException mre) {
29
			return "MqttException";
30
		}
31
	}
32
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/SSLNetworkModule.java (-88 lines)
Lines 1-88 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal;
13
14
import java.io.IOException;
15
16
import javax.net.ssl.SSLSocket;
17
import javax.net.ssl.SSLSocketFactory;
18
19
import org.eclipse.paho.client.mqttv3.MqttException;
20
import org.eclipse.paho.client.mqttv3.logging.Logger;
21
import org.eclipse.paho.client.mqttv3.logging.LoggerFactory;
22
23
/**
24
 * A network module for connecting over SSL.
25
 */
26
public class SSLNetworkModule extends TCPNetworkModule {
27
	private String[] enabledCiphers;
28
	private int handshakeTimeoutSecs;
29
30
	final static String className = SSLNetworkModule.class.getName();
31
	Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className);
32
33
	/**
34
	 * Constructs a new SSLNetworkModule using the specified host and
35
	 * port.  The supplied SSLSocketFactory is used to supply the network
36
	 * socket.
37
	 */
38
	public SSLNetworkModule(SSLSocketFactory factory, String host, int port, String resourceContext) {
39
		super(factory, host, port, resourceContext);
40
		log.setResourceName(resourceContext);
41
	}
42
43
	/**
44
	 * Returns the enabled cipher suites.
45
	 */
46
	public String[] getEnabledCiphers() {
47
		return enabledCiphers;
48
	}
49
50
	/**
51
	 * Sets the enabled cipher suites on the underlying network socket.
52
	 */
53
	public void setEnabledCiphers(String[] enabledCiphers) {
54
		final String methodName = "setEnabledCiphers";
55
		this.enabledCiphers = enabledCiphers;
56
		if ((socket != null) && (enabledCiphers != null)) {
57
			if (log.isLoggable(Logger.FINE)) {
58
				String ciphers = "";
59
				for (int i=0;i<enabledCiphers.length;i++) {
60
					if (i>0) {
61
						ciphers+=",";
62
					}
63
					ciphers+=enabledCiphers[i];
64
				}
65
				//@TRACE 260=setEnabledCiphers ciphers={0}
66
				log.fine(className,methodName,"260",new Object[]{ciphers});
67
			}
68
			((SSLSocket) socket).setEnabledCipherSuites(enabledCiphers);
69
		}
70
	}
71
	
72
	public void setSSLhandshakeTimeout(int timeout) {
73
		this.handshakeTimeoutSecs = timeout;
74
	}
75
	
76
	public void start() throws IOException, MqttException {
77
		super.start();
78
		setEnabledCiphers(enabledCiphers);
79
		int soTimeout = socket.getSoTimeout();
80
		if ( soTimeout == 0 ) {
81
			// RTC 765: Set a timeout to avoid the SSL handshake being blocked indefinitely
82
			socket.setSoTimeout(this.handshakeTimeoutSecs*1000);
83
		}
84
		((SSLSocket)socket).startHandshake();
85
		// reset timeout to default value
86
		socket.setSoTimeout(soTimeout);   
87
	}
88
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/TCPNetworkModule.java (-103 lines)
Lines 1-103 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal;
13
14
import java.io.IOException;
15
import java.io.InputStream;
16
import java.io.OutputStream;
17
import java.net.ConnectException;
18
import java.net.InetSocketAddress;
19
import java.net.Socket;
20
import java.net.SocketAddress;
21
22
import javax.net.SocketFactory;
23
24
import org.eclipse.paho.client.mqttv3.MqttException;
25
import org.eclipse.paho.client.mqttv3.logging.Logger;
26
import org.eclipse.paho.client.mqttv3.logging.LoggerFactory;
27
28
/**
29
 * A network module for connecting over TCP. 
30
 */
31
public class TCPNetworkModule implements NetworkModule {
32
	protected Socket socket;
33
	private SocketFactory factory;
34
	private String host;
35
	private int port;
36
	private int conTimeout;
37
	
38
	final static String className = TCPNetworkModule.class.getName();
39
	Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className);
40
	
41
	/**
42
	 * Constructs a new TCPNetworkModule using the specified host and
43
	 * port.  The supplied SocketFactory is used to supply the network
44
	 * socket.
45
	 */
46
	public TCPNetworkModule(SocketFactory factory, String host, int port, String resourceContext) {
47
		log.setResourceName(resourceContext);
48
		this.factory = factory;
49
		this.host = host;
50
		this.port = port;
51
		
52
	}
53
54
	/**
55
	 * Starts the module, by creating a TCP socket to the server.
56
	 */
57
	public void start() throws IOException, MqttException {
58
		final String methodName = "start";
59
		try {
60
//			InetAddress localAddr = InetAddress.getLocalHost();
61
//			socket = factory.createSocket(host, port, localAddr, 0);
62
			// @TRACE 252=connect to host {0} port {1} timeout {2}
63
			log.fine(className,methodName, "252", new Object[] {host, new Integer(port), new Long(conTimeout*1000)});
64
			SocketAddress sockaddr = new InetSocketAddress(host, port);
65
			socket = factory.createSocket();
66
			socket.connect(sockaddr, conTimeout*1000);
67
		
68
			// SetTcpNoDelay was originally set ot true disabling Nagle's algorithm. 
69
			// This should not be required.
70
//			socket.setTcpNoDelay(true);	// TCP_NODELAY on, which means we do not use Nagle's algorithm
71
		}
72
		catch (ConnectException ex) {
73
			//@TRACE 250=Failed to create TCP socket
74
			log.fine(className,methodName,"250",null,ex);
75
			throw new MqttException(MqttException.REASON_CODE_SERVER_CONNECT_ERROR, ex);
76
		}
77
	}
78
79
	public InputStream getInputStream() throws IOException {
80
		return socket.getInputStream();
81
	}
82
	
83
	public OutputStream getOutputStream() throws IOException {
84
		return socket.getOutputStream();
85
	}
86
87
	/**
88
	 * Stops the module, by closing the TCP socket.
89
	 */
90
	public void stop() throws IOException {
91
		if (socket != null) {
92
			socket.close();
93
		}
94
	}
95
	
96
	/**
97
	 * Set the maximum time to wait for a socket to be established
98
	 * @param timeout
99
	 */
100
	public void setConnectTimeout(int timeout) {
101
		this.conTimeout = timeout;
102
	}
103
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/Token.java (-348 lines)
Lines 1-348 Link Here
1
package org.eclipse.paho.client.mqttv3.internal;
2
3
import org.eclipse.paho.client.mqttv3.IMqttActionListener;
4
import org.eclipse.paho.client.mqttv3.IMqttAsyncClient;
5
import org.eclipse.paho.client.mqttv3.MqttException;
6
import org.eclipse.paho.client.mqttv3.MqttMessage;
7
import org.eclipse.paho.client.mqttv3.internal.wire.MqttAck;
8
import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage;
9
import org.eclipse.paho.client.mqttv3.logging.Logger;
10
import org.eclipse.paho.client.mqttv3.logging.LoggerFactory;
11
12
public class Token {
13
	volatile private boolean completed = false;
14
	private boolean pendingComplete = false;
15
	private boolean sent = false;
16
	
17
	private Object responseLock = new Object();
18
	private Object sentLock = new Object();
19
	
20
	protected MqttMessage message = null; 
21
	private MqttWireMessage response = null;
22
	private MqttException exception = null;
23
	private String[] topics = null;
24
	
25
	private String key;
26
	
27
	private IMqttAsyncClient client = null;
28
	private IMqttActionListener callback = null;
29
	
30
	private Object userContext = null;
31
	
32
	public int messageID = 0;
33
	public boolean notified = false;
34
	
35
	public Token(String logContext) {
36
		log.setResourceName(logContext);
37
	}
38
	
39
	public int getMessageID() {
40
		return messageID;
41
	}
42
43
	public void setMessageID(int messageID) {
44
		this.messageID = messageID;
45
	}
46
47
	final static String className = Token.class.getName();
48
	Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className);
49
	
50
	public boolean checkResult() throws MqttException {
51
		if ( getException() != null)  {
52
			throw getException();
53
		}
54
		return true;
55
	}
56
57
	public MqttException getException() {
58
		return exception;
59
	}
60
61
	public boolean isComplete() {
62
		return completed;
63
	}
64
65
	protected boolean isCompletePending() {
66
		return pendingComplete;
67
	}
68
69
	protected boolean isInUse() {
70
		return (getClient() != null && !isComplete());
71
	}
72
73
	public void setActionCallback(IMqttActionListener listener) {
74
		this.callback  = listener;
75
76
	}
77
	public IMqttActionListener getActionCallback() {
78
		return callback;
79
	}
80
81
	public void waitForCompletion() throws MqttException {
82
		waitForCompletion(-1);
83
	}
84
85
	public void waitForCompletion(long timeout) throws MqttException {
86
		final String methodName = "waitForCompletion";
87
		//@TRACE 407=key={0} wait max={1} token={2}
88
		log.fine(className,methodName, "407",new Object[]{getKey(), new Long(timeout), this});
89
90
		MqttWireMessage resp = waitForResponse(timeout);
91
		if (resp == null && !completed) {
92
			//@TRACE 406=key={0} timed out token={1}
93
			log.fine(className,methodName, "406",new Object[]{getKey(), this});
94
			throw new MqttException(MqttException.REASON_CODE_CLIENT_TIMEOUT);
95
		}
96
		checkResult();
97
	}
98
	
99
	/**
100
	 * Waits for the message delivery to complete, but doesn't throw an exception
101
	 * in the case of a NACK.  It does still throw an exception if something else
102
	 * goes wrong (e.g. an IOException).  This is used for packets like CONNECT, 
103
	 * which have useful information in the ACK that needs to be accessed.
104
	 */
105
	protected MqttWireMessage waitForResponse() throws MqttException {
106
		return waitForResponse(-1);
107
	}
108
	
109
	protected MqttWireMessage waitForResponse(long timeout) throws MqttException {
110
		final String methodName = "waitForResponse";
111
		synchronized (responseLock) {
112
			//@TRACE 400=>key={0} timeout={1} sent={2} completed={3} hasException={4} response={5} token={6}
113
			log.fine(className, methodName, "400",new Object[]{getKey(), new Long(timeout),new Boolean(sent),new Boolean(completed),(exception==null)?"false":"true",response,this},exception);
114
115
			if (!this.completed) {
116
				if (this.exception == null) {
117
					try {
118
						//@TRACE 408=key={0} wait max={1}
119
						log.fine(className,methodName,"408",new Object[] {getKey(),new Long(timeout)});
120
	
121
						if (timeout == -1) {
122
							responseLock.wait();
123
						} else {
124
							responseLock.wait(timeout);
125
						}
126
					} catch (InterruptedException e) {
127
						exception = new MqttException(e);
128
					}
129
				}
130
				if (!this.completed) {
131
					if (this.exception != null) {
132
						//@TRACE 401=failed with exception
133
						log.fine(className,methodName,"401",null,exception);
134
						throw exception;
135
					}
136
				}
137
			}
138
		}
139
		//@TRACE 402=key={0} response={1}
140
		log.fine(className,methodName, "402",new Object[]{getKey(), this.response});
141
		return this.response;
142
	}
143
	
144
	/**
145
	 * Mark the token as complete and ready for users to be notified.
146
	 * @param msg response message. Optional - there are no response messages for some flows
147
	 * @param ex if there was a problem store the exception in the token.
148
	 */
149
	protected void markComplete(MqttWireMessage msg, MqttException ex) {
150
		final String methodName = "markComplete";
151
		//@TRACE 404=>key={0} response={1} excep={2}
152
		log.fine(className,methodName,"404",new Object[]{getKey(),msg,ex});
153
		
154
		synchronized(responseLock) {
155
			// ACK means that everything was OK, so mark the message for garbage collection.
156
			if (msg instanceof MqttAck) {
157
				this.message = null;
158
			}
159
			this.pendingComplete = true;
160
			this.response = msg;
161
			this.exception = ex;
162
		}
163
	}
164
	/**
165
	 * Notifies this token that a response message (an ACK or NACK) has been
166
	 * received.
167
	 */
168
		protected void notifyComplete() {
169
			final String methodName = "notifyComplete";
170
			//@TRACE 411=>key={0} response={1} excep={2}
171
			log.fine(className,methodName,"404",new Object[]{getKey(),this.response, this.exception});
172
173
			synchronized (responseLock) {
174
				// If pending complete is set then normally the token can be marked
175
				// as complete and users notified. An abnormal error may have 
176
				// caused the client to shutdown beween pending complete being set
177
				// and notifying the user.  In this case - the action must be failed.
178
				if (exception == null && pendingComplete) {
179
					completed = true;
180
					pendingComplete = false;
181
				} else {
182
					pendingComplete = false;
183
				}
184
				
185
				responseLock.notifyAll();
186
			}
187
			synchronized (sentLock) {
188
				sent=true;	
189
				sentLock.notifyAll();
190
			}
191
		}
192
	
193
//	/**
194
//	 * Notifies this token that an exception has occurred.  This is only
195
//	 * used for things like IOException, and not for MQTT NACKs.
196
//	 */
197
//	protected void notifyException() {
198
//		final String methodName = "notifyException";
199
//		//@TRACE 405=token={0} excep={1}
200
//		log.fine(className,methodName, "405",new Object[]{this,this.exception});
201
//		synchronized (responseLock) {
202
//			responseLock.notifyAll();
203
//		}
204
//		synchronized (sentLock) {
205
//			sentLock.notifyAll();
206
//		}
207
//	}
208
209
	public void waitUntilSent() throws MqttException {
210
		final String methodName = "waitUntilSent";
211
		synchronized (sentLock) {
212
			synchronized (responseLock) {
213
				if (this.exception != null) {
214
					throw this.exception;
215
				}
216
			}
217
			if (!sent) {
218
				try {
219
					//@TRACE 409=wait key={0}
220
					log.fine(className,methodName, "409",new Object[]{getKey()});
221
222
					sentLock.wait();
223
				} catch (InterruptedException e) {
224
				}
225
			}
226
			
227
			if (!sent) {
228
				if (this.exception == null) {
229
					throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_UNEXPECTED_ERROR);
230
				}
231
				throw this.exception;
232
			}
233
		}
234
	}
235
	
236
	/**
237
	 * Notifies this token that the associated message has been sent
238
	 * (i.e. written to the TCP/IP socket).
239
	 */
240
	protected void notifySent() {
241
		final String methodName = "notifySent";
242
		//@TRACE 403=> key={0}
243
		log.fine(className, methodName, "403",new Object[]{getKey()});
244
		synchronized (responseLock) {
245
			this.response = null;
246
			this.completed = false;
247
		}
248
		synchronized (sentLock) {
249
			sent = true;
250
			sentLock.notifyAll();
251
		}
252
	}
253
	
254
	public IMqttAsyncClient getClient() {
255
		return client;
256
	}
257
	
258
	protected void setClient(IMqttAsyncClient client) {
259
		this.client = client;
260
	}
261
262
	public void reset() throws MqttException {
263
		final String methodName = "reset";
264
		if (isInUse() ) {
265
			// Token is already in use - cannot reset 
266
			throw new MqttException(MqttException.REASON_CODE_TOKEN_INUSE);
267
		}
268
		//@TRACE 410=> key={0}
269
		log.fine(className, methodName, "410",new Object[]{getKey()});
270
		
271
		client = null;
272
		completed = false;
273
		response = null;
274
		sent = false;
275
		exception = null;
276
		userContext = null;
277
	}
278
279
	public MqttMessage getMessage() {
280
		return message;
281
	}
282
	
283
	public MqttWireMessage getWireMessage() {
284
		return response;
285
	}
286
287
	
288
	public void setMessage(MqttMessage msg) {
289
		this.message = msg;
290
	}
291
	
292
	public String[] getTopics() {
293
		return topics;
294
	}
295
	
296
	public void setTopics(String[] topics) {
297
		this.topics = topics;
298
	}
299
	
300
	public Object getUserContext() {
301
		return userContext;
302
	}
303
304
	public void setUserContext(Object userContext) {
305
		this.userContext = userContext;	
306
	}
307
308
	public void setKey(String key) {
309
		this.key = key;
310
	}
311
312
	public String getKey() {
313
		return key;
314
	}
315
316
	public void setException(MqttException exception) {
317
		synchronized(responseLock) {
318
			this.exception = exception;
319
		}
320
	}
321
322
	public boolean isNotified() {
323
		return notified;
324
	}
325
326
	public void setNotified(boolean notified) {
327
		this.notified = notified;
328
	}
329
330
	public String toString() {
331
		StringBuffer tok = new StringBuffer();
332
		tok.append("key="+getKey());
333
		tok.append(" ,topics=");
334
		if (getTopics() != null) {
335
			for (int i=0; i<getTopics().length; i++) {
336
				tok.append(getTopics()[i]+", ");
337
			} 
338
		}
339
		tok.append(" ,usercontext="+getUserContext());
340
		tok.append(" ,isComplete="+isComplete());
341
		tok.append(" ,isNotified="+isNotified());
342
		tok.append(" ,exception="+getException());
343
		tok.append(" ,actioncallback="+getActionCallback());
344
345
		return tok.toString();
346
	}
347
348
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/logcat.properties (-132 lines)
Lines 1-132 Link Here
1
0=MQTT Catalog
2
200=internalSend key={0} message={1} token={2}
3
213=fail: token in use: key={0} message={1} token={2}
4
208=failed: not connected
5
224=failed: not disconnected
6
214=state=CONNECTING
7
207=connect failed: not disconnected {0}
8
215=state=CONNECTED
9
204=connect failed: rc={0}
10
216=state=DISCONNECTING
11
217=state=DISCONNECTED
12
222=>
13
223=failed: in closed state
14
211=failed: already disconnected
15
219=failed: already disconnecting
16
210=failed: called on callback thread
17
218=state=DISCONNECTING
18
220=>
19
212=connect failed: unexpected exception
20
209=connect failed: unexpected exception
21
221=>
22
603=clearState
23
602=key={0} exception
24
601=key={0} message={1}
25
600=>
26
604=inbound QoS 2 publish key={0} message={1}
27
605=outbound QoS 2 pubrel key={0} message={1}
28
606=outbound QoS 2 completed key={0} message={1}
29
607=outbound QoS 2 publish key={0} message={1}
30
608=outbound QoS 1 publish key={0} message={1}
31
609=removing orphaned pubrel key={0}
32
610=QoS 2 publish key={0}
33
611=QoS 2 pubrel key={0}
34
612=QoS 1 publish key={0}
35
613= sending {0} msgs at max inflight window
36
628=pending publish key={0} qos={1} message={2}
37
615=pending send key={0} message {1}
38
618=key={0} QoS={1}
39
620=ping needed. keepAlive={0} lastOutboundActivity={1} lastInboundActivity={2}
40
619=Timed out as no activity, keepAlive={0} lastOutboundActivity={1} lastInboundActivity={2}
41
644=nothing to send, wait for {0} ms
42
621=no outstanding flows and not connected
43
617=+1 inflightpubrels={0}
44
623=+1 actualInFlight={0}
45
622=inflight window full
46
625=key={0}
47
646=-1 actualInFlight={0}
48
626=quiescing={0} actualInFlight={1} pendingFlows={2} inFlightPubRels={3} callbackQuiesce={4} tokens={5}
49
627=received key={0} message={1}
50
651=received key={0} message={1}
51
629=received key={0} token={1} message={2}
52
650=removed Qos 1 publish. key={0}
53
645=removed QoS 2 publish/pubrel. key={0}, -1 inFlightPubRels={1}
54
648=key{0}, msg={1}, excep={2}
55
649=key={0},excep={1}
56
631=connected
57
632=reason {0}
58
633=disconnected
59
637=timeout={0}
60
639=wait for outstanding: actualInFlight={0} pendingFlows={1} inFlightPubRels={2} tokens={3}
61
640=finished
62
638=notifying queueLock holders
63
641=remove publish from persistence. key={0}
64
700=stopping
65
701=notify workAvailable and wait for run
66
703=stopped
67
704=wait for workAvailable
68
706=notify spaceAvailable
69
714=callback threw exception
70
705=callback and notify for key={0}
71
708=call connectionLost
72
720=exception from connectionLost {0}
73
716=call onSuccess key={0}
74
717=call onFailure key {0}
75
709=wait for spaceAvailable
76
710=new msg avail, notify workAvailable
77
711=quiesce notify spaceAvailable
78
713=call messageArrived key={0} topic={1}
79
715=new workAvailable. key={0}
80
719=callback threw ex:
81
855=starting
82
850=stopping
83
851=stopped
84
852=network read message
85
856=Stopping, MQttException
86
853=Stopping due to IOException
87
854=<
88
800=stopping sender
89
801=stopped
90
802=network send key={0} msg={1}
91
803=get message returned null, stopping}
92
805=<
93
804=exception
94
308=<>
95
306=key={0}
96
302=existing key={0} message={1} token={2}
97
303=creating new token key={0} message={1} token={2}
98
300=key={0} message={1}
99
307=key={0} token={1}
100
309=resp={0}
101
310=>
102
311=>
103
312=>
104
305=> {0} tokens
105
260=setEnabledCiphers ciphers={0}
106
252=connect to host {0} port {1} timeout {2}
107
250=Failed to create TCP socket
108
407=key={0} wait max={1} token={2}
109
406=key={0} timed out token={1}
110
400=>key={0} timeout={1} sent={2} completed={3} hasException={4} response={5} token={6}
111
408=key={0} wait max={1}
112
401=failed with exception
113
402=key={0} response={1}
114
404=>key={0} response={1} excep={2}
115
411=>key={0} response={1} excep={2}
116
409=wait key={0}
117
403=> key={0}
118
410=> key={0}
119
101=<init> ClientID={0} ServerURI={1} PersistenceType={2}
120
115=URI={0}
121
103=cleanSession={0} connectionTimeout={1} TimekeepAlive={2} userName={3} password={4} will={5} userContext={6} callback={7}
122
104=> quiesceTimeout={0} userContext={1} callback={2}
123
105=< exception
124
108=<
125
106=Subscribe topic={0} userContext={1} callback={2}
126
109=<
127
107=Unsubscribe topic={0} userContext={1} callback={2}
128
110=<
129
111=< topic={0} message={1}userContext={1} callback={2}
130
112=<
131
113=<
132
114=>
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages.properties (-35 lines)
Lines 1-35 Link Here
1
#/* 
2
# * Copyright (c) 2009, 2012 IBM Corp.
3
# *
4
# * All rights reserved. This program and the accompanying materials
5
# * are made available under the terms of the Eclipse Public License v1.0
6
# * which accompanies this distribution, and is available at
7
# * http://www.eclipse.org/legal/epl-v10.html
8
# *
9
# * Contributors:
10
# *    Dave Locke - initial API and implementation and/or initial documentation
11
# */
12
# NLS_MESSAGEFORMAT_VAR
13
# NLS_ENCODING=UNICODE
14
1=Invalid protocol version
15
2=Invalid client ID
16
3=Broker unavailable
17
4=Bad user name or password
18
5=Not authorized to connect
19
6=Unexpected error
20
32000=Timed out waiting for a response from the server
21
32100=Client is connected
22
32101=Client is disconnected
23
32102=Client is currently disconnecting
24
32103=Unable to connect to server
25
32104=Client is not connected
26
32105=The specified SocketFactory type does not match the broker URI
27
32106=SSL configuration error
28
32107=Disconnecting is not allowed from a callback method
29
32108=Unrecognized packet
30
32109=Connection lost
31
32110=Connect already in progress
32
32111=Client is closed
33
32200=Persistence already in use
34
32201=Token already in use
35
32202=Too many publishes in progress
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_cs.properties (-30 lines)
Lines 1-30 Link Here
1
#/* 
2
# * Copyright (c) 2009, 2012 IBM Corp.
3
# *
4
# * All rights reserved. This program and the accompanying materials
5
# * are made available under the terms of the Eclipse Public License v1.0
6
# * which accompanies this distribution, and is available at
7
# * http://www.eclipse.org/legal/epl-v10.html
8
# *
9
# * Contributors:
10
# *    Dave Locke - initial API and implementation and/or initial documentation
11
# */
12
# NLS_MESSAGEFORMAT_VAR
13
# NLS_ENCODING=UNICODE
14
1=Neplatn\u00e1 verze protokolu
15
2=Neplatn\u00e9 ID klienta
16
3=Nedostupn\u00fd zprost\u0159edkovatel
17
4=Chybn\u00e9 jm\u00e9no u\u017eivatele nebo heslo
18
5=Chyb\u00ed autorizace pro p\u0159ipojen\u00ed
19
32000=Vypr\u0161en\u00ed \u010dasov\u00e9ho limitu pro odpov\u011b\u010f ze serveru
20
32100=Klient je ji\u017e p\u0159ipojen
21
32101=Klient je ji\u017e odpojen
22
32102=Klient se aktu\u00e1ln\u011b odpojuje
23
32103=Nelze se p\u0159ipojit k serveru
24
32104=Klient nen\u00ed p\u0159ipojen
25
32105=Ur\u010den\u00fd typ polo\u017eky SocketFactory neodpov\u00edd\u00e1 identifik\u00e1toru URI zprost\u0159edkovatele.
26
32106=Chyba konfigurace zabezpe\u010den\u00ed SSL
27
32107=Z metody zp\u011btn\u00e9ho vol\u00e1n\u00ed nen\u00ed povoleno odpojen\u00ed
28
32108=Nerozpoznan\u00fd paket
29
32109=P\u0159ipojen\u00ed bylo ztraceno.
30
32200=Perzistence je ji\u017e pou\u017e\u00edv\u00e1na.
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_de.properties (-30 lines)
Lines 1-30 Link Here
1
#/* 
2
# * Copyright (c) 2009, 2012 IBM Corp.
3
# *
4
# * All rights reserved. This program and the accompanying materials
5
# * are made available under the terms of the Eclipse Public License v1.0
6
# * which accompanies this distribution, and is available at
7
# * http://www.eclipse.org/legal/epl-v10.html
8
# *
9
# * Contributors:
10
# *    Dave Locke - initial API and implementation and/or initial documentation
11
# */
12
# NLS_MESSAGEFORMAT_VAR
13
# NLS_ENCODING=UNICODE
14
1=Protokollversion ung\u00fcltig
15
2=Client-ID ung\u00fcltig
16
3=Broker nicht verf\u00fcgbar
17
4=Benutzername oder Kennwort falsch
18
5=Keine Berechtigung f\u00fcr Verbindung
19
32000=Zeitlimit\u00fcberschreitung beim Warten auf eine Antwort vom Server
20
32100=Verbindung zu Client besteht bereits
21
32101=Verbindung zu Client ist bereits getrennt
22
32102=Verbindung zu Client wird derzeit getrennt
23
32103=Verbindung zu Server kann nicht hergestellt werden
24
32104=Keine Verbindung zu Client
25
32105=Der angegebene Socket-Factorytyp entspricht nicht der Broker-URI
26
32106=SSL-Konfigurationsfehler
27
32107=Trennung einer Verbindung \u00fcber eine Callback-Methode ist nicht zul\u00e4ssig
28
32108=Paket nicht erkannt
29
32109=Verbindung wurde getrennt
30
32200=Persistenz wird bereits verwendet
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_es.properties (-30 lines)
Lines 1-30 Link Here
1
#/* 
2
# * Copyright (c) 2009, 2012 IBM Corp.
3
# *
4
# * All rights reserved. This program and the accompanying materials
5
# * are made available under the terms of the Eclipse Public License v1.0
6
# * which accompanies this distribution, and is available at
7
# * http://www.eclipse.org/legal/epl-v10.html
8
# *
9
# * Contributors:
10
# *    Dave Locke - initial API and implementation and/or initial documentation
11
# */
12
# NLS_MESSAGEFORMAT_VAR
13
# NLS_ENCODING=UNICODE
14
1=Versi\u00f3n de protocolo incorrecta
15
2=Identificador de cliente incorrecto
16
3=Intermediario no disponible
17
4=Nombre de usuario o contrase\u00f1a incorrecto
18
5=No autorizado a conectarse
19
32000=Tiempo de espera excedido al esperar una respuesta del servidor
20
32100=Cliente ya conectado
21
32101=Cliente ya desconectado
22
32102=El cliente se est\u00e1 desconectando
23
32103=No es posible conectarse al servidor
24
32104=El cliente no est\u00e1 conectado
25
32105=El tipo SocketFactory especificado no coincide con el URI del intermediario
26
32106=Error de configuraci\u00f3n SSL
27
32107=No se permite la desconexi\u00f3n desde un m\u00e9todo de devoluci\u00f3n de llamada
28
32108=Paquete no reconocido
29
32109=Se ha perdido la conexi\u00f3n
30
32200=La persistencia ya se est\u00e1 utilizando
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_fr.properties (-30 lines)
Lines 1-30 Link Here
1
#/* 
2
# * Copyright (c) 2009, 2012 IBM Corp.
3
# *
4
# * All rights reserved. This program and the accompanying materials
5
# * are made available under the terms of the Eclipse Public License v1.0
6
# * which accompanies this distribution, and is available at
7
# * http://www.eclipse.org/legal/epl-v10.html
8
# *
9
# * Contributors:
10
# *    Dave Locke - initial API and implementation and/or initial documentation
11
# */
12
# NLS_MESSAGEFORMAT_VAR
13
# NLS_ENCODING=UNICODE
14
1=Version de protocole incorrecte
15
2=ID client incorrect
16
3=Courtier indisponible
17
4=Nom d'utilisateur ou mot de passe incorrect
18
5=L'utilisateur n'est pas autoris\u00e9 \u00e0 se connecter
19
32000=Expiration du d\u00e9lai d'attente d'une r\u00e9ponse du serveur
20
32100=Client d\u00e9j\u00e0 connect\u00e9
21
32101=Client d\u00e9j\u00e0 d\u00e9connect\u00e9
22
32102=Client en cours de d\u00e9connexion
23
32103=Impossible de se connecter au serveur
24
32104=Client non connect\u00e9
25
32105=Le type SocketFactory sp\u00e9cifi\u00e9 ne correspond pas \u00e0 l'URI de courtier
26
32106=Erreur de configuration SSL
27
32107=D\u00e9connexion non autoris\u00e9e pour une m\u00e9thode de rappel
28
32108=Paquet non reconnu
29
32109=Connexion perdue
30
32200=La persistance est d\u00e9j\u00e0 en cours d'utilisation
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_hu.properties (-30 lines)
Lines 1-30 Link Here
1
#/* 
2
# * Copyright (c) 2009, 2012 IBM Corp.
3
# *
4
# * All rights reserved. This program and the accompanying materials
5
# * are made available under the terms of the Eclipse Public License v1.0
6
# * which accompanies this distribution, and is available at
7
# * http://www.eclipse.org/legal/epl-v10.html
8
# *
9
# * Contributors:
10
# *    Dave Locke - initial API and implementation and/or initial documentation
11
# */
12
# NLS_MESSAGEFORMAT_VAR
13
# NLS_ENCODING=UNICODE
14
1=\u00c9rv\u00e9nytelen protokoll v\u00e1ltozat
15
2=\u00c9rv\u00e9nytelen \u00fcgyf\u00e9lazonos\u00edt\u00f3
16
3=K\u00f6zvet\u00edt\u0151 nem el\u00e9rhet\u0151
17
4=Rossz felhaszn\u00e1l\u00f3i n\u00e9v vagy jelsz\u00f3
18
5=Nem jogosult csatlakozni
19
32000=T\u00fall\u00e9pte a megengedett id\u0151t a kiszolg\u00e1l\u00f3 v\u00e1lasz\u00e1ra v\u00e1rva
20
32100=Az \u00fcgyf\u00e9l m\u00e1r csatlakozik
21
32101=Az \u00fcgyf\u00e9l m\u00e1r sz\u00e9tkapcsolt
22
32102=Az \u00fcgyf\u00e9l \u00e9pp megszak\u00edtja a kapcsolatot
23
32103=Nem lehet kapcsol\u00f3dni a kiszolg\u00e1l\u00f3hoz
24
32104=Az \u00fcgyf\u00e9l nincs csatlakoztatva
25
32105=A megadott SocketFactory t\u00edpus nem illeszkedik a k\u00f6zvet\u00edt\u0151 URI azonos\u00edt\u00f3hoz
26
32106=SSL konfigur\u00e1ci\u00f3s hiba
27
32107=A megszak\u00edt\u00e1s visszah\u00edv\u00e1s met\u00f3dusb\u00f3l nem enged\u00e9lyezett
28
32108=Ismeretlen csomag
29
32109=Kapcsolat elveszett
30
32200=A megmarad\u00f3 \u00e1llapot m\u00e1r haszn\u00e1latban van
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_it.properties (-30 lines)
Lines 1-30 Link Here
1
#/* 
2
# * Copyright (c) 2009, 2012 IBM Corp.
3
# *
4
# * All rights reserved. This program and the accompanying materials
5
# * are made available under the terms of the Eclipse Public License v1.0
6
# * which accompanies this distribution, and is available at
7
# * http://www.eclipse.org/legal/epl-v10.html
8
# *
9
# * Contributors:
10
# *    Dave Locke - initial API and implementation and/or initial documentation
11
# */
12
# NLS_MESSAGEFORMAT_VAR
13
# NLS_ENCODING=UNICODE
14
1=Versione di protocollo non valida
15
2=ID client non valido
16
3=Broker non disponibile
17
4=Nome utente o password non validi
18
5=Non autorizzato per la connessione
19
32000=Scaduto in attesa di una risposta dal server
20
32100=Client gi\u00e0 connesso
21
32101=Client gi\u00e0 disconnesso
22
32102=Client in fase di disconnessione
23
32103=Impossibile effettuare la connessione al server
24
32104=Client non connesso
25
32105=Il tipo SocketFactory specificato non corrisponde all'URI del broker
26
32106=Errore di configurazione SSL
27
32107=Disconnessione non consentita da un metodo callback
28
32108=Pacchetto non riconosciuto
29
32109=Connessione persa
30
32200=Persistenza gi\u00e0 in uso
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_ja.properties (-30 lines)
Lines 1-30 Link Here
1
#/* 
2
# * Copyright (c) 2009, 2012 IBM Corp.
3
# *
4
# * All rights reserved. This program and the accompanying materials
5
# * are made available under the terms of the Eclipse Public License v1.0
6
# * which accompanies this distribution, and is available at
7
# * http://www.eclipse.org/legal/epl-v10.html
8
# *
9
# * Contributors:
10
# *    Dave Locke - initial API and implementation and/or initial documentation
11
# */
12
# NLS_MESSAGEFORMAT_VAR
13
# NLS_ENCODING=UNICODE
14
1=\u7121\u52b9\u306a\u30d7\u30ed\u30c8\u30b3\u30eb\u30fb\u30d0\u30fc\u30b8\u30e7\u30f3\u3067\u3059
15
2=\u7121\u52b9\u306a\u30af\u30e9\u30a4\u30a2\u30f3\u30c8 ID \u3067\u3059
16
3=\u30d6\u30ed\u30fc\u30ab\u30fc\u304c\u4f7f\u7528\u4e0d\u53ef\u3067\u3059
17
4=\u9593\u9055\u3063\u305f\u30e6\u30fc\u30b6\u30fc\u540d\u307e\u305f\u306f\u30d1\u30b9\u30ef\u30fc\u30c9\u3067\u3059
18
5=\u63a5\u7d9a\u3059\u308b\u6a29\u9650\u304c\u3042\u308a\u307e\u305b\u3093
19
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
20
32100=\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306f\u65e2\u306b\u63a5\u7d9a\u6e08\u307f\u3067\u3059
21
32101=\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306f\u65e2\u306b\u5207\u65ad\u6e08\u307f\u3067\u3059
22
32102=\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306f\u73fe\u5728\u5207\u65ad\u4e2d\u3067\u3059
23
32103=\u30b5\u30fc\u30d0\u30fc\u306b\u63a5\u7d9a\u3067\u304d\u307e\u305b\u3093
24
32104=\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306f\u63a5\u7d9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093
25
32105=\u6307\u5b9a\u3055\u308c\u305f SocketFactory \u30bf\u30a4\u30d7\u306f\u30d6\u30ed\u30fc\u30ab\u30fc URI \u3068\u4e00\u81f4\u3057\u307e\u305b\u3093
26
32106=SSL \u69cb\u6210\u30a8\u30e9\u30fc\u3067\u3059
27
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
28
32108=\u8b58\u5225\u3055\u308c\u3066\u3044\u306a\u3044\u30d1\u30b1\u30c3\u30c8\u3067\u3059
29
32109=\u63a5\u7d9a\u55aa\u5931
30
32200=\u30d1\u30fc\u30b7\u30b9\u30bf\u30f3\u30b9\u306f\u3059\u3067\u306b\u4f7f\u7528\u4e2d\u3067\u3059\u3002
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_ko.properties (-30 lines)
Lines 1-30 Link Here
1
#/* 
2
# * Copyright (c) 2009, 2012 IBM Corp.
3
# *
4
# * All rights reserved. This program and the accompanying materials
5
# * are made available under the terms of the Eclipse Public License v1.0
6
# * which accompanies this distribution, and is available at
7
# * http://www.eclipse.org/legal/epl-v10.html
8
# *
9
# * Contributors:
10
# *    Dave Locke - initial API and implementation and/or initial documentation
11
# */
12
# NLS_MESSAGEFORMAT_VAR
13
# NLS_ENCODING=UNICODE
14
1=\uc62c\ubc14\ub974\uc9c0 \uc54a\uc740 \ud504\ub85c\ud1a0\ucf5c \ubc84\uc804
15
2=\uc62c\ubc14\ub974\uc9c0 \uc54a\uc740 \ud074\ub77c\uc774\uc5b8\ud2b8 ID
16
3=\ube0c\ub85c\ucee4 \uc0ac\uc6a9 \ubd88\uac00\ub2a5
17
4=\uc798\ubabb\ub41c \uc0ac\uc6a9\uc790 \uc774\ub984 \ub610\ub294 \ube44\ubc00\ubc88\ud638
18
5=\uc5f0\uacb0\ud560 \uc218 \uc788\ub294 \uad8c\ud55c\uc774 \ubd80\uc5ec\ub418\uc9c0 \uc54a\uc74c
19
32000=\uc11c\ubc84\uc5d0\uc11c \uc751\ub2f5\uc744 \uae30\ub2e4\ub9ac\ub294 \uc911 \uc81c\ud55c\uc2dc\uac04 \ucd08\uacfc
20
32100=\ud074\ub77c\uc774\uc5b8\ud2b8\uac00 \uc774\ubbf8 \uc5f0\uacb0\ub428
21
32101=\ud074\ub77c\uc774\uc5b8\ud2b8\uac00 \uc774\ubbf8 \uc5f0\uacb0\uc774 \ub04a\uae40
22
32102=\ud604\uc7ac \ud074\ub77c\uc774\uc5b8\ud2b8\uac00 \uc5f0\uacb0\uc744 \ub04a\ub294 \uc911
23
32103=\uc11c\ubc84\uc5d0 \uc5f0\uacb0\ud560 \uc218 \uc5c6\uc74c
24
32104=\ud074\ub77c\uc774\uc5b8\ud2b8\uac00 \uc5f0\uacb0\ub418\uc9c0 \uc54a\uc74c
25
32105=\uc9c0\uc815\ub41c SocketFactory \uc720\ud615\uc774 \ube0c\ub85c\ucee4 URI\uc640 \uc77c\uce58\ud558\uc9c0 \uc54a\uc74c
26
32106=SSL \uad6c\uc131 \uc624\ub958
27
32107=\ucf5c\ubc31 \uba54\uc18c\ub4dc\ub85c\ubd80\ud130 \uc5f0\uacb0\uc744 \ub04a\ub294 \uac83\uc774 \ud5c8\uc6a9\ub418\uc9c0 \uc54a\uc74c
28
32108=\uc778\uc2dd\ub418\uc9c0 \uc54a\uc740 \ud328\ud0b7
29
32109=\uc5f0\uacb0 \uc720\uc2e4
30
32200=\uc9c0\uc18d \ud30c\uc77c\uc744 \uc774\ubbf8 \uc0ac\uc6a9 \uc911
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_pl.properties (-30 lines)
Lines 1-30 Link Here
1
#/* 
2
# * Copyright (c) 2009, 2012 IBM Corp.
3
# *
4
# * All rights reserved. This program and the accompanying materials
5
# * are made available under the terms of the Eclipse Public License v1.0
6
# * which accompanies this distribution, and is available at
7
# * http://www.eclipse.org/legal/epl-v10.html
8
# *
9
# * Contributors:
10
# *    Dave Locke - initial API and implementation and/or initial documentation
11
# */
12
# NLS_MESSAGEFORMAT_VAR
13
# NLS_ENCODING=UNICODE
14
1=Niepoprawna wersja protoko\u0142u
15
2=Niepoprawny identyfikator klienta
16
3=Broker niedost\u0119pny
17
4=Niepoprawna nazwa u\u017cytkownika lub has\u0142o
18
5=Brak autoryzacji do nawi\u0105zania po\u0142\u0105czenia
19
32000=Przekroczono limit czasu oczekiwania na odpowied\u017a z serwera
20
32100=Po\u0142\u0105czenie z klientem zosta\u0142o ju\u017c nawi\u0105zane
21
32101=Klient ju\u017c si\u0119 roz\u0142\u0105czy\u0142
22
32102=Klient roz\u0142\u0105cza si\u0119
23
32103=Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia z serwerem
24
32104=Po\u0142\u0105czenie z klientem nie jest nawi\u0105zane
25
32105=Podany typ fabryki SocketFactory nie jest zgodny z identyfikatorem URI brokera
26
32106=B\u0142\u0105d konfiguracji protoko\u0142u SSL
27
32107=Roz\u0142\u0105czenie nie jest dozwolone w metodzie procedury zwrotnej
28
32108=Nierozpoznany pakiet
29
32109=Utracono po\u0142\u0105czenie
30
32200=Trwa\u0142o\u015b\u0107 jest ju\u017c w u\u017cyciu
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_pt_BR.properties (-30 lines)
Lines 1-30 Link Here
1
#/* 
2
# * Copyright (c) 2009, 2012 IBM Corp.
3
# *
4
# * All rights reserved. This program and the accompanying materials
5
# * are made available under the terms of the Eclipse Public License v1.0
6
# * which accompanies this distribution, and is available at
7
# * http://www.eclipse.org/legal/epl-v10.html
8
# *
9
# * Contributors:
10
# *    Dave Locke - initial API and implementation and/or initial documentation
11
# */
12
# NLS_MESSAGEFORMAT_VAR
13
# NLS_ENCODING=UNICODE
14
1=Vers\u00e3o de protocolo inv\u00e1lida
15
2=ID de cliente inv\u00e1lido
16
3=Broker indispon\u00edvel
17
4=Nome de usu\u00e1rio ou senha inv\u00e1lidos
18
5=N\u00e3o autorizado a conectar
19
32000=Tempo limite atingido ao aguardar por uma resposta do servidor
20
32100=Cliente j\u00e1 conectado
21
32101=Cliente j\u00e1 desconectado
22
32102=Cliente desconectando atualmente
23
32103=N\u00e3o \u00e9 poss\u00edvel se conectar ao servidor
24
32104=O cliente n\u00e3o est\u00e1 conectado
25
32105=O tipo SocketFactory especificado n\u00e3o corresponde ao URI do broker
26
32106=Erro de configura\u00e7\u00e3o de SSL
27
32107=A desconex\u00e3o n\u00e3o \u00e9 permitida a partir de um m\u00e9todo de retorno de chamada
28
32108=Pacote n\u00e3o reconhecido
29
32109=Conex\u00e3o perdida
30
32200=Persist\u00eancia j\u00e1 em uso
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_ru.properties (-30 lines)
Lines 1-30 Link Here
1
#/* 
2
# * Copyright (c) 2009, 2012 IBM Corp.
3
# *
4
# * All rights reserved. This program and the accompanying materials
5
# * are made available under the terms of the Eclipse Public License v1.0
6
# * which accompanies this distribution, and is available at
7
# * http://www.eclipse.org/legal/epl-v10.html
8
# *
9
# * Contributors:
10
# *    Dave Locke - initial API and implementation and/or initial documentation
11
# */
12
# NLS_MESSAGEFORMAT_VAR
13
# NLS_ENCODING=UNICODE
14
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
15
2=\u041d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0439 \u0418\u0414 \u043a\u043b\u0438\u0435\u043d\u0442\u0430
16
3=\u041f\u043e\u0441\u0440\u0435\u0434\u043d\u0438\u043a \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d
17
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
18
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
19
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
20
32100=\u041a\u043b\u0438\u0435\u043d\u0442 \u0443\u0436\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d
21
32101=\u041a\u043b\u0438\u0435\u043d\u0442 \u0443\u0436\u0435 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d
22
32102=\u041a\u043b\u0438\u0435\u043d\u0442 \u043e\u0442\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f
23
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
24
32104=\u041a\u043b\u0438\u0435\u043d\u0442 \u043d\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d
25
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
26
32106=\u041e\u0448\u0438\u0431\u043a\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 SSL
27
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
28
32108=\u041d\u0435\u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u043d\u043d\u044b\u0439 \u043f\u0430\u043a\u0435\u0442
29
32109=\u0421\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043f\u043e\u0442\u0435\u0440\u044f\u043d\u043e
30
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
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_zh_CN.properties (-30 lines)
Lines 1-30 Link Here
1
#/* 
2
# * Copyright (c) 2009, 2012 IBM Corp.
3
# *
4
# * All rights reserved. This program and the accompanying materials
5
# * are made available under the terms of the Eclipse Public License v1.0
6
# * which accompanies this distribution, and is available at
7
# * http://www.eclipse.org/legal/epl-v10.html
8
# *
9
# * Contributors:
10
# *    Dave Locke - initial API and implementation and/or initial documentation
11
# */
12
# NLS_MESSAGEFORMAT_VAR
13
# NLS_ENCODING=UNICODE
14
1=\u65e0\u6548\u534f\u8bae\u7248\u672c
15
2=\u65e0\u6548\u5ba2\u6237\u673a\u6807\u8bc6
16
3=\u4ee3\u7406\u7a0b\u5e8f\u4e0d\u53ef\u7528
17
4=\u9519\u8bef\u7684\u7528\u6237\u540d\u6216\u5bc6\u7801
18
5=\u65e0\u6743\u8fde\u63a5
19
32000=\u7b49\u5f85\u6765\u81ea\u670d\u52a1\u5668\u7684\u54cd\u5e94\u65f6\u8d85\u65f6
20
32100=\u5ba2\u6237\u673a\u5df2\u8fde\u63a5
21
32101=\u5ba2\u6237\u673a\u5df2\u65ad\u5f00\u8fde\u63a5
22
32102=\u5ba2\u6237\u673a\u6b63\u5728\u65ad\u5f00\u8fde\u63a5
23
32103=\u65e0\u6cd5\u8fde\u63a5\u81f3\u670d\u52a1\u5668
24
32104=\u5ba2\u6237\u673a\u672a\u8fde\u63a5
25
32105=\u6307\u5b9a\u7684 SocketFactory \u7c7b\u578b\u4e0e\u4ee3\u7406\u7a0b\u5e8f URI \u4e0d\u5339\u914d
26
32106=SSL \u914d\u7f6e\u9519\u8bef
27
32107=\u4e0d\u5141\u8bb8\u901a\u8fc7\u56de\u8c03\u65b9\u6cd5\u65ad\u5f00\u8fde\u63a5
28
32108=\u4e0d\u53ef\u8bc6\u522b\u7684\u5305
29
32109=\u5df2\u65ad\u5f00\u8fde\u63a5
30
32200=\u6301\u4e45\u6027\u5df2\u5728\u4f7f\u7528\u4e2d
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/nls/messages_zh_TW.properties (-30 lines)
Lines 1-30 Link Here
1
#/* 
2
# * Copyright (c) 2009, 2012 IBM Corp.
3
# *
4
# * All rights reserved. This program and the accompanying materials
5
# * are made available under the terms of the Eclipse Public License v1.0
6
# * which accompanies this distribution, and is available at
7
# * http://www.eclipse.org/legal/epl-v10.html
8
# *
9
# * Contributors:
10
# *    Dave Locke - initial API and implementation and/or initial documentation
11
# */
12
# NLS_MESSAGEFORMAT_VAR
13
# NLS_ENCODING=UNICODE
14
1=\u901a\u8a0a\u5354\u5b9a\u7248\u672c\u7121\u6548
15
2=\u7528\u6236\u7aef ID \u7121\u6548
16
3=\u5206\u914d\u7ba1\u7406\u7cfb\u7d71\u7121\u6cd5\u4f7f\u7528
17
4=\u4f7f\u7528\u8005\u540d\u7a31\u6216\u5bc6\u78bc\u4e0d\u7576
18
5=\u672a\u7372\u6388\u6b0a\u9023\u63a5
19
32000=\u7b49\u5f85\u4f3a\u670d\u5668\u7684\u56de\u61c9\u6642\u903e\u6642
20
32100=\u7528\u6236\u7aef\u5df2\u9023\u63a5
21
32101=\u7528\u6236\u7aef\u5df2\u4e2d\u65b7\u9023\u7dda
22
32102=\u7528\u6236\u7aef\u76ee\u524d\u6b63\u5728\u4e2d\u65b7\u9023\u7dda
23
32103=\u7121\u6cd5\u9023\u63a5\u5230\u4f3a\u670d\u5668
24
32104=\u7528\u6236\u7aef\u672a\u9023\u63a5
25
32105=\u6307\u5b9a\u7684 SocketFactory \u985e\u578b\u8207\u5206\u914d\u7ba1\u7406\u7cfb\u7d71 URI \u4e0d\u7b26
26
32106=SSL \u914d\u7f6e\u932f\u8aa4
27
32107=\u4e0d\u5bb9\u8a31\u8207\u56de\u547c\u65b9\u6cd5\u4e2d\u65b7\u9023\u7dda
28
32108=\u5c01\u5305\u7121\u6cd5\u8fa8\u8b58
29
32109=\u9023\u7dda\u907a\u5931
30
32200=\u6301\u7e8c\u6027\u5df2\u5728\u4f7f\u7528\u4e2d
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/security/SSLSocketFactoryFactory.java (-1352 lines)
Lines 1-1352 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.security;
13
14
import java.io.FileInputStream;
15
import java.io.FileNotFoundException;
16
import java.io.IOException;
17
import java.security.KeyManagementException;
18
import java.security.KeyStore;
19
import java.security.KeyStoreException;
20
import java.security.NoSuchAlgorithmException;
21
import java.security.NoSuchProviderException;
22
import java.security.UnrecoverableKeyException;
23
import java.security.cert.CertificateException;
24
import java.util.Hashtable;
25
import java.util.Iterator;
26
import java.util.Properties;
27
import java.util.Set;
28
import java.util.Vector;
29
30
import javax.net.ssl.KeyManager;
31
import javax.net.ssl.KeyManagerFactory;
32
import javax.net.ssl.SSLContext;
33
import javax.net.ssl.SSLSocketFactory;
34
import javax.net.ssl.TrustManager;
35
import javax.net.ssl.TrustManagerFactory;
36
37
import org.eclipse.paho.client.mqttv3.MqttSecurityException;
38
//import org.eclipse.paho.client.mqttv3.internal.comms.MqttDirectException;
39
//import org.eclipse.paho.client.mqttv3.internal.comms.MqttSSLInitException;
40
import org.eclipse.paho.client.mqttv3.logging.Logger;
41
42
43
/**
44
 * An SSLSocketFactoryFactory provides a socket factory and a server socket
45
 * factory that then can be used to create SSL client sockets or SSL server
46
 * sockets.
47
 * <p>
48
 * The SSLSocketFactoryFactory is configured using IBM SSL properties, i.e.
49
 * properties of the format "com.ibm.ssl.propertyName", e.g.
50
 * "com.ibm.ssl.keyStore". The class supports multiple configurations, each
51
 * configuration is identified using a name or configuration ID. The
52
 * configuration ID with "null" is used as a default configuration. When a
53
 * socket factory is being created for a given configuration, properties of that
54
 * configuration are first picked. If a property is not defined there, then that
55
 * property is looked up in the default configuration. Finally, if a property
56
 * element is still not found, then the corresponding system property is
57
 * inspected, i.e. javax.net.ssl.keyStore. If the system property is not set
58
 * either, then the system's default value is used (if available) or an
59
 * exception is thrown.
60
 * <p>
61
 * The SSLSocketFacotryFactory can be reconfigured at any time. A
62
 * reconfiguration does not affect existing socket factories.
63
 * <p>
64
 * All properties share the same key space; i.e. the configuration ID is not
65
 * part of the property keys.
66
 * <p>
67
 * The methods should be called in the following order:
68
 * <ol>
69
 * <li><b>isSupportedOnJVM()</b>: to check whether this class is supported on
70
 * the runtime platform. Not all runtimes support SSL/TLS.</li>
71
 * <li><b>SSLSocketFactoryFactory()</b>: the constructor. Clients 
72
 * (in the same JVM) may share an SSLSocketFactoryFactory, or have one each.</li>
73
 * <li><b>initialize(properties, configID)</b>: to initialize this object with
74
 * the required SSL properties for a configuration. This may be called multiple
75
 * times, once for each required configuration.It may be called again to change the required SSL
76
 * properties for a particular configuration</li>
77
 * <li><b>getEnabledCipherSuites(configID)</b>: to later set the enabled
78
 * cipher suites on the socket [see below].</li>
79
 * </ol>
80
 * <ul>
81
 * <li><i>For an MQTT server:</i></li>
82
 * <ol>
83
 * <li><b>getKeyStore(configID)</b>: Optionally, to check that if there is no
84
 * keystore, then that all the enabled cipher suits are anonymous.</li>
85
 * <li><b>createServerSocketFactory(configID)</b>: to create an
86
 * SSLServerSocketFactory.</li>
87
 * <li><b>getClientAuthentication(configID)</b>: to later set on the
88
 * SSLServerSocket (itself created from the SSLServerSocketFactory) whether
89
 * client authentication is needed.</li>
90
 * </ol>
91
 * <li><i>For an MQTT client:</i></li>
92
 * <ol>
93
 * <li><b>createSocketFactory(configID)</b>: to create an SSLSocketFactory.</li>
94
 * </ol>
95
 * </ul>
96
 */
97
public class SSLSocketFactoryFactory {
98
	private final static String CLASS_NAME = "org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory";
99
	/**
100
	 * Property keys specific to the client).
101
	 */
102
	public final static String SSLPROTOCOL="com.ibm.ssl.protocol";
103
	public final static String JSSEPROVIDER="com.ibm.ssl.contextProvider";
104
	public final static String KEYSTORE="com.ibm.ssl.keyStore";
105
	public final static String KEYSTOREPWD="com.ibm.ssl.keyStorePassword";
106
	public final static String KEYSTORETYPE="com.ibm.ssl.keyStoreType";
107
	public final static String KEYSTOREPROVIDER="com.ibm.ssl.keyStoreProvider";
108
	public final static String KEYSTOREMGR="com.ibm.ssl.keyManager";
109
	public final static String TRUSTSTORE="com.ibm.ssl.trustStore";
110
	public final static String TRUSTSTOREPWD="com.ibm.ssl.trustStorePassword";
111
	public final static String TRUSTSTORETYPE="com.ibm.ssl.trustStoreType";
112
	public final static String TRUSTSTOREPROVIDER="com.ibm.ssl.trustStoreProvider";
113
	public final static String TRUSTSTOREMGR="com.ibm.ssl.trustManager";
114
	public final static String CIPHERSUITES="com.ibm.ssl.enabledCipherSuites";
115
	public final static String CLIENTAUTH="com.ibm.ssl.clientAuthentication";
116
	
117
	/**
118
	 * Property keys used for java system properties
119
	 */
120
	public final static String SYSKEYSTORE="javax.net.ssl.keyStore";
121
	public final static String SYSKEYSTORETYPE="javax.net.ssl.keyStoreType";
122
	public final static String SYSKEYSTOREPWD="javax.net.ssl.keyStorePassword";
123
	public final static String SYSTRUSTSTORE="javax.net.ssl.trustStore";
124
	public final static String SYSTRUSTSTORETYPE="javax.net.ssl.trustStoreType";
125
	public final static String SYSTRUSTSTOREPWD="javax.net.ssl.trustStorePassword";
126
	public final static String SYSKEYMGRALGO="ssl.KeyManagerFactory.algorithm";
127
	public final static String SYSTRUSTMGRALGO="ssl.TrustManagerFactory.algorithm";
128
	
129
130
	public final static String DEFAULT_PROTOCOL = "TLS";  // "SSL_TLS" is not supported by DesktopEE
131
	
132
	private final static String propertyKeys[] = { SSLPROTOCOL, JSSEPROVIDER,
133
			KEYSTORE, KEYSTOREPWD, KEYSTORETYPE, KEYSTOREPROVIDER, KEYSTOREMGR, 
134
			TRUSTSTORE, TRUSTSTOREPWD, TRUSTSTORETYPE, TRUSTSTOREPROVIDER, 
135
			TRUSTSTOREMGR, CIPHERSUITES, CLIENTAUTH};
136
137
	private Hashtable configs; // a hashtable that maps configIDs to properties.
138
139
	private Properties defaultProperties;
140
141
	private static final byte[] key = { (byte) 0x9d, (byte) 0xa7, (byte) 0xd9,
142
		(byte) 0x80, (byte) 0x05, (byte) 0xb8, (byte) 0x89, (byte) 0x9c };
143
144
	private static final String xorTag = "{xor}";
145
	
146
	private Logger logger = null;
147
148
149
	/**
150
	 * Not all of the JVM/Platforms support all of its
151
	 * security features. This method determines if is supported.
152
	 * 
153
	 * @return whether dependent classes can be instantiated on the current
154
	 *         JVM/platform.
155
	 * 
156
	 * @throws Error
157
	 *             if any unexpected error encountered whilst checking. Note
158
	 *             this should not be a ClassNotFoundException, which should
159
	 *             cause the method to return false.
160
	 */
161
	public static boolean isSupportedOnJVM() throws LinkageError, ExceptionInInitializerError {
162
		String requiredClassname = "javax.net.ssl.SSLServerSocketFactory";
163
		try {
164
			Class.forName(requiredClassname);
165
		} catch (ClassNotFoundException e) {
166
			return false;
167
		}
168
		return true;
169
	}
170
171
172
	/**
173
	 * Create new instance of class.
174
	 * Constructor used by clients.
175
	 */
176
	public SSLSocketFactoryFactory() {
177
		configs = new Hashtable();
178
	}
179
	
180
	/**
181
	 * Create new instance of class.
182
	 * Constructor used by the broker.
183
	 */
184
	public SSLSocketFactoryFactory(Logger logger) {
185
		this();
186
		this.logger = logger;
187
	}
188
189
	/**
190
	 * Checks whether a key belongs to the supported IBM SSL property keys.
191
	 * 
192
	 * @param key
193
	 * @return whether a key belongs to the supported IBM SSL property keys.
194
	 */
195
	private boolean keyValid(String key) {
196
		int i = 0;
197
		while (i < propertyKeys.length) {
198
			if (propertyKeys[i].equals(key)) {
199
				break;
200
			}
201
			++i;
202
		}
203
		return i < propertyKeys.length;
204
	}
205
206
	/**
207
	 * Checks whether the property keys belong to the supported IBM SSL property
208
	 * key set.
209
	 * 
210
	 * @param properties
211
	 * @throws IllegalArgumentException
212
	 *             if any of the properties is not a valid IBM SSL property key.
213
	 */
214
	private void checkPropertyKeys(Properties properties)
215
			throws IllegalArgumentException {
216
		Set keys = properties.keySet();
217
		Iterator i = keys.iterator();
218
		while (i.hasNext()) {
219
			String k = (String) i.next();
220
			if (!keyValid(k)) {
221
				throw new IllegalArgumentException(k + " is not a valid IBM SSL property key.");
222
			}
223
		}
224
	}
225
226
	/**
227
	 * Convert byte array to char array, where each char is constructed from two
228
	 * bytes.
229
	 * 
230
	 * @param b
231
	 *            byte array
232
	 * @return char array
233
	 */
234
	public static char[] toChar(byte[] b) {
235
		if(b==null) return null;
236
		char[] c= new char[b.length/2]; 
237
		int i=0; int j=0;
238
		while(i<b.length) {
239
			c[j++] = (char) ((b[i++] & 0xFF) + ((b[i++] & 0xFF)<<8));
240
		}
241
		return c;
242
	}
243
	
244
	/**
245
	 * Convert char array to byte array, where each char is split into two
246
	 * bytes.
247
	 * 
248
	 * @param c
249
	 *            char array
250
	 * @return byte array
251
	 */
252
	public static byte[] toByte(char[] c) {
253
		if(c==null) return null;
254
		byte[] b=new byte[c.length*2];
255
		int i=0; int j=0;
256
		while(j<c.length) {
257
			b[i++] = (byte) (c[j] & 0xFF);
258
			b[i++] = (byte) ((c[j++] >> 8)& 0xFF);
259
		}
260
		return b;
261
	}
262
	
263
	/**
264
	 * Obfuscates the password using a simple and not very secure XOR mechanism.
265
	 * This should not be used for cryptographical purpose, it's a simple
266
	 * scrambler to obfuscate clear-text passwords.
267
	 * 
268
	 * @see org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory#deObfuscate
269
	 * 
270
	 * @param password
271
	 *            The password to be encrypted, as a char[] array.
272
	 * @return An obfuscated password as a String.
273
	 */
274
	public static String obfuscate(char[] password) {
275
		if (password == null)
276
			return null;
277
		byte[] bytes = toByte(password);
278
		for (int i = 0; i < bytes.length; i++) {
279
			bytes[i] = (byte) ((bytes[i] ^ key[i % key.length]) & 0x00ff);
280
		}
281
		String encryptedValue = xorTag
282
				+ new String(SimpleBase64Encoder.encode(bytes));
283
		return encryptedValue;
284
	}
285
286
	/**
287
	 * The inverse operation of obfuscate: returns a cleartext password that was
288
	 * previously obfuscated using the XOR scrambler.
289
	 * 
290
	 * @see org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory#obfuscate
291
	 * 
292
	 * @param ePassword
293
	 *            An obfuscated password.
294
	 * @return An array of char, containing the clear text password.
295
	 */
296
	public static char[] deObfuscate(String ePassword) {
297
		if (ePassword == null)
298
			return null;
299
		byte[] bytes = null;
300
		try {
301
			bytes = SimpleBase64Encoder.decode(ePassword.substring(xorTag
302
					.length()));
303
		} catch (Exception e) {
304
			return null;
305
		}
306
307
		for (int i = 0; i < bytes.length; i++) {
308
			bytes[i] = (byte) ((bytes[i] ^ key[i % key.length]) & 0x00ff);
309
		}
310
		return toChar(bytes);
311
	}
312
313
	/**
314
	 * Converts an array of ciphers into a single String.
315
	 * 
316
	 * @param ciphers
317
	 *            The array of cipher names.
318
	 * @return A string containing the name of the ciphers, separated by comma.
319
	 */
320
	public static String packCipherSuites(String[] ciphers) {
321
		String cipherSet=null;
322
		if (ciphers != null) {
323
			StringBuffer buf = new StringBuffer();
324
			for (int i = 0; i < ciphers.length; i++) {
325
				buf.append(ciphers[i]);
326
				if (i < ciphers.length - 1) {
327
					buf.append(',');
328
				}
329
			}
330
			cipherSet = buf.toString();
331
		}
332
		return cipherSet;
333
	}
334
335
	/**
336
	 * Inverse operation of packCipherSuites: converts a string of cipher names
337
	 * into an array of cipher names
338
	 * 
339
	 * @param ciphers
340
	 *            A list of ciphers, separated by comma.
341
	 * @return An array of string, each string containing a single cipher name.
342
	 */
343
	public static String[] unpackCipherSuites(String ciphers) {
344
		// can't use split as split is not available on all java platforms.
345
		if(ciphers==null) return null;
346
		Vector c=new Vector();
347
		int i=ciphers.indexOf(',');
348
		int j=0;
349
		// handle all commas.
350
		while(i>-1) {
351
			// add stuff before and up to (but not including) the comma.
352
			c.add(ciphers.substring(j, i));
353
			j=i+1; // skip the comma.
354
			i=ciphers.indexOf(',',j);
355
		}
356
		// add last element after the comma or only element if no comma is present.
357
		c.add(ciphers.substring(j));
358
		String[] s = new String[c.size()];
359
		c.toArray(s);
360
		return s;
361
	}
362
363
	/**
364
	 * Obfuscate any key & trust store passwords within the given properties.
365
	 * 
366
	 * @see org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory#obfuscate
367
	 * 
368
	 * @param p
369
	 *            properties
370
	 */
371
	private void convertPassword(Properties p) {
372
		String pw = p.getProperty(KEYSTOREPWD);
373
		if (pw != null && !pw.startsWith(xorTag)) {
374
			String epw = obfuscate(pw.toCharArray());
375
			p.put(KEYSTOREPWD, epw);
376
		}
377
		pw = p.getProperty(TRUSTSTOREPWD);
378
		if (pw != null && !pw.startsWith(xorTag)) {
379
			String epw = obfuscate(pw.toCharArray());
380
			p.put(TRUSTSTOREPWD, epw);
381
		}
382
	}
383
384
	/**
385
	 * Returns the properties object for configuration configID or creates a new
386
	 * one if required.
387
	 * 
388
	 * @param configID
389
	 *            The configuration identifier for selecting a configuration or
390
	 *            null for the default configuration.
391
	 * @return the properties object for configuration configID
392
	 */
393
//	private Properties getOrCreate(String configID) {
394
//		Properties res = null;
395
//		if (configID == null) {
396
//			if (this.defaultProperties == null) {
397
//				this.defaultProperties = new Properties();
398
//			}
399
//			res = this.defaultProperties;
400
//		} else {
401
//			res = (Properties) this.configs.get(configID);
402
//			if (res == null) {
403
//				res = new Properties();
404
//				this.configs.put(configID, res);
405
//			}
406
//		}
407
//		return res;
408
//	}
409
410
	/**
411
	 * Initializes the SSLSocketFactoryFactory with the provided properties for
412
	 * the provided configuration.
413
	 * 
414
	 * @param props
415
	 *            A properties object containing IBM SSL properties that are
416
	 *            qualified by one or more configuration identifiers.
417
	 * @param configID
418
	 *            The configuration identifier for selecting a configuration or
419
	 *            null for the default configuration.
420
	 * @throws IllegalArgumentException
421
	 *             if any of the properties is not a valid IBM SSL property key.
422
	 */
423
	public void initialize(Properties props, String configID)
424
			throws IllegalArgumentException {
425
		checkPropertyKeys(props);
426
		// copy the properties.
427
		Properties p = new Properties();
428
		p.putAll(props);
429
		convertPassword(p);
430
		if (configID != null) {
431
			this.configs.put(configID, p);
432
		} else {
433
			this.defaultProperties = p;
434
		}
435
	}
436
437
	/**
438
	 * Merges the given IBM SSL properties into the existing configuration,
439
	 * overwriting existing properties. This method is used to selectively
440
	 * change properties for a given configuration. The method throws an
441
	 * IllegalArgumentException if any of the properties is not a valid IBM SSL
442
	 * property key.
443
	 * 
444
	 * @param props
445
	 *            A properties object containing IBM SSL properties
446
	 * @param configID
447
	 *            The configuration identifier for selecting a configuration or
448
	 *            null for the default configuration.
449
	 * @throws IllegalArgumentException
450
	 *             if any of the properties is not a valid IBM SSL property key.
451
	 */
452
	public void merge(Properties props, String configID)
453
			throws IllegalArgumentException {
454
		checkPropertyKeys(props);
455
		Properties p = this.defaultProperties;
456
		if (configID == null) {
457
			p = (Properties) this.configs.get(configID);
458
		}
459
		if (p == null) {
460
			p = new Properties();
461
		}
462
		convertPassword(props);
463
		p.putAll(props);
464
		if (configID != null) {
465
			this.configs.put(configID, p);
466
		} else {
467
			this.defaultProperties = p;
468
		}
469
470
	}
471
472
	/**
473
	 * Remove the configuration of a given configuration identifier.
474
	 * 
475
	 * @param configID
476
	 *            The configuration identifier for selecting a configuration or
477
	 *            null for the default configuration.
478
	 * @return true, if the configuation could be removed.
479
	 */
480
	public boolean remove(String configID) {
481
		boolean res = false;
482
		if (configID != null) {
483
			res = this.configs.remove(configID) != null;
484
		} else {
485
			if(null != this.defaultProperties) {
486
				res = true;
487
				this.defaultProperties = null;
488
			}
489
		}
490
		return res;
491
	}
492
493
	/**
494
	 * Returns the configuration of the SSLSocketFactoryFactory for a given
495
	 * configuration. Note that changes in the property are reflected in the
496
	 * SSLSocketFactoryFactory.
497
	 * 
498
	 * @param configID
499
	 *            The configuration identifier for selecting a configuration or
500
	 *            null for the default configuration.
501
	 * @return A property object containing the current configuration of the
502
	 *         SSLSocketFactoryFactory.  Note that it could be null.
503
	 */
504
	public Properties getConfiguration(String configID) {
505
		return (Properties) (configID == null ? this.defaultProperties
506
				: this.configs.get(configID));
507
	}
508
509
	/**
510
	 * @return Returns the set of configuration IDs that exist in the SSLSocketFactoryFactory.
511
	 */
512
//	public String[] getConfigurationIDs() {
513
//		Set s = this.configs.keySet();
514
//		String[] configs = new String[s.size()];
515
//		configs = (String[]) s.toArray(configs);
516
//		return configs;
517
//	}
518
519
	/**
520
	 * If the value is not null, then put it in the properties object using the
521
	 * key. If the value is null, then remove the entry in the properties object
522
	 * with the key.
523
	 * 
524
	 * @param p
525
	 * @param key
526
	 * @param value
527
	 */
528
//	private final void putOrRemove(Properties p, String key, String value) {
529
//		if (value == null) {
530
//			p.remove(key);
531
//		} else {
532
//			p.put(key, value);
533
//		}
534
//	}
535
536
	/**
537
	 * Sets the SSL protocol variant. If protocol is NULL then an existing value
538
	 * will be removed.
539
	 * 
540
	 * @param configID
541
	 *            The configuration identifier for selecting a configuration or
542
	 *            null for the default configuration.
543
	 * @param protocol
544
	 *            One of SSL, SSLv3, TLS, TLSv1, SSL_TLS
545
	 */
546
//	public void setSSLProtocol(String configID, String protocol) {
547
//		Properties p = getOrCreate(configID);
548
//		putOrRemove(p, SSLPROTOCOL, protocol);
549
//	}
550
551
	/**
552
	 * Sets the JSSE context provider. If provider is null, then an existing
553
	 * value will be removed.
554
	 * 
555
	 * @param configID
556
	 *            The configuration identifier for selecting a configuration or
557
	 *            null for the default configuration.
558
	 * @param provider
559
	 *            The JSSE provider. For example "IBMJSSE2" or "SunJSSE".
560
	 */
561
//	public void setJSSEProvider(String configID, String provider) {
562
//		Properties p = getOrCreate(configID);
563
//		putOrRemove(p, JSSEPROVIDER, provider);
564
//	}
565
566
	/**
567
	 * Sets the filename of the keyStore object. A null value is ignored.
568
	 * 
569
	 * @param configID
570
	 *            The configuration identifier for selecting a configuration or
571
	 *            null for the default configuration.
572
	 * @param keyStore
573
	 *            A filename that points to a valid keystore.
574
	 */
575
//	public void setKeyStore(String configID, String keyStore) {
576
//		if (keyStore == null)
577
//			return;
578
//		Properties p = getOrCreate(configID);
579
//		putOrRemove(p, KEYSTORE, keyStore);
580
//	}
581
582
	/**
583
	 * Sets the password that is used for the keystore. The password must be
584
	 * provided in plain text, but it will be stored internally in a scrambled
585
	 * XOR format.
586
	 * 
587
	 * @see org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory#obfuscate
588
	 * 
589
	 * @param configID
590
	 *            The configuration identifier for selecting a configuration or
591
	 *            null for the default configuration.
592
	 * @param password
593
	 *            The keystore password
594
	 */
595
//	public void setKeyStorePassword(String configID, char[] password) {
596
//		if (password == null)
597
//			return;
598
//		Properties p = getOrCreate(configID);
599
//		// convert password, using XOR-based scrambling.
600
//		String ePasswd = obfuscate(password);
601
//		for(int i=0;i<password.length;i++) {
602
//			password[i]=' ';
603
//		}
604
//		putOrRemove(p, KEYSTOREPWD, ePasswd);
605
//	}
606
607
	/**
608
	 * Sets the keystore provider. The corresponding provider must be installed
609
	 * in the system. Example values: "IBMJCE" or "IBMJCEFIPS".
610
	 * 
611
	 * @param configID
612
	 *            The configuration identifier for selecting a configuration or
613
	 *            null for the default configuration.
614
	 * @param provider
615
	 *            The name of a java cryptography extension
616
	 */
617
//	public void setKeyStoreProvider(String configID, String provider) {
618
//		Properties p = getOrCreate(configID);
619
//		putOrRemove(p, KEYSTOREPROVIDER, provider);
620
//	}
621
622
	/**
623
	 * Sets the keystore type. For example, PKCS12, JKS or JCEKS. The types that
624
	 * are supported depend on the keystore provider.
625
	 * 
626
	 * @param configID
627
	 *            The configuration identifier for selecting a configuration or
628
	 *            null for the default configuration.
629
	 * @param type
630
	 *            The keystore type
631
	 */
632
//	public void setKeyStoreType(String configID, String type) {
633
//		Properties p = getOrCreate(configID);
634
//		putOrRemove(p, KEYSTORETYPE, type);
635
//	}
636
637
	/**
638
	 * Sets a custom key manager and the algorithm that it uses. The keymanager
639
	 * is specified in the format "algorithm|provider", for example
640
	 * "IbmX509|IBMJSSE2". The provider might be empty, in which case the
641
	 * default provider is configured with the specified algorithm. The key
642
	 * manager must implement the javax.net.ssl.X509KeyManager interface.
643
	 * 
644
	 * @param configID
645
	 *            The configuration identifier for selecting a configuration or
646
	 *            null for the default configuration.
647
	 * @param keymanager
648
	 *            An algorithm, provider pair as secified above.
649
	 */
650
//	public void setCustomKeyManager(String configID, String keymanager) {
651
//		Properties p = getOrCreate(configID);
652
//		putOrRemove(p, CUSTOMKEYMGR, keymanager);
653
//	}
654
655
	/**
656
	 * Sets the filename of the truststore object.
657
	 * 
658
	 * @param configID
659
	 *            The configuration identifier for selecting a configuration or
660
	 *            null for the default configuration.
661
	 * @param trustStore
662
	 *            A filename that points to a valid truststore.
663
	 */
664
//	public void setTrustStore(String configID, String trustStore) {
665
//		Properties p = getOrCreate(configID);
666
//		putOrRemove(p, TRUSTSTORE, trustStore);
667
//	}
668
669
	/**
670
	 * Sets the password that is used for the truststore. The password must be
671
	 * provided in plain text, but it will be stored internally in a scrambled
672
	 * XOR format.
673
	 * 
674
	 * @see org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory#obfuscate
675
	 * 
676
	 * @param configID
677
	 *            The configuration identifier for selecting a configuration or
678
	 *            null for the default configuration.
679
	 * @param password
680
	 *            The truststore password.
681
	 */
682
//	public void setTrustStorePassword(String configID, char[] password) {
683
//		Properties p = getOrCreate(configID);
684
//		// convert password, using XOR-based scrambling.
685
//		String ePasswd = obfuscate(password);
686
//		for(int i=0;i<password.length;i++) {
687
//			password[i]=' ';
688
//		}
689
//		putOrRemove(p, TRUSTSTOREPWD, ePasswd);
690
//	}
691
692
	/**
693
	 * Sets the truststore provider. The corresponding provider must be
694
	 * installed in the system. Example values: "IBMJCE" or "IBMJCEFIPS".
695
	 * 
696
	 * @param configID
697
	 *            The configuration identifier for selecting a configuration or
698
	 *            null for the default configuration.
699
	 * @param provider
700
	 *            The name of a java cryptography extension.
701
	 */
702
//	public void setTrustStoreProvider(String configID, String provider) {
703
//		Properties p = getOrCreate(configID);
704
//		putOrRemove(p, TRUSTSTOREPROVIDER, provider);
705
//	}
706
707
	/**
708
	 * Sets the truststore type. For example, PKCS12, JKS or JCEKS. The types
709
	 * that are supported depend on the truststore provider.
710
	 * 
711
	 * @param configID
712
	 *            The configuration identifier for selecting a configuration or
713
	 *            null for the default configuration.
714
	 * @param type
715
	 *            The truststore type.
716
	 */
717
//	public void setTrustStoreType(String configID, String type) {
718
//		Properties p = getOrCreate(configID);
719
//		putOrRemove(p, TRUSTSTORETYPE, type);
720
//	}
721
722
	/**
723
	 * Sets a custom trust managers and the algorithm that it uses. The
724
	 * trustmanager is specified in the format "algorithm|provider", for example
725
	 * "IbmX509|IBMJSSE2". The provider might be empty, in which case the
726
	 * default provider is configured with the specified algorithm. The trust
727
	 * manager must implement the javax.net.ssl.X509TrustManager interface.
728
	 * 
729
	 * @param configID
730
	 *            The configuration identifier for selecting a configuration or
731
	 *            null for the default configuration.
732
	 * @param trustmanager
733
	 *            An algorithm, provider pair as secified above.
734
	 */
735
//	public void setCustomTrustManager(String configID, String trustmanager) {
736
//		Properties p = getOrCreate(configID);
737
//		putOrRemove(p, CUSTOMTRUSTMGR, trustmanager);
738
//	}
739
740
	/**
741
	 * Sets the list of enabled ciphers. For a list of acceptable values, see
742
	 * the documentation of the underlying JSSE.
743
	 * 
744
	 * @param configID
745
	 *            The configuration identifier for selecting a configuration or
746
	 *            null for the default configuration.
747
	 * @param ciphers
748
	 *            An array of cipher suite names such as
749
	 *            SSL_RSA_WITH_AES_128_CBC_SHA.
750
	 */
751
//	public void setEnabledCipherSuites(String configID, String[] ciphers) {
752
//		if (ciphers == null)
753
//			return;
754
//		Properties p = getOrCreate(configID);
755
//		String cipherSet = packCipherSuites(ciphers);
756
//		putOrRemove(p, CIPHERSUITES, cipherSet);
757
//	}
758
759
	/**
760
	 * Specifies whether the client is required to provide a valid certificate
761
	 * to the client during SSL negotiation.
762
	 * 
763
	 * @param configID
764
	 *            The configuration identifier for selecting a configuration or
765
	 *            null for the default configuration.
766
	 * @param clientAuth
767
	 *            true, if clients are required to authenticate, false
768
	 *            otherwise.
769
	 */
770
//	public void setClientAuthentication(String configID, boolean clientAuth) {
771
//		Properties p = getOrCreate(configID);
772
//		p.put(CLIENTAUTH, Boolean.toString(clientAuth));
773
//	}
774
775
	/**
776
	 * Returns the property of a given key or null if it doesn't exist. It first
777
	 * scans the indicated configuration, then the default configuration, then
778
	 * the system properties.
779
	 * 
780
	 * @param configID
781
	 *            The configuration identifier for selecting a configuration or
782
	 *            null for the default configuration.
783
	 * @param ibmKey
784
	 * @param sysProperty
785
	 *            The key for the System property.
786
	 * @return the property of a given key or null if it doesn't exist.
787
	 */
788
	private String getProperty(String configID, String ibmKey, String sysProperty) {
789
		String res = null;
790
		res = getPropertyFromConfig(configID, ibmKey);
791
		if ( res != null ) {
792
			return res;
793
		}
794
		// scan system property, if it exists.
795
		if (sysProperty != null) {
796
			res = System.getProperty(sysProperty);
797
		}
798
		return res;
799
	}
800
801
	/**
802
	 * Returns the property of a given key or null if it doesn't exist. It first
803
	 * scans the indicated configuration, then the default configuration
804
	 * 
805
	 * @param configID
806
	 *            The configuration identifier for selecting a configuration or
807
	 *            null for the default configuration.
808
	 * @param ibmKey
809
	 * @return the property of a given key or null if it doesn't exist. It first
810
	 *         scans the indicated configuration, then the default configuration
811
	 */
812
	private String getPropertyFromConfig(String configID, String ibmKey) {
813
		String res = null;
814
		Properties p =null;
815
		if(configID!=null) {;
816
			p = (Properties) configs.get(configID);
817
		}
818
		if (p != null) {
819
			res = p.getProperty(ibmKey);
820
			if (res != null)
821
				return res;
822
		}
823
		// not found in config. try default properties.
824
		p = (Properties) this.defaultProperties;
825
		if (p != null) {
826
			res = p.getProperty(ibmKey);
827
			if (res != null)
828
				return res;
829
		}
830
		return res;
831
	}
832
833
	/**
834
	 * Gets the SSL protocol variant of the indicated configuration or the
835
	 * default configuration.
836
	 * 
837
	 * @param configID
838
	 *            The configuration identifier for selecting a configuration or
839
	 *            null for the default configuration.
840
	 * @return The SSL protocol variant.
841
	 */
842
	public String getSSLProtocol(String configID) {
843
		return getProperty(configID, SSLPROTOCOL, null);
844
	}
845
846
	/**
847
	 * Gets the JSSE provider of the indicated configuration
848
	 * 
849
	 * @param configID
850
	 *            The configuration identifier for selecting a configuration or
851
	 *            null for the default configuration.
852
	 * @return The JSSE provider.
853
	 */
854
	public String getJSSEProvider(String configID) {
855
		return getProperty(configID, JSSEPROVIDER, null);
856
	}
857
858
//	/**
859
//	 * Get the XPD Keystore if running on the XPD platform (otherwise null).
860
//	 * 
861
//	 * @return the XPD Keystore if running on the XPD platform (otherwise null).
862
//	 * @throws MqttDirectException
863
//	 */
864
//	private KeyStore getXPDKeystore() throws MqttDirectException {
865
//		KeyStore keyStore = null;
866
//		try {
867
//			Class secPlatClass = Class.forName("com.ibm.rcp.security.auth.SecurePlatform");
868
//			Method m = secPlatClass.getMethod("getKeyStore", null);
869
//			Object secPlat = m.invoke(null,null); // getKeyStore is static
870
//			m = secPlatClass.getMethod("isLoggedIn", null);
871
//			Boolean b = (Boolean) m.invoke(secPlat, null);
872
//			if (b.booleanValue()) {
873
//				// login to secure platform was done.
874
//				m = secPlatClass.getMethod("getKeyStore", null);
875
//				keyStore = (KeyStore) m.invoke(secPlat, null);
876
//			}
877
//		} catch (ClassNotFoundException e) {
878
//			/*
879
//			 * DEVELOPER NOTE: This is not an error. This means that we are not
880
//			 * running on XPD runtime and therefore we can not get XPD keystore.
881
//			 * [Next step for the caller, is try to get the keystore from System
882
//			 * properties (see getKeyStore() method).]
883
//			 */
884
//		} catch (IllegalAccessException e) {
885
//			Object[] inserts = { e.getLocalizedMessage() };
886
//			throw new MqttSSLInitException(3026, inserts, e);
887
//		} catch (SecurityException e) {
888
//			Object[] inserts = { e.getLocalizedMessage() };
889
//			throw new MqttSSLInitException(3026, inserts, e);
890
//		} catch (NoSuchMethodException e) {
891
//			Object[] inserts = { e.getLocalizedMessage() };
892
//			throw new MqttSSLInitException(3026, inserts, e);
893
//		} catch (IllegalArgumentException e) {
894
//			Object[] inserts = { e.getLocalizedMessage() };
895
//			throw new MqttSSLInitException(3026, inserts, e);
896
//		} catch (InvocationTargetException e) {
897
//			Object[] inserts = { e.getLocalizedMessage() };
898
//			throw new MqttSSLInitException(3026, inserts, e);
899
//		}
900
//		return keyStore;
901
//	}
902
903
	/**
904
	 * Gets the name of the keystore file that is used.
905
	 * 
906
	 * @param configID
907
	 *            The configuration identifier for selecting a configuration or
908
	 *            null for the default configuration.
909
	 * @return The name of the file that contains the keystore.
910
	 */
911
	public String getKeyStore(String configID) { //throws MqttDirectException {
912
		String ibmKey = KEYSTORE;
913
		String sysProperty = SYSKEYSTORE;
914
		
915
		String res = null;
916
		res = getPropertyFromConfig(configID, ibmKey);
917
		if ( res != null ) {
918
			return res;
919
		}
920
		
921
//		// check for the XPD keystore here
922
//		if ( ibmKey != null && ibmKey.equals(KEYSTORE) ) {
923
//			KeyStore keyStore = getXPDKeystore();
924
//			if (keyStore != null)
925
//				return res = "Lotus Expeditor";
926
//		}
927
		
928
		// scan system property, if it exists.
929
		if (sysProperty != null) {
930
			res = System.getProperty(sysProperty);
931
		}
932
		
933
		return res;
934
	}
935
936
	/**
937
	 * Gets the plain-text password that is used for the keystore.
938
	 * 
939
	 * @param configID
940
	 *            The configuration identifier for selecting a configuration or
941
	 *            null for the default configuration.
942
	 * @return The password in plain text.
943
	 */
944
	public char[] getKeyStorePassword(String configID) {
945
		String pw = getProperty(configID, KEYSTOREPWD, SYSKEYSTOREPWD);
946
		char[] r=null;
947
		if (pw!=null) {
948
			if (pw.startsWith(xorTag)) {
949
				r = deObfuscate(pw);
950
			} else {
951
				r = pw.toCharArray();
952
			}
953
		}
954
		return r;
955
	}
956
957
	/**
958
	 * Gets the type of keystore.
959
	 * 
960
	 * @param configID
961
	 *            The configuration identifier for selecting a configuration or
962
	 *            null for the default configuration.
963
	 * @return The keystore type.
964
	 */
965
	public String getKeyStoreType(String configID) {
966
		return getProperty(configID, KEYSTORETYPE, SYSKEYSTORETYPE);
967
	}
968
969
	/**
970
	 * Gets the keystore provider.
971
	 * 
972
	 * @param configID
973
	 *            The configuration identifier for selecting a configuration or
974
	 *            null for the default configuration.
975
	 * @return The name of the keystore provider.
976
	 */
977
	public String getKeyStoreProvider(String configID) {
978
		return getProperty(configID, KEYSTOREPROVIDER, null);
979
	}
980
981
	/**
982
	 * Gets the key manager algorithm that is used.
983
	 * 
984
	 * @param configID
985
	 *            The configuration identifier for selecting a configuration or
986
	 *            null for the default configuration.
987
	 * @return The key manager algorithm.
988
	 */
989
	public String getKeyManager(String configID) {
990
		return getProperty(configID, KEYSTOREMGR, SYSKEYMGRALGO);
991
	}
992
	
993
	/**
994
	 * Gets the name of the truststore file that is used.
995
	 * 
996
	 * @param configID
997
	 *            The configuration identifier for selecting a configuration or
998
	 *            null for the default configuration.
999
	 * @return The name of the file that contains the truststore.
1000
	 */
1001
	public String getTrustStore(String configID) {
1002
		return getProperty(configID, TRUSTSTORE, SYSTRUSTSTORE);
1003
	}
1004
1005
	/**
1006
	 * Gets the plain-text password that is used for the truststore.
1007
	 * 
1008
	 * @param configID
1009
	 *            The configuration identifier for selecting a configuration or
1010
	 *            null for the default configuration.
1011
	 * @return The password in plain text.
1012
	 */
1013
	public char[] getTrustStorePassword(String configID) {
1014
		String pw = getProperty(configID, TRUSTSTOREPWD, SYSTRUSTSTOREPWD);
1015
		char[] r=null;
1016
		if (pw!=null) {
1017
			if(pw.startsWith(xorTag)) {
1018
				r = deObfuscate(pw);
1019
			} else {
1020
				r = pw.toCharArray();
1021
			}
1022
		}
1023
		return r;
1024
	}
1025
1026
	/**
1027
	 * Gets the type of truststore.
1028
	 * 
1029
	 * @param configID
1030
	 *            The configuration identifier for selecting a configuration or
1031
	 *            null for the default configuration.
1032
	 * @return The truststore type.
1033
	 */
1034
	public String getTrustStoreType(String configID) {
1035
		return getProperty(configID, TRUSTSTORETYPE, null);
1036
	}
1037
1038
	/**
1039
	 * Gets the truststore provider.
1040
	 * 
1041
	 * @param configID
1042
	 *            The configuration identifier for selecting a configuration or
1043
	 *            null for the default configuration.
1044
	 * @return The name of the truststore provider.
1045
	 */
1046
	public String getTrustStoreProvider(String configID) {
1047
		return getProperty(configID, TRUSTSTOREPROVIDER, null);
1048
	}
1049
1050
	/**
1051
	 * Gets the trust manager algorithm that is used.
1052
	 * 
1053
	 * @param configID
1054
	 *            The configuration identifier for selecting a configuration or
1055
	 *            null for the default configuration.
1056
	 * @return The trust manager algorithm.
1057
	 */
1058
	public String getTrustManager(String configID) {
1059
		return getProperty(configID, TRUSTSTOREMGR, SYSTRUSTMGRALGO);
1060
	}
1061
	
1062
	/**
1063
	 * Returns an array with the enabled ciphers.
1064
	 * 
1065
	 * @param configID
1066
	 *            The configuration identifier for selecting a configuration or
1067
	 *            null for the default configuration.
1068
	 * @return an array with the enabled ciphers
1069
	 */
1070
	public String[] getEnabledCipherSuites(String configID) {
1071
		String ciphers = getProperty(configID, CIPHERSUITES, null);
1072
		String[] res = unpackCipherSuites(ciphers);
1073
		return res;
1074
	}
1075
1076
	/**
1077
	 * Returns whether client authentication is required.
1078
	 * 
1079
	 * @param configID
1080
	 *            The configuration identifier for selecting a configuration or
1081
	 *            null for the default configuration.
1082
	 * @return true, if clients are required to authenticate, false otherwise.
1083
	 */
1084
	public boolean getClientAuthentication(String configID) {
1085
		String auth = getProperty(configID, CLIENTAUTH, null);
1086
		boolean res = false;
1087
		if (auth != null) {
1088
			res = Boolean.valueOf(auth).booleanValue();
1089
		}
1090
		return res;
1091
	}
1092
1093
	/**
1094
	 * Initializes key- and truststore. Returns an SSL context factory. If no
1095
	 * SSLProtocol is already set, uses DEFAULT_PROTOCOL
1096
	 * 
1097
	 * @see org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory#DEFAULT_PROTOCOL
1098
	 * 
1099
	 * @param configID
1100
	 *            The configuration ID
1101
	 * @return An SSL context factory.
1102
	 * @throws MqttDirectException
1103
	 */
1104
	private SSLContext getSSLContext(String configID)
1105
			throws MqttSecurityException{
1106
		final String METHOD_NAME = "getSSLContext";
1107
		SSLContext ctx = null;
1108
		
1109
		String protocol = getSSLProtocol(configID);
1110
		if (protocol == null) {
1111
			protocol = DEFAULT_PROTOCOL;
1112
		}
1113
		if (logger != null) {
1114
			// 12000 "SSL initialization: configID = {0}, protocol = {1}"
1115
			logger.fine(CLASS_NAME, METHOD_NAME, "12000", new Object[] {configID!=null ? configID : "null (broker defaults)", 
1116
					protocol});
1117
		}
1118
		
1119
		String provider = getJSSEProvider(configID);
1120
		try {
1121
			if (provider == null) {
1122
				ctx = SSLContext.getInstance(protocol);
1123
			} else {
1124
				ctx = SSLContext.getInstance(protocol, provider);
1125
			}
1126
			if (logger != null) {
1127
				// 12001 "SSL initialization: configID = {0}, provider = {1}"
1128
				logger.fine(CLASS_NAME, METHOD_NAME, "12001", new Object[] {configID!=null ? configID : "null (broker defaults)", 
1129
						ctx.getProvider().getName()});
1130
			}
1131
			
1132
			String keyStoreName = getProperty(configID, KEYSTORE, null);
1133
			KeyStore keyStore=null;
1134
			KeyManagerFactory keyMgrFact=null;
1135
			KeyManager[] keyMgr=null;
1136
//			if(keyStoreName==null) {
1137
//				// try to instantiate XPD keyStore.
1138
//				keyStore=getXPDKeystore();
1139
//				if (logger != null) {
1140
//					if (keyStore == null) {
1141
//						// 12002 "SSL initialization: configID = {0}, XPD keystore not available"
1142
//						logger.fine(CLASS_NAME, METHOD_NAME, "12002", new Object[]{configID!=null ? configID : "null (broker defaults)"});
1143
//					} else {
1144
//						// 12003 "SSL initialization: configID = {0}, XPD keystore available"
1145
//						logger.fine(CLASS_NAME, METHOD_NAME, "12003", new Object[]{configID!=null ? configID : "null (broker defaults)"});
1146
//					}
1147
//				}
1148
//			}
1149
			
1150
			if(keyStore==null) {
1151
				if(keyStoreName==null) {
1152
					/*
1153
					 * No keystore in config, XPD keystore not available. Try to
1154
					 * get config from system properties.
1155
					 */
1156
					keyStoreName = getProperty(configID, KEYSTORE, SYSKEYSTORE);
1157
				}
1158
				if (logger != null) {
1159
					// 12004 "SSL initialization: configID = {0}, keystore = {1}"
1160
					logger.fine(CLASS_NAME, METHOD_NAME, "12004", new Object[]{configID!=null ? configID : "null (broker defaults)", 
1161
							keyStoreName!=null ? keyStoreName : "null"});
1162
				}
1163
				
1164
				char[] keyStorePwd=getKeyStorePassword(configID);
1165
				if (logger != null) {
1166
					// 12005 "SSL initialization: configID = {0}, keystore password = {1}"
1167
					logger.fine(CLASS_NAME, METHOD_NAME, "12005", new Object[]{configID!=null ? configID : "null (broker defaults)", 
1168
							keyStorePwd!=null ? obfuscate(keyStorePwd) : "null"});
1169
				}
1170
				
1171
				String keyStoreType=getKeyStoreType(configID);
1172
				if(keyStoreType==null) {
1173
					keyStoreType = KeyStore.getDefaultType();
1174
				}
1175
				if (logger != null) {
1176
					// 12006 "SSL initialization: configID = {0}, keystore type = {1}"
1177
					logger.fine(CLASS_NAME, METHOD_NAME, "12006", new Object[]{configID!=null ? configID : "null (broker defaults)", 
1178
							keyStoreType!=null ? keyStoreType : "null"});
1179
				}
1180
				
1181
				String keyMgrAlgo = KeyManagerFactory.getDefaultAlgorithm();
1182
				String keyMgrProvider = getKeyStoreProvider(configID);
1183
				String keyManager = getKeyManager(configID);
1184
				if (keyManager != null) {
1185
					keyMgrAlgo = keyManager;
1186
				}
1187
				
1188
				if(keyStoreName!=null && keyStoreType!=null  && keyMgrAlgo!=null) {
1189
					try {
1190
						keyStore=KeyStore.getInstance(keyStoreType);
1191
						keyStore.load(new FileInputStream(keyStoreName), keyStorePwd);
1192
						if(keyMgrProvider!=null) {
1193
							keyMgrFact = KeyManagerFactory.getInstance(keyMgrAlgo, keyMgrProvider);
1194
						} else {
1195
							keyMgrFact = KeyManagerFactory.getInstance(keyMgrAlgo);
1196
						}
1197
						if (logger != null) {
1198
							// 12010 "SSL initialization: configID = {0}, keystore manager algorithm = {1}"
1199
							logger.fine(CLASS_NAME, METHOD_NAME, "12010", new Object[]{configID!=null ? configID : "null (broker defaults)", 
1200
									keyMgrAlgo!=null ? keyMgrAlgo : "null"});
1201
							// 12009 "SSL initialization: configID = {0}, keystore manager provider = {1}"
1202
							logger.fine(CLASS_NAME, METHOD_NAME, "12009", new Object[]{configID!=null ? configID : "null (broker defaults)", 
1203
									keyMgrFact.getProvider().getName()});				
1204
						}
1205
						keyMgrFact.init(keyStore, keyStorePwd);
1206
						keyMgr=keyMgrFact.getKeyManagers();
1207
					} catch (KeyStoreException e) {
1208
						throw new MqttSecurityException(e);
1209
					} catch (CertificateException e) {
1210
						throw new MqttSecurityException(e);
1211
					} catch (FileNotFoundException e) {
1212
						throw new MqttSecurityException(e);
1213
					} catch (IOException e) {
1214
						throw new MqttSecurityException(e);
1215
					} catch (UnrecoverableKeyException e) {
1216
						throw new MqttSecurityException(e);
1217
					}
1218
				}
1219
			}
1220
			// keystore loaded, keymanagers instantiated if possible
1221
			// now the same for the truststore.
1222
			String trustStoreName = getTrustStore(configID);
1223
			if (logger != null) {
1224
				// 12011 "SSL initialization: configID = {0}, truststore = {1}"
1225
				logger.fine(CLASS_NAME, METHOD_NAME, "12011", new Object[]{configID!=null ? configID : "null (broker defaults)", 
1226
						trustStoreName!=null ? trustStoreName : "null"});
1227
			}
1228
			KeyStore trustStore=null;
1229
			TrustManagerFactory trustMgrFact=null;
1230
			TrustManager[] trustMgr=null;
1231
			char[] trustStorePwd=getTrustStorePassword(configID);
1232
			if (logger != null) {
1233
				// 12012 "SSL initialization: configID = {0}, truststore password = {1}"
1234
				logger.fine(CLASS_NAME, METHOD_NAME, "12012", new Object[]{configID!=null ? configID : "null (broker defaults)", 
1235
						trustStorePwd!=null ? obfuscate(trustStorePwd) : "null"});
1236
			}
1237
			String trustStoreType=getTrustStoreType(configID);
1238
			if(trustStoreType==null) {
1239
				trustStoreType = KeyStore.getDefaultType();
1240
			}
1241
			if (logger != null) {
1242
				// 12013 "SSL initialization: configID = {0}, truststore type = {1}"
1243
				logger.fine(CLASS_NAME, METHOD_NAME, "12013", new Object[]{configID!=null ? configID : "null (broker defaults)", 
1244
						trustStoreType!=null ? trustStoreType : "null"});
1245
			}
1246
			
1247
			String trustMgrAlgo = TrustManagerFactory.getDefaultAlgorithm();
1248
			String trustMgrProvider = getTrustStoreProvider(configID);
1249
			String trustManager = getTrustManager(configID);
1250
			if (trustManager != null) {
1251
				trustMgrAlgo = trustManager;
1252
			}
1253
					
1254
			if(trustStoreName!=null && trustStoreType!=null && trustMgrAlgo!=null) {
1255
				try {
1256
					trustStore=KeyStore.getInstance(trustStoreType);
1257
					trustStore.load(new FileInputStream(trustStoreName), trustStorePwd);
1258
					if(trustMgrProvider!=null) {
1259
						trustMgrFact = TrustManagerFactory.getInstance(trustMgrAlgo, trustMgrProvider);
1260
					} else {
1261
						trustMgrFact = TrustManagerFactory.getInstance(trustMgrAlgo);
1262
					}
1263
					if (logger != null) {
1264
						
1265
						// 12017 "SSL initialization: configID = {0}, truststore manager algorithm = {1}"
1266
						logger.fine(CLASS_NAME, METHOD_NAME, "12017", new Object[]{configID!=null ? configID : "null (broker defaults)", 
1267
								trustMgrAlgo!=null ? trustMgrAlgo : "null"});
1268
						
1269
						// 12016 "SSL initialization: configID = {0}, truststore manager provider = {1}"
1270
						logger.fine(CLASS_NAME, METHOD_NAME, "12016", new Object[]{configID!=null ? configID : "null (broker defaults)", 
1271
								trustMgrFact.getProvider().getName()});		
1272
					}
1273
					trustMgrFact.init(trustStore);
1274
					trustMgr=trustMgrFact.getTrustManagers();
1275
				} catch (KeyStoreException e) {
1276
					throw new MqttSecurityException(e);
1277
				} catch (CertificateException e) {
1278
					throw new MqttSecurityException(e);
1279
				} catch (FileNotFoundException e) {
1280
					throw new MqttSecurityException(e);
1281
				} catch (IOException e) {
1282
					throw new MqttSecurityException(e);
1283
				} 
1284
			}
1285
			// done.
1286
			ctx.init(keyMgr, trustMgr, null);
1287
		} catch (NoSuchAlgorithmException e) {
1288
			throw new MqttSecurityException(e);
1289
		} catch (NoSuchProviderException e) {
1290
			throw new MqttSecurityException(e);
1291
		} catch (KeyManagementException e) {
1292
			throw new MqttSecurityException(e);
1293
		}
1294
		return ctx;
1295
	}
1296
1297
//	/**
1298
//	 * Returns an SSL server socket factory for the given configuration. If no
1299
//	 * SSLProtocol is already set, uses DEFAULT_PROTOCOL. Throws
1300
//	 * IllegalArgumentException if the server socket factory could not be
1301
//	 * created due to underlying configuration problems.
1302
//	 * 
1303
//	 * @see org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory#DEFAULT_PROTOCOL
1304
//	 * 
1305
//	 * @param configID
1306
//	 *            The configuration identifier for selecting a configuration.
1307
//	 * @return An SSLServerSocketFactory
1308
//	 * @throws MqttDirectException
1309
//	 */
1310
//	public SSLServerSocketFactory createServerSocketFactory(String configID)
1311
//			throws MqttDirectException {
1312
//		final String METHOD_NAME = "createServerSocketFactory";
1313
//		SSLContext ctx = getSSLContext(configID);
1314
//		if (logger != null) {
1315
//			// 12018 "SSL initialization: configID = {0}, application-enabled cipher suites = {1}"
1316
//			logger.fine(CLASS_NAME, METHOD_NAME, "12018", new Object[]{configID!=null ? configID : "null (broker defaults)", 
1317
//					getEnabledCipherSuites(configID)!=null ? getProperty(configID, CIPHERSUITES, null) : "null (using platform-enabled cipher suites)"});
1318
//			
1319
//			// 12019 "SSL initialization: configID = {0}, client authentication = {1}"
1320
//			logger.fine(CLASS_NAME, METHOD_NAME, "12019", new Object[]{configID!=null ? configID : "null (broker defaults)", 
1321
//					new Boolean (getClientAuthentication(configID)).toString()});
1322
//		}
1323
//		
1324
//		return ctx.getServerSocketFactory();
1325
//	}
1326
1327
	/**
1328
	 * Returns an SSL socket factory for the given configuration. If no
1329
	 * SSLProtocol is already set, uses DEFAULT_PROTOCOL. Throws
1330
	 * IllegalArgumentException if the socket factory could not be created due
1331
	 * to underlying configuration problems.
1332
	 * 
1333
	 * @see org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory#DEFAULT_PROTOCOL
1334
	 * @param configID
1335
	 *            The configuration identifier for selecting a configuration.
1336
	 * @return An SSLSocketFactory
1337
	 * @throws MqttDirectException
1338
	 */
1339
	public SSLSocketFactory createSocketFactory(String configID) 
1340
			throws MqttSecurityException {
1341
		final String METHOD_NAME = "createSocketFactory";
1342
		SSLContext ctx = getSSLContext(configID);
1343
		if (logger != null) {
1344
			// 12020 "SSL initialization: configID = {0}, application-enabled cipher suites = {1}"
1345
			logger.fine(CLASS_NAME, METHOD_NAME, "12020", new Object[]{configID!=null ? configID : "null (broker defaults)", 
1346
					getEnabledCipherSuites(configID)!=null ? getProperty(configID, CIPHERSUITES, null) : "null (using platform-enabled cipher suites)"});
1347
		}
1348
			
1349
		return ctx.getSocketFactory();
1350
	}
1351
1352
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/security/SimpleBase64Encoder.java (-123 lines)
Lines 1-123 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.security;
13
14
public class SimpleBase64Encoder {
15
16
	// if this string is changed, then the decode method must also be adapted.
17
	private final static String PWDCHARS_STRING = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
18
	private final static char[] PWDCHARS_ARRAY = PWDCHARS_STRING.toCharArray();
19
20
	/**
21
	 * Encodes an array of byte into a string of printable ASCII characters
22
	 * using a base-64 encoding.
23
	 * @param bytes The array of bytes to e encoded
24
	 * @return The encoded array.
25
	 */
26
	public static String encode(byte[] bytes) {
27
		// Allocate a string buffer.
28
		int len = bytes.length;
29
		final StringBuffer encoded = new StringBuffer((len+2)/3*4);		
30
		int i=0;
31
		int j=len;
32
		while(j>=3){
33
			encoded.append(to64((((bytes[i] & 0xff) << 16)
34
				| (int) ((bytes[i+1] & 0xff) << 8) | (int) (bytes[i+2] & 0xff)),4));
35
			i+=3;
36
			j-=3;
37
		}
38
		// j==2 | j==1 | j==0
39
		if(j==2) {
40
			// there is a rest of 2 bytes. This encodes into 3 chars.
41
			encoded.append(to64(((bytes[i] &0xff)<<8) | ((bytes[i+1] & 0xff)),3));
42
		}
43
		if(j==1) {
44
			// there is a rest of 1 byte. This encodes into 1 char.
45
			encoded.append(to64(((bytes[i] & 0xff)),2));
46
		}
47
		return encoded.toString();
48
	}
49
50
	public static byte[] decode(String string) {
51
		byte[] encoded=string.getBytes();
52
		int len=encoded.length;
53
		byte[] decoded=new byte[len*3/4];
54
		int i=0;
55
		int j=len;
56
		int k=0;
57
		while(j>=4) {
58
			long d=from64(encoded, i, 4);
59
			j-=4;
60
			i+=4;
61
			for(int l=2;l>=0;l--) {
62
				decoded[k+l]=(byte) (d & 0xff);
63
				d=d >>8;
64
			}
65
			k+=3;
66
		}
67
		// j==3 | j==2 
68
		if(j==3) {
69
			long d=from64(encoded, i, 3);
70
			for(int l=1;l>=0;l--) {
71
				decoded[k+l]=(byte) (d & 0xff);
72
				d=d >>8;
73
			}			
74
		}
75
		if(j==2) {
76
			long d=from64(encoded, i, 2);
77
			decoded[k]=(byte) (d & 0xff);
78
		}			
79
		return decoded;
80
	}
81
82
	/* the core conding routine. Translates an input integer into
83
	 * a string of the given length.*/
84
	private final static String to64(long input, int size) {
85
		final StringBuffer result = new StringBuffer(size);
86
		while (size > 0) {
87
			size--;
88
			result.append(PWDCHARS_ARRAY[((int) (input & 0x3f))]);
89
			input = input >> 6;
90
		}
91
		return result.toString();
92
	}
93
94
	/*
95
	 * The reverse operation of to64
96
	 */
97
	private final static long from64(byte[] encoded, int idx, int size) {
98
		long res=0;
99
		int f=0;
100
		while(size>0) {
101
			size--;
102
			long r=0;
103
			// convert encoded[idx] back into a 6-bit value.
104
			byte d=encoded[idx++];
105
			if(d=='/') {
106
				r=1;
107
			}
108
			if(d>='0' && d<='9') {
109
				r=2+d-'0';
110
			}
111
			if(d>='A' && d<='Z') {
112
				r=12+d-'A';
113
			}
114
			if(d>='a' && d<='z') {
115
				r=38+d-'a';
116
			}
117
			res=res+((long)r << f);
118
			f+=6;
119
		}
120
		return res;
121
	}
122
123
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/CountingInputStream.java (-54 lines)
Lines 1-54 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import java.io.IOException;
15
import java.io.InputStream;
16
17
/**
18
 * An input stream that counts the bytes read from it.
19
 */
20
public class CountingInputStream extends InputStream {
21
	private InputStream in;
22
	private int counter;
23
24
	/**
25
	 * Constructs a new <code>CountingInputStream</code> wrapping the supplied
26
	 * input stream.
27
	 */
28
	public CountingInputStream(InputStream in) {
29
		this.in = in;
30
		this.counter = 0;
31
	}
32
	
33
	public int read() throws IOException {
34
		int i = in.read();
35
		if (i != -1) {
36
			counter++;
37
		}
38
		return i;
39
	}
40
41
	/**
42
	 * Returns the number of bytes read since the last reset.
43
	 */
44
	public int getCounter() {
45
		return counter;
46
	}
47
	
48
	/**
49
	 * Resets the counter to zero.
50
	 */
51
	public void resetCounter() {
52
		counter = 0;
53
	}
54
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttAck.java (-27 lines)
Lines 1-27 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
15
/**
16
 * Abstract super-class of all acknowledgement messages.
17
 */
18
public abstract class MqttAck extends MqttWireMessage {
19
	public MqttAck(byte type) {
20
		super(type);
21
	}
22
	
23
	protected byte getMessageInfo() {
24
		return 0;
25
	}
26
27
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttConnack.java (-55 lines)
Lines 1-55 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import java.io.ByteArrayInputStream;
15
import java.io.DataInputStream;
16
import java.io.IOException;
17
18
import org.eclipse.paho.client.mqttv3.MqttException;
19
20
21
/**
22
 * An on-the-wire representation of an MQTT CONNACK.
23
 */
24
public class MqttConnack extends MqttAck {
25
	private int returnCode;
26
	
27
	public MqttConnack(byte info, byte[] variableHeader) throws IOException {
28
		super(MqttWireMessage.MESSAGE_TYPE_CONNACK);
29
		ByteArrayInputStream bais = new ByteArrayInputStream(variableHeader);
30
		DataInputStream dis = new DataInputStream(bais);
31
		dis.readByte();
32
		returnCode = dis.readUnsignedByte();
33
		dis.close();
34
	}
35
	
36
	public int getReturnCode() {
37
		return returnCode;
38
	}
39
40
	protected byte[] getVariableHeader() throws MqttException {
41
		// Not needed, as the client never encodes a CONNACK
42
		return new byte[0];
43
	}
44
	
45
	/**
46
	 * Returns whether or not this message needs to include a message ID.
47
	 */
48
	public boolean isMessageIdRequired() {
49
		return false;
50
	}
51
	
52
	public String getKey() {
53
		return new String("Con");
54
	}
55
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttConnect.java (-131 lines)
Lines 1-131 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import java.io.ByteArrayOutputStream;
15
import java.io.DataOutputStream;
16
import java.io.IOException;
17
18
import org.eclipse.paho.client.mqttv3.MqttException;
19
import org.eclipse.paho.client.mqttv3.MqttMessage;
20
21
/**
22
 * An on-the-wire representation of an MQTT CONNECT message.
23
 */
24
public class MqttConnect extends MqttWireMessage {
25
	private String clientId;
26
	private boolean cleanSession;
27
	private MqttMessage willMessage;
28
	private String userName;
29
	private char[] password;
30
	private int keepAliveInterval;
31
	private String willDestination;
32
	public static String KEY="Con";
33
	
34
	
35
	public MqttConnect(String clientId,
36
			boolean cleanSession,
37
			int keepAliveInterval,
38
			String userName,
39
			char[] password,
40
			MqttMessage willMessage,
41
			String willDestination) {
42
		super(MqttWireMessage.MESSAGE_TYPE_CONNECT);
43
		this.clientId = clientId;
44
		this.cleanSession = cleanSession;
45
		this.keepAliveInterval = keepAliveInterval;
46
		this.userName = userName;
47
		this.password = password;
48
		this.willMessage = willMessage;
49
		this.willDestination = willDestination;
50
	}
51
	
52
	protected byte getMessageInfo() {
53
		return (byte) 0;
54
	}
55
56
	public boolean isCleanSession() {
57
		return cleanSession;
58
	}
59
	
60
	protected byte[] getVariableHeader() throws MqttException {
61
		try {
62
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
63
			DataOutputStream dos = new DataOutputStream(baos);
64
			this.encodeUTF8(dos,"MQIsdp");			
65
			dos.write(3);
66
			byte connectFlags = 0;
67
			
68
			if (cleanSession) {
69
				connectFlags |= 0x02;
70
			}
71
			
72
			if (willMessage != null ) {
73
				connectFlags |= 0x04;
74
				connectFlags |= (willMessage.getQos()<<3);
75
				if (willMessage.isRetained()) {
76
					connectFlags |= 0x20;
77
				}
78
			}
79
			
80
			if (userName != null) {
81
				connectFlags |= 0x80;
82
				if (password != null) {
83
					connectFlags |= 0x40;
84
				}
85
			}
86
			dos.write(connectFlags);
87
			dos.writeShort(keepAliveInterval);
88
			dos.flush();
89
			return baos.toByteArray();
90
		} catch(IOException ioe) {
91
			throw new MqttException(ioe);
92
		}
93
	}
94
	
95
	public byte[] getPayload() throws MqttException {
96
		try {
97
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
98
			DataOutputStream dos = new DataOutputStream(baos);
99
			this.encodeUTF8(dos,clientId);
100
			
101
			if (willMessage != null) {
102
				this.encodeUTF8(dos,willDestination);
103
				dos.writeShort(willMessage.getPayload().length);
104
				dos.write(willMessage.getPayload());
105
			}
106
			
107
			if (userName != null) {
108
				this.encodeUTF8(dos,userName);
109
				if (password != null) {
110
					this.encodeUTF8(dos,new String(password));
111
				}
112
			}
113
			dos.flush();
114
			return baos.toByteArray();
115
		}
116
		catch (IOException ex) {
117
			throw new MqttException(ex);
118
		}
119
	}
120
	
121
	/**
122
	 * Returns whether or not this message needs to include a message ID.
123
	 */
124
	public boolean isMessageIdRequired() {
125
		return false;
126
	}
127
	
128
	public String getKey() {
129
		return new String(KEY);
130
	}
131
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttDisconnect.java (-44 lines)
Lines 1-44 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import org.eclipse.paho.client.mqttv3.MqttException;
15
16
/**
17
 * An on-the-wire representation of an MQTT DISCONNECT message.
18
 */
19
public class MqttDisconnect extends MqttWireMessage {
20
	public static String KEY="Disc";
21
	
22
	public MqttDisconnect() {
23
		super(MqttWireMessage.MESSAGE_TYPE_DISCONNECT);
24
	}
25
	
26
	protected byte getMessageInfo() {
27
		return (byte) 0;
28
	}
29
30
	protected byte[] getVariableHeader() throws MqttException {
31
		return new byte[0];
32
	}
33
34
	/**
35
	 * Returns whether or not this message needs to include a message ID.
36
	 */
37
	public boolean isMessageIdRequired() {
38
		return false;
39
	}
40
	
41
	public String getKey() {
42
		return new String(KEY);
43
	}
44
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttInputStream.java (-69 lines)
Lines 1-69 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import java.io.ByteArrayOutputStream;
15
import java.io.DataInputStream;
16
import java.io.IOException;
17
import java.io.InputStream;
18
19
import org.eclipse.paho.client.mqttv3.MqttException;
20
import org.eclipse.paho.client.mqttv3.internal.ExceptionHelper;
21
22
23
/**
24
 * An <code>MqttInputStream</code> lets applications read instances of
25
 * <code>MqttWireMessage</code>. 
26
 */
27
public class MqttInputStream extends InputStream {
28
	private DataInputStream in;
29
30
	public MqttInputStream(InputStream in) {
31
		this.in = new DataInputStream(in);
32
	}
33
	
34
	public int read() throws IOException {
35
		return in.read();
36
	}
37
	
38
	public int available() throws IOException {
39
		return in.available();
40
	}
41
	
42
	public void close() throws IOException {
43
		in.close();
44
	}
45
	
46
	/**
47
	 * Reads an <code>MqttWireMessage</code> from the stream.
48
	 */
49
	public MqttWireMessage readMqttWireMessage() throws IOException, MqttException {
50
		ByteArrayOutputStream bais = new ByteArrayOutputStream();
51
		byte first = in.readByte();
52
		byte type = (byte) ((first >>> 4) & 0x0F);
53
		if ((type < MqttWireMessage.MESSAGE_TYPE_CONNECT) ||
54
			(type > MqttWireMessage.MESSAGE_TYPE_DISCONNECT)) {
55
			// Invalid MQTT message type...
56
			throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_INVALID_MESSAGE);
57
		}
58
		long remLen = MqttWireMessage.readMBI(in).getValue();
59
		bais.write(first);
60
		// bit silly, we decode it then encode it
61
		bais.write(MqttWireMessage.encodeMBI(remLen));
62
		byte[] packet = new byte[(int)(bais.size()+remLen)];
63
		in.readFully(packet,bais.size(),packet.length - bais.size());
64
		byte[] header = bais.toByteArray();
65
		System.arraycopy(header,0,packet,0, header.length);
66
		MqttWireMessage message = MqttWireMessage.createWireMessage(packet);
67
		return message;
68
	}
69
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttOutputStream.java (-64 lines)
Lines 1-64 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import java.io.BufferedOutputStream;
15
import java.io.IOException;
16
import java.io.OutputStream;
17
18
import org.eclipse.paho.client.mqttv3.MqttException;
19
20
21
/**
22
 * An <code>MqttOutputStream</code> lets applications write instances of
23
 * <code>MqttWireMessage</code>. 
24
 */
25
public class MqttOutputStream extends OutputStream {
26
	private BufferedOutputStream out;
27
	
28
	public MqttOutputStream(OutputStream out) {
29
		this.out = new BufferedOutputStream(out);
30
	}
31
	
32
	public void close() throws IOException {
33
		out.close();
34
	}
35
	
36
	public void flush() throws IOException {
37
		out.flush();
38
	}
39
	
40
	public void write(byte[] b) throws IOException {
41
		out.write(b);
42
	}
43
	
44
	public void write(byte[] b, int off, int len) throws IOException {
45
		out.write(b, off, len);
46
	}
47
	
48
	public void write(int b) throws IOException {
49
		out.write(b);
50
	}
51
52
	/**
53
	 * Writes an <code>MqttWireMessage</code> to the stream.
54
	 */
55
	public void write(MqttWireMessage message) throws IOException, MqttException {
56
		byte[] bytes = message.getHeader();
57
		byte[] pl = message.getPayload();
58
//		out.write(message.getHeader());
59
//		out.write(message.getPayload());
60
		out.write(bytes,0,bytes.length);
61
		out.write(pl,0,pl.length);
62
	}
63
}
64
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPersistableWireMessage.java (-63 lines)
Lines 1-63 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import org.eclipse.paho.client.mqttv3.MqttException;
15
import org.eclipse.paho.client.mqttv3.MqttPersistable;
16
import org.eclipse.paho.client.mqttv3.MqttPersistenceException;
17
18
public abstract class MqttPersistableWireMessage extends MqttWireMessage
19
		implements MqttPersistable {
20
	
21
	public MqttPersistableWireMessage(byte type) {
22
		super(type);
23
	}
24
	
25
	public byte[] getHeaderBytes() throws MqttPersistenceException {
26
		try {
27
			return getHeader();
28
		}
29
		catch (MqttException ex) {
30
			throw new MqttPersistenceException(ex.getCause());
31
		}
32
	}
33
	
34
	public int getHeaderLength() throws MqttPersistenceException {
35
		return getHeaderBytes().length;
36
	}
37
38
	public int getHeaderOffset() throws MqttPersistenceException{
39
		return 0;
40
	}
41
42
//	public String getKey() throws MqttPersistenceException {
43
//		return new Integer(getMessageId()).toString();
44
//	}
45
46
	public byte[] getPayloadBytes() throws MqttPersistenceException {
47
		try {
48
			return getPayload();
49
		}
50
		catch (MqttException ex) {
51
			throw new MqttPersistenceException(ex.getCause());
52
		}
53
	}
54
	
55
	public int getPayloadLength() throws MqttPersistenceException {
56
		return 0;
57
	}
58
59
	public int getPayloadOffset() throws MqttPersistenceException {
60
		return 0;
61
	}
62
63
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPingReq.java (-44 lines)
Lines 1-44 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import org.eclipse.paho.client.mqttv3.MqttException;
15
16
/**
17
 * An on-the-wire representation of an MQTT PINGREQ message.
18
 */
19
public class MqttPingReq extends MqttWireMessage {
20
	public MqttPingReq() {
21
		super(MqttWireMessage.MESSAGE_TYPE_PINGREQ);
22
	}
23
	
24
	/**
25
	 * Returns <code>false</code> as message IDs are not required for MQTT
26
	 * PINGREQ messages.
27
	 */
28
	public boolean isMessageIdRequired() {
29
		return false;
30
	}
31
32
	protected byte[] getVariableHeader() throws MqttException {
33
		return new byte[0];
34
	}
35
	
36
	protected byte getMessageInfo() {
37
		return 0;
38
	}
39
	
40
	public String getKey() {
41
		return new String("Ping");
42
	}
43
}
44
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPingResp.java (-40 lines)
Lines 1-40 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import org.eclipse.paho.client.mqttv3.MqttException;
15
16
17
/**
18
 * An on-the-wire representation of an MQTT PINGRESP.
19
 */
20
public class MqttPingResp extends MqttAck {
21
	public MqttPingResp(byte info, byte[] variableHeader) {
22
		super(MqttWireMessage.MESSAGE_TYPE_PINGRESP);
23
	}
24
	
25
	protected byte[] getVariableHeader() throws MqttException {
26
		// Not needed, as the client never encodes a PINGRESP
27
		return new byte[0];
28
	}
29
	
30
	/**
31
	 * Returns whether or not this message needs to include a message ID.
32
	 */
33
	public boolean isMessageIdRequired() {
34
		return false;
35
	}
36
	
37
	public String getKey() {
38
		return new String("Ping");
39
	}
40
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubAck.java (-42 lines)
Lines 1-42 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import java.io.ByteArrayInputStream;
15
import java.io.DataInputStream;
16
import java.io.IOException;
17
18
import org.eclipse.paho.client.mqttv3.MqttException;
19
20
21
22
/**
23
 * An on-the-wire representation of an MQTT PUBACK message.
24
 */
25
public class MqttPubAck extends MqttAck {
26
	public MqttPubAck(byte info, byte[] data) throws IOException {
27
		super(MqttWireMessage.MESSAGE_TYPE_PUBACK);
28
		ByteArrayInputStream bais = new ByteArrayInputStream(data);
29
		DataInputStream dis = new DataInputStream(bais);
30
		msgId = dis.readUnsignedShort();
31
		dis.close();
32
	}
33
	
34
	public MqttPubAck(MqttPublish publish) {
35
		super(MqttWireMessage.MESSAGE_TYPE_PUBACK);
36
		msgId = publish.getMessageId();
37
	}
38
	
39
	protected byte[] getVariableHeader() throws MqttException {
40
		return encodeMessageId();
41
	}
42
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubComp.java (-47 lines)
Lines 1-47 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import java.io.ByteArrayInputStream;
15
import java.io.DataInputStream;
16
import java.io.IOException;
17
18
import org.eclipse.paho.client.mqttv3.MqttException;
19
20
21
22
/**
23
 * An on-the-wire representation of an MQTT PUBCOMP message.
24
 */
25
public class MqttPubComp extends MqttAck {
26
	public MqttPubComp(byte info, byte[] data) throws IOException {
27
		super(MqttWireMessage.MESSAGE_TYPE_PUBCOMP);
28
		ByteArrayInputStream bais = new ByteArrayInputStream(data);
29
		DataInputStream dis = new DataInputStream(bais);
30
		msgId = dis.readUnsignedShort();
31
		dis.close();
32
	}
33
	
34
	public MqttPubComp(MqttPublish publish) {
35
		super(MqttWireMessage.MESSAGE_TYPE_PUBCOMP);
36
		this.msgId = publish.getMessageId();
37
	}
38
	
39
	public MqttPubComp(int msgId) {
40
		super(MqttWireMessage.MESSAGE_TYPE_PUBCOMP);
41
		this.msgId = msgId;
42
	}
43
	
44
	protected byte[] getVariableHeader() throws MqttException {
45
		return encodeMessageId();
46
	}
47
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubRec.java (-42 lines)
Lines 1-42 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import java.io.ByteArrayInputStream;
15
import java.io.DataInputStream;
16
import java.io.IOException;
17
18
import org.eclipse.paho.client.mqttv3.MqttException;
19
20
21
22
/**
23
 * An on-the-wire representation of an MQTT PUBREC message.
24
 */
25
public class MqttPubRec extends MqttAck {
26
	public MqttPubRec(byte info, byte[] data) throws IOException {
27
		super(MqttWireMessage.MESSAGE_TYPE_PUBREC);
28
		ByteArrayInputStream bais = new ByteArrayInputStream(data);
29
		DataInputStream dis = new DataInputStream(bais);
30
		msgId = dis.readUnsignedShort();
31
		dis.close();
32
	}
33
	
34
	public MqttPubRec(MqttPublish publish) {
35
		super(MqttWireMessage.MESSAGE_TYPE_PUBREC);
36
		msgId = publish.getMessageId();
37
	}
38
	
39
	protected byte[] getVariableHeader() throws MqttException {
40
		return encodeMessageId();
41
	}
42
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPubRel.java (-56 lines)
Lines 1-56 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import java.io.ByteArrayInputStream;
15
import java.io.DataInputStream;
16
import java.io.IOException;
17
18
import org.eclipse.paho.client.mqttv3.MqttException;
19
20
/**
21
 * An on-the-wire representation of an MQTT PUBREL message.
22
 */
23
public class MqttPubRel extends MqttPersistableWireMessage {
24
25
	/**
26
	 * Createa a pubrel message based on a pubrec
27
	 * @param pubRec
28
	 */
29
	public MqttPubRel(MqttPubRec pubRec) {
30
		super(MqttWireMessage.MESSAGE_TYPE_PUBREL);
31
		this.setMessageId(pubRec.getMessageId());
32
	}
33
	
34
	/**
35
	 * Creates a pubrel based on a pubrel set of bytes read fro the network
36
	 * @param info
37
	 * @param data
38
	 * @throws IOException
39
	 */
40
	public MqttPubRel(byte info, byte[] data) throws IOException {
41
		super(MqttWireMessage.MESSAGE_TYPE_PUBREL);
42
		ByteArrayInputStream bais = new ByteArrayInputStream(data);
43
		DataInputStream dis = new DataInputStream(bais);
44
		msgId = dis.readUnsignedShort();
45
		dis.close();
46
	}
47
48
	protected byte[] getVariableHeader() throws MqttException {
49
		return encodeMessageId();
50
	}
51
	
52
	protected byte getMessageInfo() {
53
		return (byte)( 2 | (this.duplicate?8:0));
54
	}
55
56
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttPublish.java (-136 lines)
Lines 1-136 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import java.io.ByteArrayInputStream;
15
import java.io.ByteArrayOutputStream;
16
import java.io.DataInputStream;
17
import java.io.DataOutputStream;
18
import java.io.IOException;
19
20
import org.eclipse.paho.client.mqttv3.MqttException;
21
import org.eclipse.paho.client.mqttv3.MqttMessage;
22
23
24
/**
25
 * An on-the-wire representation of an MQTT SEND message.
26
 */
27
public class MqttPublish extends MqttPersistableWireMessage {
28
	
29
	private MqttMessage message;
30
	private String topicName;
31
	
32
	private byte[] encodedPayload = null;
33
	
34
	public MqttPublish(String name, MqttMessage message) {
35
		super(MqttWireMessage.MESSAGE_TYPE_PUBLISH);
36
		this.topicName = name;
37
		this.message = message;
38
	}
39
	
40
	/**
41
	 * Constructs a new MqttPublish object.
42
	 * @param info the message info byte
43
	 * @param data the variable header and payload bytes
44
	 */
45
	public MqttPublish(byte info, byte[] data) throws MqttException, IOException {
46
		super(MqttWireMessage.MESSAGE_TYPE_PUBLISH);
47
		this.message = new MqttReceivedMessage();
48
		message.setQos((info >> 1) & 0x03);
49
		if ((info & 0x01) == 0x01) {
50
			message.setRetained(true);
51
		}
52
		if ((info & 0x08) == 0x08) {
53
			((MqttReceivedMessage) message).setDuplicate(true);
54
		}
55
		
56
		ByteArrayInputStream bais = new ByteArrayInputStream(data);
57
		CountingInputStream counter = new CountingInputStream(bais);
58
		DataInputStream dis = new DataInputStream(counter);
59
		topicName = this.decodeUTF8(dis);
60
		if (message.getQos() > 0) {
61
			msgId = dis.readUnsignedShort();
62
		}
63
		byte[] payload = new byte[data.length-counter.getCounter()];
64
		dis.readFully(payload);
65
		dis.close();
66
		message.setPayload(payload);
67
	}
68
	
69
	protected byte getMessageInfo() {
70
		byte info = (byte) (message.getQos() << 1);
71
		if (message.isRetained()) {
72
			info |= 0x01;
73
		}
74
		if (message.isDuplicate() || duplicate ) {
75
			info |= 0x08;
76
		}
77
		
78
		return info;
79
	}
80
	
81
	public String getTopicName() {
82
		return topicName;
83
	}
84
	
85
	public MqttMessage getMessage() {
86
		return message;
87
	}
88
	
89
	protected static byte[] encodePayload(MqttMessage message) {
90
		return message.getPayload();
91
	}
92
93
	public byte[] getPayload() throws MqttException {
94
		if (encodedPayload == null) {
95
			encodedPayload = encodePayload(message);
96
		}
97
		return encodedPayload;
98
	}
99
100
	public int getPayloadLength() {
101
		int length = 0;
102
		try {
103
			length = getPayload().length;
104
		} catch(MqttException me) {
105
		}
106
		return length;
107
	}
108
	
109
	public void setMessageId(int msgId) {
110
		super.setMessageId(msgId);
111
		if (message instanceof MqttReceivedMessage) {
112
			((MqttReceivedMessage)message).setMessageId(msgId);
113
		}
114
	}
115
	
116
	protected byte[] getVariableHeader() throws MqttException {
117
		try {
118
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
119
			DataOutputStream dos = new DataOutputStream(baos);
120
			this.encodeUTF8(dos, topicName);
121
			if (message.getQos() > 0) {
122
				dos.writeShort(msgId);
123
			}
124
			dos.flush();
125
			return baos.toByteArray();
126
		}
127
		catch (IOException ex) {
128
			throw new MqttException(ex);
129
		}
130
	}
131
	
132
	public boolean isMessageIdRequired() {
133
		// all publishes require a message ID as it's used as the key to the token store
134
		return true;
135
	}
136
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttReceivedMessage.java (-33 lines)
Lines 1-33 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import org.eclipse.paho.client.mqttv3.MqttMessage;
15
16
public class MqttReceivedMessage extends MqttMessage {
17
	
18
	private int messageId;
19
	
20
	public void setMessageId(int msgId) {
21
		this.messageId = msgId;
22
	}
23
24
	public int getMessageId() {
25
		return messageId;
26
	}
27
	
28
	// This method exists here to get around the protected visibility of the
29
	// super class method.
30
	public void setDuplicate(boolean value) {
31
		super.setDuplicate(value);
32
	}
33
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttSuback.java (-47 lines)
Lines 1-47 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import java.io.ByteArrayInputStream;
15
import java.io.DataInputStream;
16
import java.io.IOException;
17
18
import org.eclipse.paho.client.mqttv3.MqttException;
19
20
21
/**
22
 * An on-the-wire representation of an MQTT SUBACK.
23
 */
24
public class MqttSuback extends MqttAck {
25
	private int[] grantedQos;		// Not currently made available to anyone. 
26
	
27
	public MqttSuback(byte info, byte[] data) throws IOException {
28
		super(MqttWireMessage.MESSAGE_TYPE_SUBACK);
29
		ByteArrayInputStream bais = new ByteArrayInputStream(data);
30
		DataInputStream dis = new DataInputStream(bais);
31
		msgId = dis.readUnsignedShort();
32
		int index = 0;
33
		grantedQos = new int[data.length-2];
34
		int qos = dis.read();
35
		while (qos != -1) {
36
			grantedQos[index] = qos;
37
			index++;
38
			qos = dis.read();
39
		}
40
		dis.close();
41
	}
42
	
43
	protected byte[] getVariableHeader() throws MqttException {
44
		// Not needed, as the client never encodes a SUBACK
45
		return new byte[0];
46
	}
47
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttSubscribe.java (-83 lines)
Lines 1-83 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import java.io.ByteArrayOutputStream;
15
import java.io.DataOutputStream;
16
import java.io.IOException;
17
18
import org.eclipse.paho.client.mqttv3.MqttException;
19
import org.eclipse.paho.client.mqttv3.MqttMessage;
20
21
22
/**
23
 * An on-the-wire representation of an MQTT SUBSCRIBE message.
24
 */
25
public class MqttSubscribe extends MqttWireMessage {
26
	private String[] names;
27
	private int[] qos;
28
29
	/**
30
	 * Constructor for on an on hte wire MQTT subscribe message
31
	 * @param names - one or more topics to subscribe to 
32
	 * @param qos - the max QOS that each each topic will be subscribed at 
33
	 */
34
	public MqttSubscribe(String[] names, int[] qos) {
35
		super(MqttWireMessage.MESSAGE_TYPE_SUBSCRIBE);
36
		this.names = names;
37
		this.qos = qos;
38
		
39
		if (names.length != qos.length) {
40
		throw new IllegalArgumentException();
41
		}
42
		
43
		for (int i=0;i<qos.length;i++) {
44
			MqttMessage.validateQos(qos[i]);
45
		}
46
	}
47
	
48
	protected byte getMessageInfo() {
49
		return (byte)(2 | (this.duplicate?8:0));
50
	}
51
	
52
	protected byte[] getVariableHeader() throws MqttException {
53
		try {
54
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
55
			DataOutputStream dos = new DataOutputStream(baos);
56
			dos.writeShort(msgId);
57
			dos.flush();
58
			return baos.toByteArray();
59
		}
60
		catch (IOException ex) {
61
			throw new MqttException(ex);
62
		}
63
	}
64
	
65
	public byte[] getPayload() throws MqttException {
66
		try {
67
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
68
			DataOutputStream dos = new DataOutputStream(baos);
69
			for (int i=0; i<names.length; i++) {
70
				this.encodeUTF8(dos,names[i]);
71
				dos.writeByte(qos[i]);
72
			}
73
			return baos.toByteArray();
74
		}
75
		catch (IOException ex) {
76
			throw new MqttException(ex);
77
		}
78
	}
79
	
80
	public boolean isRetryable() {
81
		return true;
82
	}
83
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttUnsubAck.java (-38 lines)
Lines 1-38 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import java.io.ByteArrayInputStream;
15
import java.io.DataInputStream;
16
import java.io.IOException;
17
18
import org.eclipse.paho.client.mqttv3.MqttException;
19
20
21
/**
22
 * An on-the-wire representation of an MQTT UNSUBACK.
23
 */
24
public class MqttUnsubAck extends MqttAck {
25
	
26
	public MqttUnsubAck(byte info, byte[] data) throws IOException {
27
		super(MqttWireMessage.MESSAGE_TYPE_UNSUBACK);
28
		ByteArrayInputStream bais = new ByteArrayInputStream(data);
29
		DataInputStream dis = new DataInputStream(bais);
30
		msgId = dis.readUnsignedShort();
31
		dis.close();
32
	}
33
	
34
	protected byte[] getVariableHeader() throws MqttException {
35
		// Not needed, as the client never encodes an UNSUBACK
36
		return new byte[0];
37
	}
38
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttUnsubscribe.java (-64 lines)
Lines 1-64 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import java.io.ByteArrayOutputStream;
15
import java.io.DataOutputStream;
16
import java.io.IOException;
17
18
import org.eclipse.paho.client.mqttv3.MqttException;
19
20
21
/**
22
 * An on-the-wire representation of an MQTT UNSUBSCRIBE message.
23
 */
24
public class MqttUnsubscribe extends MqttWireMessage {
25
	
26
	private String[] names;
27
28
	/**
29
	 * Constructs an MqttUnsubscribe
30
	 */
31
	public MqttUnsubscribe(String[] names) {
32
		super(MqttWireMessage.MESSAGE_TYPE_UNSUBSCRIBE);
33
		this.names = names;
34
	}
35
	
36
	protected byte getMessageInfo() {
37
		return (byte)( 2 | (this.duplicate?8:0));
38
	}
39
	
40
	protected byte[] getVariableHeader() throws MqttException {
41
		try {
42
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
43
			DataOutputStream dos = new DataOutputStream(baos);
44
			dos.writeShort(msgId);
45
			dos.flush();
46
			return baos.toByteArray();
47
		}
48
		catch (IOException ex) {
49
			throw new MqttException(ex);
50
		}
51
	}
52
53
	public byte[] getPayload() throws MqttException {
54
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
55
			DataOutputStream dos = new DataOutputStream(baos);
56
			for (int i=0; i<names.length; i++) {
57
				this.encodeUTF8(dos, names[i]);
58
			}
59
			return baos.toByteArray();
60
	}
61
	public boolean isRetryable() {
62
		return true;
63
	}
64
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MqttWireMessage.java (-329 lines)
Lines 1-329 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import java.io.ByteArrayInputStream;
15
import java.io.ByteArrayOutputStream;
16
import java.io.DataInputStream;
17
import java.io.DataOutputStream;
18
import java.io.IOException;
19
import java.io.InputStream;
20
import java.io.UnsupportedEncodingException;
21
22
import org.eclipse.paho.client.mqttv3.MqttException;
23
import org.eclipse.paho.client.mqttv3.MqttPersistable;
24
import org.eclipse.paho.client.mqttv3.internal.ExceptionHelper;
25
26
27
/**
28
 * An on-the-wire representation of an MQTT message.
29
 */
30
public abstract class MqttWireMessage {
31
	protected static final String STRING_ENCODING = "UTF-8";
32
	
33
	public static final byte MESSAGE_TYPE_CONNECT = 1;
34
	public static final byte MESSAGE_TYPE_CONNACK = 2;
35
	public static final byte MESSAGE_TYPE_PUBLISH = 3;
36
	public static final byte MESSAGE_TYPE_PUBACK = 4;
37
	public static final byte MESSAGE_TYPE_PUBREC = 5;
38
	public static final byte MESSAGE_TYPE_PUBREL = 6;
39
	public static final byte MESSAGE_TYPE_PUBCOMP = 7;
40
	public static final byte MESSAGE_TYPE_SUBSCRIBE = 8;
41
	public static final byte MESSAGE_TYPE_SUBACK = 9;
42
	public static final byte MESSAGE_TYPE_UNSUBSCRIBE = 10;
43
	public static final byte MESSAGE_TYPE_UNSUBACK = 11;
44
	public static final byte MESSAGE_TYPE_PINGREQ = 12;
45
	public static final byte MESSAGE_TYPE_PINGRESP = 13;
46
	public static final byte MESSAGE_TYPE_DISCONNECT = 14;
47
	
48
	/** The type of the message (e.g. CONNECT, PUBLISH, PUBACK) */
49
	private byte type;
50
	/** The MQTT message ID */
51
	protected int msgId;
52
	
53
	protected boolean duplicate = false;
54
	
55
	private byte[] encodedHeader = null;
56
	
57
	public MqttWireMessage(byte type) {
58
		this.type = type;
59
		// Use zero as the default message ID.  Can't use -1, as that is serialized
60
		// as 65535, which would be a valid ID.
61
		this.msgId = 0;
62
	}
63
	
64
	/**
65
	 * Sub-classes should override this to encode the message info.
66
	 * Only the least-significant four bits will be used.
67
	 */
68
	abstract protected byte getMessageInfo();
69
	
70
	/**
71
	 * Sub-classes should override this method to supply the payload bytes.
72
	 */
73
	public byte[] getPayload() throws MqttException {
74
		return new byte[0];
75
	}
76
	
77
	/**
78
	 * Returns the type of the message.
79
	 */
80
	public byte getType() {
81
		return type;
82
	}
83
	
84
	/**
85
	 * Returns the MQTT message ID.
86
	 */
87
	public int getMessageId() {
88
		return msgId;
89
	}
90
	
91
	/**
92
	 * Sets the MQTT message ID.
93
	 */
94
	public void setMessageId(int msgId) {
95
		this.msgId = msgId;
96
	}
97
	
98
	/** 
99
	 * Returns a key associated with the message. For most message types
100
	 * this will be unique. For connect, disconnect and ping only one 
101
	 * message of this type is allowed so a fixed key will be returned
102
	 * @return key a key associated with the message
103
	 */
104
	public String getKey() {
105
		return new Integer(getMessageId()).toString();
106
	}
107
	
108
	public byte[] getHeader() throws MqttException {
109
		if (encodedHeader == null) {
110
			try {
111
				int first = ((getType() & 0x0f) << 4) ^ (getMessageInfo() & 0x0f);
112
				byte[] varHeader = getVariableHeader();
113
				int remLen = varHeader.length + getPayload().length;
114
115
				ByteArrayOutputStream baos = new ByteArrayOutputStream();
116
				DataOutputStream dos = new DataOutputStream(baos);
117
				dos.writeByte(first);
118
				dos.write(encodeMBI(remLen));
119
				dos.write(varHeader);
120
				dos.flush();
121
				encodedHeader = baos.toByteArray();
122
			} catch(IOException ioe) {
123
				throw new MqttException(ioe);
124
			}
125
		}
126
		return encodedHeader;
127
	}
128
	
129
	protected abstract byte[] getVariableHeader() throws MqttException;
130
131
132
	/**
133
	 * Returns whether or not this message needs to include a message ID.
134
	 */
135
	public boolean isMessageIdRequired() {
136
		return true;
137
	}
138
	
139
	public static MqttWireMessage createWireMessage(MqttPersistable data) throws MqttException {
140
		byte[] payload = data.getPayloadBytes();
141
		// The persistable interface allows a message to be restored entirely in the header array
142
		// Need to treat these two arrays as a single array of bytes and use the decoding
143
		// logic to identify the true header/payload split
144
		if (payload == null) {
145
			payload = new byte[0];
146
		}
147
		MultiByteArrayInputStream mbais = new MultiByteArrayInputStream(
148
				data.getHeaderBytes(),
149
				data.getHeaderOffset(),
150
				data.getHeaderLength(),
151
				payload,
152
				data.getPayloadOffset(),
153
				data.getPayloadLength());
154
		return createWireMessage(mbais);
155
	}
156
	
157
	public static MqttWireMessage createWireMessage(byte[] bytes) throws MqttException {
158
		ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
159
		return createWireMessage(bais);
160
	}
161
162
	private static MqttWireMessage createWireMessage(InputStream inputStream) throws MqttException {
163
		try {
164
			CountingInputStream counter = new CountingInputStream(inputStream);
165
			DataInputStream in = new DataInputStream(counter);
166
			int first = in.readUnsignedByte();
167
			byte type = (byte) (first >> 4);
168
			byte info = (byte) (first &= 0x0f);
169
			long remLen = readMBI(in).getValue();
170
			long totalToRead = counter.getCounter() + remLen;
171
172
			MqttWireMessage result;
173
			long remainder = totalToRead - counter.getCounter();
174
			byte[] data = new byte[0];
175
			// The remaining bytes must be the payload...
176
			if (remainder > 0) {
177
				data = new byte[(int) remainder];
178
				in.readFully(data, 0, data.length);
179
			}
180
				
181
			if (type == MqttWireMessage.MESSAGE_TYPE_PUBLISH) {
182
				result = new MqttPublish(info, data);
183
			}
184
			else if (type == MqttWireMessage.MESSAGE_TYPE_PUBACK) {
185
				result = new MqttPubAck(info, data);
186
			}
187
			else if (type == MqttWireMessage.MESSAGE_TYPE_PUBCOMP) {
188
				result = new MqttPubComp(info, data);
189
			}
190
			else if (type == MqttWireMessage.MESSAGE_TYPE_CONNACK) {
191
				result = new MqttConnack(info, data);
192
			}
193
			else if (type == MqttWireMessage.MESSAGE_TYPE_PINGRESP) {
194
				result = new MqttPingResp(info, data);
195
			}
196
			else if (type == MqttWireMessage.MESSAGE_TYPE_SUBACK) {
197
				result = new MqttSuback(info, data);
198
			}
199
			else if (type == MqttWireMessage.MESSAGE_TYPE_UNSUBACK) {
200
				result = new MqttUnsubAck(info, data);
201
			}
202
			else if (type == MqttWireMessage.MESSAGE_TYPE_PUBREL) {
203
				result = new MqttPubRel(info, data);
204
			}
205
			else if (type == MqttWireMessage.MESSAGE_TYPE_PUBREC) {
206
				result = new MqttPubRec(info, data);
207
			}
208
			else {
209
				throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_UNEXPECTED_ERROR);
210
			}
211
			return result;
212
		} catch(IOException io) {
213
			throw new MqttException(io);
214
		}
215
	}
216
		
217
	protected static byte[] encodeMBI( long number) {
218
		int numBytes = 0;
219
		long no = number;
220
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
221
		// Encode the remaining length fields in the four bytes
222
		do {
223
			byte digit = (byte)(no % 128);
224
			no = no / 128;
225
			if (no > 0) {
226
				digit |= 0x80;
227
			}
228
			bos.write(digit);
229
			numBytes++;
230
		} while ( (no > 0) && (numBytes<4) );
231
		
232
		return bos.toByteArray();
233
	}
234
	
235
	/**
236
	 * Decodes an MQTT Multi-Byte Integer from the given stream.
237
	 */
238
	protected static MultiByteInteger readMBI(DataInputStream in) throws IOException {
239
		byte digit;
240
		long msgLength = 0;
241
		int multiplier = 1;
242
		int count = 0;
243
		
244
		do {
245
			digit = in.readByte();
246
			count++;
247
			msgLength += ((digit & 0x7F) * multiplier);
248
			multiplier *= 128;
249
		} while ((digit & 0x80) != 0);
250
		
251
		return new MultiByteInteger(msgLength, count);
252
	}
253
	
254
	protected byte[] encodeMessageId() throws MqttException {
255
		try {
256
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
257
			DataOutputStream dos = new DataOutputStream(baos);
258
			dos.writeShort(msgId);
259
			dos.flush();
260
			return baos.toByteArray();
261
		}
262
		catch (IOException ex) {
263
			throw new MqttException(ex);
264
		}
265
	}
266
	
267
	public boolean isRetryable() {
268
		return false;
269
	}
270
	
271
	public void setDuplicate(boolean duplicate) {
272
		this.duplicate = duplicate;
273
	}
274
275
	/**
276
	 * Encodes a String given into UTF-8, before writing this to the DataOutputStream the length of the
277
	 * encoded string is encoded into two bytes and then written to the DataOutputStream. @link{DataOutputStream#writeUFT(String)}
278
	 * should be no longer used. @link{DataOutputStream#writeUFT(String)} does not correctly encode UTF-16 surrogate characters.
279
	 * 
280
	 * @param dos The stream to write the encoded UTF-8 String to.
281
	 * @param stringToEncode The String to be encoded 
282
	 * @throws MqttException Thrown when an error occurs with either the encoding or writing the data to the stream
283
	 */
284
	protected void encodeUTF8(DataOutputStream dos, String stringToEncode) throws MqttException
285
	{
286
		try {
287
288
			byte[] encodedString = stringToEncode.getBytes("UTF-8");
289
			byte byte1 = (byte) ((encodedString.length >>> 8) & 0xFF);
290
			byte byte2 =  (byte) ((encodedString.length >>> 0) & 0xFF);  
291
			
292
293
			dos.write(byte1);
294
			dos.write(byte2);
295
			dos.write(encodedString);
296
		}
297
		catch(UnsupportedEncodingException ex)
298
		{
299
			throw new MqttException(ex);
300
		} catch (IOException ex) {
301
			throw new MqttException(ex);
302
		}
303
	}
304
	
305
	/**
306
	 * Decodes a UTF-8 string from the DataInputStream provided. @link(DataInoutStream#readUTF()) should be no longer used, because  @link(DataInoutStream#readUTF()) 
307
	 * does not decode UTF-16 surrogate characters correctly.
308
	 * 
309
	 * @param input The input stream from which to read the encoded string
310
	 * @return a decoded String from the DataInputStream
311
	 * @throws MqttException thrown when an error occurs with either reading from the stream or
312
	 * decoding the encoded string.
313
	 */
314
	protected String decodeUTF8(DataInputStream input) throws MqttException
315
	{
316
		int encodedLength;
317
		try {
318
			encodedLength = input.readUnsignedShort();
319
320
			byte[] encodedString = new byte[encodedLength];
321
				input.readFully(encodedString);
322
323
			return new String(encodedString, "UTF-8");
324
		} catch (IOException ex) {
325
			throw new MqttException(ex);
326
		}
327
	}
328
329
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MultiByteArrayInputStream.java (-52 lines)
Lines 1-52 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
import java.io.IOException;
15
import java.io.InputStream;
16
17
public class MultiByteArrayInputStream extends InputStream {
18
19
	private byte[] bytesA;
20
	private int offsetA;
21
	private int lengthA;
22
	private byte[] bytesB;
23
	private int offsetB;
24
	private int lengthB;
25
	
26
	private int pos = 0;
27
	
28
	public MultiByteArrayInputStream(byte[] bytesA, int offsetA, int lengthA, byte[] bytesB, int offsetB, int lengthB) {
29
		this.bytesA = bytesA;
30
		this.bytesB = bytesB;
31
		this.offsetA = offsetA;
32
		this.offsetB = offsetB;
33
		this.lengthA = lengthA;
34
		this.lengthB = lengthB;
35
	}
36
	public int read() throws IOException {
37
		int result = -1;
38
		if (pos<lengthA) {
39
			result = bytesA[offsetA+pos];
40
		} else if (pos<lengthA+lengthB) {
41
			result = bytesB[offsetB+pos-lengthA];
42
		} else {
43
			return -1;
44
		}
45
		if (result < 0) {
46
			result += 256;
47
		}
48
		pos++;
49
		return result;
50
	}
51
52
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/internal/wire/MultiByteInteger.java (-44 lines)
Lines 1-44 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.internal.wire;
13
14
/**
15
 * Represents a Multi-Byte Integer (MBI), as defined by the MQTT V3
16
 * specification.
17
 */
18
public class MultiByteInteger {
19
	private long value;
20
	private int length;
21
	
22
	public MultiByteInteger(long value) {
23
		this(value, -1);
24
	}
25
	
26
	public MultiByteInteger(long value, int length) {
27
		this.value = value;
28
		this.length = length;
29
	}
30
	
31
	/**
32
	 * Returns the number of bytes read when decoding this MBI.
33
	 */
34
	public int getEncodedLength() {
35
		return length;
36
	}
37
38
	/**
39
	 * Returns the value of this MBI.
40
	 */
41
	public long getValue() {
42
		return value;
43
	}
44
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/logging/JSR47Logger.java (-272 lines)
Lines 1-272 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.logging;
13
import java.text.MessageFormat;
14
import java.util.MissingResourceException;
15
import java.util.ResourceBundle;
16
import java.util.logging.Handler;
17
import java.util.logging.LogRecord;
18
import java.util.logging.MemoryHandler;
19
20
/**
21
 * Implementation of the the logger interface that uses java.uti.logging
22
 * 
23
 * A Logger that utilises Java's built in logging facility - java.util.logging.
24
 * <p>A sample java.util.logging properties file - jsr47min.properties is provided that demonstrates
25
 * how to run with a memory based trace facility that runs with minimal performance 
26
 * overhead. The memory buffer can be dumped when a log/trace record is written matching 
27
 * the MemoryHandlers trigger level or when the push method is invoked on the MemoryHandler. 
28
 * {@link org.eclipse.paho.client.mqttv3.util.Debug Debug} provides method to make it easy
29
 * to dump the memory buffer as well as other useful debug info. 
30
 */
31
public class JSR47Logger implements Logger {
32
	private java.util.logging.Logger	julLogger			= null;
33
	private ResourceBundle				logMessageCatalog	= null;
34
	private ResourceBundle				traceMessageCatalog	= null;
35
	private String						catalogID			= null;
36
	private String						resourceName		= null;
37
	private String						loggerName			= null;
38
39
	/**
40
	 * 
41
	 * @param logMsgCatalog  The resource bundle associated with this logger
42
	 * @param loggerID			The suffix for the loggerName (will be appeneded to org.eclipse.paho.client.mqttv3
43
	 * @param resourceContext	A context for the logger e.g. clientID or appName...
44
	 */
45
	public void initialise(ResourceBundle logMsgCatalog, String loggerID, String resourceContext ) { 
46
		this.traceMessageCatalog = logMessageCatalog;
47
		this.resourceName = resourceContext;
48
//		loggerName = "org.eclipse.paho.client.mqttv3." + ((null == loggerID || 0 == loggerID.length()) ? "internal" : loggerID);
49
		loggerName = loggerID;
50
		this.julLogger = java.util.logging.Logger.getLogger(loggerName);
51
		this.logMessageCatalog = logMsgCatalog;
52
		this.traceMessageCatalog = logMsgCatalog;
53
		this.catalogID = logMessageCatalog.getString("0");
54
		
55
	}
56
	
57
	public void setResourceName(String logContext) {
58
		this.resourceName = logContext;
59
	}
60
61
	public boolean isLoggable(int level) {
62
		return julLogger.isLoggable(mapJULLevel(level)); // || InternalTracer.isLoggable(level);
63
	}
64
65
	public void severe(String sourceClass, String sourceMethod, String msg) {
66
		log(SEVERE, sourceClass, sourceMethod, msg, null, null);
67
	}
68
69
	public void severe(String sourceClass, String sourceMethod, String msg, Object[] inserts) {
70
		log(SEVERE, sourceClass, sourceMethod, msg, inserts, null);
71
	}
72
73
	public void severe(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown) {
74
		log(SEVERE, sourceClass, sourceMethod, msg, inserts, thrown);
75
	}
76
77
	public void warning(String sourceClass, String sourceMethod, String msg) {
78
		log(WARNING, sourceClass, sourceMethod, msg, null, null);
79
	}
80
81
	public void warning(String sourceClass, String sourceMethod, String msg, Object[] inserts) {
82
		log(WARNING, sourceClass, sourceMethod, msg, inserts, null);
83
	}
84
85
	public void warning(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown) {
86
		log(WARNING, sourceClass, sourceMethod, msg, inserts, thrown);
87
	}
88
89
	public void info(String sourceClass, String sourceMethod, String msg) {
90
		log(INFO, sourceClass, sourceMethod, msg, null, null);
91
	}
92
93
	public void info(String sourceClass, String sourceMethod, String msg, Object[] inserts) {
94
		log(INFO, sourceClass, sourceMethod, msg, inserts, null);
95
	}
96
97
	public void info(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown) {
98
		log(INFO, sourceClass, sourceMethod, msg, inserts, thrown);
99
	}
100
101
	public void config(String sourceClass, String sourceMethod, String msg) {
102
		log(CONFIG, sourceClass, sourceMethod, msg, null, null);
103
	}
104
105
	public void config(String sourceClass, String sourceMethod, String msg, Object[] inserts) {
106
		log(CONFIG, sourceClass, sourceMethod, msg, inserts, null);
107
	}
108
109
	public void config(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown) {
110
		log(CONFIG, sourceClass, sourceMethod, msg, inserts, thrown);
111
	}
112
113
	public void log(int level, String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown) {
114
//		InternalTracer.log(this.catalogID, level, sourceClass, sourceMethod, msg, inserts, thrown);
115
		java.util.logging.Level julLevel = mapJULLevel(level);
116
		if (julLogger.isLoggable(julLevel)) {
117
			logToJsr47(julLevel, sourceClass, sourceMethod, this.catalogID, this.logMessageCatalog, msg, inserts, thrown);
118
		}
119
	}
120
121
//	public void setTrace(Trace trace) {
122
//		InternalTracer.setTrace(trace);
123
//	}
124
125
	public void fine(String sourceClass, String sourceMethod, String msg) {
126
		trace(FINE, sourceClass, sourceMethod, msg, null, null);
127
	}
128
129
	public void fine(String sourceClass, String sourceMethod, String msg, Object[] inserts) {
130
		trace(FINE, sourceClass, sourceMethod, msg, inserts, null);
131
	}
132
	
133
	public void fine(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex) {
134
		trace(FINE, sourceClass, sourceMethod, msg, inserts, ex);
135
	}
136
137
	public void finer(String sourceClass, String sourceMethod, String msg) {
138
		trace(FINER, sourceClass, sourceMethod, msg, null, null);
139
	}
140
141
	public void finer(String sourceClass, String sourceMethod, String msg, Object[] inserts) {
142
		trace(FINER, sourceClass, sourceMethod, msg, inserts, null);
143
	}
144
	
145
	public void finer(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex) {
146
		trace(FINER, sourceClass, sourceMethod, msg, inserts, ex);
147
	}
148
149
	public void finest(String sourceClass, String sourceMethod, String msg) {
150
		trace(FINEST, sourceClass, sourceMethod, msg, null, null);
151
	}
152
153
	public void finest(String sourceClass, String sourceMethod, String msg, Object[] inserts) {
154
		trace(FINEST, sourceClass, sourceMethod, msg, inserts, null);
155
	}
156
	
157
	public void finest(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex) {
158
		trace(FINEST, sourceClass, sourceMethod, msg, inserts, ex);
159
	}
160
161
162
	public void trace(int level, String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex) {
163
		java.util.logging.Level julLevel = mapJULLevel(level);
164
		boolean isJULLoggable = julLogger.isLoggable(julLevel);
165
//		if (FINE == level || isJULLoggable || InternalTracer.isLoggable(level)) {
166
//			InternalTracer.traceForced(level, sourceClass, sourceMethod, msg, inserts);
167
//		}
168
		if (isJULLoggable) {
169
			logToJsr47(julLevel, sourceClass, sourceMethod, this.catalogID, this.traceMessageCatalog, msg, inserts, ex);
170
		}
171
	}
172
173
	
174
	private String getResourceMessage(ResourceBundle messageCatalog, String msg) {
175
		String message;
176
		try {
177
			message = messageCatalog.getString(msg);
178
		} catch (MissingResourceException e) {
179
			// This is acceptable, simply return the given msg string.
180
			message = msg;
181
		}
182
		return message;
183
	}
184
185
	private void logToJsr47(java.util.logging.Level julLevel, String sourceClass, String sourceMethod, String catalogName,
186
			ResourceBundle messageCatalog, String msg, Object[] inserts, Throwable thrown) {
187
//		LogRecord logRecord = new LogRecord(julLevel, msg);
188
		String formattedWithArgs = msg;
189
		if (msg.indexOf("=====")== -1) {
190
			formattedWithArgs = MessageFormat.format(getResourceMessage(messageCatalog, msg), inserts);
191
		}
192
		LogRecord logRecord = new LogRecord(julLevel, resourceName + ": " +formattedWithArgs);
193
		
194
		logRecord.setSourceClassName(sourceClass);
195
		logRecord.setSourceMethodName(sourceMethod);
196
		logRecord.setLoggerName(loggerName);
197
//		logRecord.setResourceBundleName(catalogName);
198
//		logRecord.setResourceBundle(messageCatalog);
199
//		if (null != inserts) {
200
//			logRecord.setParameters(inserts);
201
//		}
202
		if (null != thrown) {
203
			logRecord.setThrown(thrown);
204
		}
205
206
		julLogger.log(logRecord);
207
	}
208
209
	private java.util.logging.Level mapJULLevel(int level) {
210
		java.util.logging.Level julLevel = null;
211
212
		switch (level) {
213
			case SEVERE:
214
				julLevel = java.util.logging.Level.SEVERE;
215
				break;
216
			case WARNING:
217
				julLevel = java.util.logging.Level.WARNING;
218
				break;
219
			case INFO:
220
				julLevel = java.util.logging.Level.INFO;
221
				break;
222
			case CONFIG:
223
				julLevel = java.util.logging.Level.CONFIG;
224
				break;
225
			case FINE:
226
				julLevel = java.util.logging.Level.FINE;
227
				break;
228
			case FINER:
229
				julLevel = java.util.logging.Level.FINER;
230
				break;
231
			case FINEST:
232
				julLevel = java.util.logging.Level.FINEST;
233
				break;
234
		}
235
236
		return julLevel;
237
	}
238
239
	public String formatMessage(String msg, Object[] inserts) {
240
		String formatString;
241
		try {
242
			formatString = logMessageCatalog.getString(msg);
243
		} catch (MissingResourceException e) {
244
			formatString = msg;
245
		}
246
		return formatString;
247
	}
248
249
	public void dumpTrace() {
250
			dumpMemoryTrace47(julLogger);
251
	}
252
253
	protected static void dumpMemoryTrace47(java.util.logging.Logger logger) {
254
		MemoryHandler mHand = null;
255
256
		if (logger!= null) {
257
			Handler[] handlers = logger.getHandlers();
258
			
259
		    for (int i=0; i<handlers.length; i++) {
260
		      if (handlers[i] instanceof java.util.logging.MemoryHandler) {
261
		        synchronized (handlers[i]) {
262
		        	mHand = ((java.util.logging.MemoryHandler)handlers[i]);
263
		        	mHand.push();
264
		        	return;
265
		        } // synchronized (handler).
266
		      }      
267
		    } // for handlers...
268
		    dumpMemoryTrace47(logger.getParent());
269
		}
270
	}
271
	
272
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/logging/Logger.java (-572 lines)
Lines 1-572 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.logging;
13
14
import java.util.ResourceBundle;
15
16
/**
17
 * A Logger object is used to send log and trace messages to a platform
18
 * specific logging implementation. Loggers are named, using a hierarchical 
19
 * dot-separated name-space.
20
 * Logger names can be arbitrary strings, but they should normally be based on
21
 * the component or the package name of the logged component
22
 * 
23
 * Logger objects may be obtained by calls on one of the getLogger factory
24
 * methods. These will either create a new Logger or return a suitable existing
25
 * Logger.
26
 * 
27
 * <p>
28
 * The int levels define a set of standard logging levels that can be used to
29
 * control logging output. The logging levels are ordered and are specified by
30
 * ordered integers. Enabling logging at a given level also enables logging at
31
 * all higher levels.
32
 * <p>
33
 * Clients should use the the convenience methods such as severe() and fine() or
34
 * one of the predefined level constants such as Logger.SEVERE and Logger.FINE
35
 * with the appropriate log(int level...) or trace(int level...) methods.
36
 * <p>
37
 * The levels in descending order are:
38
 * <ul>
39
 * <li>SEVERE (log - highest value)</li>
40
 * <li>WARNING (log)</li>
41
 * <li>INFO (log)</li>
42
 * <li>CONFIG (log)</li>
43
 * <li>FINE (trace)</li>
44
 * <li>FINER (trace)</li>
45
 * <li>FINEST (trace - lowest value)</li>
46
 * </ul>
47
 * <p>
48
 */
49
public interface Logger {
50
	/**
51
	 * SEVERE is a message level indicating a serious failure.
52
	 * <p>
53
	 * In general SEVERE messages should describe events that are of
54
	 * considerable importance and which will prevent normal program execution.
55
	 * They should be reasonably intelligible to end users and to system
56
	 * administrators.
57
	 */
58
	public static final int	SEVERE	= 1;
59
	/**
60
	 * WARNING is a message level indicating a potential problem.
61
	 * <p>
62
	 * In general WARNING messages should describe events that will be of
63
	 * interest to end users or system managers, or which indicate potential
64
	 * problems.
65
	 */
66
	public static final int	WARNING	= 2;
67
	/**
68
	 * INFO is a message level for informational messages.
69
	 * <p>
70
	 * Typically INFO messages will be written to the console or its equivalent.
71
	 * So the INFO level should only be used for reasonably significant messages
72
	 * that will make sense to end users and system admins.
73
	 */
74
	public static final int	INFO	= 3;
75
	/**
76
	 * CONFIG is a message level for static configuration messages.
77
	 * <p>
78
	 * CONFIG messages are intended to provide a variety of static configuration
79
	 * information, to assist in debugging problems that may be associated with
80
	 * particular configurations. For example, CONFIG message might include the
81
	 * CPU type, the graphics depth, the GUI look-and-feel, etc.
82
	 */
83
	public static final int	CONFIG	= 4;
84
	/**
85
	 * FINE is a message level providing tracing information.
86
	 * <p>
87
	 * All of FINE, FINER, and FINEST are intended for relatively detailed
88
	 * tracing. The exact meaning of the three levels will vary between
89
	 * subsystems, but in general, FINEST should be used for the most voluminous
90
	 * detailed output, FINER for somewhat less detailed output, and FINE for
91
	 * the lowest volume (and most important) messages.
92
	 * <p>
93
	 * In general the FINE level should be used for information that will be
94
	 * broadly interesting to developers who do not have a specialized interest
95
	 * in the specific subsystem.
96
	 * <p>
97
	 * FINE messages might include things like minor (recoverable) failures.
98
	 * Issues indicating potential performance problems are also worth logging
99
	 * as FINE.
100
	 */
101
	public static final int	FINE	= 5;
102
	/**
103
	 * FINER indicates a fairly detailed tracing message. By default logging
104
	 * calls for entering, returning, or throwing an exception are traced at
105
	 * this level.
106
	 */
107
	public static final int	FINER	= 6;
108
	/**
109
	 * FINEST indicates a highly detailed tracing message.
110
	 */
111
	public static final int	FINEST	= 7;
112
	
113
	public void initialise(ResourceBundle messageCatalog, String loggerID, String resourceName);
114
	
115
	/**
116
	 * Set a name that can be used to provide context with each log record.
117
	 * This overrides the value passed in on initialise
118
	 */
119
	public void setResourceName(String logContext);
120
	
121
	/**
122
	 * Check if a message of the given level would actually be logged by this
123
	 * logger. This check is based on the Loggers effective level, which may be
124
	 * inherited from its parent.
125
	 * 
126
	 * @param level
127
	 *            a message logging level.
128
	 * @return true if the given message level is currently being logged.
129
	 */
130
	public boolean isLoggable(int level);
131
132
	/**
133
	 * Log a message, specifying source class and method, if the logger is
134
	 * currently enabled for the given message level.
135
	 * 
136
	 * @param sourceClass
137
	 *            Name of class that issued the logging request.
138
	 * @param sourceMethod
139
	 *            Name of method that issued the logging request.
140
	 * @param msg
141
	 *            The key in the message localization catalog for the message or
142
	 *            the actual message itself. During formatting, if the logger
143
	 *            has a mapping for the msg string, then the msg string is
144
	 *            replaced by the localized value. Otherwise the original msg
145
	 *            string is used.
146
	 */
147
	public void severe(String sourceClass, String sourceMethod, String msg);
148
149
	/**
150
	 * Log a message, specifying source class and method, with an array of
151
	 * object arguments, if the logger is currently enabled for the given
152
	 * message level.
153
	 * 
154
	 * @param sourceClass
155
	 *            Name of class that issued the logging request.
156
	 * @param sourceMethod
157
	 *            Name of method that issued the logging request.
158
	 * @param msg
159
	 *            The key in the message localization catalog for the message or
160
	 *            the actual message itself. During formatting, if the logger
161
	 *            has a mapping for the msg string, then the msg string is
162
	 *            replaced by the localized value. Otherwise the original msg
163
	 *            string is used. The formatter uses java.text.MessageFormat
164
	 *            style formatting to format parameters, so for example a format
165
	 *            string "{0} {1}" would format two inserts into the message.
166
	 * @param inserts
167
	 *            Array of parameters to the message.
168
	 */
169
	public void severe(String sourceClass, String sourceMethod, String msg, Object[] inserts);
170
171
	/**
172
	 * Log a message, specifying source class and method, with an array of
173
	 * object arguments and a throwable, if the logger is currently enabled for
174
	 * the given message level.
175
	 * 
176
	 * @param sourceClass
177
	 *            Name of class that issued the logging request.
178
	 * @param sourceMethod
179
	 *            Name of method that issued the logging request.
180
	 * @param msg
181
	 *            The key in the message localization catalog for the message or
182
	 *            the actual message itself. During formatting, if the logger
183
	 *            has a mapping for the msg string, then the msg string is
184
	 *            replaced by the localized value. Otherwise the original msg
185
	 *            string is used. The formatter uses java.text.MessageFormat
186
	 *            style formatting to format parameters, so for example a format
187
	 *            string "{0} {1}" would format two inserts into the message.
188
	 * @param inserts
189
	 *            Array of parameters to the message.
190
	 * @param thrown
191
	 *            Throwable associated with log message.
192
	 */
193
	public void severe(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown);
194
195
	/**
196
	 * Log a message, specifying source class and method, if the logger is
197
	 * currently enabled for the given message level.
198
	 * 
199
	 * @param sourceClass
200
	 *            Name of class that issued the logging request.
201
	 * @param sourceMethod
202
	 *            Name of method that issued the logging request.
203
	 * @param msg
204
	 *            The key in the message localization catalog for the message or
205
	 *            the actual message itself. During formatting, if the logger
206
	 *            has a mapping for the msg string, then the msg string is
207
	 *            replaced by the localized value. Otherwise the original msg
208
	 *            string is used.
209
	 */
210
	public void warning(String sourceClass, String sourceMethod, String msg);
211
212
	/**
213
	 * Log a message, specifying source class and method, with an array of
214
	 * object arguments, if the logger is currently enabled for the given
215
	 * message level.
216
	 * 
217
	 * @param sourceClass
218
	 *            Name of class that issued the logging request.
219
	 * @param sourceMethod
220
	 *            Name of method that issued the logging request.
221
	 * @param msg
222
	 *            The key in the message localization catalog for the message or
223
	 *            the actual message itself. During formatting, if the logger
224
	 *            has a mapping for the msg string, then the msg string is
225
	 *            replaced by the localized value. Otherwise the original msg
226
	 *            string is used. The formatter uses java.text.MessageFormat
227
	 *            style formatting to format parameters, so for example a format
228
	 *            string "{0} {1}" would format two inserts into the message.
229
	 * @param inserts
230
	 *            Array of parameters to the message.
231
	 */
232
	public void warning(String sourceClass, String sourceMethod, String msg, Object[] inserts);
233
234
	/**
235
	 * Log a message, specifying source class and method, with an array of
236
	 * object arguments and a throwable, if the logger is currently enabled for
237
	 * the given message level.
238
	 * 
239
	 * @param sourceClass
240
	 *            Name of class that issued the logging request.
241
	 * @param sourceMethod
242
	 *            Name of method that issued the logging request.
243
	 * @param msg
244
	 *            The key in the message localization catalog for the message or
245
	 *            the actual message itself. During formatting, if the logger
246
	 *            has a mapping for the msg string, then the msg string is
247
	 *            replaced by the localized value. Otherwise the original msg
248
	 *            string is used. The formatter uses java.text.MessageFormat
249
	 *            style formatting to format parameters, so for example a format
250
	 *            string "{0} {1}" would format two inserts into the message.
251
	 * @param inserts
252
	 *            Array of parameters to the message.
253
	 * @param thrown
254
	 *            Throwable associated with log message.
255
	 */
256
	public void warning(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown);
257
258
	/**
259
	 * Log a message, specifying source class and method, if the logger is
260
	 * currently enabled for the given message level.
261
	 * 
262
	 * @param sourceClass
263
	 *            Name of class that issued the logging request.
264
	 * @param sourceMethod
265
	 *            Name of method that issued the logging request.
266
	 * @param msg
267
	 *            The key in the message localization catalog for the message or
268
	 *            the actual message itself. During formatting, if the logger
269
	 *            has a mapping for the msg string, then the msg string is
270
	 *            replaced by the localized value. Otherwise the original msg
271
	 *            string is used.
272
	 */
273
	public void info(String sourceClass, String sourceMethod, String msg);
274
275
	/**
276
	 * Log a message, specifying source class and method, with an array of
277
	 * object arguments, if the logger is currently enabled for the given
278
	 * message level.
279
	 * 
280
	 * @param sourceClass
281
	 *            Name of class that issued the logging request.
282
	 * @param sourceMethod
283
	 *            Name of method that issued the logging request.
284
	 * @param msg
285
	 *            The key in the message localization catalog for the message or
286
	 *            the actual message itself. During formatting, if the logger
287
	 *            has a mapping for the msg string, then the msg string is
288
	 *            replaced by the localized value. Otherwise the original msg
289
	 *            string is used. The formatter uses java.text.MessageFormat
290
	 *            style formatting to format parameters, so for example a format
291
	 *            string "{0} {1}" would format two inserts into the message.
292
	 * @param inserts
293
	 *            Array of parameters to the message.
294
	 */
295
	public void info(String sourceClass, String sourceMethod, String msg, Object[] inserts);
296
297
	/**
298
	 * Log a message, specifying source class and method, with an array of
299
	 * object arguments and a throwable, if the logger is currently enabled for
300
	 * the given message level.
301
	 * 
302
	 * @param sourceClass
303
	 *            Name of class that issued the logging request.
304
	 * @param sourceMethod
305
	 *            Name of method that issued the logging request.
306
	 * @param msg
307
	 *            The key in the message localization catalog for the message or
308
	 *            the actual message itself. During formatting, if the logger
309
	 *            has a mapping for the msg string, then the msg string is
310
	 *            replaced by the localized value. Otherwise the original msg
311
	 *            string is used. The formatter uses java.text.MessageFormat
312
	 *            style formatting to format parameters, so for example a format
313
	 *            string "{0} {1}" would format two inserts into the message.
314
	 * @param inserts
315
	 *            Array of parameters to the message.
316
	 * @param thrown
317
	 *            Throwable associated with log message.
318
	 */
319
	public void info(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown);
320
321
	/**
322
	 * Log a message, specifying source class and method, if the logger is
323
	 * currently enabled for the given message level.
324
	 * 
325
	 * @param sourceClass
326
	 *            Name of class that issued the logging request.
327
	 * @param sourceMethod
328
	 *            Name of method that issued the logging request.
329
	 * @param msg
330
	 *            The key in the message localization catalog for the message or
331
	 *            the actual message itself. During formatting, if the logger
332
	 *            has a mapping for the msg string, then the msg string is
333
	 *            replaced by the localized value. Otherwise the original msg
334
	 *            string is used.
335
	 */
336
	public void config(String sourceClass, String sourceMethod, String msg);
337
338
	/**
339
	 * Log a message, specifying source class and method, with an array of
340
	 * object arguments, if the logger is currently enabled for the given
341
	 * message level.
342
	 * 
343
	 * @param sourceClass
344
	 *            Name of class that issued the logging request.
345
	 * @param sourceMethod
346
	 *            Name of method that issued the logging request.
347
	 * @param msg
348
	 *            The key in the message localization catalog for the message or
349
	 *            the actual message itself. During formatting, if the logger
350
	 *            has a mapping for the msg string, then the msg string is
351
	 *            replaced by the localized value. Otherwise the original msg
352
	 *            string is used. The formatter uses java.text.MessageFormat
353
	 *            style formatting to format parameters, so for example a format
354
	 *            string "{0} {1}" would format two inserts into the message.
355
	 * @param inserts
356
	 *            Array of parameters to the message.
357
	 */
358
	public void config(String sourceClass, String sourceMethod, String msg, Object[] inserts);
359
360
	/**
361
	 * Log a message, specifying source class and method, with an array of
362
	 * object arguments and a throwable, if the logger is currently enabled for
363
	 * the given message level.
364
	 * 
365
	 * @param sourceClass
366
	 *            Name of class that issued the logging request.
367
	 * @param sourceMethod
368
	 *            Name of method that issued the logging request.
369
	 * @param msg
370
	 *            The key in the message localization catalog for the message or
371
	 *            the actual message itself. During formatting, if the logger
372
	 *            has a mapping for the msg string, then the msg string is
373
	 *            replaced by the localized value. Otherwise the original msg
374
	 *            string is used. The formatter uses java.text.MessageFormat
375
	 *            style formatting to format parameters, so for example a format
376
	 *            string "{0} {1}" would format two inserts into the message.
377
	 * @param inserts
378
	 *            Array of parameters to the message.
379
	 * @param thrown
380
	 *            Throwable associated with log message.
381
	 */
382
	public void config(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown);
383
384
	/**
385
	 * Trace a message, specifying source class and method, if the logger is
386
	 * currently enabled for the given message level.
387
	 * 
388
	 * @param sourceClass
389
	 *            Name of class that issued the logging request.
390
	 * @param sourceMethod
391
	 *            Name of method that issued the logging request.
392
	 * @param msg
393
	 *            The key in the message catalog for the message or the actual
394
	 *            message itself. During formatting, if the logger has a mapping
395
	 *            for the msg string, then the msg string is replaced by the
396
	 *            value. Otherwise the original msg string is used.
397
	 */
398
	public void fine(String sourceClass, String sourceMethod, String msg);
399
400
	/**
401
	 * Trace a message, specifying source class and method, with an array of
402
	 * object arguments, if the logger is currently enabled for the given
403
	 * message level.
404
	 * 
405
	 * @param sourceClass
406
	 *            Name of class that issued the logging request.
407
	 * @param sourceMethod
408
	 *            Name of method that issued the logging request.
409
	 * @param msg
410
	 *            The key in the message catalog for the message or the actual
411
	 *            message itself. During formatting, if the logger has a mapping
412
	 *            for the msg string, then the msg string is replaced by the
413
	 *            value. Otherwise the original msg string is used. The
414
	 *            formatter uses java.text.MessageFormat style formatting to
415
	 *            format parameters, so for example a format string "{0} {1}"
416
	 *            would format two inserts into the message.
417
	 * @param inserts
418
	 *            Array of parameters to the message.
419
	 */
420
	public void fine(String sourceClass, String sourceMethod, String msg, Object[] inserts);
421
	
422
	public void fine(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex);
423
424
	/**
425
	 * Trace a message, specifying source class and method, if the logger is
426
	 * currently enabled for the given message level.
427
	 * 
428
	 * @param sourceClass
429
	 *            Name of class that issued the logging request.
430
	 * @param sourceMethod
431
	 *            Name of method that issued the logging request.
432
	 * @param msg
433
	 *            The key in the message catalog for the message or the actual
434
	 *            message itself. During formatting, if the logger has a mapping
435
	 *            for the msg string, then the msg string is replaced by the
436
	 *            value. Otherwise the original msg string is used.
437
	 */
438
	public void finer(String sourceClass, String sourceMethod, String msg);
439
440
	/**
441
	 * Trace a message, specifying source class and method, with an array of
442
	 * object arguments, if the logger is currently enabled for the given
443
	 * message level.
444
	 * 
445
	 * @param sourceClass
446
	 *            Name of class that issued the logging request.
447
	 * @param sourceMethod
448
	 *            Name of method that issued the logging request.
449
	 * @param msg
450
	 *            The key in the message catalog for the message or the actual
451
	 *            message itself. During formatting, if the logger has a mapping
452
	 *            for the msg string, then the msg string is replaced by the
453
	 *            value. Otherwise the original msg string is used. The
454
	 *            formatter uses java.text.MessageFormat style formatting to
455
	 *            format parameters, so for example a format string "{0} {1}"
456
	 *            would format two inserts into the message.
457
	 * @param inserts
458
	 *            Array of parameters to the message.
459
	 */
460
	public void finer(String sourceClass, String sourceMethod, String msg, Object[] inserts);
461
	
462
	public void finer(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex);
463
464
	/**
465
	 * Trace a message, specifying source class and method, if the logger is
466
	 * currently enabled for the given message level.
467
	 * 
468
	 * @param sourceClass
469
	 *            Name of class that issued the logging request.
470
	 * @param sourceMethod
471
	 *            Name of method that issued the logging request.
472
	 * @param msg
473
	 *            The key in the message catalog for the message or the actual
474
	 *            message itself. During formatting, if the logger has a mapping
475
	 *            for the msg string, then the msg string is replaced by the
476
	 *            value. Otherwise the original msg string is used.
477
	 */
478
	public void finest(String sourceClass, String sourceMethod, String msg);
479
480
	/**
481
	 * Trace a message, specifying source class and method, with an array of
482
	 * object arguments, if the logger is currently enabled for the given
483
	 * message level.
484
	 * 
485
	 * @param sourceClass
486
	 *            Name of class that issued the logging request.
487
	 * @param sourceMethod
488
	 *            Name of method that issued the logging request.
489
	 * @param msg
490
	 *            The key in the message catalog for the message or the actual
491
	 *            message itself. During formatting, if the logger has a mapping
492
	 *            for the msg string, then the msg string is replaced by the
493
	 *            value. Otherwise the original msg string is used. The
494
	 *            formatter uses java.text.MessageFormat style formatting to
495
	 *            format parameters, so for example a format string "{0} {1}"
496
	 *            would format two inserts into the message.
497
	 * @param inserts
498
	 *            Array of parameters to the message.
499
	 */
500
	public void finest(String sourceClass, String sourceMethod, String msg, Object[] inserts);
501
	
502
	public void finest(String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex);
503
504
	/**
505
	 * Log a message, specifying source class and method, with an array of
506
	 * object arguments and a throwable, if the logger is currently enabled for
507
	 * the given message level.
508
	 * 
509
	 * @param level
510
	 *            One of the message level identifiers, e.g. SEVERE.
511
	 * @param sourceClass
512
	 *            Name of class that issued the logging request.
513
	 * @param sourceMethod
514
	 *            Name of method that issued the logging request.
515
	 * @param msg
516
	 *            The key in the message localization catalog for the message or
517
	 *            the actual message itself. During formatting, if the logger
518
	 *            has a mapping for the msg string, then the msg string is
519
	 *            replaced by the localized value. Otherwise the original msg
520
	 *            string is used. The formatter uses java.text.MessageFormat
521
	 *            style formatting to format parameters, so for example a format
522
	 *            string "{0} {1}" would format two inserts into the message.
523
	 * @param inserts
524
	 *            Array of parameters to the message, may be null.
525
	 * @param thrown
526
	 *            Throwable associated with log message.
527
	 */
528
	public void log(int level, String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable thrown);
529
530
	/**
531
	 * Log a trace message, specifying source class and method, with an array of
532
	 * object arguments and a throwable, if the logger is currently enabled for
533
	 * the given message level.
534
	 * 
535
	 * @param level
536
	 *            One of the message level identifiers, e.g. SEVERE.
537
	 * @param sourceClass
538
	 *            Name of class that issued the logging request.
539
	 * @param sourceMethod
540
	 *            Name of method that issued the logging request.
541
	 * @param msg
542
	 *            The key in the message catalog for the message or the actual
543
	 *            message itself. During formatting, if the logger has a mapping
544
	 *            for the msg string, then the msg string is replaced by the
545
	 *            value. Otherwise the original msg string is used. The
546
	 *            formatter uses java.text.MessageFormat style formatting to
547
	 *            format parameters, so for example a format string "{0} {1}"
548
	 *            would format two inserts into the message.
549
	 * @param inserts
550
	 *            Array of parameters to the message, may be null.
551
	 */
552
	public void trace(int level, String sourceClass, String sourceMethod, String msg, Object[] inserts, Throwable ex);
553
554
	/**
555
	 * Format a log message without causing it to be written to the log.
556
	 * 
557
	 * @param msg
558
	 *            The key in the message localization catalog for the message or
559
	 *            the actual message itself. During formatting, if the logger
560
	 *            has a mapping for the msg string, then the msg string is
561
	 *            replaced by the localized value. Otherwise the original msg
562
	 *            string is used. The formatter uses java.text.MessageFormat
563
	 *            style formatting to format parameters, so for example a format
564
	 *            string "{0} {1}" would format two inserts into the message.
565
	 * @param inserts
566
	 *            Array of parameters to the message.
567
	 * @return The formatted message for the current locale.
568
	 */
569
	public String formatMessage(String msg, Object[] inserts);
570
	
571
	public void dumpTrace();
572
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/logging/LoggerFactory.java (-152 lines)
Lines 1-152 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.logging;
13
14
import java.lang.reflect.Method;
15
16
/**
17
 * LoggerFactory will create a logger instance ready for use by the caller. 
18
 * 
19
 * The default is to create a logger that utilises the Java's built in 
20
 * logging facility java.util.logging (JSR47).  It is possible to override
21
 * this for systems where JSR47 is not available or an alternative logging
22
 * facility is needed by using setLogger and passing the the class name of 
23
 * a logger that implements {@link Logger}
24
 */
25
import java.util.MissingResourceException;
26
import java.util.ResourceBundle;
27
/**
28
 * A factory that returns a logger for use by the MQTT client. 
29
 * 
30
 * The default log and trace facility uses Java's build in log facility:-
31
 * java.util.logging.  For systems where this is not available or where
32
 * an alternative logging framework is required the logging facility can be 
33
 * replaced using {@link org.eclipse.paho.client.mqttv3.logging.LoggerFactory#setLogger(String)}
34
 * which takes an implementation of the {@link org.eclipse.paho.client.mqttv3.logging.Logger}
35
 * interface.
36
 */
37
public class LoggerFactory {
38
	/**
39
	 * Default message catalog.
40
	 */
41
	public final static String MQTT_CLIENT_MSG_CAT = "org.eclipse.paho.client.mqttv3.internal.nls.logcat";
42
	private final static String className = LoggerFactory.class.getName();
43
	
44
	private static String overrideloggerClassName = null;
45
	/**
46
	 * Default logger that uses java.util.logging. 
47
	 */
48
	private static String jsr47LoggerClassName = "org.eclipse.paho.client.mqttv3.logging.JSR47Logger"; 
49
	
50
	/**
51
	 * Find or create a logger for a named package/class. 
52
	 * If a logger has already been created with the given name 
53
	 * it is returned. Otherwise a new logger is created. By default a logger
54
	 * that uses java.util.logging will be returned.
55
	 * 
56
	 * @param messageCatalogName the resource bundle containing the logging messages.
57
	 * @param loggerID  unique name to identify this logger.
58
	 * @return a suitable Logger.
59
	 * @throws Exception
60
	 */
61
	public static Logger getLogger(String messageCatalogName, String loggerID) {
62
		String loggerClassName = overrideloggerClassName;
63
		Logger logger = null;
64
		
65
		if (loggerClassName == null) {
66
			loggerClassName = jsr47LoggerClassName;
67
		}
68
//			logger = getJSR47Logger(ResourceBundle.getBundle(messageCatalogName), loggerID, null) ;
69
		logger = getLogger(loggerClassName, ResourceBundle.getBundle(messageCatalogName), loggerID, null) ;
70
//		}
71
72
		if (null == logger) {
73
			throw new MissingResourceException("Error locating the logging class", className, loggerID);
74
		}
75
76
		return logger;
77
	}
78
79
80
	/**
81
	 * Return an instance of a logger
82
	 * 
83
	 * @param the class name of the load to load
84
	 * @param messageCatalog  the resource bundle containing messages 
85
	 * @param loggerID  an identifier for the logger 
86
	 * @param resourceName a name or context to associate with this logger instance.  
87
	 * @return a ready for use logger
88
	 */
89
	private static Logger getLogger(String loggerClassName, ResourceBundle messageCatalog, String loggerID, String resourceName) { //, FFDC ffdc) {
90
		Logger logger  = null;
91
		Class logClass = null;
92
		
93
		try {
94
			logClass = Class.forName(loggerClassName);
95
		} catch (NoClassDefFoundError ncdfe) {
96
			return null;
97
		} catch (ClassNotFoundException cnfe) {
98
			return null;
99
		}
100
		if (null != logClass) {
101
			// Now instantiate the log
102
			try {
103
				logger = (Logger)logClass.newInstance();
104
			} catch (IllegalAccessException e) {
105
				return null;
106
			} catch (InstantiationException e) {
107
				return null;
108
			} catch (ExceptionInInitializerError e) {
109
				return null;
110
			} catch (SecurityException e) {
111
				return null;
112
			}
113
			logger.initialise(messageCatalog, loggerID, resourceName);
114
		}
115
116
		return logger;
117
	}
118
119
	/**
120
	 * When run in JSR47, this allows access to the properties in the logging.properties
121
	 * file.
122
	 * If not run in JSR47, or the property isn't set, returns null.
123
	 * @param name the property to return
124
	 * @return the property value, or null if it isn't set or JSR47 isn't being used
125
	 */
126
	public static String getLoggingProperty(String name) {
127
		String result = null;
128
		try {
129
			// Hide behind reflection as java.util.logging is guaranteed to be
130
			// available.
131
			Class logManagerClass = Class.forName("java.util.logging.LogManager");
132
			Method m1 = logManagerClass.getMethod("getLogManager", new Class[]{});
133
			Object logManagerInstance = m1.invoke(null, null);
134
			Method m2 = logManagerClass.getMethod("getProperty", new Class[]{String.class});
135
			result = (String)m2.invoke(logManagerInstance,new Object[]{name});
136
		} catch(Exception e) {
137
			// Any error, assume JSR47 isn't available and return null
138
			result = null;
139
		}
140
		return result;
141
	}
142
143
	/**
144
	 * Set the class name of the logger that the LoggerFactory will load
145
	 * If not set getLogger will attempt to create a logger 
146
	 * appropriate for the platform.
147
	 * @param loggerClassName - Logger implementation class name to use.
148
	 */
149
	public static void setLogger(String loggerClassName) {
150
		LoggerFactory.overrideloggerClassName = loggerClassName;
151
	}
152
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/logging/SimpleLogFormatter.java (-91 lines)
Lines 1-91 Link Here
1
package org.eclipse.paho.client.mqttv3.logging;
2
3
import java.io.PrintWriter;
4
import java.io.StringWriter;
5
import java.text.MessageFormat;
6
import java.util.Date;
7
import java.util.logging.Formatter;
8
import java.util.logging.LogRecord;
9
10
/**
11
 * SimpleLogFormatter prints a single line 
12
 * log record in human readable form.
13
 */
14
public class SimpleLogFormatter extends Formatter {
15
	
16
	final String ls = System.getProperty("line.separator");
17
    /**
18
     * Constructs a <code>SimpleFormatter</code> object.
19
     */
20
    public SimpleLogFormatter() {
21
        super();
22
    }
23
24
    /**
25
     * Format the logrecord as a single line with well defined columns.
26
     */
27
    public String format(LogRecord r) {
28
        StringBuffer sb = new StringBuffer();
29
        sb.append(r.getLevel().getName()+"\t");
30
        sb.append(MessageFormat.format("{0, date, yy-MM-dd} {0, time, kk:mm:ss.SSSS} ",
31
                new Object[] { new Date(r.getMillis()) })+"\t");
32
        String cnm = r.getSourceClassName();
33
        String cn="";
34
        if (cnm != null) {
35
	        int cnl = cnm.length();
36
	        if (cnl>20) {
37
	        	cn = r.getSourceClassName().substring(cnl-19);
38
	        } else {
39
	        	char sp[] = {' '};
40
	        	StringBuffer sb1= new StringBuffer().append(cnm);
41
	        	cn = sb1.append(sp,0, 1).toString();
42
	        }        
43
        }
44
        sb.append(cn+"\t").append(" ");
45
        sb.append(left(r.getSourceMethodName(),23,' ')+"\t");
46
        sb.append(r.getThreadID()+"\t"); 
47
        sb.append(formatMessage(r)).append(ls);
48
        if (null != r.getThrown()) {
49
            sb.append("Throwable occurred: "); 
50
            Throwable t = r.getThrown();
51
            PrintWriter pw = null;
52
            try {
53
                StringWriter sw = new StringWriter();
54
                pw = new PrintWriter(sw);
55
                t.printStackTrace(pw);
56
                sb.append(sw.toString());
57
            } finally {
58
                if (pw != null) {
59
                    try {
60
                        pw.close();
61
                    } catch (Exception e) {
62
                        // ignore
63
                    }
64
                }
65
            }
66
        }
67
        return sb.toString();
68
    }
69
    
70
	/**
71
	   * Left justify a string.
72
	   *
73
	   * @param s the string to justify
74
	   * @param width the field width to justify within
75
	   * @param fillChar the character to fill with
76
	   *
77
	   * @return the justified string.
78
	   */
79
	  public static String left(String s, int width, char fillChar) {
80
	    if (s.length() >= width) {
81
	      return s;
82
	    }
83
	    StringBuffer sb = new StringBuffer(width);
84
	    sb.append(s);
85
	    for (int i = width - s.length(); --i >= 0;) {
86
	      sb.append(fillChar);
87
	    }
88
	    return sb.toString();
89
	  }
90
91
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/logging/jsr47min.properties (-83 lines)
Lines 1-83 Link Here
1
# Properties file which configures the operation of the JDK logging facility.
2
#
3
# The configuration in this file is the suggesgted configuration
4
# for collecting trace for helping debug problems related to the
5
# Paho MQTT client.  It configures trace to be continuosly collected
6
# in memory with minimal impact on performance. 
7
# 
8
# When the push trigger (by default a Severe level message) or a 
9
# specific request is made to "push" the in memory trace then it 
10
# is "pushed" to the configured target handler. By default
11
# this is the standard java.util.logging.FileHandler. The Paho Debug 
12
# class can be used to push the memory trace to its target 
13
# 
14
# To enable trace either:
15
# - use this properties file as is and set the logging facility up 
16
#   to use it by configuring the util logging system property e.g.
17
#
18
# >java -Djava.util.logging.config.file=<location>\jsr47min.properties
19
#
20
# - This contents of this file can also be merged with another
21
#   java.util.logging config file to ensure provide wider logging 
22
#   and trace including Paho trace 
23
24
# Global logging properties.
25
# ------------------------------------------
26
# The set of handlers to be loaded upon startup.
27
# Comma-separated list of class names.
28
# - Root handlers are not enabled by default - just handlers on the Paho packages. 
29
#handlers=java.util.logging.MemoryHandler,java.util.logging.FileHandler, java.util.logging.ConsoleHandler
30
31
# Default global logging level.
32
# Loggers and Handlers may override this level
33
#.level=INFO
34
35
# Loggers
36
# ------------------------------------------
37
# A memoryhandler is attached to the paho packages
38
# and the level specified to collected all trace related
39
# to paho packages.  This will override any root/global
40
# level handlers if set.  
41
org.eclipse.paho.client.mqttv3.handlers=java.util.logging.MemoryHandler
42
org.eclipse.paho.client.mqttv3.level=ALL
43
# It is possible to set more granular trace on a per class basis e.g.
44
#org.eclipse.paho.client.mqttv3.internal.ClientComms.level=ALL
45
46
# Handlers
47
# -----------------------------------------
48
# Note: the target handler that is associated with the MemoryHandler is not a root handler 
49
# and hence not returned when getting the handlers from root. It appears accessing 
50
# target handler programatically is not possible as target is a private variable in 
51
# class MemoryHandler
52
java.util.logging.MemoryHandler.level=FINEST
53
java.util.logging.MemoryHandler.size=10000
54
java.util.logging.MemoryHandler.push=SEVERE
55
java.util.logging.MemoryHandler.target=java.util.logging.FileHandler
56
#java.util.logging.MemoryHandler.target=java.util.logging.ConsoleHandler
57
58
59
# --- FileHandler ---
60
# Override of global logging level
61
java.util.logging.FileHandler.level=ALL
62
63
# Naming style for the output file:
64
# (The output file is placed in the directory
65
# defined by the "user.home" System property.)
66
# See java.util.logging for more options 
67
java.util.logging.FileHandler.pattern=%h/paho%u.log
68
69
# Limiting size of output file in bytes:
70
java.util.logging.FileHandler.limit=200000
71
72
# Number of output files to cycle through, by appending an
73
# integer to the base file name:
74
java.util.logging.FileHandler.count=3
75
76
# Style of output (Simple or XML):
77
java.util.logging.FileHandler.formatter=org.eclipse.paho.client.mqttv3.logging.SimpleLogFormatter
78
79
# --- ConsoleHandler ---
80
# Override of global logging level
81
#java.util.logging.ConsoleHandler.level=INFO
82
#java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
83
#java.util.logging.ConsoleHandler.formatter=org.eclipse.paho.client.mqttv3.logging.SimpleLogFormatter
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/logging/package.html (-18 lines)
Lines 1-18 Link Here
1
<body>
2
Provides facilities to write and format log and trace to help debug problems.
3
4
<p>The default log and trace facility uses Java's build in log facility:-
5
java.util.logging.  For systems where this is not available or where
6
an alternative logging framework is required the logging facility can be 
7
replaced using {@link org.eclipse.paho.client.mqttv3.logging.LoggerFactory#setLogger(String)}
8
which takes an implementation of the {@link org.eclipse.paho.client.mqttv3.logging.Logger}
9
interface.
10
11
<p>A sample java.util.logging properties file - jsr47min.properties is provided that demonstrates
12
how to run with a memory based trace facility that runs with minimal performance 
13
overhead. The memory buffer can be dumped when a log/trace record is written matching 
14
the MemoryHandlers trigger level or when the push method is invoked on the MemoryHandler. 
15
{@link org.eclipse.paho.client.mqttv3.util.Debug Debug} provides method to make it easy
16
to dump the memory buffer as well as other useful debug info. 
17
18
</body>
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/package.html (-127 lines)
Lines 1-127 Link Here
1
<body>
2
Contains a programming interface enabling applications to communicate with an MQTT server
3
4
<p>
5
The MQ Telemetry Transport (MQTT) is a lightweight broker-based publish/subscribe 
6
messaging protocol designed to be open, simple, lightweight and easy to implement. 
7
These characteristics make it ideal for use in constrained environments, for example, 
8
but not limited to:
9
<ul>
10
  <li>Where the network is expensive, has low bandwidth or is unreliable such as mobile and vsat networks
11
  <li>When run on an embedded or mobile device with limited processor, memory or battery
12
</ul>
13
<p>Features of the protocol include:
14
<ul>
15
  <li>The publish/subscribe message pattern to provide one-to-many message 
16
  distribution and decoupling of applications
17
  <li>A messaging transport that is agnostic to the content of the payload
18
  <li>The use of TCP/IP to provide network connectivity
19
  <li>The use of SSL/TLS to provide network security and trust
20
  <li>Three qualities of service for message delivery which are maintained across 
21
  network, client and server breaks.
22
  <ul>
23
    <li>"At most once", where messages are delivered according to the best efforts 
24
     of the underlying TCP/IP network. Message loss or duplication can occur. 
25
     This level could be used, for example, with ambient sensor data where it 
26
     does not matter if an individual reading is lost as the next one will be published soon after.
27
    <li>"At least once", where messages are assured to arrive but duplicates may occur.
28
    <li>"Exactly once", where message are assured to arrive exactly once. This 
29
     level could be used, for example, with billing systems where duplicate or 
30
     lost messages could lead to incorrect charges being applied.
31
  </ul>
32
  The quality of service for message delivery is met even if the network connection 
33
  breaks, or the client or the server stop while a message is being delivered
34
  <li>A small transport overhead (the fixed-length header is just 2 bytes), and 
35
   protocol exchanges minimised to reduce network traffic
36
  <li>A mechanism to notify interested parties to an abnormal disconnection of
37
   a client using the Last Will and Testament feature
38
</ul>
39
40
<p>The basic means of operating the client is:</p>
41
<ol>
42
  <li>Create an instance of {@link org.eclipse.paho.client.mqttv3.MqttClient} or 
43
	{@link org.eclipse.paho.client.mqttv3.MqttAsyncClient}, providing
44
	the address of an MQTT server and a unique client identifier.</li>
45
  <li><code>connect</code> to the server</li>
46
  <li>Exchange messages with the server:
47
  <ul>
48
	<li><code>publish messages</code> to the server, 
49
	 via a <code>topic</code>.</li>
50
	<li><code>subscribe</code> to one more <code>topics</code>. The server will send any messages
51
	 it receives on those topics to the client. The client will be informed when a message 
52
	 arrives via a <code>callback</code> 
53
  </ul>
54
  <li><code>disconnect</code> from the server.</li>
55
</ol>
56
57
<p>The programming model and concepts like the protocol are small and easy to use. Key concepts 
58
to use when creating MQTT application include:
59
<ul>
60
  <li>Every client instance that connects to an MQTT server must have a unique client identifier.
61
    If a second instance of a client with the same ID connects to a server the first instance will be
62
    disconnected.
63
  <li>For message delivery to be reliable and withstand both abnormal network breaks and clients/server
64
    outages the client must use a persistent store to hold messages while they are being delivered. This is 
65
    the default case where a file based persistent store 
66
    {@link org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence MqttDefaultFilePersistence} is used. 
67
  <li>When connecting the {@link org.eclipse.paho.client.mqttv3.MqttConnectOptions#setCleanSession(boolean) cleansession}
68
   option has a big impact on the operation of the client. If set to false:
69
  <ul>
70
    <li>Message delivery will match the quality of service specified when the message was published even across
71
      failures of the network, client or server
72
    <li>The server will store messages for active subscriptions on behalf of the client when the client is not connected.
73
      The server will deliver these messages to the client the next time it connects.
74
  </ul>
75
  If set to true:
76
  <ul>
77
    <li>Any state stored on the client and server related to the client will be cleansed 
78
      before the connection is fully started.  Subscriptions from earlier sessions will be unsubscribed
79
      and any messages still in-flight from previous sessions will be deleted.
80
    <li>When the client disconnects either as the result of the application requesting a disconnect
81
      or a network failure, state related to the client will be cleansed just as at connect time. 
82
    <li>Messages will only be delivered to the quality of service requested at publish time if 
83
      the connection is maintained while the message is being delivered
84
  </ul>
85
  <li>When subscribing for messages the subscription can be for an absolute topic or a wildcarded topic.
86
  <li>When unsubscribing the topic to be unsubscribed must match one specified on an earlier subscribe.
87
  <li>There are two MQTT client libraries to choose from:
88
  <ol>
89
    <li>{@link org.eclipse.paho.client.mqttv3.IMqttAsyncClient MqttAsyncClient} which provides a non-blocking interface where
90
      methods return before the requested operation has completed. The completion of the operation
91
      can be monitored by in several ways:
92
    <ul>
93
      <li>Use the {@link org.eclipse.paho.client.mqttv3.IMqttToken#waitForCompletion waitForCompletion}
94
       call on the token returned from the operation. This will block 
95
        until the operation completes. 
96
      <li>Pass a {@link org.eclipse.paho.client.mqttv3.IMqttActionListener IMqttActionListener}
97
        to the operation. The listener will then be called back when the operation completes.
98
      <li>Set a {@link org.eclipse.paho.client.mqttv3.MqttCallback MqttCallback} on the client. It
99
        will be notified when a message arrive, a message have been delivered to the server and when the
100
        connection to the server is lost.
101
    </ul>
102
    <li>{@link org.eclipse.paho.client.mqttv3.IMqttClient MqttClient} where methods block until
103
      the operation has completed.
104
  </ol>
105
  <li>For both the blocking and non-blocking clients some operations are asynchronous.  This includes:
106
  <ul>
107
    <li>Notification that a new message has arrived:
108
      {@link org.eclipse.paho.client.mqttv3.MqttCallback#messageArrived messageArrived}.
109
    <li>Notification that the connection to the server has broken:
110
      {@link org.eclipse.paho.client.mqttv3.MqttCallback#connectionLost connectionLost}.
111
    <li>Notification that a message has been delivered to the server: 
112
      {@link org.eclipse.paho.client.mqttv3.MqttCallback#deliveryComplete deliveryComplete}.
113
  </ul>
114
  A client registers interest in these notifications by registering a 
115
  {@link org.eclipse.paho.client.mqttv3.MqttCallback MqttCallback} on the client
116
  <li>There are a number of programs that demonstrate the different modes of 
117
    writing MQTT applications
118
  <ul>
119
    <li>{@link org.eclipse.paho.sample.mqttv3app.Sample} uses the blocking client interface
120
    <li>{@link org.eclipse.paho.sample.mqttv3app.SampleAsyncCallBack} uses the asynchronous client with
121
      callbacks which are notified when an operation completes
122
    <li>{@link org.eclipse.paho.sample.mqttv3app.SampleAsyncWait} uses the asynchronous client and
123
      shows how to use the token returned from each operation to block until the operation completes.
124
  </ul>
125
</ul>
126
127
</body>
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/persist/MemoryPersistence.java (-89 lines)
Lines 1-89 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.persist;
13
14
import java.util.Enumeration;
15
import java.util.Hashtable;
16
17
import org.eclipse.paho.client.mqttv3.MqttClientPersistence;
18
import org.eclipse.paho.client.mqttv3.MqttPersistable;
19
import org.eclipse.paho.client.mqttv3.MqttPersistenceException;
20
21
/**
22
 * Persistence that uses memory
23
 * 
24
 * In cases where reliability is not required across client or device 
25
 * restarts memory this memory peristence can be used. In cases where
26
 * reliability is required like when clean session is set to false
27
 * then a non-volatile form of persistence should be used. 
28
 * 
29
 */
30
public class MemoryPersistence implements MqttClientPersistence {
31
32
	private Hashtable data;
33
	
34
	/* (non-Javadoc)
35
	 * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#close()
36
	 */
37
	public void close() throws MqttPersistenceException {
38
		data.clear();
39
	}
40
41
	/* (non-Javadoc)
42
	 * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#keys()
43
	 */
44
	public Enumeration keys() throws MqttPersistenceException {
45
		return data.keys();
46
	}
47
48
	/* (non-Javadoc)
49
	 * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#get(java.lang.String)
50
	 */
51
	public MqttPersistable get(String key) throws MqttPersistenceException {
52
		return (MqttPersistable)data.get(key);
53
	}
54
55
	/* (non-Javadoc)
56
	 * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#open(java.lang.String, java.lang.String)
57
	 */
58
	public void open(String clientId, String serverURI) throws MqttPersistenceException {
59
		this.data = new Hashtable();
60
	}
61
62
	/* (non-Javadoc)
63
	 * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#put(java.lang.String, org.eclipse.paho.client.mqttv3.MqttPersistable)
64
	 */
65
	public void put(String key, MqttPersistable persistable) throws MqttPersistenceException {
66
		data.put(key, persistable);
67
	}
68
69
	/* (non-Javadoc)
70
	 * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#remove(java.lang.String)
71
	 */
72
	public void remove(String key) throws MqttPersistenceException {
73
		data.remove(key);
74
	}
75
76
	/* (non-Javadoc)
77
	 * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#clear()
78
	 */
79
	public void clear() throws MqttPersistenceException {
80
		data.clear();
81
	}
82
83
	/* (non-Javadoc)
84
	 * @see org.eclipse.paho.client.mqttv3.MqttClientPersistence#containsKey(java.lang.String)
85
	 */
86
	public boolean containsKey(String key) throws MqttPersistenceException {
87
		return data.containsKey(key);
88
	}
89
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/persist/MqttDefaultFilePersistence.java (-288 lines)
Lines 1-288 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.persist;
13
14
import java.io.File;
15
import java.io.FileFilter;
16
import java.io.FileInputStream;
17
import java.io.FileOutputStream;
18
import java.io.FilenameFilter;
19
import java.io.IOException;
20
import java.util.Enumeration;
21
import java.util.Vector;
22
23
import org.eclipse.paho.client.mqttv3.MqttClientPersistence;
24
import org.eclipse.paho.client.mqttv3.MqttPersistable;
25
import org.eclipse.paho.client.mqttv3.MqttPersistenceException;
26
import org.eclipse.paho.client.mqttv3.internal.FileLock;
27
import org.eclipse.paho.client.mqttv3.internal.MqttPersistentData;
28
29
/**
30
 * An implementation of the {@link MqttClientPersistence} interface that provides
31
 * file based persistence.
32
 * 
33
 * A directory is specified when the Persistence object is created. When the persistence
34
 * is then opened (see {@link #open(String, String)}), a sub-directory is made beneath the base
35
 * for this client ID and connection key. This allows one persistence base directory
36
 * to be shared by multiple clients.
37
 * 
38
 * The sub-directory's name is created from a concatenation of the client ID and connection key
39
 * with any instance of '/', '\\', ':' or ' ' removed.
40
 */
41
public class MqttDefaultFilePersistence implements MqttClientPersistence {
42
43
	private File dataDir;
44
	private File clientDir = null;
45
	private FileLock fileLock = null;
46
	private static final String MESSAGE_FILE_EXTENSION = ".msg";
47
	private static final String MESSAGE_BACKUP_FILE_EXTENSION = ".bup";
48
	private static final String LOCK_FILENAME = ".lck"; 
49
	
50
	private static final FilenameFilter FILE_FILTER = new FilenameFilter() { 
51
		public boolean accept(File dir, String name) { return name.endsWith(MESSAGE_FILE_EXTENSION); }
52
		};
53
	
54
	public MqttDefaultFilePersistence()  { //throws MqttPersistenceException {
55
		this(System.getProperty("user.dir"));
56
	}
57
	
58
	/**
59
	 * Create an file-based persistent data store within the specified directory.
60
	 * @param directory the directory to use.
61
	 */
62
	public MqttDefaultFilePersistence(String directory) { //throws MqttPersistenceException {
63
		dataDir = new File(directory);
64
	}
65
	
66
	public void open(String clientId, String theConnection) throws MqttPersistenceException {
67
		
68
		if (dataDir.exists() && !dataDir.isDirectory()) {
69
			throw new MqttPersistenceException();
70
		} else if (!dataDir.exists() ) {
71
			if (!dataDir.mkdirs()) {
72
				throw new MqttPersistenceException();
73
			}
74
		} 
75
		if (!dataDir.canWrite()) {
76
			throw new MqttPersistenceException();
77
		}
78
		
79
		
80
		StringBuffer keyBuffer = new StringBuffer();
81
		for (int i=0;i<clientId.length();i++) {
82
			char c = clientId.charAt(i);
83
			if (isSafeChar(c)) {
84
				keyBuffer.append(c);
85
			}
86
		}
87
		keyBuffer.append("-");
88
		for (int i=0;i<theConnection.length();i++) {
89
			char c = theConnection.charAt(i);
90
			if (isSafeChar(c)) {
91
				keyBuffer.append(c);
92
			}
93
		}
94
		String key = keyBuffer.toString();
95
96
		clientDir = new File(dataDir,key);
97
98
		if (!clientDir.exists()) {
99
			clientDir.mkdir();
100
		}
101
	
102
		try {
103
			fileLock = new FileLock(clientDir,LOCK_FILENAME);
104
		} catch (Exception e) {
105
			throw new MqttPersistenceException(MqttPersistenceException.REASON_CODE_PERSISTENCE_IN_USE);
106
		}
107
108
		// Scan the directory for .backup files. These will
109
		// still exist if the JVM exited during addMessage, before
110
		// the new message was written to disk and the backup removed.
111
		restoreBackups(clientDir);
112
		
113
	}
114
115
	/**
116
	 * Checks whether the persistence has been opened.
117
	 * @throws MqttPersistenceException if the persistence has not been opened.
118
	 */
119
	private void checkIsOpen() throws MqttPersistenceException {
120
		if (clientDir == null) {
121
			throw new MqttPersistenceException();
122
		}
123
	}
124
125
	public void close() throws MqttPersistenceException {
126
127
//		checkIsOpen();
128
		if (fileLock != null) {
129
			fileLock.release();
130
		}
131
132
		if (getFiles().length == 0) {
133
			clientDir.delete();
134
		}
135
		clientDir = null;
136
	}
137
138
	/**
139
	 * Writes the specified persistent data to the previously specified persistence directory.
140
	 * This method uses a safe overwrite policy to ensure IO errors do not lose messages.
141
	 * @param message
142
	 * @throws MqttPersistenceException
143
	 */
144
	public void put(String key, MqttPersistable message) throws MqttPersistenceException {
145
		checkIsOpen();
146
		File file = new File(clientDir, key+MESSAGE_FILE_EXTENSION);
147
		File backupFile = new File(clientDir, key+MESSAGE_FILE_EXTENSION+MESSAGE_BACKUP_FILE_EXTENSION);
148
		
149
		if (file.exists()) {
150
			// Backup the existing file so the overwrite can be rolled-back 
151
			boolean result = file.renameTo(backupFile);
152
			if (!result) {
153
				backupFile.delete();
154
				file.renameTo(backupFile);
155
			}
156
		}
157
		try {
158
			FileOutputStream fos = new FileOutputStream(file);
159
			fos.write(message.getHeaderBytes(), message.getHeaderOffset(), message.getHeaderLength());
160
			if (message.getPayloadBytes()!=null) {
161
				fos.write(message.getPayloadBytes(), message.getPayloadOffset(), message.getPayloadLength());
162
			}
163
			fos.getFD().sync();
164
			fos.close();
165
			if (backupFile.exists()) {
166
				// The write has completed successfully, delete the backup 
167
				backupFile.delete();
168
			}
169
		}
170
		catch (IOException ex) {
171
			throw new MqttPersistenceException(ex);
172
		} 
173
		finally {
174
			if (backupFile.exists()) {
175
				// The write has failed - restore the backup
176
				boolean result = backupFile.renameTo(file);
177
				if (!result) {
178
					file.delete();
179
					backupFile.renameTo(file);
180
				}
181
			}
182
		}
183
	}
184
185
	public MqttPersistable get(String key) throws MqttPersistenceException {
186
		checkIsOpen();
187
		MqttPersistable result;
188
		try {
189
			File file = new File(clientDir, key+MESSAGE_FILE_EXTENSION);
190
			FileInputStream fis = new FileInputStream(file);
191
			int size = fis.available();
192
			byte[] data = new byte[size];
193
			int read = 0;
194
			while (read<size) {
195
				read += fis.read(data,read,size-read);
196
			}
197
			fis.close();
198
			result = new MqttPersistentData(key, data, 0, data.length, null, 0, 0);
199
		} 
200
		catch(IOException ex) {
201
			throw new MqttPersistenceException(ex);
202
		}
203
		return result;
204
	}
205
206
207
	/**
208
	 * Deletes the data with the specified key from the previously specified persistence directory.
209
	 */
210
	public void remove(String key) throws MqttPersistenceException {
211
		checkIsOpen();
212
		File file = new File(clientDir, key+MESSAGE_FILE_EXTENSION);
213
		if (file.exists()) {
214
			file.delete();
215
		}
216
	}
217
	
218
	/**
219
	 * Returns all of the persistent data from the previously specified persistence directory.
220
	 * @return all of the persistent data from the persistence directory.
221
	 * @throws MqttPersistenceException
222
	 */
223
	public Enumeration keys() throws MqttPersistenceException {
224
		checkIsOpen();
225
		File[] files = getFiles();
226
		Vector result = new Vector(files.length);
227
		for (int i=0;i<files.length;i++) {
228
			String filename = files[i].getName();
229
			String key = filename.substring(0,filename.length()-MESSAGE_FILE_EXTENSION.length());
230
			result.addElement(key);
231
		}
232
		return result.elements();
233
	}
234
	
235
	private File[] getFiles() throws MqttPersistenceException {
236
		checkIsOpen();
237
		File[] files = clientDir.listFiles(FILE_FILTER);
238
		if (files == null) {
239
			throw new MqttPersistenceException();
240
		}
241
		return files;
242
	}
243
	
244
	private boolean isSafeChar(char c) {
245
		return Character.isJavaIdentifierPart(c) || c=='-';
246
	}
247
	
248
	/**
249
	 * Identifies any backup files in the specified directory and restores them
250
	 * to their original file. This will overwrite any existing file of the same
251
	 * name. This is safe as a stray backup file will only exist if a problem
252
	 * occured whilst writing to the original file.
253
	 * @param dir The directory in which to scan and restore backups
254
	 */
255
	private void restoreBackups(File dir) throws MqttPersistenceException {
256
		File[] files = dir.listFiles(new FileFilter() {
257
			public boolean accept(File f) {
258
				return f.getName().endsWith(MESSAGE_BACKUP_FILE_EXTENSION);
259
			}
260
		});
261
		if (files == null) {
262
			throw new MqttPersistenceException();
263
		}
264
265
		for (int i=0;i<files.length;i++) {
266
			File originalFile = new File(dir,files[i].getName().substring(0,files[i].getName().length()-MESSAGE_BACKUP_FILE_EXTENSION.length()));
267
			boolean result = files[i].renameTo(originalFile);
268
			if (!result) {
269
				originalFile.delete();
270
				files[i].renameTo(originalFile);
271
			}
272
		}
273
	}
274
275
	public boolean containsKey(String key) throws MqttPersistenceException {
276
		checkIsOpen();
277
		File file = new File(clientDir, key+MESSAGE_FILE_EXTENSION);
278
		return file.exists();
279
	}
280
281
	public void clear() throws MqttPersistenceException {
282
		checkIsOpen();
283
		File[] files = getFiles();
284
		for (int i=0; i<files.length; i++) {
285
			files[i].delete();
286
		}
287
	}
288
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/persist/package.html (-12 lines)
Lines 1-12 Link Here
1
<body>
2
Contains implementations of the MqttClientPersistence interface.
3
4
<p>
5
An MQTT client needs a persistence mechanism to store messages while they 
6
are in the process of being delivered. This package contains several 
7
implementations of the interface.  If a persistence class is not
8
specified on the constructor to an MQTT client, 
9
{@link org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence MqttDefaultFilePersistence} 
10
is used by default. 
11
12
</body>
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/util/Debug.java (-177 lines)
Lines 1-177 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
package org.eclipse.paho.client.mqttv3.util;
13
14
import java.util.Enumeration;
15
import java.util.Properties;
16
17
import org.eclipse.paho.client.mqttv3.internal.ClientComms;
18
import org.eclipse.paho.client.mqttv3.logging.Logger;
19
import org.eclipse.paho.client.mqttv3.logging.LoggerFactory;
20
21
/**
22
 * Utility to help debug problems with the Paho MQTT client
23
 * Once initialised a call to dumpClientDebug will force any memory trace
24
 * together with pertinent client and system state to the main log facility.
25
 * 
26
 * No client wide lock is taken when the dump is progress. This means the 
27
 * set of client state may not be consistent as the client can still be 
28
 * processing work while the dump is in progress.
29
 */
30
public class Debug {
31
	
32
	final static String className = ClientComms.class.getName();
33
	Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,className);
34
	
35
	static String separator = "==============";
36
	static String lineSep = System.getProperty("line.separator","\n");
37
	
38
	String clientID;
39
	ClientComms comms;
40
	
41
	/**
42
	 * Set the debug facility up for a specific client
43
	 * @param clientID  the ID of the client being debugged
44
	 * @param comms    the ClientComms object of the client being debugged
45
	 */
46
	public Debug(String clientID, ClientComms comms) {
47
		this.clientID = clientID;
48
		this.comms = comms;
49
		log.setResourceName(clientID);
50
	}
51
52
	/**
53
	 * Dump maximum debug info.
54
	 * This includes state specific to a client as well 
55
	 * as debug that is JVM wide like trace and system properties.
56
	 * All state is written as debug log entries. 
57
	 */
58
	public void dumpClientDebug() { 
59
		dumpClientComms();
60
		dumpConOptions();
61
		dumpClientState();
62
		dumpBaseDebug();
63
	}
64
	
65
	/**
66
	 * Dump of JVM wide debug info.
67
	 * This includes trace and system properties.
68
	 * Includes trace and system properties
69
	 */
70
	public void dumpBaseDebug() {
71
		dumpVersion();
72
		dumpSystemProperties();
73
		dumpMemoryTrace();
74
	}
75
76
	/**
77
	 * If memory trace is being used a request is made to push it 
78
	 * to the target handler.
79
	 */
80
	protected void dumpMemoryTrace() {
81
		log.dumpTrace();
82
	}
83
	
84
	/**
85
	 * Dump information that show the version of the MQTT client being used.
86
	 */
87
	protected void dumpVersion() {
88
		StringBuffer vInfo = new StringBuffer();
89
    	vInfo.append(lineSep+separator+" Version Info "+ separator+lineSep);
90
    	vInfo.append(left("Version",20,' ') + ":  "+ ClientComms.VERSION + lineSep);
91
    	vInfo.append(left("Build Level",20,' ') + ":  "+ ClientComms.BUILD_LEVEL + lineSep);
92
    	vInfo.append(separator+separator+separator+lineSep);
93
    	log.fine(className,"dumpVersion", vInfo.toString());
94
	}
95
96
	/**
97
	 * Dump the current set of system.properties to a log record
98
	 */
99
	public void dumpSystemProperties() {
100
		
101
	    Properties sysProps = System.getProperties();
102
    	log.fine(className,"dumpSystemProperties", dumpProperties(sysProps, "SystemProperties").toString());
103
	}
104
105
	/**
106
	 * Dump interesting variables from ClientState
107
	 */
108
	public void dumpClientState() {
109
		Properties props = null;
110
	    if (comms != null && comms.getClientState() != null ) {
111
	    	props = comms.getClientState().getDebug();
112
	    	log.fine(className,"dumpClientState", dumpProperties(props, clientID + " : ClientState").toString());
113
	    }
114
	}
115
116
	/**
117
	 * Dump interesting variables from ClientComms
118
	 */
119
	public void dumpClientComms() {
120
		Properties props = null;
121
	    if (comms != null) {
122
	    	props = comms.getDebug();
123
	    	log.fine(className,"dumpClientComms", dumpProperties(props, clientID + " : ClientComms").toString());
124
	    }
125
	}
126
	
127
	/**
128
	 * Dump Connection options
129
	 */
130
	public void dumpConOptions() {
131
		Properties props = null;
132
	    if (comms != null) {
133
	    	props = comms.getConOptions().getDebug();
134
	    	log.fine(className,"dumpConOptions", dumpProperties(props, clientID + " : Connect Options").toString());
135
	    }
136
	}
137
138
139
	/**
140
	 * Return a set of properties as a formatted string
141
	 */
142
	public static String dumpProperties(Properties props, String name) {
143
		
144
		StringBuffer propStr = new StringBuffer();
145
	    Enumeration propsE = props.propertyNames();
146
    	propStr.append(lineSep+separator+" "+name+" "+ separator+lineSep);
147
	    while (propsE.hasMoreElements()) {
148
	    	String key = (String)propsE.nextElement();
149
	    	propStr.append(left(key,28,' ') + ":  "+ props.get(key)+lineSep);
150
	    }
151
    	propStr.append(separator+separator+separator+lineSep);
152
153
    	return propStr.toString();
154
	}
155
	
156
	/**
157
	   * Left justify a string.
158
	   *
159
	   * @param s the string to justify
160
	   * @param width the field width to justify within
161
	   * @param fillChar the character to fill with
162
	   *
163
	   * @return the justified string.
164
	   */
165
	  public static String left(String s, int width, char fillChar) {
166
	    if (s.length() >= width) {
167
	      return s;
168
	    }
169
	    StringBuffer sb = new StringBuffer(width);
170
	    sb.append(s);
171
	    for (int i = width - s.length(); --i >= 0;) {
172
	      sb.append(fillChar);
173
	    }
174
	    return sb.toString();
175
	  }
176
	
177
}
(-)a/org.eclipse.paho.client.mqttv3/src/org/eclipse/paho/client/mqttv3/util/package.html (-5 lines)
Lines 1-5 Link Here
1
<body>
2
Provides helpers and utilities.
3
4
5
</body>
(-)a/org.eclipse.paho.sample.mqttv3app/pom.xml (+26 lines)
Added Link Here
1
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
3
4
    <parent>
5
        <groupId>org.eclipse.paho</groupId>
6
        <artifactId>paho-parent</artifactId>
7
        <version>0.9.0</version>
8
    </parent>
9
10
    <modelVersion>4.0.0</modelVersion>
11
    <artifactId>paho-mqtt-client-sample</artifactId>
12
    <version>0.9.0</version>
13
    <packaging>jar</packaging>
14
    <name>Paho :: A sample MQTT Client</name>
15
    <description>A sample MQTT Client.</description> 
16
    <url>${paho.url}</url>
17
    
18
    <dependencies>
19
        <dependency>
20
          <groupId>org.eclipse.paho</groupId>
21
          <artifactId>paho-mqtt-client</artifactId>
22
          <version>${project.version}</version>
23
          <scope>compile</scope>
24
        </dependency>
25
    </dependencies>
26
</project>
(-)a/org.eclipse.paho.sample.mqttv3app/src/main/java/org/eclipse/paho/sample/mqttv3app/Sample.java (+392 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
13
package org.eclipse.paho.sample.mqttv3app;
14
15
import java.io.IOException;
16
import java.sql.Timestamp;
17
18
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
19
import org.eclipse.paho.client.mqttv3.MqttCallback;
20
import org.eclipse.paho.client.mqttv3.MqttClient;
21
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
22
import org.eclipse.paho.client.mqttv3.MqttException;
23
import org.eclipse.paho.client.mqttv3.MqttMessage;
24
import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence;
25
26
/**
27
 * A sample application that demonstrates how to use the MQTT v3 Client blocking api.
28
 * 
29
 * It can be run from the command line in one of two modes: 
30
 *  - as a publisher, sending a single message to a topic on the server
31
 *  - as a subscriber, listening for messages from the server
32
 *  
33
 *  There are three versions of the sample that implement the same features
34
 *  but do so using using different programming styles:
35
 *  <ol>
36
 *  <li>Sample (this one) which uses the API which blocks until the operation completes</li>
37
 *  <li>SampleAsyncWait shows how to use the asynchronous API with waiters that block until 
38
 *  an action completes</li>
39
 *  <li>SampleAsyncCallBack shows how to use the asynchronous API where events are
40
 *  used to notify the application when an action completes<li>
41
 *  </ol>
42
 *  
43
 *  If the application is run with the -h parameter then info is displayed that 
44
 *  describes all of the options / parameters. 
45
 */
46
public class Sample implements MqttCallback {
47
	
48
	/**
49
	 * The main entry point of the sample.
50
	 * 
51
	 * This method handles parsing of the arguments specified on the
52
	 * command-line before performing the specified action.
53
	 */
54
	public static void main(String[] args) {
55
		
56
		// Default settings:
57
		boolean quietMode 	= false;
58
		String action 		= "publish";
59
		String topic 		= "";
60
		String message 		= "Message from blocking MQTTv3 Java client sample";
61
		int qos 			= 2;
62
		String broker 		= "m2m.eclipse.org";
63
		int port 			= 1883;
64
		String clientId 	= null;
65
		String subTopic		= "Sample/#";
66
		String pubTopic 	= "Sample/Java/v3";
67
		boolean cleanSession = true;			// Non durable subscriptions 
68
		boolean ssl = false;
69
		String password = null;
70
		String userName = null;
71
		// Parse the arguments - 
72
		for (int i=0; i<args.length; i++) {
73
			// Check this is a valid argument
74
			if (args[i].length() == 2 && args[i].startsWith("-")) {
75
				char arg = args[i].charAt(1);
76
				// Handle arguments that take no-value
77
				switch(arg) {
78
					case 'h': case '?':	printHelp(); return;
79
					case 'q': quietMode = true;	continue;
80
				}
81
				
82
				// Now handle the arguments that take a value and 
83
				// ensure one is specified
84
				if (i == args.length -1 || args[i+1].charAt(0) == '-') {
85
					System.out.println("Missing value for argument: "+args[i]);
86
					printHelp();
87
					return;
88
				}
89
				switch(arg) {
90
					case 'a': action = args[++i];                 break;
91
					case 't': topic = args[++i];                  break;
92
					case 'm': message = args[++i];                break;
93
					case 's': qos = Integer.parseInt(args[++i]);  break;
94
					case 'b': broker = args[++i];                 break;
95
					case 'p': port = Integer.parseInt(args[++i]); break;
96
					case 'i': clientId = args[++i];				  break;
97
					case 'c': cleanSession = Boolean.valueOf(args[++i]).booleanValue();  break;
98
          case 'k': System.getProperties().put("javax.net.ssl.keyStore", args[++i]); break;
99
          case 'w': System.getProperties().put("javax.net.ssl.keyStorePassword", args[++i]); break;
100
          case 'r': System.getProperties().put("javax.net.ssl.trustStore", args[++i]); break;
101
          case 'v': ssl = Boolean.valueOf(args[++i]).booleanValue(); break;
102
          case 'u': userName = args[++i];               break;
103
          case 'z': password = args[++i];               break;
104
					default: 
105
						System.out.println("Unrecognised argument: "+args[i]);
106
						printHelp(); 
107
						return;
108
				}
109
			} else {
110
				System.out.println("Unrecognised argument: "+args[i]);
111
				printHelp(); 
112
				return;
113
			}
114
		}
115
		
116
		// Validate the provided arguments
117
		if (!action.equals("publish") && !action.equals("subscribe")) {
118
			System.out.println("Invalid action: "+action);
119
			printHelp();
120
			return;
121
		}
122
		if (qos < 0 || qos > 2) {
123
			System.out.println("Invalid QoS: "+qos);
124
			printHelp();
125
			return;
126
		}
127
		if (topic.equals("")) {
128
			// Set the default topic according to the specified action
129
			if (action.equals("publish")) {
130
				topic = pubTopic;
131
			} else {
132
				topic = subTopic;
133
			}
134
		}
135
			
136
		String protocol = "tcp://";
137
138
    if (ssl) {
139
      protocol = "ssl://";
140
    }
141
142
    String url = protocol + broker + ":" + port;
143
		
144
		if (clientId == null || clientId.equals("")) {
145
			clientId = "SampleJavaV3_"+action;
146
		}
147
148
		// With a valid set of arguments, the real work of 
149
		// driving the client API can begin
150
		try {
151
			// Create an instance of this class
152
			Sample sampleClient = new Sample(url, clientId, cleanSession, quietMode,userName,password);
153
			
154
			// Perform the requested action
155
			if (action.equals("publish")) {
156
				sampleClient.publish(topic,qos,message.getBytes());
157
			} else if (action.equals("subscribe")) {
158
				sampleClient.subscribe(topic,qos);
159
			}
160
		} catch(MqttException me) {
161
			// Display full details of any exception that occurs 
162
			System.out.println("reason "+me.getReasonCode());
163
			System.out.println("msg "+me.getMessage());
164
			System.out.println("loc "+me.getLocalizedMessage());
165
			System.out.println("cause "+me.getCause());
166
			System.out.println("excep "+me);
167
			me.printStackTrace();
168
		}
169
	}
170
    
171
	// Private instance variables
172
	private MqttClient 			client;
173
	private String 				brokerUrl;
174
	private boolean 			quietMode;
175
	private MqttConnectOptions 	conOpt;
176
	private boolean 			clean;
177
	private String password;
178
	private String userName;
179
	
180
	/**
181
	 * Constructs an instance of the sample client wrapper
182
	 * @param brokerUrl the url of the server to connect to
183
	 * @param clientId the client id to connect with
184
	 * @param cleanSession clear state at end of connection or not (durable or non-durable subscriptions) 
185
	 * @param quietMode whether debug should be printed to standard out
186
   * @param userName the username to connect with
187
   * @param password the password for the user
188
	 * @throws MqttException
189
	 */
190
    public Sample(String brokerUrl, String clientId, boolean cleanSession, boolean quietMode, String userName, String password) throws MqttException {
191
    	this.brokerUrl = brokerUrl;
192
    	this.quietMode = quietMode;
193
    	this.clean 	   = cleanSession;
194
    	this.password = password;
195
    	this.userName = userName;
196
    	//This sample stores in a temporary directory... where messages temporarily
197
    	// stored until the message has been delivered to the server. 
198
    	//..a real application ought to store them somewhere 
199
    	// where they are not likely to get deleted or tampered with
200
    	String tmpDir = System.getProperty("java.io.tmpdir");
201
    	MqttDefaultFilePersistence dataStore = new MqttDefaultFilePersistence(tmpDir); 
202
    	
203
    	try {
204
    		// Construct the connection options object that contains connection parameters 
205
    		// such as cleansession and LWAT
206
	    	conOpt = new MqttConnectOptions();
207
	    	conOpt.setCleanSession(clean);
208
	    	if(password != null ) {
209
	    	  conOpt.setPassword(this.password.toCharArray());
210
	    	}
211
	    	if(userName != null) {
212
	    	  conOpt.setUserName(this.userName);	   
213
	    	}
214
215
    		// Construct an MQTT blocking mode client
216
			client = new MqttClient(this.brokerUrl,clientId, dataStore);
217
			
218
			// Set this wrapper as the callback handler
219
	    	client.setCallback(this);
220
	    	
221
		} catch (MqttException e) {
222
			e.printStackTrace();
223
			log("Unable to set up client: "+e.toString());
224
			System.exit(1);
225
		}
226
    }
227
228
    /**
229
     * Publish / send a message to an MQTT server
230
     * @param topicName the name of the topic to publish to
231
     * @param qos the quality of service to delivery the message at (0,1,2)
232
     * @param payload the set of bytes to send to the MQTT server 
233
     * @throws MqttException
234
     */
235
    public void publish(String topicName, int qos, byte[] payload) throws MqttException {
236
    	
237
    	// Connect to the MQTT server
238
    	log("Connecting to "+brokerUrl + " with client ID "+client.getClientId());
239
    	client.connect(conOpt);
240
    	log("Connected");
241
    	
242
    	String time = new Timestamp(System.currentTimeMillis()).toString();
243
    	log("Publishing at: "+time+ " to topic \""+topicName+"\" qos "+qos);
244
    	
245
    	// Create and configure a message
246
   		MqttMessage message = new MqttMessage(payload);
247
    	message.setQos(qos);
248
 
249
    	// Send the message to the server, control is not returned until
250
    	// it has been delivered to the server meeting the specified
251
    	// quality of service.
252
    	client.publish(topicName, message);
253
    	
254
    	// Disconnect the client
255
    	client.disconnect();
256
    	log("Disconnected");
257
    }
258
    
259
    /**
260
     * Subscribe to a topic on an MQTT server
261
     * Once subscribed this method waits for the messages to arrive from the server 
262
     * that match the subscription. It continues listening for messages until the enter key is 
263
     * pressed.
264
     * @param topicName to subscribe to (can be wild carded)
265
     * @param qos the maximum quality of service to receive messages at for this subscription 
266
     * @throws MqttException
267
     */
268
    public void subscribe(String topicName, int qos) throws MqttException {
269
    	
270
    	// Connect to the MQTT server
271
    	client.connect(conOpt);
272
    	log("Connected to "+brokerUrl+" with client ID "+client.getClientId());
273
274
    	// Subscribe to the requested topic
275
    	// The QOS specified is the maximum level that messages will be sent to the client at. 
276
    	// For instance if QOS 1 is specified, any messages originally published at QOS 2 will 
277
    	// be downgraded to 1 when delivering to the client but messages published at 1 and 0 
278
    	// will be received at the same level they were published at. 
279
    	log("Subscribing to topic \""+topicName+"\" qos "+qos);
280
    	client.subscribe(topicName, qos);
281
282
    	// Continue waiting for messages until the Enter is pressed
283
    	log("Press <Enter> to exit");
284
		try {
285
			System.in.read();
286
		} catch (IOException e) {
287
			//If we can't read we'll just exit
288
		}
289
		
290
		// Disconnect the client from the server
291
		client.disconnect();
292
		log("Disconnected");
293
    }
294
295
    /**
296
     * Utility method to handle logging. If 'quietMode' is set, this method does nothing
297
     * @param message the message to log
298
     */
299
    private void log(String message) {
300
    	if (!quietMode) {
301
    		System.out.println(message);
302
    	}
303
    }
304
305
	/****************************************************************/
306
	/* Methods to implement the MqttCallback interface              */
307
	/****************************************************************/
308
    
309
    /**
310
     * @see MqttCallback#connectionLost(Throwable)
311
     */
312
	public void connectionLost(Throwable cause) {
313
		// Called when the connection to the server has been lost.
314
		// An application may choose to implement reconnection
315
		// logic at this point. This sample simply exits.
316
		log("Connection to " + brokerUrl + " lost!" + cause);
317
		System.exit(1);
318
	}
319
320
    /**
321
     * @see MqttCallback#deliveryComplete(IMqttDeliveryToken)
322
     */
323
	public void deliveryComplete(IMqttDeliveryToken token) {
324
		// Called when a message has been delivered to the
325
		// server. The token passed in here is the same one
326
		// that was passed to or returned from the original call to publish.
327
		// This allows applications to perform asynchronous 
328
		// delivery without blocking until delivery completes.
329
		//
330
		// This sample demonstrates asynchronous deliver and 
331
		// uses the token.waitForCompletion() call in the main thread which
332
		// blocks until the delivery has completed. 
333
		// Additionally the deliveryComplete method will be called if 
334
		// the callback is set on the client
335
		// 
336
		// If the connection to the server breaks before delivery has completed
337
		// delivery of a message will complete after the client has re-connected.
338
		// The getPendinTokens method will provide tokens for any messages
339
		// that are still to be delivered.
340
	}
341
342
    /**
343
     * @see MqttCallback#messageArrived(String, MqttMessage)
344
     */
345
	public void messageArrived(String topic, MqttMessage message) throws MqttException {
346
		// Called when a message arrives from the server that matches any
347
		// subscription made by the client		
348
		String time = new Timestamp(System.currentTimeMillis()).toString();
349
		System.out.println("Time:\t" +time +
350
                           "  Topic:\t" + topic + 
351
                           "  Message:\t" + new String(message.getPayload()) +
352
                           "  QoS:\t" + message.getQos());
353
	}
354
355
	/****************************************************************/
356
	/* End of MqttCallback methods                                  */
357
	/****************************************************************/
358
359
	   static void printHelp() {
360
	      System.out.println(
361
	          "Syntax:\n\n" +
362
	              "    Sample [-h] [-a publish|subscribe] [-t <topic>] [-m <message text>]\n" +
363
	              "            [-s 0|1|2] -b <hostname|IP address>] [-p <brokerport>] [-i <clientID>]\n\n" +
364
	              "    -h  Print this help text and quit\n" +
365
	              "    -q  Quiet mode (default is false)\n" +
366
	              "    -a  Perform the relevant action (default is publish)\n" +
367
	              "    -t  Publish/subscribe to <topic> instead of the default\n" +
368
	              "            (publish: \"Sample/Java/v3\", subscribe: \"Sample/#\")\n" +
369
	              "    -m  Use <message text> instead of the default\n" +
370
	              "            (\"Message from MQTTv3 Java client\")\n" +
371
	              "    -s  Use this QoS instead of the default (2)\n" +
372
	              "    -b  Use this name/IP address instead of the default (localhost)\n" +
373
	              "    -p  Use this port instead of the default (1883)\n\n" +
374
	              "    -i  Use this client ID instead of SampleJavaV3_<action>\n" +
375
	              "    -c  Connect to the server with a clean session (default is false)\n" +
376
	              "     \n\n Security Options \n" +
377
	              "     -u Username \n" +
378
	              "     -z Password \n" +
379
	              "     \n\n SSL Options \n" +
380
	              "    -v  SSL enabled; true - (default is false) " +
381
	              "    -k  Use this JKS format key store to verify the client\n" +
382
	              "    -w  Passpharse to verify certificates in the keys store\n" +
383
	              "    -r  Use this JKS format keystore to verify the server\n" +
384
	              " If javax.net.ssl properties have been set only the -v flag needs to be set\n" +
385
	              "Delimit strings containing spaces with \"\"\n\n" +
386
	              "Publishers transmit a single message then disconnect from the server.\n" +
387
	              "Subscribers remain connected to the server and receive appropriate\n" +
388
	              "messages until <enter> is pressed.\n\n"
389
	          );
390
    }
391
392
}
(-)a/org.eclipse.paho.sample.mqttv3app/src/main/java/org/eclipse/paho/sample/mqttv3app/SampleAsyncCallBack.java (+648 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
13
package org.eclipse.paho.sample.mqttv3app;
14
15
import java.io.IOException;
16
import java.sql.Timestamp;
17
18
import org.eclipse.paho.client.mqttv3.IMqttActionListener;
19
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
20
import org.eclipse.paho.client.mqttv3.IMqttToken;
21
import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
22
import org.eclipse.paho.client.mqttv3.MqttCallback;
23
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
24
import org.eclipse.paho.client.mqttv3.MqttException;
25
import org.eclipse.paho.client.mqttv3.MqttMessage;
26
import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence;
27
28
/**
29
 * A sample application that demonstrates how to use the MQTT v3 Client api in
30
 * non-blocking callback/notification mode.
31
 * 
32
 * It can be run from the command line in one of two modes: 
33
 *  - as a publisher, sending a single message to a topic on the server
34
 *  - as a subscriber, listening for messages from the server
35
 *  
36
 *  There are three versions of the sample that implement the same features
37
 *  but do so using using different programming styles:
38
 *  <ol>
39
 *  <li>Sample (this one) which uses the API which blocks until the operation completes</li>
40
 *  <li>SampleAsyncWait shows how to use the asynchronous API with waiters that block until 
41
 *  an action completes</li>
42
 *  <li>SampleAsyncCallBack shows how to use the asynchronous API where events are
43
 *  used to notify the application when an action completes<li>
44
 *  </ol>
45
 *  
46
 *  If the application is run with the -h parameter then info is displayed that 
47
 *  describes all of the options / parameters. 
48
 */
49
public class SampleAsyncCallBack implements MqttCallback {
50
	
51
	int state = BEGIN;	
52
53
	static final int BEGIN = 0;
54
	static final int CONNECTED = 1;
55
	static final int PUBLISHED = 2;
56
	static final int SUBSCRIBED = 3;
57
	static final int DISCONNECTED = 4;
58
	static final int FINISH = 5;
59
	static final int ERROR = 6;
60
	static final int DISCONNECT = 7;
61
	
62
	/**
63
	 * The main entry point of the sample.
64
	 * 
65
	 * This method handles parsing the arguments specified on the
66
	 * command-line before performing the specified action.
67
	 */
68
	public static void main(String[] args) {
69
		
70
		// Default settings:
71
		boolean quietMode 	= false;
72
		String action 		= "publish";
73
		String topic 		= "";
74
		String message 		= "Message from async calback MQTTv3 Java client sample";
75
		int qos 			= 2;
76
		String broker 		= "m2m.eclipse.org";
77
		int port 			= 1883;
78
		String clientId 	= null;
79
		String subTopic		= "Sample/#";
80
		String pubTopic 	= "Sample/Java/v3";
81
		boolean cleanSession = true;			// Non durable subscriptions 
82
		boolean ssl = false;
83
    String password = null;
84
    String userName = null;
85
		
86
		// Parse the arguments - 
87
		for (int i=0; i<args.length; i++) {
88
			// Check this is a valid argument
89
			if (args[i].length() == 2 && args[i].startsWith("-")) {
90
				char arg = args[i].charAt(1);
91
				// Handle arguments that take no-value
92
				switch(arg) {
93
					case 'h': case '?':	printHelp(); return;
94
					case 'q': quietMode = true;	continue;
95
				}
96
				
97
				// Now handle the arguments that take a value and 
98
				// ensure one is specified
99
				if (i == args.length -1 || args[i+1].charAt(0) == '-') {
100
					System.out.println("Missing value for argument: "+args[i]);
101
					printHelp();
102
					return;
103
				}
104
				switch(arg) {
105
					case 'a': action = args[++i];                 break;
106
					case 't': topic = args[++i];                  break;
107
					case 'm': message = args[++i];                break;
108
					case 's': qos = Integer.parseInt(args[++i]);  break;
109
					case 'b': broker = args[++i];                 break;
110
					case 'p': port = Integer.parseInt(args[++i]); break;
111
					case 'i': clientId = args[++i];				  break;
112
					case 'c': cleanSession = Boolean.valueOf(args[++i]).booleanValue();  break;
113
          case 'k': System.getProperties().put("javax.net.ssl.keyStore", args[++i]); break;
114
          case 'w': System.getProperties().put("javax.net.ssl.keyStorePassword", args[++i]); break;
115
          case 'r': System.getProperties().put("javax.net.ssl.trustStore", args[++i]); break;
116
          case 'v': ssl = Boolean.valueOf(args[++i]).booleanValue();  break;
117
          case 'u': userName = args[++i];               break;
118
          case 'z': password = args[++i];               break;
119
					default: 
120
						System.out.println("Unrecognised argument: "+args[i]);
121
						printHelp(); 
122
						return;
123
				}
124
			} else {
125
				System.out.println("Unrecognised argument: "+args[i]);
126
				printHelp(); 
127
				return;
128
			}
129
		}
130
		
131
		// Validate the provided arguments
132
		if (!action.equals("publish") && !action.equals("subscribe")) {
133
			System.out.println("Invalid action: "+action);
134
			printHelp();
135
			return;
136
		}
137
		if (qos < 0 || qos > 2) {
138
			System.out.println("Invalid QoS: "+qos);
139
			printHelp();
140
			return;
141
		}
142
		if (topic.equals("")) {
143
			// Set the default topic according to the specified action
144
			if (action.equals("publish")) {
145
				topic = pubTopic;
146
			} else {
147
				topic = subTopic;
148
			}
149
		}
150
			
151
		String protocol = "tcp://";
152
153
    if (ssl) {
154
      protocol = "ssl://";
155
    }
156
157
    String url = protocol + broker + ":" + port;
158
		
159
		if (clientId == null || clientId.equals("")) {
160
			clientId = "SampleJavaV3_"+action;
161
		}
162
163
		// With a valid set of arguments, the real work of 
164
		// driving the client API can begin
165
		try {
166
			// Create an instance of the Sample client wrapper
167
			SampleAsyncCallBack sampleClient = new SampleAsyncCallBack(url,clientId,cleanSession, quietMode,userName,password);
168
			
169
			// Perform the specified action
170
			if (action.equals("publish")) {
171
				sampleClient.publish(topic,qos,message.getBytes());
172
			} else if (action.equals("subscribe")) {
173
				sampleClient.subscribe(topic,qos);
174
			}
175
		} catch(MqttException me) {
176
			// Display full details of any exception that occurs
177
			System.out.println("reason "+me.getReasonCode());
178
			System.out.println("msg "+me.getMessage());
179
			System.out.println("loc "+me.getLocalizedMessage());
180
			System.out.println("cause "+me.getCause());
181
			System.out.println("excep "+me);
182
			me.printStackTrace();
183
		} catch (Throwable th) {
184
			System.out.println("Throwable caught "+th);
185
			th.printStackTrace();
186
		}
187
	}
188
    
189
	// Private instance variables	
190
	MqttAsyncClient 	client;
191
	String 				brokerUrl;
192
	private boolean 			quietMode;
193
	private MqttConnectOptions 	conOpt;
194
	private boolean 			clean;
195
	Throwable 			ex = null;
196
	Object 				waiter = new Object();
197
	boolean 			donext = false;
198
	private String password;
199
	private String userName;
200
	
201
	/**
202
	 * Constructs an instance of the sample client wrapper
203
	 * @param brokerUrl the url to connect to
204
	 * @param clientId the client id to connect with
205
	 * @param cleanSession clear state at end of connection or not (durable or non-durable subscriptions)
206
	 * @param quietMode whether debug should be printed to standard out
207
	 * @param userName the username to connect with
208
	 * @param password the password for the user
209
	 * @throws MqttException
210
	 */
211
    public SampleAsyncCallBack(String brokerUrl, String clientId, boolean cleanSession, 
212
    		boolean quietMode, String userName, String password) throws MqttException {
213
    	this.brokerUrl = brokerUrl;
214
    	this.quietMode = quietMode;
215
    	this.clean 	   = cleanSession;
216
      this.password = password;
217
      this.userName = userName;
218
    	//This sample stores in a temporary directory... where messages temporarily
219
    	// stored until the message has been delivered to the server. 
220
    	//..a real application ought to store them somewhere 
221
    	// where they are not likely to get deleted or tampered with
222
    	String tmpDir = System.getProperty("java.io.tmpdir");
223
    	MqttDefaultFilePersistence dataStore = new MqttDefaultFilePersistence(tmpDir); 
224
    	
225
    	try {
226
    		// Construct the object that contains connection parameters 
227
    		// such as cleansession and LWAT
228
	    	conOpt = new MqttConnectOptions();
229
	    	conOpt.setCleanSession(clean);
230
	    	if(password != null ) {
231
          conOpt.setPassword(this.password.toCharArray());
232
        }
233
        if(userName != null) {
234
          conOpt.setUserName(this.userName);     
235
        }
236
	    	
237
    		// Construct the MqttClient instance
238
			client = new MqttAsyncClient(this.brokerUrl,clientId, dataStore);
239
			
240
			// Set this wrapper as the callback handler
241
	    	client.setCallback(this);
242
	    	
243
		} catch (MqttException e) {
244
			e.printStackTrace();
245
			log("Unable to set up client: "+e.toString());
246
			System.exit(1);
247
		}
248
    }
249
250
    /**
251
     * Publish / send a message to an MQTT server
252
     * @param topicName the name of the topic to publish to
253
     * @param qos the quality of service to delivery the message at (0,1,2)
254
     * @param payload the set of bytes to send to the MQTT server 
255
     * @throws MqttException
256
     */
257
    public void publish(String topicName, int qos, byte[] payload) throws Throwable {
258
    	// Use a state machine to decide which step to do next. State change occurs 
259
    	// when a notification is received that an MQTT action has completed
260
    	while (state != FINISH) {
261
    		switch (state) {
262
    			case BEGIN:
263
    				// Connect using a non blocking connect
264
    		    	MqttConnector con = new MqttConnector();
265
    		    	con.doConnect();
266
    				break;
267
    			case CONNECTED:
268
    				// Publish using a non blocking publisher
269
    				Publisher pub = new Publisher();
270
    				pub.doPublish(topicName, qos, payload);
271
    				break;
272
    			case PUBLISHED:
273
    				state = DISCONNECT;
274
    				donext = true;
275
    				break;
276
    			case DISCONNECT:
277
    				Disconnector disc = new Disconnector();
278
    				disc.doDisconnect();
279
    				break;
280
    			case ERROR:
281
    				throw ex;
282
    			case DISCONNECTED:
283
    				state = FINISH;
284
    				donext = true;
285
    				break;		
286
    		}
287
    		
288
//    		if (state != FINISH) {
289
    			// Wait until notified about a state change and then perform next action
290
    			waitForStateChange(10000);
291
//    		}
292
    	}	
293
    }
294
295
    /**
296
     * Wait for a maximum amount of time for a state change event to occur
297
     * @param maxTTW  maximum time to wait in milliseconds
298
     * @throws MqttException
299
     */
300
	private void waitForStateChange(int maxTTW ) throws MqttException {
301
		synchronized (waiter) {
302
    		if (!donext ) {
303
    			try {
304
					waiter.wait(maxTTW);
305
				} catch (InterruptedException e) {
306
					log("timed out");
307
					e.printStackTrace();
308
				}
309
				
310
				if (ex != null) {
311
					throw (MqttException)ex;
312
				}
313
    		}
314
    		donext = false;
315
    	}
316
	}
317
318
    /**
319
     * Subscribe to a topic on an MQTT server
320
     * Once subscribed this method waits for the messages to arrive from the server 
321
     * that match the subscription. It continues listening for messages until the enter key is 
322
     * pressed.
323
     * @param topicName to subscribe to (can be wild carded)
324
     * @param qos the maximum quality of service to receive messages at for this subscription 
325
     * @throws MqttException
326
     */
327
    public void subscribe(String topicName, int qos) throws Throwable {
328
    	// Use a state machine to decide which step to do next. State change occurs 
329
    	// when a notification is received that an MQTT action has completed
330
    	while (state != FINISH) {
331
    		switch (state) {
332
    			case BEGIN:
333
    				// Connect using a non blocking connect
334
    		    	MqttConnector con = new MqttConnector();
335
    		    	con.doConnect();
336
    				break;
337
    			case CONNECTED:
338
    				// Subscribe using a non blocking subscribe
339
    				Subscriber sub = new Subscriber();
340
    				sub.doSubscribe(topicName, qos);
341
    				break;
342
    			case SUBSCRIBED:
343
    		    	// Block until Enter is pressed allowing messages to arrive
344
    		    	log("Press <Enter> to exit");
345
    				try {
346
    					System.in.read();
347
    				} catch (IOException e) {
348
    					//If we can't read we'll just exit
349
    				}
350
    				state = DISCONNECT;
351
    				donext = true;
352
    				break;
353
    			case DISCONNECT:
354
    				Disconnector disc = new Disconnector();
355
    				disc.doDisconnect();
356
    				break;
357
    			case ERROR:
358
    				throw ex;
359
    			case DISCONNECTED:
360
    				state = FINISH;
361
    				donext = true;
362
    				break;		
363
    		}
364
    		
365
//    		if (state != FINISH && state != DISCONNECT) {
366
    			waitForStateChange(10000);
367
    		}
368
//    	}    	
369
    }
370
371
    /**
372
     * Utility method to handle logging. If 'quietMode' is set, this method does nothing
373
     * @param message the message to log
374
     */
375
    void log(String message) {
376
    	if (!quietMode) {
377
    		System.out.println(message);
378
    	}
379
    }
380
381
	/****************************************************************/
382
	/* Methods to implement the MqttCallback interface              */
383
	/****************************************************************/
384
    
385
    /**
386
     * @see MqttCallback#connectionLost(Throwable)
387
     */
388
	public void connectionLost(Throwable cause) {
389
		// Called when the connection to the server has been lost.
390
		// An application may choose to implement reconnection
391
		// logic at this point. This sample simply exits.
392
		log("Connection to " + brokerUrl + " lost!" + cause);
393
		System.exit(1);
394
	}
395
396
    /**
397
     * @see MqttCallback#deliveryComplete(IMqttDeliveryToken)
398
     */
399
	public void deliveryComplete(IMqttDeliveryToken token) {
400
		// Called when a message has been delivered to the
401
		// server. The token passed in here is the same one
402
		// that was returned from the original call to publish.
403
		// This allows applications to perform asynchronous 
404
		// delivery without blocking until delivery completes.
405
		//
406
		// This sample demonstrates asynchronous deliver, registering 
407
		// a callback to be notified on each call to publish.
408
		//
409
		// The deliveryComplete method will also be called if 
410
		// the callback is set on the client
411
		// 
412
		log("Delivery complete callback: Publish Completed "+token.getTopics());	
413
	}
414
415
    /**
416
     * @see MqttCallback#messageArrived(String, MqttMessage)
417
     */
418
	public void messageArrived(String topic, MqttMessage message) throws MqttException {
419
		// Called when a message arrives from the server that matches any
420
		// subscription made by the client		
421
		String time = new Timestamp(System.currentTimeMillis()).toString();
422
		System.out.println("Time:\t" +time +
423
                           "  Topic:\t" + topic + 
424
                           "  Message:\t" + new String(message.getPayload()) +
425
                           "  QoS:\t" + message.getQos());
426
	}
427
428
	/****************************************************************/
429
	/* End of MqttCallback methods                                  */
430
	/****************************************************************/
431
    static void printHelp() {
432
      System.out.println(
433
          "Syntax:\n\n" +
434
              "    Sample [-h] [-a publish|subscribe] [-t <topic>] [-m <message text>]\n" +
435
              "            [-s 0|1|2] -b <hostname|IP address>] [-p <brokerport>] [-i <clientID>]\n\n" +
436
              "    -h  Print this help text and quit\n" +
437
              "    -q  Quiet mode (default is false)\n" +
438
              "    -a  Perform the relevant action (default is publish)\n" +
439
              "    -t  Publish/subscribe to <topic> instead of the default\n" +
440
              "            (publish: \"Sample/Java/v3\", subscribe: \"Sample/#\")\n" +
441
              "    -m  Use <message text> instead of the default\n" +
442
              "            (\"Message from MQTTv3 Java client\")\n" +
443
              "    -s  Use this QoS instead of the default (2)\n" +
444
              "    -b  Use this name/IP address instead of the default (localhost)\n" +
445
              "    -p  Use this port instead of the default (1883)\n\n" +
446
              "    -i  Use this client ID instead of SampleJavaV3_<action>\n" +
447
              "    -c  Connect to the server with a clean session (default is false)\n" +
448
              "     \n\n Security Options \n" +
449
              "     -u Username \n" +
450
              "     -z Password \n" +
451
              "     \n\n SSL Options \n" +
452
              "    -v  SSL enabled; true - (default is false) " +
453
              "    -k  Use this JKS format key store to verify the client\n" +
454
              "    -w  Passpharse to verify certificates in the keys store\n" +
455
              "    -r  Use this JKS format keystore to verify the server\n" +
456
              " If javax.net.ssl properties have been set only the -v flag needs to be set\n" +
457
              "Delimit strings containing spaces with \"\"\n\n" +
458
              "Publishers transmit a single message then disconnect from the server.\n" +
459
              "Subscribers remain connected to the server and receive appropriate\n" +
460
              "messages until <enter> is pressed.\n\n"
461
          );
462
    }
463
        	
464
	/**
465
	 * Connect in a non blocking way and then sit back and wait to be 
466
	 * notified that the action has completed.
467
	 */
468
    public class MqttConnector {
469
		
470
		public MqttConnector() {
471
		}
472
		
473
		public void doConnect() {
474
	    	// Connect to the server
475
			// Get a token and setup an asynchronous listener on the token which
476
			// will be notified once the connect completes
477
	    	log("Connecting to "+brokerUrl + " with client ID "+client.getClientId());
478
	
479
	    	IMqttActionListener conListener = new IMqttActionListener() {			
480
				public void onSuccess(IMqttToken asyncActionToken) {
481
			    	log("Connected");
482
			    	state = CONNECTED;
483
			    	carryOn();
484
				}
485
				
486
				public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
487
					ex = exception;
488
					state = ERROR;
489
					log ("connect failed" +exception);
490
					carryOn();
491
				}
492
				
493
				public void carryOn() {
494
			    	synchronized (waiter) {
495
			    		donext=true;
496
			    		waiter.notifyAll();
497
			    	}
498
				}
499
			};
500
	    			
501
	    	try {
502
	    		// Connect using a non blocking connect
503
	    		client.connect(conOpt,"Connect sample context", conListener);
504
			} catch (MqttException e) {
505
				// If though it is a non blocking connect an exception can be 
506
				// thrown if validation of parms fails or other checks such 
507
				// as already connected fail.
508
				state = ERROR;
509
				donext = true;
510
				ex = e;
511
			}
512
		}
513
	}
514
515
	/**
516
	 * Publish in a non blocking way and then sit back and wait to be 
517
	 * notified that the action has completed.
518
	 */
519
	public class Publisher {
520
		public void doPublish(String topicName, int qos, byte[] payload) {
521
		 	// Send / publish a message to the server
522
			// Get a token and setup an asynchronous listener on the token which
523
			// will be notified once the message has been delivered
524
	   		MqttMessage message = new MqttMessage(payload);
525
	    	message.setQos(qos);
526
		
527
528
	    	String time = new Timestamp(System.currentTimeMillis()).toString();
529
	    	log("Publishing at: "+time+ " to topic \""+topicName+"\" qos "+qos);
530
531
	    	// Setup a listener object to be notified when the publish completes.
532
	    	// 
533
	    	IMqttActionListener pubListener = new IMqttActionListener() {	
534
				public void onSuccess(IMqttToken asyncActionToken) {
535
			    	log("Publish Completed");
536
			    	state = PUBLISHED;
537
			    	carryOn();
538
				}
539
				
540
				public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
541
					ex = exception;
542
					state = ERROR;
543
					log ("Publish failed" +exception);
544
					carryOn();
545
				}
546
				
547
				public void carryOn() {
548
			    	synchronized (waiter) {
549
			    		donext=true;
550
			    		waiter.notifyAll();
551
			    	}
552
				}
553
			};
554
555
	    	try {
556
		    	// Publish the message
557
	    		client.publish(topicName, message, "Pub sample context", pubListener);
558
	    	} catch (MqttException e) {
559
				state = ERROR;
560
				donext = true;
561
				ex = e;
562
			}
563
		}
564
	}
565
	
566
	/**
567
	 * Subscribe in a non blocking way and then sit back and wait to be 
568
	 * notified that the action has completed.
569
	 */
570
	public class Subscriber {
571
		public void doSubscribe(String topicName, int qos) {
572
		 	// Make a subscription 
573
			// Get a token and setup an asynchronous listener on the token which
574
			// will be notified once the subscription is in place.
575
	    	log("Subscribing to topic \""+topicName+"\" qos "+qos);
576
577
	    	IMqttActionListener subListener = new IMqttActionListener() {
578
				public void onSuccess(IMqttToken asyncActionToken) {
579
			    	log("Subscribe Completed");
580
			    	state = SUBSCRIBED;
581
			    	carryOn();
582
				}
583
				
584
				public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
585
					ex = exception;
586
					state = ERROR;
587
					log ("Subscribe failed" +exception);
588
					carryOn();
589
				}
590
				
591
				public void carryOn() {
592
			    	synchronized (waiter) {
593
			    		donext=true;
594
			    		waiter.notifyAll();
595
			    	}
596
				}
597
			};
598
	    	
599
	    	try {
600
	    		client.subscribe(topicName, qos, "Subscribe sample context", subListener);
601
	    	} catch (MqttException e) {
602
				state = ERROR;
603
				donext = true;
604
				ex = e;
605
			}
606
		}
607
	}
608
	
609
	/**
610
	 * Disconnect in a non blocking way and then sit back and wait to be 
611
	 * notified that the action has completed.
612
	 */
613
	public class Disconnector {
614
		public void doDisconnect() {
615
	    	// Disconnect the client
616
	    	log("Disconnecting");
617
618
	    	IMqttActionListener discListener = new IMqttActionListener() {		
619
				public void onSuccess(IMqttToken asyncActionToken) {
620
			    	log("Disconnect Completed");
621
			    	state = DISCONNECTED;
622
			    	carryOn();
623
				}
624
				
625
				public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
626
					ex = exception;
627
					state = ERROR;
628
					log ("Disconnect failed" +exception);
629
					carryOn();
630
				}
631
				public void carryOn() {
632
			    	synchronized (waiter) {
633
			    		donext=true;
634
			    		waiter.notifyAll();
635
			    	}
636
				}
637
			};
638
	    	
639
	    	try {
640
	    		client.disconnect("Disconnect sample context", discListener);
641
	    	} catch (MqttException e) {
642
				state = ERROR;
643
				donext = true;
644
				ex = e;
645
			}
646
		}
647
	}
648
}
(-)a/org.eclipse.paho.sample.mqttv3app/src/main/java/org/eclipse/paho/sample/mqttv3app/SampleAsyncWait.java (+421 lines)
Added Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
13
package org.eclipse.paho.sample.mqttv3app;
14
15
import java.io.IOException;
16
import java.sql.Timestamp;
17
18
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
19
import org.eclipse.paho.client.mqttv3.IMqttToken;
20
import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
21
import org.eclipse.paho.client.mqttv3.MqttCallback;
22
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
23
import org.eclipse.paho.client.mqttv3.MqttException;
24
import org.eclipse.paho.client.mqttv3.MqttMessage;
25
import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence;
26
27
/**
28
 * A sample application that demonstrates how to use the MQTT v3 Client api in
29
 * non-blocking waiter mode.
30
 * 
31
 * It can be run from the command line in one of two modes: 
32
 *  - as a publisher, sending a single message to a topic on the server
33
 *  - as a subscriber, listening for messages from the server
34
 *  
35
 *  There are three versions of the sample that implement the same features
36
 *  but do so using using different programming styles:
37
 *  <ol>
38
 *  <li>Sample (this one) which uses the API which blocks until the operation completes</li>
39
 *  <li>SampleAsyncWait shows how to use the asynchronous API with waiters that block until 
40
 *  an action completes</li>
41
 *  <li>SampleAsyncCallBack shows how to use the asynchronous API where events are
42
 *  used to notify the application when an action completes<li>
43
 *  </ol>
44
 *  
45
 *  If the application is run with the -h parameter then info is displayed that 
46
 *  describes all of the options / parameters. 
47
 */
48
49
public class SampleAsyncWait implements MqttCallback {
50
	
51
	/**
52
	 * The main entry point of the sample.
53
	 * 
54
	 * This method handles parsing the arguments specified on the
55
	 * command-line before performing the specified action.
56
	 */
57
	public static void main(String[] args) {
58
		
59
		// Default settings:
60
		boolean quietMode 	= false;
61
		String action 		= "publish";
62
		String topic 		= "";
63
		String message 		= "Message from async waiter MQTTv3 Java client sample";
64
		int qos 			= 2;
65
		String broker 		= "m2m.eclipse.org";
66
		int port 			= 1883;
67
		String clientId 	= null;
68
		String subTopic		= "Sample/#";
69
		String pubTopic 	= "Sample/Java/v3";
70
		boolean cleanSession = true;			// Non durable subscriptions 
71
		boolean ssl = false;
72
		String userName = null;
73
		String password = null;
74
		
75
		// Parse the arguments - 
76
		for (int i=0; i<args.length; i++) {
77
			// Check this is a valid argument
78
			if (args[i].length() == 2 && args[i].startsWith("-")) {
79
				char arg = args[i].charAt(1);
80
				// Handle arguments that take no-value
81
				switch(arg) {
82
					case 'h': case '?':	printHelp(); return;
83
					case 'q': quietMode = true;	continue;
84
				}
85
				
86
				// Now handle the arguments that take a value and 
87
				// ensure one is specified
88
				if (i == args.length -1 || args[i+1].charAt(0) == '-') {
89
					System.out.println("Missing value for argument: "+args[i]);
90
					printHelp();
91
					return;
92
				}
93
				switch(arg) {
94
					case 'a': action = args[++i];                 break;
95
					case 't': topic = args[++i];                  break;
96
					case 'm': message = args[++i];                break;
97
					case 's': qos = Integer.parseInt(args[++i]);  break;
98
					case 'b': broker = args[++i];                 break;
99
					case 'p': port = Integer.parseInt(args[++i]); break;
100
					case 'i': clientId = args[++i];				  break;
101
					case 'c': cleanSession = Boolean.valueOf(args[++i]).booleanValue();  break;
102
	        case 'k': System.getProperties().put("javax.net.ssl.keyStore", args[++i]); break;
103
	        case 'w': System.getProperties().put("javax.net.ssl.keyStorePassword", args[++i]); break;
104
	        case 'r': System.getProperties().put("javax.net.ssl.trustStore", args[++i]);  break;
105
	        case 'v': ssl = Boolean.valueOf(args[++i]).booleanValue();  break;
106
          case 'u': userName = args[++i];               break;
107
          case 'z': password = args[++i];               break;
108
					default: 
109
						System.out.println("Unrecognised argument: "+args[i]);
110
						printHelp(); 
111
						return;
112
				}
113
			} else {
114
				System.out.println("Unrecognised argument: "+args[i]);
115
				printHelp(); 
116
				return;
117
			}
118
		}
119
		
120
		// Validate the provided arguments
121
		if (!action.equals("publish") && !action.equals("subscribe")) {
122
			System.out.println("Invalid action: "+action);
123
			printHelp();
124
			return;
125
		}
126
		if (qos < 0 || qos > 2) {
127
			System.out.println("Invalid QoS: "+qos);
128
			printHelp();
129
			return;
130
		}
131
		if (topic.equals("")) {
132
			// Set the default topic according to the specified action
133
			if (action.equals("publish")) {
134
				topic = pubTopic;
135
			} else {
136
				topic = subTopic;
137
			}
138
		}
139
			
140
		String protocol = "tcp://";
141
142
    if (ssl) {
143
      protocol = "ssl://";
144
    }
145
146
    String url = protocol + broker + ":" + port;
147
		
148
		if (clientId == null || clientId.equals("")) {
149
			clientId = "SampleJavaV3_"+action;
150
		}
151
152
		// With a valid set of arguments, the real work of 
153
		// driving the client API can begin
154
		try {
155
			// Create an instance of this class
156
			SampleAsyncWait sampleClient = new SampleAsyncWait(url,clientId,cleanSession, quietMode,userName,password);
157
			
158
			// Perform the specified action
159
			if (action.equals("publish")) {
160
				sampleClient.publish(topic,qos,message.getBytes());
161
			} else if (action.equals("subscribe")) {
162
				sampleClient.subscribe(topic,qos);
163
			}
164
		} catch(MqttException me) {
165
			// Display full details of any exception that occurs			
166
			System.out.println("reason "+me.getReasonCode());
167
			System.out.println("msg "+me.getMessage());
168
			System.out.println("loc "+me.getLocalizedMessage());
169
			System.out.println("cause "+me.getCause());
170
			System.out.println("excep "+me);
171
			me.printStackTrace();
172
		}
173
	}
174
    
175
	// Private instance variables
176
	private MqttAsyncClient 	client;
177
	private String 				brokerUrl;
178
	private boolean 			quietMode;
179
	private MqttConnectOptions 	conOpt;
180
	private boolean 			clean;
181
	private String password;
182
	private String userName;
183
	
184
	/**
185
	 * Constructs an instance of the sample client wrapper
186
	 * @param brokerUrl the url to connect to
187
	 * @param clientId the client id to connect with
188
	 * @param cleanSession clear state at end of connection or not (durable or non-durable subscriptions)
189
	 * @param quietMode whether debug should be printed to standard out
190
   * @param userName the username to connect with
191
	 * @param password the password for the user
192
	 * @throws MqttException
193
	 */
194
    public SampleAsyncWait(String brokerUrl, String clientId, boolean cleanSession, 
195
    		boolean quietMode, String userName, String password) throws MqttException {
196
    	this.brokerUrl = brokerUrl;
197
    	this.quietMode = quietMode;
198
    	this.clean 	   = cleanSession;
199
    	this.userName = userName;
200
    	this.password = password;
201
    	//This sample stores in a temporary directory... where messages temporarily
202
    	// stored until the message has been delivered to the server. 
203
    	//..a real application ought to store them somewhere 
204
    	// where they are not likely to get deleted or tampered with
205
    	String tmpDir = System.getProperty("java.io.tmpdir");
206
    	MqttDefaultFilePersistence dataStore = new MqttDefaultFilePersistence(tmpDir); 
207
    	
208
    	try {
209
    		// Construct the connection options object that contains connection parameters 
210
    		// such as cleansession and LWAT
211
	    	conOpt = new MqttConnectOptions();
212
	    	conOpt.setCleanSession(clean);
213
	    	if(password != null ) {
214
          conOpt.setPassword(this.password.toCharArray());
215
        }
216
        if(userName != null) {
217
          conOpt.setUserName(this.userName);     
218
        }
219
220
    		// Construct a non blocking MQTT client instance
221
			client = new MqttAsyncClient(this.brokerUrl,clientId, dataStore);
222
			
223
			// Set this wrapper as the callback handler
224
	    	client.setCallback(this);
225
	    	
226
		} catch (MqttException e) {
227
			e.printStackTrace();
228
			log("Unable to set up client: "+e.toString());
229
			System.exit(1);
230
		}
231
    }
232
233
    /**
234
     * Publish / send a message to an MQTT server
235
     * @param topicName the name of the topic to publish to
236
     * @param qos the quality of service to delivery the message at (0,1,2)
237
     * @param payload the set of bytes to send to the MQTT server 
238
     * @throws MqttException
239
     */
240
    public void publish(String topicName, int qos, byte[] payload) throws MqttException {
241
    	
242
    	// Connect to the MQTT server 
243
    	// issue a non-blocking connect and then use the token to wait until the
244
    	// connect completes. An exception is thrown if connect fails.
245
    	log("Connecting to "+brokerUrl + " with client ID "+client.getClientId());
246
    	IMqttToken conToken = client.connect(conOpt,null,null);
247
    	conToken.waitForCompletion();
248
    	log("Connected");
249
 
250
       	String time = new Timestamp(System.currentTimeMillis()).toString();
251
    	log("Publishing at: "+time+ " to topic \""+topicName+"\" qos "+qos);
252
    	
253
    	// Construct the message to send
254
   		MqttMessage message = new MqttMessage(payload);
255
    	message.setQos(qos);
256
	
257
    	// Send the message to the server, control is returned as soon 
258
    	// as the MQTT client has accepted to deliver the message. 
259
    	// Use the delivery token to wait until the message has been
260
    	// delivered	
261
    	IMqttDeliveryToken pubToken = client.publish(topicName, message, null, null);
262
    	pubToken.waitForCompletion(); 	
263
    	log("Published");   
264
    	
265
    	// Disconnect the client
266
    	// Issue the disconnect and then use a token to wait until 
267
    	// the disconnect completes.
268
    	log("Disconnecting");
269
    	IMqttToken discToken = client.disconnect(null, null);
270
    	discToken.waitForCompletion();
271
    	log("Disconnected");
272
    }
273
    
274
    /**
275
     * Subscribe to a topic on an MQTT server
276
     * Once subscribed this method waits for the messages to arrive from the server 
277
     * that match the subscription. It continues listening for messages until the enter key is 
278
     * pressed.
279
     * @param topicName to subscribe to (can be wild carded)
280
     * @param qos the maximum quality of service to receive messages at for this subscription 
281
     * @throws MqttException
282
     */
283
    public void subscribe(String topicName, int qos) throws MqttException {
284
    	
285
    	// Connect to the MQTT server 
286
    	// issue a non-blocking connect and then use the token to wait until the
287
    	// connect completes. An exception is thrown if connect fails.
288
    	log("Connecting to "+brokerUrl + " with client ID "+client.getClientId());
289
    	IMqttToken conToken = client.connect(conOpt,null, null);
290
    	conToken.waitForCompletion();
291
    	log("Connected");
292
293
    	// Subscribe to the requested topic.
294
    	// Control is returned as soon client has accepted to deliver the subscription. 
295
    	// Use a token to wait until the subscription is in place.
296
    	log("Subscribing to topic \""+topicName+"\" qos "+qos);
297
  
298
    	IMqttToken subToken = client.subscribe(topicName, qos, null, null);
299
    	subToken.waitForCompletion();
300
    	log("Subscribed to topic \""+topicName);
301
302
    	// Continue waiting for messages until the Enter is pressed
303
    	log("Press <Enter> to exit");
304
		try {
305
			System.in.read();
306
		} catch (IOException e) {
307
			//If we can't read we'll just exit
308
		}
309
		
310
    	// Disconnect the client
311
    	// Issue the disconnect and then use the token to wait until 
312
    	// the disconnect completes.
313
    	log("Disconnecting");
314
    	IMqttToken discToken = client.disconnect(null, null);
315
    	discToken.waitForCompletion();
316
    	log("Disconnected");
317
    }
318
319
    /**
320
     * Utility method to handle logging. If 'quietMode' is set, this method does nothing
321
     * @param message the message to log
322
     */
323
    private void log(String message) {
324
    	if (!quietMode) {
325
    		System.out.println(message);
326
    	}
327
    }
328
329
	/****************************************************************/
330
	/* Methods to implement the MqttCallback interface              */
331
	/****************************************************************/
332
    
333
    /**
334
     * @see MqttCallback#connectionLost(Throwable)
335
     */
336
	public void connectionLost(Throwable cause) {
337
		// Called when the connection to the server has been lost.
338
		// An application may choose to implement reconnection
339
		// logic at this point. This sample simply exits.
340
		log("Connection to " + brokerUrl + " lost!" + cause);
341
		System.exit(1);
342
	}
343
344
    /**
345
     * @see MqttCallback#deliveryComplete(IMqttDeliveryToken)
346
     */
347
	public void deliveryComplete(IMqttDeliveryToken token) {
348
		// Called when a message has been delivered to the
349
		// server. The token passed in here is the same one
350
		// that was passed to or returned from the original call to publish.
351
		// This allows applications to perform asynchronous 
352
		// delivery without blocking until delivery completes.
353
		//
354
		// This sample demonstrates asynchronous deliver and 
355
		// uses the token.waitForCompletion() call in the main thread which
356
		// blocks until the delivery has completed. 
357
		// Additionally the deliveryComplete method will be called if 
358
		// the callback is set on the client
359
		// 
360
		// If the connection to the server breaks before delivery has completed
361
		// delivery of a message will complete after the client has re-connected.
362
		// The getPendinTokens method will provide tokens for any messages
363
		// that are still to be delivered.
364
		try {
365
			log("Delivery complete callback: Publish Completed "+token.getMessage());
366
		} catch (Exception ex) {
367
			log("Exception in delivery complete callback"+ex);
368
		}
369
	}
370
371
    /**
372
     * @see MqttCallback#messageArrived(String, MqttMessage)
373
     */
374
	public void messageArrived(String topic, MqttMessage message) throws MqttException {
375
		// Called when a message arrives from the server that matches any
376
		// subscription made by the client		
377
		String time = new Timestamp(System.currentTimeMillis()).toString();	
378
		System.out.println("Time:\t" +time +
379
                           "  Topic:\t" + topic + 
380
                           "  Message:\t" + new String(message.getPayload()) +
381
                           "  QoS:\t" + message.getQos());
382
	}
383
384
	/****************************************************************/
385
	/* End of MqttCallback methods                                  */
386
	/****************************************************************/
387
388
    static void printHelp() {
389
      System.out.println(
390
          "Syntax:\n\n" +
391
              "    Sample [-h] [-a publish|subscribe] [-t <topic>] [-m <message text>]\n" +
392
              "            [-s 0|1|2] -b <hostname|IP address>] [-p <brokerport>] [-i <clientID>]\n\n" +
393
              "    -h  Print this help text and quit\n" +
394
              "    -q  Quiet mode (default is false)\n" +
395
              "    -a  Perform the relevant action (default is publish)\n" +
396
              "    -t  Publish/subscribe to <topic> instead of the default\n" +
397
              "            (publish: \"Sample/Java/v3\", subscribe: \"Sample/#\")\n" +
398
              "    -m  Use <message text> instead of the default\n" +
399
              "            (\"Message from MQTTv3 Java client\")\n" +
400
              "    -s  Use this QoS instead of the default (2)\n" +
401
              "    -b  Use this name/IP address instead of the default (localhost)\n" +
402
              "    -p  Use this port instead of the default (1883)\n\n" +
403
              "    -i  Use this client ID instead of SampleJavaV3_<action>\n" +
404
              "    -c  Connect to the server with a clean session (default is false)\n" +
405
              "     \n\n Security Options \n" +
406
              "     -u Username \n" +
407
              "     -z Password \n" +
408
              "     \n\n SSL Options \n" +
409
              "    -v  SSL enabled; true - (default is false) " +
410
              "    -k  Use this JKS format key store to verify the client\n" +
411
              "    -w  Passpharse to verify certificates in the keys store\n" +
412
              "    -r  Use this JKS format keystore to verify the server\n" +
413
              " If javax.net.ssl properties have been set only the -v flag needs to be set\n" +
414
              "Delimit strings containing spaces with \"\"\n\n" +
415
              "Publishers transmit a single message then disconnect from the server.\n" +
416
              "Subscribers remain connected to the server and receive appropriate\n" +
417
              "messages until <enter> is pressed.\n\n"
418
          );
419
    }
420
421
}
(-)a/org.eclipse.paho.sample.mqttv3app/src/main/java/org/eclipse/paho/sample/mqttv3app/package.html (+3 lines)
Added Link Here
1
<body>
2
Contains a set of sample applications that show how to use the MQTT programming interface.
3
</body>
(-)a/org.eclipse.paho.sample.mqttv3app/src/org/eclipse/paho/sample/mqttv3app/Sample.java (-392 lines)
Lines 1-392 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
13
package org.eclipse.paho.sample.mqttv3app;
14
15
import java.io.IOException;
16
import java.sql.Timestamp;
17
18
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
19
import org.eclipse.paho.client.mqttv3.MqttCallback;
20
import org.eclipse.paho.client.mqttv3.MqttClient;
21
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
22
import org.eclipse.paho.client.mqttv3.MqttException;
23
import org.eclipse.paho.client.mqttv3.MqttMessage;
24
import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence;
25
26
/**
27
 * A sample application that demonstrates how to use the MQTT v3 Client blocking api.
28
 * 
29
 * It can be run from the command line in one of two modes: 
30
 *  - as a publisher, sending a single message to a topic on the server
31
 *  - as a subscriber, listening for messages from the server
32
 *  
33
 *  There are three versions of the sample that implement the same features
34
 *  but do so using using different programming styles:
35
 *  <ol>
36
 *  <li>Sample (this one) which uses the API which blocks until the operation completes</li>
37
 *  <li>SampleAsyncWait shows how to use the asynchronous API with waiters that block until 
38
 *  an action completes</li>
39
 *  <li>SampleAsyncCallBack shows how to use the asynchronous API where events are
40
 *  used to notify the application when an action completes<li>
41
 *  </ol>
42
 *  
43
 *  If the application is run with the -h parameter then info is displayed that 
44
 *  describes all of the options / parameters. 
45
 */
46
public class Sample implements MqttCallback {
47
	
48
	/**
49
	 * The main entry point of the sample.
50
	 * 
51
	 * This method handles parsing of the arguments specified on the
52
	 * command-line before performing the specified action.
53
	 */
54
	public static void main(String[] args) {
55
		
56
		// Default settings:
57
		boolean quietMode 	= false;
58
		String action 		= "publish";
59
		String topic 		= "";
60
		String message 		= "Message from blocking MQTTv3 Java client sample";
61
		int qos 			= 2;
62
		String broker 		= "m2m.eclipse.org";
63
		int port 			= 1883;
64
		String clientId 	= null;
65
		String subTopic		= "Sample/#";
66
		String pubTopic 	= "Sample/Java/v3";
67
		boolean cleanSession = true;			// Non durable subscriptions 
68
		boolean ssl = false;
69
		String password = null;
70
		String userName = null;
71
		// Parse the arguments - 
72
		for (int i=0; i<args.length; i++) {
73
			// Check this is a valid argument
74
			if (args[i].length() == 2 && args[i].startsWith("-")) {
75
				char arg = args[i].charAt(1);
76
				// Handle arguments that take no-value
77
				switch(arg) {
78
					case 'h': case '?':	printHelp(); return;
79
					case 'q': quietMode = true;	continue;
80
				}
81
				
82
				// Now handle the arguments that take a value and 
83
				// ensure one is specified
84
				if (i == args.length -1 || args[i+1].charAt(0) == '-') {
85
					System.out.println("Missing value for argument: "+args[i]);
86
					printHelp();
87
					return;
88
				}
89
				switch(arg) {
90
					case 'a': action = args[++i];                 break;
91
					case 't': topic = args[++i];                  break;
92
					case 'm': message = args[++i];                break;
93
					case 's': qos = Integer.parseInt(args[++i]);  break;
94
					case 'b': broker = args[++i];                 break;
95
					case 'p': port = Integer.parseInt(args[++i]); break;
96
					case 'i': clientId = args[++i];				  break;
97
					case 'c': cleanSession = Boolean.valueOf(args[++i]).booleanValue();  break;
98
          case 'k': System.getProperties().put("javax.net.ssl.keyStore", args[++i]); break;
99
          case 'w': System.getProperties().put("javax.net.ssl.keyStorePassword", args[++i]); break;
100
          case 'r': System.getProperties().put("javax.net.ssl.trustStore", args[++i]); break;
101
          case 'v': ssl = Boolean.valueOf(args[++i]).booleanValue(); break;
102
          case 'u': userName = args[++i];               break;
103
          case 'z': password = args[++i];               break;
104
					default: 
105
						System.out.println("Unrecognised argument: "+args[i]);
106
						printHelp(); 
107
						return;
108
				}
109
			} else {
110
				System.out.println("Unrecognised argument: "+args[i]);
111
				printHelp(); 
112
				return;
113
			}
114
		}
115
		
116
		// Validate the provided arguments
117
		if (!action.equals("publish") && !action.equals("subscribe")) {
118
			System.out.println("Invalid action: "+action);
119
			printHelp();
120
			return;
121
		}
122
		if (qos < 0 || qos > 2) {
123
			System.out.println("Invalid QoS: "+qos);
124
			printHelp();
125
			return;
126
		}
127
		if (topic.equals("")) {
128
			// Set the default topic according to the specified action
129
			if (action.equals("publish")) {
130
				topic = pubTopic;
131
			} else {
132
				topic = subTopic;
133
			}
134
		}
135
			
136
		String protocol = "tcp://";
137
138
    if (ssl) {
139
      protocol = "ssl://";
140
    }
141
142
    String url = protocol + broker + ":" + port;
143
		
144
		if (clientId == null || clientId.equals("")) {
145
			clientId = "SampleJavaV3_"+action;
146
		}
147
148
		// With a valid set of arguments, the real work of 
149
		// driving the client API can begin
150
		try {
151
			// Create an instance of this class
152
			Sample sampleClient = new Sample(url, clientId, cleanSession, quietMode,userName,password);
153
			
154
			// Perform the requested action
155
			if (action.equals("publish")) {
156
				sampleClient.publish(topic,qos,message.getBytes());
157
			} else if (action.equals("subscribe")) {
158
				sampleClient.subscribe(topic,qos);
159
			}
160
		} catch(MqttException me) {
161
			// Display full details of any exception that occurs 
162
			System.out.println("reason "+me.getReasonCode());
163
			System.out.println("msg "+me.getMessage());
164
			System.out.println("loc "+me.getLocalizedMessage());
165
			System.out.println("cause "+me.getCause());
166
			System.out.println("excep "+me);
167
			me.printStackTrace();
168
		}
169
	}
170
    
171
	// Private instance variables
172
	private MqttClient 			client;
173
	private String 				brokerUrl;
174
	private boolean 			quietMode;
175
	private MqttConnectOptions 	conOpt;
176
	private boolean 			clean;
177
	private String password;
178
	private String userName;
179
	
180
	/**
181
	 * Constructs an instance of the sample client wrapper
182
	 * @param brokerUrl the url of the server to connect to
183
	 * @param clientId the client id to connect with
184
	 * @param cleanSession clear state at end of connection or not (durable or non-durable subscriptions) 
185
	 * @param quietMode whether debug should be printed to standard out
186
   * @param userName the username to connect with
187
   * @param password the password for the user
188
	 * @throws MqttException
189
	 */
190
    public Sample(String brokerUrl, String clientId, boolean cleanSession, boolean quietMode, String userName, String password) throws MqttException {
191
    	this.brokerUrl = brokerUrl;
192
    	this.quietMode = quietMode;
193
    	this.clean 	   = cleanSession;
194
    	this.password = password;
195
    	this.userName = userName;
196
    	//This sample stores in a temporary directory... where messages temporarily
197
    	// stored until the message has been delivered to the server. 
198
    	//..a real application ought to store them somewhere 
199
    	// where they are not likely to get deleted or tampered with
200
    	String tmpDir = System.getProperty("java.io.tmpdir");
201
    	MqttDefaultFilePersistence dataStore = new MqttDefaultFilePersistence(tmpDir); 
202
    	
203
    	try {
204
    		// Construct the connection options object that contains connection parameters 
205
    		// such as cleansession and LWAT
206
	    	conOpt = new MqttConnectOptions();
207
	    	conOpt.setCleanSession(clean);
208
	    	if(password != null ) {
209
	    	  conOpt.setPassword(this.password.toCharArray());
210
	    	}
211
	    	if(userName != null) {
212
	    	  conOpt.setUserName(this.userName);	   
213
	    	}
214
215
    		// Construct an MQTT blocking mode client
216
			client = new MqttClient(this.brokerUrl,clientId, dataStore);
217
			
218
			// Set this wrapper as the callback handler
219
	    	client.setCallback(this);
220
	    	
221
		} catch (MqttException e) {
222
			e.printStackTrace();
223
			log("Unable to set up client: "+e.toString());
224
			System.exit(1);
225
		}
226
    }
227
228
    /**
229
     * Publish / send a message to an MQTT server
230
     * @param topicName the name of the topic to publish to
231
     * @param qos the quality of service to delivery the message at (0,1,2)
232
     * @param payload the set of bytes to send to the MQTT server 
233
     * @throws MqttException
234
     */
235
    public void publish(String topicName, int qos, byte[] payload) throws MqttException {
236
    	
237
    	// Connect to the MQTT server
238
    	log("Connecting to "+brokerUrl + " with client ID "+client.getClientId());
239
    	client.connect(conOpt);
240
    	log("Connected");
241
    	
242
    	String time = new Timestamp(System.currentTimeMillis()).toString();
243
    	log("Publishing at: "+time+ " to topic \""+topicName+"\" qos "+qos);
244
    	
245
    	// Create and configure a message
246
   		MqttMessage message = new MqttMessage(payload);
247
    	message.setQos(qos);
248
 
249
    	// Send the message to the server, control is not returned until
250
    	// it has been delivered to the server meeting the specified
251
    	// quality of service.
252
    	client.publish(topicName, message);
253
    	
254
    	// Disconnect the client
255
    	client.disconnect();
256
    	log("Disconnected");
257
    }
258
    
259
    /**
260
     * Subscribe to a topic on an MQTT server
261
     * Once subscribed this method waits for the messages to arrive from the server 
262
     * that match the subscription. It continues listening for messages until the enter key is 
263
     * pressed.
264
     * @param topicName to subscribe to (can be wild carded)
265
     * @param qos the maximum quality of service to receive messages at for this subscription 
266
     * @throws MqttException
267
     */
268
    public void subscribe(String topicName, int qos) throws MqttException {
269
    	
270
    	// Connect to the MQTT server
271
    	client.connect(conOpt);
272
    	log("Connected to "+brokerUrl+" with client ID "+client.getClientId());
273
274
    	// Subscribe to the requested topic
275
    	// The QOS specified is the maximum level that messages will be sent to the client at. 
276
    	// For instance if QOS 1 is specified, any messages originally published at QOS 2 will 
277
    	// be downgraded to 1 when delivering to the client but messages published at 1 and 0 
278
    	// will be received at the same level they were published at. 
279
    	log("Subscribing to topic \""+topicName+"\" qos "+qos);
280
    	client.subscribe(topicName, qos);
281
282
    	// Continue waiting for messages until the Enter is pressed
283
    	log("Press <Enter> to exit");
284
		try {
285
			System.in.read();
286
		} catch (IOException e) {
287
			//If we can't read we'll just exit
288
		}
289
		
290
		// Disconnect the client from the server
291
		client.disconnect();
292
		log("Disconnected");
293
    }
294
295
    /**
296
     * Utility method to handle logging. If 'quietMode' is set, this method does nothing
297
     * @param message the message to log
298
     */
299
    private void log(String message) {
300
    	if (!quietMode) {
301
    		System.out.println(message);
302
    	}
303
    }
304
305
	/****************************************************************/
306
	/* Methods to implement the MqttCallback interface              */
307
	/****************************************************************/
308
    
309
    /**
310
     * @see MqttCallback#connectionLost(Throwable)
311
     */
312
	public void connectionLost(Throwable cause) {
313
		// Called when the connection to the server has been lost.
314
		// An application may choose to implement reconnection
315
		// logic at this point. This sample simply exits.
316
		log("Connection to " + brokerUrl + " lost!" + cause);
317
		System.exit(1);
318
	}
319
320
    /**
321
     * @see MqttCallback#deliveryComplete(IMqttDeliveryToken)
322
     */
323
	public void deliveryComplete(IMqttDeliveryToken token) {
324
		// Called when a message has been delivered to the
325
		// server. The token passed in here is the same one
326
		// that was passed to or returned from the original call to publish.
327
		// This allows applications to perform asynchronous 
328
		// delivery without blocking until delivery completes.
329
		//
330
		// This sample demonstrates asynchronous deliver and 
331
		// uses the token.waitForCompletion() call in the main thread which
332
		// blocks until the delivery has completed. 
333
		// Additionally the deliveryComplete method will be called if 
334
		// the callback is set on the client
335
		// 
336
		// If the connection to the server breaks before delivery has completed
337
		// delivery of a message will complete after the client has re-connected.
338
		// The getPendinTokens method will provide tokens for any messages
339
		// that are still to be delivered.
340
	}
341
342
    /**
343
     * @see MqttCallback#messageArrived(String, MqttMessage)
344
     */
345
	public void messageArrived(String topic, MqttMessage message) throws MqttException {
346
		// Called when a message arrives from the server that matches any
347
		// subscription made by the client		
348
		String time = new Timestamp(System.currentTimeMillis()).toString();
349
		System.out.println("Time:\t" +time +
350
                           "  Topic:\t" + topic + 
351
                           "  Message:\t" + new String(message.getPayload()) +
352
                           "  QoS:\t" + message.getQos());
353
	}
354
355
	/****************************************************************/
356
	/* End of MqttCallback methods                                  */
357
	/****************************************************************/
358
359
	   static void printHelp() {
360
	      System.out.println(
361
	          "Syntax:\n\n" +
362
	              "    Sample [-h] [-a publish|subscribe] [-t <topic>] [-m <message text>]\n" +
363
	              "            [-s 0|1|2] -b <hostname|IP address>] [-p <brokerport>] [-i <clientID>]\n\n" +
364
	              "    -h  Print this help text and quit\n" +
365
	              "    -q  Quiet mode (default is false)\n" +
366
	              "    -a  Perform the relevant action (default is publish)\n" +
367
	              "    -t  Publish/subscribe to <topic> instead of the default\n" +
368
	              "            (publish: \"Sample/Java/v3\", subscribe: \"Sample/#\")\n" +
369
	              "    -m  Use <message text> instead of the default\n" +
370
	              "            (\"Message from MQTTv3 Java client\")\n" +
371
	              "    -s  Use this QoS instead of the default (2)\n" +
372
	              "    -b  Use this name/IP address instead of the default (localhost)\n" +
373
	              "    -p  Use this port instead of the default (1883)\n\n" +
374
	              "    -i  Use this client ID instead of SampleJavaV3_<action>\n" +
375
	              "    -c  Connect to the server with a clean session (default is false)\n" +
376
	              "     \n\n Security Options \n" +
377
	              "     -u Username \n" +
378
	              "     -z Password \n" +
379
	              "     \n\n SSL Options \n" +
380
	              "    -v  SSL enabled; true - (default is false) " +
381
	              "    -k  Use this JKS format key store to verify the client\n" +
382
	              "    -w  Passpharse to verify certificates in the keys store\n" +
383
	              "    -r  Use this JKS format keystore to verify the server\n" +
384
	              " If javax.net.ssl properties have been set only the -v flag needs to be set\n" +
385
	              "Delimit strings containing spaces with \"\"\n\n" +
386
	              "Publishers transmit a single message then disconnect from the server.\n" +
387
	              "Subscribers remain connected to the server and receive appropriate\n" +
388
	              "messages until <enter> is pressed.\n\n"
389
	          );
390
    }
391
392
}
(-)a/org.eclipse.paho.sample.mqttv3app/src/org/eclipse/paho/sample/mqttv3app/SampleAsyncCallBack.java (-648 lines)
Lines 1-648 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
13
package org.eclipse.paho.sample.mqttv3app;
14
15
import java.io.IOException;
16
import java.sql.Timestamp;
17
18
import org.eclipse.paho.client.mqttv3.IMqttActionListener;
19
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
20
import org.eclipse.paho.client.mqttv3.IMqttToken;
21
import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
22
import org.eclipse.paho.client.mqttv3.MqttCallback;
23
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
24
import org.eclipse.paho.client.mqttv3.MqttException;
25
import org.eclipse.paho.client.mqttv3.MqttMessage;
26
import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence;
27
28
/**
29
 * A sample application that demonstrates how to use the MQTT v3 Client api in
30
 * non-blocking callback/notification mode.
31
 * 
32
 * It can be run from the command line in one of two modes: 
33
 *  - as a publisher, sending a single message to a topic on the server
34
 *  - as a subscriber, listening for messages from the server
35
 *  
36
 *  There are three versions of the sample that implement the same features
37
 *  but do so using using different programming styles:
38
 *  <ol>
39
 *  <li>Sample (this one) which uses the API which blocks until the operation completes</li>
40
 *  <li>SampleAsyncWait shows how to use the asynchronous API with waiters that block until 
41
 *  an action completes</li>
42
 *  <li>SampleAsyncCallBack shows how to use the asynchronous API where events are
43
 *  used to notify the application when an action completes<li>
44
 *  </ol>
45
 *  
46
 *  If the application is run with the -h parameter then info is displayed that 
47
 *  describes all of the options / parameters. 
48
 */
49
public class SampleAsyncCallBack implements MqttCallback {
50
	
51
	int state = BEGIN;	
52
53
	static final int BEGIN = 0;
54
	static final int CONNECTED = 1;
55
	static final int PUBLISHED = 2;
56
	static final int SUBSCRIBED = 3;
57
	static final int DISCONNECTED = 4;
58
	static final int FINISH = 5;
59
	static final int ERROR = 6;
60
	static final int DISCONNECT = 7;
61
	
62
	/**
63
	 * The main entry point of the sample.
64
	 * 
65
	 * This method handles parsing the arguments specified on the
66
	 * command-line before performing the specified action.
67
	 */
68
	public static void main(String[] args) {
69
		
70
		// Default settings:
71
		boolean quietMode 	= false;
72
		String action 		= "publish";
73
		String topic 		= "";
74
		String message 		= "Message from async calback MQTTv3 Java client sample";
75
		int qos 			= 2;
76
		String broker 		= "m2m.eclipse.org";
77
		int port 			= 1883;
78
		String clientId 	= null;
79
		String subTopic		= "Sample/#";
80
		String pubTopic 	= "Sample/Java/v3";
81
		boolean cleanSession = true;			// Non durable subscriptions 
82
		boolean ssl = false;
83
    String password = null;
84
    String userName = null;
85
		
86
		// Parse the arguments - 
87
		for (int i=0; i<args.length; i++) {
88
			// Check this is a valid argument
89
			if (args[i].length() == 2 && args[i].startsWith("-")) {
90
				char arg = args[i].charAt(1);
91
				// Handle arguments that take no-value
92
				switch(arg) {
93
					case 'h': case '?':	printHelp(); return;
94
					case 'q': quietMode = true;	continue;
95
				}
96
				
97
				// Now handle the arguments that take a value and 
98
				// ensure one is specified
99
				if (i == args.length -1 || args[i+1].charAt(0) == '-') {
100
					System.out.println("Missing value for argument: "+args[i]);
101
					printHelp();
102
					return;
103
				}
104
				switch(arg) {
105
					case 'a': action = args[++i];                 break;
106
					case 't': topic = args[++i];                  break;
107
					case 'm': message = args[++i];                break;
108
					case 's': qos = Integer.parseInt(args[++i]);  break;
109
					case 'b': broker = args[++i];                 break;
110
					case 'p': port = Integer.parseInt(args[++i]); break;
111
					case 'i': clientId = args[++i];				  break;
112
					case 'c': cleanSession = Boolean.valueOf(args[++i]).booleanValue();  break;
113
          case 'k': System.getProperties().put("javax.net.ssl.keyStore", args[++i]); break;
114
          case 'w': System.getProperties().put("javax.net.ssl.keyStorePassword", args[++i]); break;
115
          case 'r': System.getProperties().put("javax.net.ssl.trustStore", args[++i]); break;
116
          case 'v': ssl = Boolean.valueOf(args[++i]).booleanValue();  break;
117
          case 'u': userName = args[++i];               break;
118
          case 'z': password = args[++i];               break;
119
					default: 
120
						System.out.println("Unrecognised argument: "+args[i]);
121
						printHelp(); 
122
						return;
123
				}
124
			} else {
125
				System.out.println("Unrecognised argument: "+args[i]);
126
				printHelp(); 
127
				return;
128
			}
129
		}
130
		
131
		// Validate the provided arguments
132
		if (!action.equals("publish") && !action.equals("subscribe")) {
133
			System.out.println("Invalid action: "+action);
134
			printHelp();
135
			return;
136
		}
137
		if (qos < 0 || qos > 2) {
138
			System.out.println("Invalid QoS: "+qos);
139
			printHelp();
140
			return;
141
		}
142
		if (topic.equals("")) {
143
			// Set the default topic according to the specified action
144
			if (action.equals("publish")) {
145
				topic = pubTopic;
146
			} else {
147
				topic = subTopic;
148
			}
149
		}
150
			
151
		String protocol = "tcp://";
152
153
    if (ssl) {
154
      protocol = "ssl://";
155
    }
156
157
    String url = protocol + broker + ":" + port;
158
		
159
		if (clientId == null || clientId.equals("")) {
160
			clientId = "SampleJavaV3_"+action;
161
		}
162
163
		// With a valid set of arguments, the real work of 
164
		// driving the client API can begin
165
		try {
166
			// Create an instance of the Sample client wrapper
167
			SampleAsyncCallBack sampleClient = new SampleAsyncCallBack(url,clientId,cleanSession, quietMode,userName,password);
168
			
169
			// Perform the specified action
170
			if (action.equals("publish")) {
171
				sampleClient.publish(topic,qos,message.getBytes());
172
			} else if (action.equals("subscribe")) {
173
				sampleClient.subscribe(topic,qos);
174
			}
175
		} catch(MqttException me) {
176
			// Display full details of any exception that occurs
177
			System.out.println("reason "+me.getReasonCode());
178
			System.out.println("msg "+me.getMessage());
179
			System.out.println("loc "+me.getLocalizedMessage());
180
			System.out.println("cause "+me.getCause());
181
			System.out.println("excep "+me);
182
			me.printStackTrace();
183
		} catch (Throwable th) {
184
			System.out.println("Throwable caught "+th);
185
			th.printStackTrace();
186
		}
187
	}
188
    
189
	// Private instance variables	
190
	MqttAsyncClient 	client;
191
	String 				brokerUrl;
192
	private boolean 			quietMode;
193
	private MqttConnectOptions 	conOpt;
194
	private boolean 			clean;
195
	Throwable 			ex = null;
196
	Object 				waiter = new Object();
197
	boolean 			donext = false;
198
	private String password;
199
	private String userName;
200
	
201
	/**
202
	 * Constructs an instance of the sample client wrapper
203
	 * @param brokerUrl the url to connect to
204
	 * @param clientId the client id to connect with
205
	 * @param cleanSession clear state at end of connection or not (durable or non-durable subscriptions)
206
	 * @param quietMode whether debug should be printed to standard out
207
	 * @param userName the username to connect with
208
	 * @param password the password for the user
209
	 * @throws MqttException
210
	 */
211
    public SampleAsyncCallBack(String brokerUrl, String clientId, boolean cleanSession, 
212
    		boolean quietMode, String userName, String password) throws MqttException {
213
    	this.brokerUrl = brokerUrl;
214
    	this.quietMode = quietMode;
215
    	this.clean 	   = cleanSession;
216
      this.password = password;
217
      this.userName = userName;
218
    	//This sample stores in a temporary directory... where messages temporarily
219
    	// stored until the message has been delivered to the server. 
220
    	//..a real application ought to store them somewhere 
221
    	// where they are not likely to get deleted or tampered with
222
    	String tmpDir = System.getProperty("java.io.tmpdir");
223
    	MqttDefaultFilePersistence dataStore = new MqttDefaultFilePersistence(tmpDir); 
224
    	
225
    	try {
226
    		// Construct the object that contains connection parameters 
227
    		// such as cleansession and LWAT
228
	    	conOpt = new MqttConnectOptions();
229
	    	conOpt.setCleanSession(clean);
230
	    	if(password != null ) {
231
          conOpt.setPassword(this.password.toCharArray());
232
        }
233
        if(userName != null) {
234
          conOpt.setUserName(this.userName);     
235
        }
236
	    	
237
    		// Construct the MqttClient instance
238
			client = new MqttAsyncClient(this.brokerUrl,clientId, dataStore);
239
			
240
			// Set this wrapper as the callback handler
241
	    	client.setCallback(this);
242
	    	
243
		} catch (MqttException e) {
244
			e.printStackTrace();
245
			log("Unable to set up client: "+e.toString());
246
			System.exit(1);
247
		}
248
    }
249
250
    /**
251
     * Publish / send a message to an MQTT server
252
     * @param topicName the name of the topic to publish to
253
     * @param qos the quality of service to delivery the message at (0,1,2)
254
     * @param payload the set of bytes to send to the MQTT server 
255
     * @throws MqttException
256
     */
257
    public void publish(String topicName, int qos, byte[] payload) throws Throwable {
258
    	// Use a state machine to decide which step to do next. State change occurs 
259
    	// when a notification is received that an MQTT action has completed
260
    	while (state != FINISH) {
261
    		switch (state) {
262
    			case BEGIN:
263
    				// Connect using a non blocking connect
264
    		    	MqttConnector con = new MqttConnector();
265
    		    	con.doConnect();
266
    				break;
267
    			case CONNECTED:
268
    				// Publish using a non blocking publisher
269
    				Publisher pub = new Publisher();
270
    				pub.doPublish(topicName, qos, payload);
271
    				break;
272
    			case PUBLISHED:
273
    				state = DISCONNECT;
274
    				donext = true;
275
    				break;
276
    			case DISCONNECT:
277
    				Disconnector disc = new Disconnector();
278
    				disc.doDisconnect();
279
    				break;
280
    			case ERROR:
281
    				throw ex;
282
    			case DISCONNECTED:
283
    				state = FINISH;
284
    				donext = true;
285
    				break;		
286
    		}
287
    		
288
//    		if (state != FINISH) {
289
    			// Wait until notified about a state change and then perform next action
290
    			waitForStateChange(10000);
291
//    		}
292
    	}	
293
    }
294
295
    /**
296
     * Wait for a maximum amount of time for a state change event to occur
297
     * @param maxTTW  maximum time to wait in milliseconds
298
     * @throws MqttException
299
     */
300
	private void waitForStateChange(int maxTTW ) throws MqttException {
301
		synchronized (waiter) {
302
    		if (!donext ) {
303
    			try {
304
					waiter.wait(maxTTW);
305
				} catch (InterruptedException e) {
306
					log("timed out");
307
					e.printStackTrace();
308
				}
309
				
310
				if (ex != null) {
311
					throw (MqttException)ex;
312
				}
313
    		}
314
    		donext = false;
315
    	}
316
	}
317
318
    /**
319
     * Subscribe to a topic on an MQTT server
320
     * Once subscribed this method waits for the messages to arrive from the server 
321
     * that match the subscription. It continues listening for messages until the enter key is 
322
     * pressed.
323
     * @param topicName to subscribe to (can be wild carded)
324
     * @param qos the maximum quality of service to receive messages at for this subscription 
325
     * @throws MqttException
326
     */
327
    public void subscribe(String topicName, int qos) throws Throwable {
328
    	// Use a state machine to decide which step to do next. State change occurs 
329
    	// when a notification is received that an MQTT action has completed
330
    	while (state != FINISH) {
331
    		switch (state) {
332
    			case BEGIN:
333
    				// Connect using a non blocking connect
334
    		    	MqttConnector con = new MqttConnector();
335
    		    	con.doConnect();
336
    				break;
337
    			case CONNECTED:
338
    				// Subscribe using a non blocking subscribe
339
    				Subscriber sub = new Subscriber();
340
    				sub.doSubscribe(topicName, qos);
341
    				break;
342
    			case SUBSCRIBED:
343
    		    	// Block until Enter is pressed allowing messages to arrive
344
    		    	log("Press <Enter> to exit");
345
    				try {
346
    					System.in.read();
347
    				} catch (IOException e) {
348
    					//If we can't read we'll just exit
349
    				}
350
    				state = DISCONNECT;
351
    				donext = true;
352
    				break;
353
    			case DISCONNECT:
354
    				Disconnector disc = new Disconnector();
355
    				disc.doDisconnect();
356
    				break;
357
    			case ERROR:
358
    				throw ex;
359
    			case DISCONNECTED:
360
    				state = FINISH;
361
    				donext = true;
362
    				break;		
363
    		}
364
    		
365
//    		if (state != FINISH && state != DISCONNECT) {
366
    			waitForStateChange(10000);
367
    		}
368
//    	}    	
369
    }
370
371
    /**
372
     * Utility method to handle logging. If 'quietMode' is set, this method does nothing
373
     * @param message the message to log
374
     */
375
    void log(String message) {
376
    	if (!quietMode) {
377
    		System.out.println(message);
378
    	}
379
    }
380
381
	/****************************************************************/
382
	/* Methods to implement the MqttCallback interface              */
383
	/****************************************************************/
384
    
385
    /**
386
     * @see MqttCallback#connectionLost(Throwable)
387
     */
388
	public void connectionLost(Throwable cause) {
389
		// Called when the connection to the server has been lost.
390
		// An application may choose to implement reconnection
391
		// logic at this point. This sample simply exits.
392
		log("Connection to " + brokerUrl + " lost!" + cause);
393
		System.exit(1);
394
	}
395
396
    /**
397
     * @see MqttCallback#deliveryComplete(IMqttDeliveryToken)
398
     */
399
	public void deliveryComplete(IMqttDeliveryToken token) {
400
		// Called when a message has been delivered to the
401
		// server. The token passed in here is the same one
402
		// that was returned from the original call to publish.
403
		// This allows applications to perform asynchronous 
404
		// delivery without blocking until delivery completes.
405
		//
406
		// This sample demonstrates asynchronous deliver, registering 
407
		// a callback to be notified on each call to publish.
408
		//
409
		// The deliveryComplete method will also be called if 
410
		// the callback is set on the client
411
		// 
412
		log("Delivery complete callback: Publish Completed "+token.getTopics());	
413
	}
414
415
    /**
416
     * @see MqttCallback#messageArrived(String, MqttMessage)
417
     */
418
	public void messageArrived(String topic, MqttMessage message) throws MqttException {
419
		// Called when a message arrives from the server that matches any
420
		// subscription made by the client		
421
		String time = new Timestamp(System.currentTimeMillis()).toString();
422
		System.out.println("Time:\t" +time +
423
                           "  Topic:\t" + topic + 
424
                           "  Message:\t" + new String(message.getPayload()) +
425
                           "  QoS:\t" + message.getQos());
426
	}
427
428
	/****************************************************************/
429
	/* End of MqttCallback methods                                  */
430
	/****************************************************************/
431
    static void printHelp() {
432
      System.out.println(
433
          "Syntax:\n\n" +
434
              "    Sample [-h] [-a publish|subscribe] [-t <topic>] [-m <message text>]\n" +
435
              "            [-s 0|1|2] -b <hostname|IP address>] [-p <brokerport>] [-i <clientID>]\n\n" +
436
              "    -h  Print this help text and quit\n" +
437
              "    -q  Quiet mode (default is false)\n" +
438
              "    -a  Perform the relevant action (default is publish)\n" +
439
              "    -t  Publish/subscribe to <topic> instead of the default\n" +
440
              "            (publish: \"Sample/Java/v3\", subscribe: \"Sample/#\")\n" +
441
              "    -m  Use <message text> instead of the default\n" +
442
              "            (\"Message from MQTTv3 Java client\")\n" +
443
              "    -s  Use this QoS instead of the default (2)\n" +
444
              "    -b  Use this name/IP address instead of the default (localhost)\n" +
445
              "    -p  Use this port instead of the default (1883)\n\n" +
446
              "    -i  Use this client ID instead of SampleJavaV3_<action>\n" +
447
              "    -c  Connect to the server with a clean session (default is false)\n" +
448
              "     \n\n Security Options \n" +
449
              "     -u Username \n" +
450
              "     -z Password \n" +
451
              "     \n\n SSL Options \n" +
452
              "    -v  SSL enabled; true - (default is false) " +
453
              "    -k  Use this JKS format key store to verify the client\n" +
454
              "    -w  Passpharse to verify certificates in the keys store\n" +
455
              "    -r  Use this JKS format keystore to verify the server\n" +
456
              " If javax.net.ssl properties have been set only the -v flag needs to be set\n" +
457
              "Delimit strings containing spaces with \"\"\n\n" +
458
              "Publishers transmit a single message then disconnect from the server.\n" +
459
              "Subscribers remain connected to the server and receive appropriate\n" +
460
              "messages until <enter> is pressed.\n\n"
461
          );
462
    }
463
        	
464
	/**
465
	 * Connect in a non blocking way and then sit back and wait to be 
466
	 * notified that the action has completed.
467
	 */
468
    public class MqttConnector {
469
		
470
		public MqttConnector() {
471
		}
472
		
473
		public void doConnect() {
474
	    	// Connect to the server
475
			// Get a token and setup an asynchronous listener on the token which
476
			// will be notified once the connect completes
477
	    	log("Connecting to "+brokerUrl + " with client ID "+client.getClientId());
478
	
479
	    	IMqttActionListener conListener = new IMqttActionListener() {			
480
				public void onSuccess(IMqttToken asyncActionToken) {
481
			    	log("Connected");
482
			    	state = CONNECTED;
483
			    	carryOn();
484
				}
485
				
486
				public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
487
					ex = exception;
488
					state = ERROR;
489
					log ("connect failed" +exception);
490
					carryOn();
491
				}
492
				
493
				public void carryOn() {
494
			    	synchronized (waiter) {
495
			    		donext=true;
496
			    		waiter.notifyAll();
497
			    	}
498
				}
499
			};
500
	    			
501
	    	try {
502
	    		// Connect using a non blocking connect
503
	    		client.connect(conOpt,"Connect sample context", conListener);
504
			} catch (MqttException e) {
505
				// If though it is a non blocking connect an exception can be 
506
				// thrown if validation of parms fails or other checks such 
507
				// as already connected fail.
508
				state = ERROR;
509
				donext = true;
510
				ex = e;
511
			}
512
		}
513
	}
514
515
	/**
516
	 * Publish in a non blocking way and then sit back and wait to be 
517
	 * notified that the action has completed.
518
	 */
519
	public class Publisher {
520
		public void doPublish(String topicName, int qos, byte[] payload) {
521
		 	// Send / publish a message to the server
522
			// Get a token and setup an asynchronous listener on the token which
523
			// will be notified once the message has been delivered
524
	   		MqttMessage message = new MqttMessage(payload);
525
	    	message.setQos(qos);
526
		
527
528
	    	String time = new Timestamp(System.currentTimeMillis()).toString();
529
	    	log("Publishing at: "+time+ " to topic \""+topicName+"\" qos "+qos);
530
531
	    	// Setup a listener object to be notified when the publish completes.
532
	    	// 
533
	    	IMqttActionListener pubListener = new IMqttActionListener() {	
534
				public void onSuccess(IMqttToken asyncActionToken) {
535
			    	log("Publish Completed");
536
			    	state = PUBLISHED;
537
			    	carryOn();
538
				}
539
				
540
				public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
541
					ex = exception;
542
					state = ERROR;
543
					log ("Publish failed" +exception);
544
					carryOn();
545
				}
546
				
547
				public void carryOn() {
548
			    	synchronized (waiter) {
549
			    		donext=true;
550
			    		waiter.notifyAll();
551
			    	}
552
				}
553
			};
554
555
	    	try {
556
		    	// Publish the message
557
	    		client.publish(topicName, message, "Pub sample context", pubListener);
558
	    	} catch (MqttException e) {
559
				state = ERROR;
560
				donext = true;
561
				ex = e;
562
			}
563
		}
564
	}
565
	
566
	/**
567
	 * Subscribe in a non blocking way and then sit back and wait to be 
568
	 * notified that the action has completed.
569
	 */
570
	public class Subscriber {
571
		public void doSubscribe(String topicName, int qos) {
572
		 	// Make a subscription 
573
			// Get a token and setup an asynchronous listener on the token which
574
			// will be notified once the subscription is in place.
575
	    	log("Subscribing to topic \""+topicName+"\" qos "+qos);
576
577
	    	IMqttActionListener subListener = new IMqttActionListener() {
578
				public void onSuccess(IMqttToken asyncActionToken) {
579
			    	log("Subscribe Completed");
580
			    	state = SUBSCRIBED;
581
			    	carryOn();
582
				}
583
				
584
				public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
585
					ex = exception;
586
					state = ERROR;
587
					log ("Subscribe failed" +exception);
588
					carryOn();
589
				}
590
				
591
				public void carryOn() {
592
			    	synchronized (waiter) {
593
			    		donext=true;
594
			    		waiter.notifyAll();
595
			    	}
596
				}
597
			};
598
	    	
599
	    	try {
600
	    		client.subscribe(topicName, qos, "Subscribe sample context", subListener);
601
	    	} catch (MqttException e) {
602
				state = ERROR;
603
				donext = true;
604
				ex = e;
605
			}
606
		}
607
	}
608
	
609
	/**
610
	 * Disconnect in a non blocking way and then sit back and wait to be 
611
	 * notified that the action has completed.
612
	 */
613
	public class Disconnector {
614
		public void doDisconnect() {
615
	    	// Disconnect the client
616
	    	log("Disconnecting");
617
618
	    	IMqttActionListener discListener = new IMqttActionListener() {		
619
				public void onSuccess(IMqttToken asyncActionToken) {
620
			    	log("Disconnect Completed");
621
			    	state = DISCONNECTED;
622
			    	carryOn();
623
				}
624
				
625
				public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
626
					ex = exception;
627
					state = ERROR;
628
					log ("Disconnect failed" +exception);
629
					carryOn();
630
				}
631
				public void carryOn() {
632
			    	synchronized (waiter) {
633
			    		donext=true;
634
			    		waiter.notifyAll();
635
			    	}
636
				}
637
			};
638
	    	
639
	    	try {
640
	    		client.disconnect("Disconnect sample context", discListener);
641
	    	} catch (MqttException e) {
642
				state = ERROR;
643
				donext = true;
644
				ex = e;
645
			}
646
		}
647
	}
648
}
(-)a/org.eclipse.paho.sample.mqttv3app/src/org/eclipse/paho/sample/mqttv3app/SampleAsyncWait.java (-421 lines)
Lines 1-421 Link Here
1
/* 
2
 * Copyright (c) 2009, 2012 IBM Corp.
3
 *
4
 * All rights reserved. This program and the accompanying materials
5
 * are made available under the terms of the Eclipse Public License v1.0
6
 * which accompanies this distribution, and is available at
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 *
9
 * Contributors:
10
 *    Dave Locke - initial API and implementation and/or initial documentation
11
 */
12
13
package org.eclipse.paho.sample.mqttv3app;
14
15
import java.io.IOException;
16
import java.sql.Timestamp;
17
18
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
19
import org.eclipse.paho.client.mqttv3.IMqttToken;
20
import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
21
import org.eclipse.paho.client.mqttv3.MqttCallback;
22
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
23
import org.eclipse.paho.client.mqttv3.MqttException;
24
import org.eclipse.paho.client.mqttv3.MqttMessage;
25
import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence;
26
27
/**
28
 * A sample application that demonstrates how to use the MQTT v3 Client api in
29
 * non-blocking waiter mode.
30
 * 
31
 * It can be run from the command line in one of two modes: 
32
 *  - as a publisher, sending a single message to a topic on the server
33
 *  - as a subscriber, listening for messages from the server
34
 *  
35
 *  There are three versions of the sample that implement the same features
36
 *  but do so using using different programming styles:
37
 *  <ol>
38
 *  <li>Sample (this one) which uses the API which blocks until the operation completes</li>
39
 *  <li>SampleAsyncWait shows how to use the asynchronous API with waiters that block until 
40
 *  an action completes</li>
41
 *  <li>SampleAsyncCallBack shows how to use the asynchronous API where events are
42
 *  used to notify the application when an action completes<li>
43
 *  </ol>
44
 *  
45
 *  If the application is run with the -h parameter then info is displayed that 
46
 *  describes all of the options / parameters. 
47
 */
48
49
public class SampleAsyncWait implements MqttCallback {
50
	
51
	/**
52
	 * The main entry point of the sample.
53
	 * 
54
	 * This method handles parsing the arguments specified on the
55
	 * command-line before performing the specified action.
56
	 */
57
	public static void main(String[] args) {
58
		
59
		// Default settings:
60
		boolean quietMode 	= false;
61
		String action 		= "publish";
62
		String topic 		= "";
63
		String message 		= "Message from async waiter MQTTv3 Java client sample";
64
		int qos 			= 2;
65
		String broker 		= "m2m.eclipse.org";
66
		int port 			= 1883;
67
		String clientId 	= null;
68
		String subTopic		= "Sample/#";
69
		String pubTopic 	= "Sample/Java/v3";
70
		boolean cleanSession = true;			// Non durable subscriptions 
71
		boolean ssl = false;
72
		String userName = null;
73
		String password = null;
74
		
75
		// Parse the arguments - 
76
		for (int i=0; i<args.length; i++) {
77
			// Check this is a valid argument
78
			if (args[i].length() == 2 && args[i].startsWith("-")) {
79
				char arg = args[i].charAt(1);
80
				// Handle arguments that take no-value
81
				switch(arg) {
82
					case 'h': case '?':	printHelp(); return;
83
					case 'q': quietMode = true;	continue;
84
				}
85
				
86
				// Now handle the arguments that take a value and 
87
				// ensure one is specified
88
				if (i == args.length -1 || args[i+1].charAt(0) == '-') {
89
					System.out.println("Missing value for argument: "+args[i]);
90
					printHelp();
91
					return;
92
				}
93
				switch(arg) {
94
					case 'a': action = args[++i];                 break;
95
					case 't': topic = args[++i];                  break;
96
					case 'm': message = args[++i];                break;
97
					case 's': qos = Integer.parseInt(args[++i]);  break;
98
					case 'b': broker = args[++i];                 break;
99
					case 'p': port = Integer.parseInt(args[++i]); break;
100
					case 'i': clientId = args[++i];				  break;
101
					case 'c': cleanSession = Boolean.valueOf(args[++i]).booleanValue();  break;
102
	        case 'k': System.getProperties().put("javax.net.ssl.keyStore", args[++i]); break;
103
	        case 'w': System.getProperties().put("javax.net.ssl.keyStorePassword", args[++i]); break;
104
	        case 'r': System.getProperties().put("javax.net.ssl.trustStore", args[++i]);  break;
105
	        case 'v': ssl = Boolean.valueOf(args[++i]).booleanValue();  break;
106
          case 'u': userName = args[++i];               break;
107
          case 'z': password = args[++i];               break;
108
					default: 
109
						System.out.println("Unrecognised argument: "+args[i]);
110
						printHelp(); 
111
						return;
112
				}
113
			} else {
114
				System.out.println("Unrecognised argument: "+args[i]);
115
				printHelp(); 
116
				return;
117
			}
118
		}
119
		
120
		// Validate the provided arguments
121
		if (!action.equals("publish") && !action.equals("subscribe")) {
122
			System.out.println("Invalid action: "+action);
123
			printHelp();
124
			return;
125
		}
126
		if (qos < 0 || qos > 2) {
127
			System.out.println("Invalid QoS: "+qos);
128
			printHelp();
129
			return;
130
		}
131
		if (topic.equals("")) {
132
			// Set the default topic according to the specified action
133
			if (action.equals("publish")) {
134
				topic = pubTopic;
135
			} else {
136
				topic = subTopic;
137
			}
138
		}
139
			
140
		String protocol = "tcp://";
141
142
    if (ssl) {
143
      protocol = "ssl://";
144
    }
145
146
    String url = protocol + broker + ":" + port;
147
		
148
		if (clientId == null || clientId.equals("")) {
149
			clientId = "SampleJavaV3_"+action;
150
		}
151
152
		// With a valid set of arguments, the real work of 
153
		// driving the client API can begin
154
		try {
155
			// Create an instance of this class
156
			SampleAsyncWait sampleClient = new SampleAsyncWait(url,clientId,cleanSession, quietMode,userName,password);
157
			
158
			// Perform the specified action
159
			if (action.equals("publish")) {
160
				sampleClient.publish(topic,qos,message.getBytes());
161
			} else if (action.equals("subscribe")) {
162
				sampleClient.subscribe(topic,qos);
163
			}
164
		} catch(MqttException me) {
165
			// Display full details of any exception that occurs			
166
			System.out.println("reason "+me.getReasonCode());
167
			System.out.println("msg "+me.getMessage());
168
			System.out.println("loc "+me.getLocalizedMessage());
169
			System.out.println("cause "+me.getCause());
170
			System.out.println("excep "+me);
171
			me.printStackTrace();
172
		}
173
	}
174
    
175
	// Private instance variables
176
	private MqttAsyncClient 	client;
177
	private String 				brokerUrl;
178
	private boolean 			quietMode;
179
	private MqttConnectOptions 	conOpt;
180
	private boolean 			clean;
181
	private String password;
182
	private String userName;
183
	
184
	/**
185
	 * Constructs an instance of the sample client wrapper
186
	 * @param brokerUrl the url to connect to
187
	 * @param clientId the client id to connect with
188
	 * @param cleanSession clear state at end of connection or not (durable or non-durable subscriptions)
189
	 * @param quietMode whether debug should be printed to standard out
190
   * @param userName the username to connect with
191
	 * @param password the password for the user
192
	 * @throws MqttException
193
	 */
194
    public SampleAsyncWait(String brokerUrl, String clientId, boolean cleanSession, 
195
    		boolean quietMode, String userName, String password) throws MqttException {
196
    	this.brokerUrl = brokerUrl;
197
    	this.quietMode = quietMode;
198
    	this.clean 	   = cleanSession;
199
    	this.userName = userName;
200
    	this.password = password;
201
    	//This sample stores in a temporary directory... where messages temporarily
202
    	// stored until the message has been delivered to the server. 
203
    	//..a real application ought to store them somewhere 
204
    	// where they are not likely to get deleted or tampered with
205
    	String tmpDir = System.getProperty("java.io.tmpdir");
206
    	MqttDefaultFilePersistence dataStore = new MqttDefaultFilePersistence(tmpDir); 
207
    	
208
    	try {
209
    		// Construct the connection options object that contains connection parameters 
210
    		// such as cleansession and LWAT
211
	    	conOpt = new MqttConnectOptions();
212
	    	conOpt.setCleanSession(clean);
213
	    	if(password != null ) {
214
          conOpt.setPassword(this.password.toCharArray());
215
        }
216
        if(userName != null) {
217
          conOpt.setUserName(this.userName);     
218
        }
219
220
    		// Construct a non blocking MQTT client instance
221
			client = new MqttAsyncClient(this.brokerUrl,clientId, dataStore);
222
			
223
			// Set this wrapper as the callback handler
224
	    	client.setCallback(this);
225
	    	
226
		} catch (MqttException e) {
227
			e.printStackTrace();
228
			log("Unable to set up client: "+e.toString());
229
			System.exit(1);
230
		}
231
    }
232
233
    /**
234
     * Publish / send a message to an MQTT server
235
     * @param topicName the name of the topic to publish to
236
     * @param qos the quality of service to delivery the message at (0,1,2)
237
     * @param payload the set of bytes to send to the MQTT server 
238
     * @throws MqttException
239
     */
240
    public void publish(String topicName, int qos, byte[] payload) throws MqttException {
241
    	
242
    	// Connect to the MQTT server 
243
    	// issue a non-blocking connect and then use the token to wait until the
244
    	// connect completes. An exception is thrown if connect fails.
245
    	log("Connecting to "+brokerUrl + " with client ID "+client.getClientId());
246
    	IMqttToken conToken = client.connect(conOpt,null,null);
247
    	conToken.waitForCompletion();
248
    	log("Connected");
249
 
250
       	String time = new Timestamp(System.currentTimeMillis()).toString();
251
    	log("Publishing at: "+time+ " to topic \""+topicName+"\" qos "+qos);
252
    	
253
    	// Construct the message to send
254
   		MqttMessage message = new MqttMessage(payload);
255
    	message.setQos(qos);
256
	
257
    	// Send the message to the server, control is returned as soon 
258
    	// as the MQTT client has accepted to deliver the message. 
259
    	// Use the delivery token to wait until the message has been
260
    	// delivered	
261
    	IMqttDeliveryToken pubToken = client.publish(topicName, message, null, null);
262
    	pubToken.waitForCompletion(); 	
263
    	log("Published");   
264
    	
265
    	// Disconnect the client
266
    	// Issue the disconnect and then use a token to wait until 
267
    	// the disconnect completes.
268
    	log("Disconnecting");
269
    	IMqttToken discToken = client.disconnect(null, null);
270
    	discToken.waitForCompletion();
271
    	log("Disconnected");
272
    }
273
    
274
    /**
275
     * Subscribe to a topic on an MQTT server
276
     * Once subscribed this method waits for the messages to arrive from the server 
277
     * that match the subscription. It continues listening for messages until the enter key is 
278
     * pressed.
279
     * @param topicName to subscribe to (can be wild carded)
280
     * @param qos the maximum quality of service to receive messages at for this subscription 
281
     * @throws MqttException
282
     */
283
    public void subscribe(String topicName, int qos) throws MqttException {
284
    	
285
    	// Connect to the MQTT server 
286
    	// issue a non-blocking connect and then use the token to wait until the
287
    	// connect completes. An exception is thrown if connect fails.
288
    	log("Connecting to "+brokerUrl + " with client ID "+client.getClientId());
289
    	IMqttToken conToken = client.connect(conOpt,null, null);
290
    	conToken.waitForCompletion();
291
    	log("Connected");
292
293
    	// Subscribe to the requested topic.
294
    	// Control is returned as soon client has accepted to deliver the subscription. 
295
    	// Use a token to wait until the subscription is in place.
296
    	log("Subscribing to topic \""+topicName+"\" qos "+qos);
297
  
298
    	IMqttToken subToken = client.subscribe(topicName, qos, null, null);
299
    	subToken.waitForCompletion();
300
    	log("Subscribed to topic \""+topicName);
301
302
    	// Continue waiting for messages until the Enter is pressed
303
    	log("Press <Enter> to exit");
304
		try {
305
			System.in.read();
306
		} catch (IOException e) {
307
			//If we can't read we'll just exit
308
		}
309
		
310
    	// Disconnect the client
311
    	// Issue the disconnect and then use the token to wait until 
312
    	// the disconnect completes.
313
    	log("Disconnecting");
314
    	IMqttToken discToken = client.disconnect(null, null);
315
    	discToken.waitForCompletion();
316
    	log("Disconnected");
317
    }
318
319
    /**
320
     * Utility method to handle logging. If 'quietMode' is set, this method does nothing
321
     * @param message the message to log
322
     */
323
    private void log(String message) {
324
    	if (!quietMode) {
325
    		System.out.println(message);
326
    	}
327
    }
328
329
	/****************************************************************/
330
	/* Methods to implement the MqttCallback interface              */
331
	/****************************************************************/
332
    
333
    /**
334
     * @see MqttCallback#connectionLost(Throwable)
335
     */
336
	public void connectionLost(Throwable cause) {
337
		// Called when the connection to the server has been lost.
338
		// An application may choose to implement reconnection
339
		// logic at this point. This sample simply exits.
340
		log("Connection to " + brokerUrl + " lost!" + cause);
341
		System.exit(1);
342
	}
343
344
    /**
345
     * @see MqttCallback#deliveryComplete(IMqttDeliveryToken)
346
     */
347
	public void deliveryComplete(IMqttDeliveryToken token) {
348
		// Called when a message has been delivered to the
349
		// server. The token passed in here is the same one
350
		// that was passed to or returned from the original call to publish.
351
		// This allows applications to perform asynchronous 
352
		// delivery without blocking until delivery completes.
353
		//
354
		// This sample demonstrates asynchronous deliver and 
355
		// uses the token.waitForCompletion() call in the main thread which
356
		// blocks until the delivery has completed. 
357
		// Additionally the deliveryComplete method will be called if 
358
		// the callback is set on the client
359
		// 
360
		// If the connection to the server breaks before delivery has completed
361
		// delivery of a message will complete after the client has re-connected.
362
		// The getPendinTokens method will provide tokens for any messages
363
		// that are still to be delivered.
364
		try {
365
			log("Delivery complete callback: Publish Completed "+token.getMessage());
366
		} catch (Exception ex) {
367
			log("Exception in delivery complete callback"+ex);
368
		}
369
	}
370
371
    /**
372
     * @see MqttCallback#messageArrived(String, MqttMessage)
373
     */
374
	public void messageArrived(String topic, MqttMessage message) throws MqttException {
375
		// Called when a message arrives from the server that matches any
376
		// subscription made by the client		
377
		String time = new Timestamp(System.currentTimeMillis()).toString();	
378
		System.out.println("Time:\t" +time +
379
                           "  Topic:\t" + topic + 
380
                           "  Message:\t" + new String(message.getPayload()) +
381
                           "  QoS:\t" + message.getQos());
382
	}
383
384
	/****************************************************************/
385
	/* End of MqttCallback methods                                  */
386
	/****************************************************************/
387
388
    static void printHelp() {
389
      System.out.println(
390
          "Syntax:\n\n" +
391
              "    Sample [-h] [-a publish|subscribe] [-t <topic>] [-m <message text>]\n" +
392
              "            [-s 0|1|2] -b <hostname|IP address>] [-p <brokerport>] [-i <clientID>]\n\n" +
393
              "    -h  Print this help text and quit\n" +
394
              "    -q  Quiet mode (default is false)\n" +
395
              "    -a  Perform the relevant action (default is publish)\n" +
396
              "    -t  Publish/subscribe to <topic> instead of the default\n" +
397
              "            (publish: \"Sample/Java/v3\", subscribe: \"Sample/#\")\n" +
398
              "    -m  Use <message text> instead of the default\n" +
399
              "            (\"Message from MQTTv3 Java client\")\n" +
400
              "    -s  Use this QoS instead of the default (2)\n" +
401
              "    -b  Use this name/IP address instead of the default (localhost)\n" +
402
              "    -p  Use this port instead of the default (1883)\n\n" +
403
              "    -i  Use this client ID instead of SampleJavaV3_<action>\n" +
404
              "    -c  Connect to the server with a clean session (default is false)\n" +
405
              "     \n\n Security Options \n" +
406
              "     -u Username \n" +
407
              "     -z Password \n" +
408
              "     \n\n SSL Options \n" +
409
              "    -v  SSL enabled; true - (default is false) " +
410
              "    -k  Use this JKS format key store to verify the client\n" +
411
              "    -w  Passpharse to verify certificates in the keys store\n" +
412
              "    -r  Use this JKS format keystore to verify the server\n" +
413
              " If javax.net.ssl properties have been set only the -v flag needs to be set\n" +
414
              "Delimit strings containing spaces with \"\"\n\n" +
415
              "Publishers transmit a single message then disconnect from the server.\n" +
416
              "Subscribers remain connected to the server and receive appropriate\n" +
417
              "messages until <enter> is pressed.\n\n"
418
          );
419
    }
420
421
}
(-)a/org.eclipse.paho.sample.mqttv3app/src/org/eclipse/paho/sample/mqttv3app/package.html (-3 lines)
Lines 1-3 Link Here
1
<body>
2
Contains a set of sample applications that show how to use the MQTT programming interface.
3
</body>
(-)a/pom.xml (+99 lines)
Added Link Here
1
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
3
	<modelVersion>4.0.0</modelVersion>
4
	<groupId>org.eclipse.paho</groupId>
5
	<artifactId>paho-parent</artifactId>
6
	<version>0.9.0</version>
7
	<packaging>pom</packaging>          
8
  <name>Paho :: Administrative Parent</name>
9
  <description>Administrative parent pom for Paho modules</description>   
10
  <url>${paho.url}</url>
11
  <inceptionYear>2011</inceptionYear>
12
  
13
  
14
  <issueManagement>
15
    <system>bugzilla</system>
16
    <url>https://bugs.eclipse.org/bugs/enter_bug.cgi?product=Paho</url>
17
  </issueManagement>
18
  <mailingLists>       
19
    <mailingList>
20
      <name>Paho Developer Mailing List</name>
21
      <archive>http://dev.eclipse.org/mhonarc/lists/paho-dev</archive>
22
      <subscribe>https://dev.eclipse.org/mailman/listinfo/paho-dev</subscribe>
23
      <unsubscribe>https://dev.eclipse.org/mailman/listinfo/paho-dev</unsubscribe>
24
    </mailingList>
25
  </mailingLists>
26
  
27
  <developers>
28
    <developer>
29
      <name>Nick O'Leary</name>
30
      <url>http://knolleary.net/</url>
31
      <organization>IBM</organization>
32
      <organizationUrl>http://www.ibm.com</organizationUrl>
33
    </developer>
34
    <developer>
35
      <name>Dave Locke</name>
36
      <organization>IBM</organization>
37
      <organizationUrl>http://www.ibm.com</organizationUrl>
38
    </developer>
39
  </developers>
40
  
41
  <contributors>
42
    <contributor>
43
      <name>Nicolas Deverge</name>
44
      <email>ndeverge@ekito.fr</email>
45
      <organization>ekito</organization>
46
      <organizationUrl>http://www.ekito.fr</organizationUrl>
47
    </contributor>
48
  </contributors>   
49
  
50
  
51
  <licenses>
52
    <license>
53
      <name>Eclipse Public License - Version 1.0</name>
54
      <url>http://www.eclipse.org/org/documents/epl-v10.php</url>
55
    </license>
56
  </licenses> 
57
  
58
  <scm>
59
    <connection>scm:git://git.eclipse.org/gitroot/paho/org.eclipse.paho.mqtt.java.git</connection>
60
    <developerConnection>scm:git://git.eclipse.org/gitroot/paho/org.eclipse.paho.mqtt.java.git</developerConnection>
61
    <url>http://git.eclipse.org/c/paho/org.eclipse.paho.mqtt.java.git/tree/</url>
62
  </scm>
63
64
  <properties>
65
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
66
    <paho.url>http://www.eclipse.org/paho</paho.url>
67
  </properties>
68
69
	<build>
70
		<plugins>
71
			<plugin>
72
				<groupId>org.apache.maven.plugins</groupId>
73
				<artifactId>maven-compiler-plugin</artifactId>
74
				<version>2.3.2</version>
75
				<configuration>
76
					<source>1.2</source>
77
					<target>1.2</target>
78
					<compilerVersion>1.5</compilerVersion>
79
				</configuration>
80
			</plugin>
81
		</plugins>
82
	</build>
83
	
84
    <modules>
85
        <module>org.eclipse.paho.client.mqttv3</module>
86
        <module>org.eclipse.paho.client.mqttv3.internal.traceformat</module>
87
        <module>org.eclipse.paho.sample.mqttv3app</module>
88
    </modules>
89
	
90
	<dependencyManagement>
91
      <dependencies>
92
        <dependency>
93
          <groupId>org.eclipse.paho</groupId>
94
          <artifactId>jpaho-mqtt-client</artifactId>
95
          <version>${project.version}</version>
96
        </dependency>
97
      </dependencies>
98
    </dependencyManagement>
99
</project>

Return to bug 382471