diff --git a/.travis.yml b/.travis.yml
index dff5f3a5d..9e2aefbbd 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1 +1,23 @@
+sudo: true
language: java
+
+matrix:
+ include:
+ - os: linux
+ dist: trusty
+ sudo: required
+# - os: osx
+
+sudo: required
+
+before_install:
+ - ./travis-install.sh
+
+script: mvn --projects org.eclipse.paho.client.mqttv3,org.eclipse.paho.client.mqttv3.test test -Dtest.server_ssl_port=18885 -Dtest.server_uri=tcp://localhost:1883 -Dtest.server_websocket_uri=ws://localhost:8080 -B
+
+addons:
+ apt:
+ sources:
+ - sourceline: 'ppa:mosquitto-dev/mosquitto-ppa'
+ packages:
+ - mosquitto
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index fb1acda28..f0ffef9ce 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -20,8 +20,6 @@ Please read the [Eclipse Foundation policy on accepting contributions via Git](h
## Contributing a change
-## Contributing a change
-
1. [Fork the repository on GitHub](https://github.com/eclipse/paho.mqtt.java/fork)
2. Clone the forked repository onto your computer: ``` git clone https://github.com//paho.mqtt.java.git ```
3. Create a new branch from the latest ```develop``` branch with ```git checkout -b YOUR_BRANCH_NAME origin/develop```
diff --git a/README.md b/README.md
index 180e483d2..fc090ddf8 100644
--- a/README.md
+++ b/README.md
@@ -32,7 +32,7 @@ Add the repository definition and the dependency definition shown below to your
Replace %REPOURL% with either ``` https://repo.eclipse.org/content/repositories/paho-releases/ ``` for the official releases, or ``` https://repo.eclipse.org/content/repositories/paho-snapshots/ ``` for the nightly snapshots. Replace %VERSION% with the level required .
-The latest release version is ```1.1.1``` and the current snapshot version is ```1.1.2-SNAPSHOT```.
+The latest release version is ```1.2.0``` and the current snapshot version is ```1.2.1-SNAPSHOT```.
```
diff --git a/org.eclipse.paho.client.eclipse.feature/feature.xml b/org.eclipse.paho.client.eclipse.feature/feature.xml
index 4a8ae7c81..e80ef9205 100644
--- a/org.eclipse.paho.client.eclipse.feature/feature.xml
+++ b/org.eclipse.paho.client.eclipse.feature/feature.xml
@@ -3,8 +3,7 @@
id="org.eclipse.paho.client.eclipse.feature"
label="%featureName"
provider-name="%providerName"
- version="1.1.1">
-
+ version="1.2.0">
%description
diff --git a/org.eclipse.paho.client.eclipse.feature/pom.xml b/org.eclipse.paho.client.eclipse.feature/pom.xml
index dadd3704b..1b088ad4b 100644
--- a/org.eclipse.paho.client.eclipse.feature/pom.xml
+++ b/org.eclipse.paho.client.eclipse.feature/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.paho
java-parent
- 1.1.1
+ 1.2.0
org.eclipse.paho.client.eclipse.feature
diff --git a/org.eclipse.paho.client.eclipse.view/META-INF/MANIFEST.MF b/org.eclipse.paho.client.eclipse.view/META-INF/MANIFEST.MF
index 4f2b727f8..15d93459b 100644
--- a/org.eclipse.paho.client.eclipse.view/META-INF/MANIFEST.MF
+++ b/org.eclipse.paho.client.eclipse.view/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %bundle.name
Bundle-SymbolicName: org.eclipse.paho.client.eclipse.view;singleton:=true
-Bundle-Version: 1.1.1
+Bundle-Version: 1.2.0
Bundle-Activator: org.eclipse.paho.client.eclipse.view.Activator
Bundle-Vendor: %bundle.provider
Bundle-Localization: plugin
diff --git a/org.eclipse.paho.client.eclipse.view/pom.xml b/org.eclipse.paho.client.eclipse.view/pom.xml
index 38d51e7d8..f91605c37 100644
--- a/org.eclipse.paho.client.eclipse.view/pom.xml
+++ b/org.eclipse.paho.client.eclipse.view/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.paho
java-parent
- 1.1.1
+ 1.2.0
org.eclipse.paho.client.eclipse.view
diff --git a/org.eclipse.paho.client.eclipse.view/src/org/eclipse/paho/client/eclipse/view/Activator.java b/org.eclipse.paho.client.eclipse.view/src/org/eclipse/paho/client/eclipse/view/Activator.java
index 5c8a08c49..bf3d7ad74 100644
--- a/org.eclipse.paho.client.eclipse.view/src/org/eclipse/paho/client/eclipse/view/Activator.java
+++ b/org.eclipse.paho.client.eclipse.view/src/org/eclipse/paho/client/eclipse/view/Activator.java
@@ -1,10 +1,14 @@
-/*
+/*******************************************************************************
* Copyright (c) 2012 Eurotech Inc. All rights reserved.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Chad Kienle
diff --git a/org.eclipse.paho.client.eclipse.view/src/org/eclipse/paho/client/eclipse/view/ClientConstants.java b/org.eclipse.paho.client.eclipse.view/src/org/eclipse/paho/client/eclipse/view/ClientConstants.java
index 0da7f4fdd..c684c578b 100644
--- a/org.eclipse.paho.client.eclipse.view/src/org/eclipse/paho/client/eclipse/view/ClientConstants.java
+++ b/org.eclipse.paho.client.eclipse.view/src/org/eclipse/paho/client/eclipse/view/ClientConstants.java
@@ -1,10 +1,14 @@
-/*
+/*******************************************************************************
* Copyright (c) 2012 Eurotech Inc. All rights reserved.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Chad Kienle
diff --git a/org.eclipse.paho.client.eclipse.view/src/org/eclipse/paho/client/eclipse/view/MqttClientView.java b/org.eclipse.paho.client.eclipse.view/src/org/eclipse/paho/client/eclipse/view/MqttClientView.java
index c96e27be4..990a4571a 100644
--- a/org.eclipse.paho.client.eclipse.view/src/org/eclipse/paho/client/eclipse/view/MqttClientView.java
+++ b/org.eclipse.paho.client.eclipse.view/src/org/eclipse/paho/client/eclipse/view/MqttClientView.java
@@ -1,10 +1,14 @@
-/*
+/*******************************************************************************
* Copyright (c) 2012 Eurotech Inc. All rights reserved.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Chad Kienle
diff --git a/org.eclipse.paho.client.mqttv3.internal.traceformat/build.xml b/org.eclipse.paho.client.mqttv3.internal.traceformat/build.xml
index 8cc6f82e6..7d9c42cd6 100644
--- a/org.eclipse.paho.client.mqttv3.internal.traceformat/build.xml
+++ b/org.eclipse.paho.client.mqttv3.internal.traceformat/build.xml
@@ -7,8 +7,8 @@
-
-
+
+
diff --git a/org.eclipse.paho.client.mqttv3.repository/category.xml b/org.eclipse.paho.client.mqttv3.repository/category.xml
index 19130aac9..459571525 100644
--- a/org.eclipse.paho.client.mqttv3.repository/category.xml
+++ b/org.eclipse.paho.client.mqttv3.repository/category.xml
@@ -1,9 +1,9 @@
-
+
-
+
diff --git a/org.eclipse.paho.client.mqttv3.repository/pom.xml b/org.eclipse.paho.client.mqttv3.repository/pom.xml
index 32262b47e..bb5dbb6e4 100644
--- a/org.eclipse.paho.client.mqttv3.repository/pom.xml
+++ b/org.eclipse.paho.client.mqttv3.repository/pom.xml
@@ -3,7 +3,7 @@
org.eclipse.paho
java-parent
- 1.1.1
+ 1.2.0
org.eclipse.paho.client.mqttv3.repository
diff --git a/org.eclipse.paho.client.mqttv3.test/.classpath b/org.eclipse.paho.client.mqttv3.test/.classpath
index 6b830f36c..69f1458a4 100644
--- a/org.eclipse.paho.client.mqttv3.test/.classpath
+++ b/org.eclipse.paho.client.mqttv3.test/.classpath
@@ -1,29 +1,33 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org.eclipse.paho.client.mqttv3.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.paho.client.mqttv3.test/.settings/org.eclipse.jdt.core.prefs
index 107056a36..62492222a 100644
--- a/org.eclipse.paho.client.mqttv3.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.paho.client.mqttv3.test/.settings/org.eclipse.jdt.core.prefs
@@ -1,12 +1,12 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
-org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.compiler.source=1.7
diff --git a/org.eclipse.paho.client.mqttv3.test/pom.xml b/org.eclipse.paho.client.mqttv3.test/pom.xml
index f012e2342..abc1ac64f 100644
--- a/org.eclipse.paho.client.mqttv3.test/pom.xml
+++ b/org.eclipse.paho.client.mqttv3.test/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.paho
java-parent
- 1.1.1
+ 1.2.0
org.eclipse.paho.client.mqttv3.test
@@ -47,14 +47,15 @@
maven-surefire-plugin
2.15
- false
- 1
${test.server_uri}
+ ${test.server_ssl_port}
+ ${test.server_websocket_uri}
${test.exclude}
+ alphabetical
@@ -64,7 +65,7 @@
org.eclipse.paho
org.eclipse.paho.client.mqttv3
- 1.1.1
+ 1.2.0
diff --git a/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/BasicTest.java b/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/BasicTest.java
index 333bc8216..f5c851b5c 100644
--- a/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/BasicTest.java
+++ b/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/BasicTest.java
@@ -95,7 +95,7 @@ public static void tearDownAfterClass() throws Exception {
/**
* @throws Exception
*/
- @Test(timeout=10000)
+ @Test
public void testConnect() throws Exception {
String methodName = Utility.getMethodName();
LoggingUtilities.banner(log, cclass, methodName);
@@ -141,7 +141,7 @@ public void testConnect() throws Exception {
/**
* @throws Exception
*/
- @Test(timeout=10000)
+ @Test
public void testHAConnect() throws Exception {
String methodName = Utility.getMethodName();
LoggingUtilities.banner(log, cclass, methodName);
@@ -191,7 +191,7 @@ public void testHAConnect() throws Exception {
/**
* @throws Exception
*/
- @Test(timeout=10000)
+ @Test
public void testPubSub() throws Exception {
String methodName = Utility.getMethodName();
LoggingUtilities.banner(log, cclass, methodName);
@@ -241,7 +241,7 @@ public void testPubSub() throws Exception {
/**
* @throws Exception
*/
- @Test(timeout=10000)
+ @Test
public void testMsgProperties() throws Exception {
String methodName = Utility.getMethodName();
LoggingUtilities.banner(log, cclass, methodName);
@@ -309,7 +309,7 @@ public void testMsgProperties() throws Exception {
/**
* @throws Exception
*/
- @Test(timeout=10000)
+ @Test
public void testConnOptDefaults() throws Exception {
String methodName = Utility.getMethodName();
LoggingUtilities.banner(log, cclass, methodName);
diff --git a/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/Issue370Test.java b/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/Issue370Test.java
new file mode 100644
index 000000000..3e529b439
--- /dev/null
+++ b/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/Issue370Test.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Red Hat Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ * Red Hat Inc - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.paho.client.mqttv3.test;
+
+import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
+import org.eclipse.paho.client.mqttv3.MqttException;
+import org.junit.Test;
+
+/**
+ * This is a reproducer for issue #370
+ */
+public class Issue370Test {
+ @Test
+ public void noOpenClose() throws MqttException {
+ final MqttAsyncClient client = new MqttAsyncClient("tcp://localhost", "foo-bar");
+ client.close();
+ }
+}
diff --git a/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/LiveTakeOverTest.java b/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/LiveTakeOverTest.java
index fba9d996e..8f2f65d4b 100644
--- a/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/LiveTakeOverTest.java
+++ b/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/LiveTakeOverTest.java
@@ -257,7 +257,7 @@ void repeatedlyPub() {
}
byte[] payload = ("Message payload " + getClass().getName() + ".publish" + (i++)).getBytes();
MqttTopic mqttTopic = mqttClient.getTopic(FirstSubTopicString);
- log.info("Publishing to..." + FirstSubTopicString);
+ log.fine("Publishing to..." + FirstSubTopicString);
mqttTopic.publish(payload, 1, false);
}
@@ -267,6 +267,7 @@ void repeatedlyPub() {
// Its likely the publish rate is too high i.e. inflight window is full
}
}
+ log.info("Sent at least " + i + " messages.");
}
public void run() {
diff --git a/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/ModelTestCase.java b/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/ModelTestCase.java
index 23cf0660c..d70722068 100644
--- a/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/ModelTestCase.java
+++ b/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/ModelTestCase.java
@@ -167,7 +167,7 @@ public static void tearDownAfterClass() throws Exception {
/**
* @throws Exception
*/
- @Test(timeout=150000)
+ //@Test(timeout=150000)
public void testRunModel() throws Exception {
log.info("Test core operations and parameters by random selection");
log.info("See file: " + logFilename + " for details of selected test sequence");
diff --git a/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/PerSubscriptionMessageHandlerTest.java b/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/PerSubscriptionMessageHandlerTest.java
index 70721a6d3..fe02d8eeb 100644
--- a/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/PerSubscriptionMessageHandlerTest.java
+++ b/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/PerSubscriptionMessageHandlerTest.java
@@ -136,7 +136,7 @@ public void messageArrived(String topic, MqttMessage message) throws Exception {
*
* @throws Exception
*/
- @Test(timeout=10000)
+ @Test
public void testSyncSubs1() throws Exception {
final String methodName = Utility.getMethodName();
LoggingUtilities.banner(log, cclass, methodName);
@@ -166,7 +166,7 @@ public void testSyncSubs1() throws Exception {
}
- @Test(timeout=10000)
+ @Test
public void testAsyncSubs1() throws Exception {
final String methodName = Utility.getMethodName();
LoggingUtilities.banner(log, cclass, methodName);
@@ -204,7 +204,7 @@ public void testAsyncSubs1() throws Exception {
* Check handlers still exist after reconnection non-cleansession
*/
- @Test(timeout=10000)
+ @Test
public void testSyncCleanSessionFalse() throws Exception {
final String methodName = Utility.getMethodName();
LoggingUtilities.banner(log, cclass, methodName);
@@ -256,7 +256,7 @@ public void testSyncCleanSessionFalse() throws Exception {
mqttClient.close();
}
- @Test(timeout=10000)
+ @Test
public void testAsyncCleanSessionFalse() throws Exception {
final String methodName = Utility.getMethodName();
LoggingUtilities.banner(log, cclass, methodName);
diff --git a/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/SSLSessionResumptionTest.java b/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/SSLSessionResumptionTest.java
index 25a7736b8..b3f9d196b 100644
--- a/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/SSLSessionResumptionTest.java
+++ b/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/SSLSessionResumptionTest.java
@@ -1,10 +1,14 @@
/**
* Copyright (c) 2011, 2014 Eurotech and/or its affiliates
*
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Cristiano De Alti - Eurotech (Initial contribution)
@@ -59,7 +63,7 @@ public static void setUpBeforeClass() throws Exception {
try {
String methodName = Utility.getMethodName();
LoggingUtilities.banner(log, cclass, methodName);
- serverURI = "ssl://" + TestProperties.getServerURI().getHost();
+ serverURI = "ssl://" + TestProperties.getServerURI().getHost() + ":" +TestProperties.getServerSSLPort();
serverHost = TestProperties.getServerURI().getHost();
serverPort = TestProperties.getServerSSLPort();
@@ -80,7 +84,7 @@ public static void setUpBeforeClass() throws Exception {
*/
@Test(timeout=30000)
public void testSSLSessionInvalidated() throws Exception {
- // System.setProperty("javax.net.debug", "all");
+ //System.setProperty("javax.net.debug", "all");
SSLSocketFactory factory = getSocketFactory();
diff --git a/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/SendReceiveAsyncTest.java b/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/SendReceiveAsyncTest.java
index 50f9ece59..2ec554a2e 100644
--- a/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/SendReceiveAsyncTest.java
+++ b/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/SendReceiveAsyncTest.java
@@ -95,7 +95,7 @@ public static void tearDownAfterClass() throws Exception {
*
* @throws Exception
*/
- @Test(timeout=10000)
+ @Test
public void testConnect() throws Exception {
final String methodName = Utility.getMethodName();
LoggingUtilities.banner(log, cclass, methodName);
@@ -142,7 +142,7 @@ public void testConnect() throws Exception {
*
* @throws Exception
*/
- @Test(timeout=10000)
+ @Test
public void testRemoteConnect() throws Exception {
final String methodName = Utility.getMethodName();
LoggingUtilities.banner(log, cclass, methodName);
@@ -214,7 +214,7 @@ public void testRemoteConnect() throws Exception {
/**
* Test client pubSub using very large messages
*/
- @Test(timeout=10000)
+ @Test
public void testLargeMessage() {
final String methodName = Utility.getMethodName();
LoggingUtilities.banner(log, cclass, methodName);
@@ -291,7 +291,7 @@ public void testLargeMessage() {
/**
* Multiple publishers and subscribers.
*/
- @Test(timeout=20000)
+ @Test
public void testMultipleClients() {
final String methodName = Utility.getMethodName();
LoggingUtilities.banner(log, cclass, methodName);
@@ -386,7 +386,7 @@ public void testMultipleClients() {
* Test the behaviour of the cleanStart flag, used to clean up before
* re-connecting.
*/
- @Test(timeout=10000)
+ @Test
public void testCleanStart() throws Exception {
final String methodName = Utility.getMethodName();
LoggingUtilities.banner(log, cclass, methodName);
@@ -532,7 +532,7 @@ public void testCleanStart() throws Exception {
*
* Since no other activity (messages from the client to the broker) is generated, the broker disconnects the client.
*/
- @Test(timeout=300000)
+ @Test
public void testVeryLargeMessageWithShortKeepAlive() {
final String methodName = Utility.getMethodName();
LoggingUtilities.banner(log, cclass, methodName);
@@ -601,7 +601,7 @@ public void testVeryLargeMessageWithShortKeepAlive() {
* Test the behavior of the connection timeout when connecting to a non MQTT server.
* i.e. ssh port 22
*/
- @Test(timeout=60000)
+ @Test
public void testConnectTimeout() throws Exception {
final String methodName = Utility.getMethodName();
LoggingUtilities.banner(log, cclass, methodName);
diff --git a/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/SendReceiveTest.java b/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/SendReceiveTest.java
index 354d42036..f14b04ef8 100644
--- a/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/SendReceiveTest.java
+++ b/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/SendReceiveTest.java
@@ -93,7 +93,7 @@ public static void tearDownAfterClass() throws Exception {
* service
* @throws Exception
*/
- @Test(timeout=10000)
+ @Test
public void testConnect() throws Exception {
final String methodName = Utility.getMethodName();
LoggingUtilities.banner(log, cclass, methodName);
@@ -129,7 +129,7 @@ public void testConnect() throws Exception {
* Test connection using a remote host name for the local host.
* @throws Exception
*/
- @Test(timeout=10000)
+ @Test
public void testRemoteConnect() throws Exception {
final String methodName = Utility.getMethodName();
LoggingUtilities.banner(log, cclass, methodName);
@@ -184,7 +184,7 @@ public void testRemoteConnect() throws Exception {
/**
* Test client pubSub using largish messages
*/
- @Test(timeout=10000)
+ @Test
public void testLargeMessage() {
final String methodName = Utility.getMethodName();
LoggingUtilities.banner(log, cclass, methodName);
@@ -245,7 +245,7 @@ public void testLargeMessage() {
/**
* Test that QOS values are preserved between MQTT publishers and subscribers.
*/
- @Test(timeout=10000)
+ @Test
public void testQoSPreserved() {
final String methodName = Utility.getMethodName();
LoggingUtilities.banner(log, cclass, methodName);
@@ -305,7 +305,7 @@ public void testQoSPreserved() {
* Multiple publishers and subscribers.
* @throws Exception
*/
- @Test(timeout=30000)
+ @Test
public void testMultipleClients() throws Exception {
final String methodName = Utility.getMethodName();
LoggingUtilities.banner(log, cclass, methodName);
@@ -388,7 +388,7 @@ public void testMultipleClients() throws Exception {
* Test the behaviour of the cleanStart flag, used to clean up before re-connecting.
* @throws Exception
*/
- @Test(timeout=10000)
+ @Test
public void testCleanStart() throws Exception {
final String methodName = Utility.getMethodName();
LoggingUtilities.banner(log, cclass, methodName);
diff --git a/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/WebSocketTest.java b/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/WebSocketTest.java
index f0c4e9f27..be3142ab7 100644
--- a/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/WebSocketTest.java
+++ b/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/WebSocketTest.java
@@ -94,7 +94,7 @@ public static void tearDownAfterClass() throws Exception {
/**
* @throws Exception
*/
- @Test(timeout=10000)
+ @Test()
public void testWebSocketConnect() throws Exception {
String methodName = Utility.getMethodName();
LoggingUtilities.banner(log, cclass, methodName);
@@ -182,7 +182,10 @@ public void testWebSocketPubSub() throws Exception {
log.info("Disconnecting...");
client.disconnect();
- }
+ } catch (MqttException exception) {
+ log.log(Level.SEVERE, "caught exception:", exception);
+ Assert.fail("Unexpected exception: " + exception);
+ }
finally {
if (client != null) {
if(client.isConnected()){
@@ -239,9 +242,10 @@ public void largePayloadTest() throws Exception{
log.info("Disconnecting...");
client.disconnect();
log.info("Disconnected...");
- } catch (Exception e){
- e.printStackTrace();
- } finally {
+ } catch (MqttException exception) {
+ log.log(Level.SEVERE, "caught exception:", exception);
+ Assert.fail("Unexpected exception: " + exception);
+ } finally {
if (client != null) {
if(client.isConnected()){
client.disconnectForcibly();
@@ -274,9 +278,10 @@ public void testBasicAuth() throws Exception {
client = clientFactory.createMqttClient(serverURIWithUserInfo, clientId);
client.connect();
client.disconnect();
- } catch (Exception e){
- e.printStackTrace();
- } finally {
+ }catch (MqttException exception) {
+ log.log(Level.SEVERE, "caught exception:", exception);
+ Assert.fail("Unexpected exception: " + exception);
+ } finally {
if (client != null) {
if(client.isConnected()){
client.disconnectForcibly();
diff --git a/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/automaticReconnect/OfflineBufferingTest.java b/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/automaticReconnect/OfflineBufferingTest.java
index 11a820f46..803bad0dd 100644
--- a/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/automaticReconnect/OfflineBufferingTest.java
+++ b/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/automaticReconnect/OfflineBufferingTest.java
@@ -50,7 +50,7 @@ public static void setUpBeforeClass() throws Exception {
topicPrefix = "OfflineBufferingTest-" + UUID.randomUUID().toString() + "-";
// Use 0 for the first time.
- proxy = new ConnectionManipulationProxyServer(serverURI.getHost(), serverURI.getPort(), 0);
+ proxy = new ConnectionManipulationProxyServer(serverURI.getHost(), serverURI.getPort(), 2883);
proxy.startProxy();
while (!proxy.isPortSet()) {
Thread.sleep(0);
@@ -68,7 +68,7 @@ public static void setUpBeforeClass() throws Exception {
* disconnected state and is then delivered once the client has reconnected
* automatically.
*/
- @Test(timeout=60000)
+ @Test
public void testSingleMessageBufferAndDeliver() throws Exception {
String methodName = Utility.getMethodName();
LoggingUtilities.banner(log, cclass, methodName);
@@ -139,7 +139,7 @@ public void testSingleMessageBufferAndDeliver() throws Exception {
* disconnected state and that they are all then delivered once the client
* has connected automatically.
*/
- @Test(timeout=60000)
+ @Test
public void testManyMessageBufferAndDeliver() throws Exception {
String methodName = Utility.getMethodName();
LoggingUtilities.banner(log, cclass, methodName);
@@ -231,7 +231,7 @@ public void testManyMessageBufferAndDeliver() throws Exception {
* Tests that the buffer correctly handles messages being buffered when the
* buffer is full and deleteOldestBufferedMessage is set to true.
*/
- @Test(timeout=60000)
+ @Test
public void testDeleteOldestBufferedMessages() throws Exception {
String methodName = Utility.getMethodName();
LoggingUtilities.banner(log, cclass, methodName);
@@ -288,7 +288,7 @@ public void testDeleteOldestBufferedMessages() throws Exception {
* Tests that A message cannot be buffered when the buffer is full and
* deleteOldestBufferedMessage is set to false.
*/
- @Test(timeout=60000)
+ @Test
public void testNoDeleteOldestBufferedMessages() throws Exception {
String methodName = Utility.getMethodName();
LoggingUtilities.banner(log, cclass, methodName);
@@ -347,7 +347,7 @@ public void testNoDeleteOldestBufferedMessages() throws Exception {
* Tests that if enabled, buffered messages are persisted to the persistence
* layer
*/
- @Test(timeout=60000)
+ @Test
public void testPersistBufferedMessages() throws Exception {
String methodName = Utility.getMethodName();
LoggingUtilities.banner(log, cclass, methodName);
@@ -407,7 +407,7 @@ public void testPersistBufferedMessages() throws Exception {
* Tests that persisted buffered messages are published correctly when the
* client connects for the first time and are un persisted.
*/
- @Test(timeout=60000)
+ @Test
public void testUnPersistBufferedMessagesOnNewClient() throws Exception {
String methodName = Utility.getMethodName();
LoggingUtilities.banner(log, cclass, methodName);
diff --git a/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/client/MqttClientPaho.java b/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/client/MqttClientPaho.java
index 60c9acbea..262871d42 100644
--- a/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/client/MqttClientPaho.java
+++ b/org.eclipse.paho.client.mqttv3.test/src/test/java/org/eclipse/paho/client/mqttv3/test/client/MqttClientPaho.java
@@ -17,6 +17,7 @@
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttClientPersistence;
import org.eclipse.paho.client.mqttv3.MqttException;
+import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
/**
@@ -30,7 +31,7 @@ public class MqttClientPaho extends MqttClient {
* @throws MqttException
*/
public MqttClientPaho(String serverURI, String clientId) throws MqttException {
- super(serverURI, clientId);
+ super(serverURI, clientId, new MemoryPersistence());
}
/**
diff --git a/org.eclipse.paho.client.mqttv3.test/src/test/resources/test.properties b/org.eclipse.paho.client.mqttv3.test/src/test/resources/test.properties
index 833c07635..7688045b5 100644
--- a/org.eclipse.paho.client.mqttv3.test/src/test/resources/test.properties
+++ b/org.eclipse.paho.client.mqttv3.test/src/test/resources/test.properties
@@ -1,12 +1,12 @@
# This is the server URI which will be set in the constructor of an MQTT Client
# The default is "tcp://:1883" with expressed in IPV4 dotted decimal notation
-SERVER_URI=tcp://iot.eclipse.org:1883
+SERVER_URI=tcp://localhost:1883
CLIENT_KEY_STORE=clientkeystore.jks
CLIENT_KEY_STORE_PASSWORD=password
CLIENT_TRUST_STORE=clientkeystore.jks
SERVER_SSL_PORT=18884
-SERVER_WEBSOCKET_URI=wss://iot.eclipse.org:443
-SERVER_SSL_PORT=8883
+SERVER_WEBSOCKET_URI=ws://localhost:8080
+
# The list of server URIs which may be set in the MQTT ConnectOptions for an HA testcase.
# There is no default value
diff --git a/org.eclipse.paho.client.mqttv3/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.paho.client.mqttv3/.settings/org.eclipse.jdt.core.prefs
index 008a2612d..9c4403fe9 100644
--- a/org.eclipse.paho.client.mqttv3/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.paho.client.mqttv3/.settings/org.eclipse.jdt.core.prefs
@@ -1,12 +1,13 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.4
+org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
-org.eclipse.jdt.core.compiler.problem.assertIdentifier=warning
-org.eclipse.jdt.core.compiler.problem.enumIdentifier=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
-org.eclipse.jdt.core.compiler.source=1.3
+org.eclipse.jdt.core.compiler.source=1.7
diff --git a/org.eclipse.paho.client.mqttv3/META-INF/MANIFEST.MF b/org.eclipse.paho.client.mqttv3/META-INF/MANIFEST.MF
index ea76130b1..28138578c 100644
--- a/org.eclipse.paho.client.mqttv3/META-INF/MANIFEST.MF
+++ b/org.eclipse.paho.client.mqttv3/META-INF/MANIFEST.MF
@@ -2,12 +2,12 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %bundle.name
Bundle-SymbolicName: org.eclipse.paho.client.mqttv3
-Bundle-Version: 1.1.1
+Bundle-Version: 1.2.0
Bundle-Localization: bundle
-Export-Package: org.eclipse.paho.client.mqttv3;version="1.1.1",
- org.eclipse.paho.client.mqttv3.logging;version="1.1.1",
- org.eclipse.paho.client.mqttv3.persist;version="1.1.1",
- org.eclipse.paho.client.mqttv3.util;version="1.1.1"
+Export-Package: org.eclipse.paho.client.mqttv3;version="1.2.0",
+ org.eclipse.paho.client.mqttv3.logging;version="1.2.0",
+ org.eclipse.paho.client.mqttv3.persist;version="1.2.0",
+ org.eclipse.paho.client.mqttv3.util;version="1.2.0"
Bundle-Vendor: %bundle.provider
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: J2SE-1.4
diff --git a/org.eclipse.paho.client.mqttv3/build.xml b/org.eclipse.paho.client.mqttv3/build.xml
index 3c5dd5f04..f13f8b949 100644
--- a/org.eclipse.paho.client.mqttv3/build.xml
+++ b/org.eclipse.paho.client.mqttv3/build.xml
@@ -16,8 +16,8 @@
-
-
+
+
diff --git a/org.eclipse.paho.client.mqttv3/pom.xml b/org.eclipse.paho.client.mqttv3/pom.xml
index 6cb734d6e..28fc4824e 100644
--- a/org.eclipse.paho.client.mqttv3/pom.xml
+++ b/org.eclipse.paho.client.mqttv3/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.paho
java-parent
- 1.1.1
+ 1.2.0
org.eclipse.paho.client.mqttv3
diff --git a/org.eclipse.paho.client.mqttv3/src/main/java-templates/org/eclipse/paho/client/mqttv3/internal/ClientComms.java b/org.eclipse.paho.client.mqttv3/src/main/java-templates/org/eclipse/paho/client/mqttv3/internal/ClientComms.java
index 9c0074edf..542d9df95 100644
--- a/org.eclipse.paho.client.mqttv3/src/main/java-templates/org/eclipse/paho/client/mqttv3/internal/ClientComms.java
+++ b/org.eclipse.paho.client.mqttv3/src/main/java-templates/org/eclipse/paho/client/mqttv3/internal/ClientComms.java
@@ -22,6 +22,8 @@
import java.util.Enumeration;
import java.util.Properties;
import java.util.Vector;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
import org.eclipse.paho.client.mqttv3.BufferedMessage;
import org.eclipse.paho.client.mqttv3.IMqttActionListener;
@@ -80,22 +82,26 @@ public class ClientComms {
private boolean closePending = false;
private boolean resting = false;
private DisconnectedMessageBuffer disconnectedMessageBuffer;
-
+
+ private ExecutorService executorService;
+
/**
* Creates a new ClientComms object, using the specified module to handle
* the network calls.
* @param client The {@link IMqttAsyncClient}
* @param persistence the {@link MqttClientPersistence} layer.
* @param pingSender the {@link MqttPingSender}
+ * @param executorService the {@link ExecutorService}
* @throws MqttException if an exception occurs whilst communicating with the server
*/
- public ClientComms(IMqttAsyncClient client, MqttClientPersistence persistence, MqttPingSender pingSender) throws MqttException {
+ public ClientComms(IMqttAsyncClient client, MqttClientPersistence persistence, MqttPingSender pingSender, ExecutorService executorService) throws MqttException {
this.conState = DISCONNECTED;
this.client = client;
this.persistence = persistence;
this.pingSender = pingSender;
this.pingSender.init(this);
-
+ this.executorService = executorService;
+
this.tokenStore = new CommsTokenStore(getClient().getClientId());
this.callback = new CommsCallback(this);
this.clientState = new ClientState(persistence, tokenStore, this.callback, this, pingSender);
@@ -108,6 +114,22 @@ CommsReceiver getReceiver() {
return receiver;
}
+ private void shutdownExecutorService() {
+ String methodName = "shutdownExecutorService";
+ executorService.shutdown();
+ try {
+ if (!executorService.awaitTermination(1, TimeUnit.SECONDS)) {
+ executorService.shutdownNow();
+ if (!executorService.awaitTermination(1, TimeUnit.SECONDS)) {
+ log.fine(CLASS_NAME, methodName, "executorService did not terminate");
+ }
+ }
+ } catch (InterruptedException ie) {
+ executorService.shutdownNow();
+ Thread.currentThread().interrupt();
+ }
+ }
+
/**
* Sends a message to the server. Does not check if connected this validation must be done
* by invoking routines.
@@ -164,8 +186,8 @@ public void sendNoWait(MqttWireMessage message, MqttToken token) throws MqttExce
} else {
this.internalSend(message, token);
}
- } else if(disconnectedMessageBuffer != null && isResting()){
- //@TRACE 508=Client Resting, Offline Buffer available. Adding message to buffer. message={0}
+ } else if(disconnectedMessageBuffer != null) {
+ //@TRACE 508=Offline Buffer available. Adding message to buffer. message={0}
log.fine(CLASS_NAME, methodName, "508", new Object[] {message.getKey()});
if(disconnectedMessageBuffer.isPersistBuffer()){
this.clientState.persistBufferedMessage(message);
@@ -185,12 +207,12 @@ public void sendNoWait(MqttWireMessage message, MqttToken token) throws MqttExce
* store which normally survives a disconnect.
* @throws MqttException if not disconnected
*/
- public void close() throws MqttException {
+ public void close(boolean force) throws MqttException {
final String methodName = "close";
synchronized (conLock) {
if (!isClosed()) {
- // Must be disconnected before close can take place
- if (!isDisconnected()) {
+ // Must be disconnected before close can take place or if we are being forced
+ if (!isDisconnected() || force) {
//@TRACE 224=failed: not disconnected
log.fine(CLASS_NAME, methodName, "224");
@@ -205,7 +227,7 @@ public void close() throws MqttException {
}
conState = CLOSED;
-
+ shutdownExecutorService();
// ShutdownConnection has already cleaned most things
clientState.close();
clientState = null;
@@ -254,7 +276,7 @@ public void connect(MqttConnectOptions options, MqttToken token) throws MqttExce
this.clientState.setMaxInflight(conOptions.getMaxInflight());
tokenStore.open();
- ConnectBG conbg = new ConnectBG(this, token, connect);
+ ConnectBG conbg = new ConnectBG(this, token, connect, executorService);
conbg.start();
}
else {
@@ -405,7 +427,7 @@ public void shutdownConnection(MqttToken token, MqttException reason) {
synchronized(conLock) {
if (closePending) {
try {
- close();
+ close(true);
} catch (Exception e) { // ignore any errors as closing
}
}
@@ -479,7 +501,7 @@ public void disconnect(MqttDisconnect disconnect, long quiesceTimeout, MqttToken
//@TRACE 218=state=DISCONNECTING
log.fine(CLASS_NAME,methodName,"218");
conState = DISCONNECTING;
- DisconnectBG discbg = new DisconnectBG(disconnect,quiesceTimeout,token);
+ DisconnectBG discbg = new DisconnectBG(disconnect,quiesceTimeout,token, executorService);
discbg.start();
}
}
@@ -497,7 +519,9 @@ public void disconnectForcibly(long quiesceTimeout, long disconnectTimeout) thro
*/
public void disconnectForcibly(long quiesceTimeout, long disconnectTimeout, boolean sendDisconnectPacket) throws MqttException {
// Allow current inbound and outbound work to complete
- clientState.quiesce(quiesceTimeout);
+ if (clientState != null) {
+ clientState.quiesce(quiesceTimeout);
+ }
MqttToken token = new MqttToken(client.getClientId());
try {
// Send disconnect packet
@@ -636,22 +660,23 @@ public Properties getDebug() {
// the socket could take time to create.
private class ConnectBG implements Runnable {
ClientComms clientComms = null;
- Thread cBg = null;
MqttToken conToken;
MqttConnect conPacket;
+ private String threadName;
- ConnectBG(ClientComms cc, MqttToken cToken, MqttConnect cPacket) {
+ ConnectBG(ClientComms cc, MqttToken cToken, MqttConnect cPacket, ExecutorService executorService) {
clientComms = cc;
conToken = cToken;
conPacket = cPacket;
- cBg = new Thread(this, "MQTT Con: "+getClient().getClientId());
+ threadName = "MQTT Con: "+getClient().getClientId();
}
void start() {
- cBg.start();
+ executorService.execute(this);
}
public void run() {
+ Thread.currentThread().setName(threadName);
final String methodName = "connectBG:run";
MqttException mqttEx = null;
//@TRACE 220=>
@@ -675,10 +700,10 @@ public void run() {
NetworkModule networkModule = networkModules[networkModuleIndex];
networkModule.start();
receiver = new CommsReceiver(clientComms, clientState, tokenStore, networkModule.getInputStream());
- receiver.start("MQTT Rec: "+getClient().getClientId());
+ receiver.start("MQTT Rec: "+getClient().getClientId(), executorService);
sender = new CommsSender(clientComms, clientState, tokenStore, networkModule.getOutputStream());
- sender.start("MQTT Snd: "+getClient().getClientId());
- callback.start("MQTT Call: "+getClient().getClientId());
+ sender.start("MQTT Snd: "+getClient().getClientId(), executorService);
+ callback.start("MQTT Call: "+getClient().getClientId(), executorService);
internalSend(conPacket, conToken);
} catch (MqttException ex) {
//@TRACE 212=connect failed: unexpected exception
@@ -699,23 +724,24 @@ public void run() {
// Kick off the disconnect processing in the background so that it does not block. For instance
// the quiesce
private class DisconnectBG implements Runnable {
- Thread dBg = null;
MqttDisconnect disconnect;
long quiesceTimeout;
MqttToken token;
+ private String threadName;
- DisconnectBG(MqttDisconnect disconnect, long quiesceTimeout, MqttToken token ) {
+ DisconnectBG(MqttDisconnect disconnect, long quiesceTimeout, MqttToken token, ExecutorService executorService) {
this.disconnect = disconnect;
this.quiesceTimeout = quiesceTimeout;
this.token = token;
}
void start() {
- dBg = new Thread(this, "MQTT Disc: "+getClient().getClientId());
- dBg.start();
+ threadName = "MQTT Disc: "+getClient().getClientId();
+ executorService.execute(this);
}
-
+
public void run() {
+ Thread.currentThread().setName(threadName);
final String methodName = "disconnectBG:run";
//@TRACE 221=>
log.fine(CLASS_NAME, methodName, "221");
@@ -806,37 +832,50 @@ public void deleteBufferedMessage(int bufferIndex){
/**
- * When the client automatically reconnects, we want to send all messages from the
+ * When the client connects, we want to send all messages from the
* buffer first before allowing the user to send any messages
*/
- public void notifyReconnect() {
- final String methodName = "notifyReconnect";
+ public void notifyConnect() {
+ final String methodName = "notifyConnect";
if(disconnectedMessageBuffer != null){
- //@TRACE 509=Client Reconnected, Offline Buffer Available. Sending Buffered Messages.
+ //@TRACE 509=Client Connected, Offline Buffer Available. Sending Buffered Messages.
log.fine(CLASS_NAME, methodName, "509");
- disconnectedMessageBuffer.setPublishCallback(new IDisconnectedBufferCallback() {
-
- public void publishBufferedMessage(BufferedMessage bufferedMessage) throws MqttException {
- if (isConnected()) {
- while(clientState.getActualInFlight() >= (clientState.getMaxInFlight()-1)){
- // We need to Yield to the other threads to allow the in flight messages to clear
- Thread.yield();
-
- }
- //@TRACE 510=Publising Buffered message message={0}
- log.fine(CLASS_NAME, methodName, "510", new Object[] {bufferedMessage.getMessage().getKey()});
- internalSend(bufferedMessage.getMessage(), bufferedMessage.getToken());
- // Delete from persistence if in there
- clientState.unPersistBufferedMessage(bufferedMessage.getMessage());
- } else {
- //@TRACE 208=failed: not connected
- log.fine(CLASS_NAME, methodName, "208");
- throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_NOT_CONNECTED);
- }
+
+ disconnectedMessageBuffer.setPublishCallback(new ReconnectDisconnectedBufferCallback(methodName));
+ executorService.execute(disconnectedMessageBuffer);
+ }
+ }
+
+ class ReconnectDisconnectedBufferCallback implements IDisconnectedBufferCallback{
+
+ final String methodName;
+
+ ReconnectDisconnectedBufferCallback(String methodName) {
+ this.methodName = methodName;
+ }
+
+ public void publishBufferedMessage(BufferedMessage bufferedMessage) throws MqttException {
+ if (isConnected()) {
+ while(clientState.getActualInFlight() >= (clientState.getMaxInFlight()-1)){
+ // We need to Yield to the other threads to allow the in flight messages to clear
+ Thread.yield();
+
}
- });
- new Thread(disconnectedMessageBuffer).start();
+ //@TRACE 510=Publising Buffered message message={0}
+ log.fine(CLASS_NAME, methodName, "510", new Object[] {bufferedMessage.getMessage().getKey()});
+ internalSend(bufferedMessage.getMessage(), bufferedMessage.getToken());
+ // Delete from persistence if in there
+ clientState.unPersistBufferedMessage(bufferedMessage.getMessage());
+ } else {
+ //@TRACE 208=failed: not connected
+ log.fine(CLASS_NAME, methodName, "208");
+ throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_NOT_CONNECTED);
+ }
}
}
+ public int getActualInFlight() {
+ return this.clientState.getActualInFlight();
+ }
+
}
diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/DisconnectedBufferOptions.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/DisconnectedBufferOptions.java
index 13db2e944..21ea06e3e 100644
--- a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/DisconnectedBufferOptions.java
+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/DisconnectedBufferOptions.java
@@ -51,6 +51,7 @@ public class DisconnectedBufferOptions {
* More information about these values can be found in the setter methods.
*/
public DisconnectedBufferOptions() {
+ // Do Nothing.
}
public int getBufferSize() {
diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/IMqttToken.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/IMqttToken.java
index 4589f95fa..d19ccde17 100644
--- a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/IMqttToken.java
+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/IMqttToken.java
@@ -1,10 +1,14 @@
-/*
+/**************************************************************************
* Copyright (c) 2009, 2012 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Dave Locke - initial API and implementation and/or initial documentation
diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttAsyncClient.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttAsyncClient.java
index 8f3ed6a30..ae5150b6b 100644
--- a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttAsyncClient.java
+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttAsyncClient.java
@@ -3,11 +3,11 @@
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
- * and Eclipse Distribution License v1.0 which accompany this distribution.
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
*
- * The Eclipse Public License is available at
+ * The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
- * and the Eclipse Distribution License is available at
+ * and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
@@ -21,25 +21,29 @@
package org.eclipse.paho.client.mqttv3;
+import java.lang.reflect.Field;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Hashtable;
import java.util.Properties;
import java.util.Timer;
import java.util.TimerTask;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
+
import org.eclipse.paho.client.mqttv3.internal.ClientComms;
import org.eclipse.paho.client.mqttv3.internal.ConnectActionListener;
import org.eclipse.paho.client.mqttv3.internal.DisconnectedMessageBuffer;
import org.eclipse.paho.client.mqttv3.internal.ExceptionHelper;
-import org.eclipse.paho.client.mqttv3.internal.LocalNetworkModule;
import org.eclipse.paho.client.mqttv3.internal.NetworkModule;
import org.eclipse.paho.client.mqttv3.internal.SSLNetworkModule;
import org.eclipse.paho.client.mqttv3.internal.TCPNetworkModule;
import org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory;
-import org.eclipse.paho.client.mqttv3.internal.websocket.WebSocketSecureNetworkModule;
import org.eclipse.paho.client.mqttv3.internal.websocket.WebSocketNetworkModule;
+import org.eclipse.paho.client.mqttv3.internal.websocket.WebSocketSecureNetworkModule;
import org.eclipse.paho.client.mqttv3.internal.wire.MqttDisconnect;
import org.eclipse.paho.client.mqttv3.internal.wire.MqttPublish;
import org.eclipse.paho.client.mqttv3.internal.wire.MqttSubscribe;
@@ -54,39 +58,49 @@
* Lightweight client for talking to an MQTT server using non-blocking methods
* that allow an operation to run in the background.
*
- * This class implements the non-blocking {@link IMqttAsyncClient} client interface
- * allowing applications to initiate MQTT actions and then carry on working while the
- * MQTT action completes on a background thread.
- * This implementation is compatible with all Java SE runtimes from 1.4.2 and up.
+ *
+ * This class implements the non-blocking {@link IMqttAsyncClient} client
+ * interface allowing applications to initiate MQTT actions and then carry on
+ * working while the MQTT action completes on a background thread. This
+ * implementation is compatible with all Java SE runtimes from 1.7 and up.
+ *
+ *
+ * An application can connect to an MQTT server using:
*
- * An application can connect to an MQTT server using:
*
* - A plain TCP socket
*
- A secure SSL/TLS socket
*
- *
- * To enable messages to be delivered even across network and client restarts
- * messages need to be safely stored until the message has been delivered at the requested
- * quality of service. A pluggable persistence mechanism is provided to store the messages.
+ *
+ *
+ * To enable messages to be delivered even across network and client restarts
+ * messages need to be safely stored until the message has been delivered at the
+ * requested quality of service. A pluggable persistence mechanism is provided
+ * to store the messages.
*
- * By default {@link MqttDefaultFilePersistence} is used to store messages to a file.
- * If persistence is set to null then messages are stored in memory and hence can be lost
- * if the client, Java runtime or device shuts down.
+ *
+ * By default {@link MqttDefaultFilePersistence} is used to store messages to a
+ * file. If persistence is set to null then messages are stored in memory and
+ * hence can be lost if the client, Java runtime or device shuts down.
*
- * If connecting with {@link MqttConnectOptions#setCleanSession(boolean)} set to true it
- * is safe to use memory persistence as all state is cleared when a client disconnects. If
- * connecting with cleanSession set to false in order to provide reliable message delivery
- * then a persistent message store such as the default one should be used.
+ *
+ * If connecting with {@link MqttConnectOptions#setCleanSession(boolean)} set to
+ * true it is safe to use memory persistence as all state is cleared when a
+ * client disconnects. If connecting with cleanSession set to false in order to
+ * provide reliable message delivery then a persistent message store such as the
+ * default one should be used.
*
- * The message store interface is pluggable. Different stores can be used by implementing
- * the {@link MqttClientPersistence} interface and passing it to the clients constructor.
+ *
+ * The message store interface is pluggable. Different stores can be used by
+ * implementing the {@link MqttClientPersistence} interface and passing it to
+ * the clients constructor.
*
*
* @see IMqttAsyncClient
*/
-public class MqttAsyncClient implements IMqttAsyncClient {
+public class MqttAsyncClient implements IMqttAsyncClient {
private static final String CLASS_NAME = MqttAsyncClient.class.getName();
- private static final Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,CLASS_NAME);
+ private static final Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT, CLASS_NAME);
private static final String CLIENT_ID_PREFIX = "paho";
private static final long QUIESCE_TIMEOUT = 30000; // ms
@@ -102,266 +116,337 @@ public class MqttAsyncClient implements IMqttAsyncClient {
private MqttConnectOptions connOpts;
private Object userContext;
private Timer reconnectTimer; // Automatic reconnect timer
- private static int reconnectDelay = 1000; // Reconnect delay, starts at 1 second
+ private static int reconnectDelay = 1000; // Reconnect delay, starts at 1
+ // second
private boolean reconnecting = false;
private static Object clientLock = new Object(); // Simple lock
-
-
+ private ScheduledExecutorService executorService;
/**
- * Create an MqttAsyncClient that is used to communicate with an MQTT server.
+ * Create an MqttAsyncClient that is used to communicate with an MQTT
+ * server.
*
- * The address of a server can be specified on the constructor. Alternatively
- * a list containing one or more servers can be specified using the
- * {@link MqttConnectOptions#setServerURIs(String[]) setServerURIs} method
- * on MqttConnectOptions.
+ * The address of a server can be specified on the constructor.
+ * Alternatively a list containing one or more servers can be specified
+ * using the {@link MqttConnectOptions#setServerURIs(String[])
+ * setServerURIs} method on MqttConnectOptions.
*
- *
The serverURI
parameter is typically used with the
- * the clientId
parameter to form a key. The key
- * is used to store and reference messages while they are being delivered.
- * Hence the serverURI specified on the constructor must still be specified even if a list
- * of servers is specified on an MqttConnectOptions object.
- * The serverURI on the constructor must remain the same across
- * restarts of the client for delivery of messages to be maintained from a given
- * client to a given server or set of servers.
+ *
+ * The serverURI
parameter is typically used with the the
+ * clientId
parameter to form a key. The key is used to store
+ * and reference messages while they are being delivered. Hence the
+ * serverURI specified on the constructor must still be specified even if a
+ * list of servers is specified on an MqttConnectOptions object. The
+ * serverURI on the constructor must remain the same across restarts of the
+ * client for delivery of messages to be maintained from a given client to a
+ * given server or set of servers.
*
- *
The address of the server to connect to is specified as a URI. Two types of
- * connection are supported tcp://
for a TCP connection and
- * ssl://
for a TCP connection secured by SSL/TLS.
- * For example:
+ *
+ * The address of the server to connect to is specified as a URI. Two types
+ * of connection are supported tcp://
for a TCP connection and
+ * ssl://
for a TCP connection secured by SSL/TLS. For example:
+ *
*
- * tcp://localhost:1883
- * ssl://localhost:8883
+ * tcp://localhost:1883
+ * ssl://localhost:8883
*
*
- * If the port is not specified, it will
- * default to 1883 for tcp://
" URIs, and 8883 for ssl://
URIs.
+ * If the port is not specified, it will default to 1883 for
+ * tcp://
" URIs, and 8883 for ssl://
URIs.
*
*
*
- * A client identifier clientId
must be specified and be less that 65535 characters.
- * It must be unique across all clients connecting to the same
- * server. The clientId is used by the server to store data related to the client,
- * hence it is important that the clientId remain the same when connecting to a server
- * if durable subscriptions or reliable messaging are required.
- *
A convenience method is provided to generate a random client id that
- * should satisfy this criteria - {@link #generateClientId()}. As the client identifier
- * is used by the server to identify a client when it reconnects, the client must use the
- * same identifier between connections if durable subscriptions or reliable
- * delivery of messages is required.
+ * A client identifier clientId
must be specified and be less
+ * that 65535 characters. It must be unique across all clients connecting to
+ * the same server. The clientId is used by the server to store data related
+ * to the client, hence it is important that the clientId remain the same
+ * when connecting to a server if durable subscriptions or reliable
+ * messaging are required.
+ *
+ * A convenience method is provided to generate a random client id that
+ * should satisfy this criteria - {@link #generateClientId()}. As the client
+ * identifier is used by the server to identify a client when it reconnects,
+ * the client must use the same identifier between connections if durable
+ * subscriptions or reliable delivery of messages is required.
*
*
* In Java SE, SSL can be configured in one of several ways, which the
* client will use in the following order:
*
*
- * - Supplying an
SSLSocketFactory
- applications can
- * use {@link MqttConnectOptions#setSocketFactory(SocketFactory)} to supply
- * a factory with the appropriate SSL settings.
- * - SSL Properties - applications can supply SSL settings as a
- * simple Java Properties using {@link MqttConnectOptions#setSSLProperties(Properties)}.
- * - Use JVM settings - There are a number of standard
- * Java system properties that can be used to configure key and trust stores.
+ * - Supplying an
SSLSocketFactory
-
+ * applications can use
+ * {@link MqttConnectOptions#setSocketFactory(SocketFactory)} to supply a
+ * factory with the appropriate SSL settings.
+ * - SSL Properties - applications can supply SSL
+ * settings as a simple Java Properties using
+ * {@link MqttConnectOptions#setSSLProperties(Properties)}.
+ * - Use JVM settings - There are a number of standard
+ * Java system properties that can be used to configure key and trust
+ * stores.
*
*
- * In Java ME, the platform settings are used for SSL connections.
+ *
+ * In Java ME, the platform settings are used for SSL connections.
+ *
*
- * An instance of the default persistence mechanism {@link MqttDefaultFilePersistence}
- * is used by the client. To specify a different persistence mechanism or to turn
- * off persistence, use the {@link #MqttAsyncClient(String, String, MqttClientPersistence)}
+ *
+ * An instance of the default persistence mechanism
+ * {@link MqttDefaultFilePersistence} is used by the client. To specify a
+ * different persistence mechanism or to turn off persistence, use the
+ * {@link #MqttAsyncClient(String, String, MqttClientPersistence)}
* constructor.
*
- * @param serverURI the address of the server to connect to, specified as a URI. Can be overridden using
- * {@link MqttConnectOptions#setServerURIs(String[])}
- * @param clientId a client identifier that is unique on the server being connected to
- * @throws IllegalArgumentException if the URI does not start with
- * "tcp://", "ssl://" or "local://".
- * @throws IllegalArgumentException if the clientId is null or is greater than 65535 characters in length
- * @throws MqttException if any other problem was encountered
+ * @param serverURI
+ * the address of the server to connect to, specified as a URI.
+ * Can be overridden using
+ * {@link MqttConnectOptions#setServerURIs(String[])}
+ * @param clientId
+ * a client identifier that is unique on the server being
+ * connected to
+ * @throws IllegalArgumentException
+ * if the URI does not start with "tcp://", "ssl://" or
+ * "local://".
+ * @throws IllegalArgumentException
+ * if the clientId is null or is greater than 65535 characters
+ * in length
+ * @throws MqttException
+ * if any other problem was encountered
*/
public MqttAsyncClient(String serverURI, String clientId) throws MqttException {
- this(serverURI,clientId, new MqttDefaultFilePersistence());
+ this(serverURI, clientId, new MqttDefaultFilePersistence());
}
-
+
/**
- * Create an MqttAsyncClient that is used to communicate with an MQTT server.
+ * Create an MqttAsyncClient that is used to communicate with an MQTT
+ * server.
*
- * The address of a server can be specified on the constructor. Alternatively
- * a list containing one or more servers can be specified using the
- * {@link MqttConnectOptions#setServerURIs(String[]) setServerURIs} method
- * on MqttConnectOptions.
+ * The address of a server can be specified on the constructor.
+ * Alternatively a list containing one or more servers can be specified
+ * using the {@link MqttConnectOptions#setServerURIs(String[])
+ * setServerURIs} method on MqttConnectOptions.
*
- *
The serverURI
parameter is typically used with the
- * the clientId
parameter to form a key. The key
- * is used to store and reference messages while they are being delivered.
- * Hence the serverURI specified on the constructor must still be specified even if a list
- * of servers is specified on an MqttConnectOptions object.
- * The serverURI on the constructor must remain the same across
- * restarts of the client for delivery of messages to be maintained from a given
- * client to a given server or set of servers.
+ *
+ * The serverURI
parameter is typically used with the the
+ * clientId
parameter to form a key. The key is used to store
+ * and reference messages while they are being delivered. Hence the
+ * serverURI specified on the constructor must still be specified even if a
+ * list of servers is specified on an MqttConnectOptions object. The
+ * serverURI on the constructor must remain the same across restarts of the
+ * client for delivery of messages to be maintained from a given client to a
+ * given server or set of servers.
*
- *
The address of the server to connect to is specified as a URI. Two types of
- * connection are supported tcp://
for a TCP connection and
- * ssl://
for a TCP connection secured by SSL/TLS.
- * For example:
+ *
+ * The address of the server to connect to is specified as a URI. Two types
+ * of connection are supported tcp://
for a TCP connection and
+ * ssl://
for a TCP connection secured by SSL/TLS. For example:
+ *
*
- * tcp://localhost:1883
- * ssl://localhost:8883
+ * tcp://localhost:1883
+ * ssl://localhost:8883
*
*
- * If the port is not specified, it will
- * default to 1883 for tcp://
" URIs, and 8883 for ssl://
URIs.
+ * If the port is not specified, it will default to 1883 for
+ * tcp://
" URIs, and 8883 for ssl://
URIs.
*
*
*
- * A client identifier clientId
must be specified and be less that 65535 characters.
- * It must be unique across all clients connecting to the same
- * server. The clientId is used by the server to store data related to the client,
- * hence it is important that the clientId remain the same when connecting to a server
- * if durable subscriptions or reliable messaging are required.
- *
A convenience method is provided to generate a random client id that
- * should satisfy this criteria - {@link #generateClientId()}. As the client identifier
- * is used by the server to identify a client when it reconnects, the client must use the
- * same identifier between connections if durable subscriptions or reliable
- * delivery of messages is required.
+ * A client identifier clientId
must be specified and be less
+ * that 65535 characters. It must be unique across all clients connecting to
+ * the same server. The clientId is used by the server to store data related
+ * to the client, hence it is important that the clientId remain the same
+ * when connecting to a server if durable subscriptions or reliable
+ * messaging are required.
+ *
+ * A convenience method is provided to generate a random client id that
+ * should satisfy this criteria - {@link #generateClientId()}. As the client
+ * identifier is used by the server to identify a client when it reconnects,
+ * the client must use the same identifier between connections if durable
+ * subscriptions or reliable delivery of messages is required.
*
*
* In Java SE, SSL can be configured in one of several ways, which the
* client will use in the following order:
*
*
- * - Supplying an
SSLSocketFactory
- applications can
- * use {@link MqttConnectOptions#setSocketFactory(SocketFactory)} to supply
- * a factory with the appropriate SSL settings.
- * - SSL Properties - applications can supply SSL settings as a
- * simple Java Properties using {@link MqttConnectOptions#setSSLProperties(Properties)}.
- * - Use JVM settings - There are a number of standard
- * Java system properties that can be used to configure key and trust stores.
+ * - Supplying an
SSLSocketFactory
-
+ * applications can use
+ * {@link MqttConnectOptions#setSocketFactory(SocketFactory)} to supply a
+ * factory with the appropriate SSL settings.
+ * - SSL Properties - applications can supply SSL
+ * settings as a simple Java Properties using
+ * {@link MqttConnectOptions#setSSLProperties(Properties)}.
+ * - Use JVM settings - There are a number of standard
+ * Java system properties that can be used to configure key and trust
+ * stores.
*
*
- * In Java ME, the platform settings are used for SSL connections.
*
- * A persistence mechanism is used to enable reliable messaging.
- * For messages sent at qualities of service (QoS) 1 or 2 to be reliably delivered,
- * messages must be stored (on both the client and server) until the delivery of the message
- * is complete. If messages are not safely stored when being delivered then
- * a failure in the client or server can result in lost messages. A pluggable
- * persistence mechanism is supported via the {@link MqttClientPersistence}
- * interface. An implementer of this interface that safely stores messages
- * must be specified in order for delivery of messages to be reliable. In
- * addition {@link MqttConnectOptions#setCleanSession(boolean)} must be set
- * to false. In the event that only QoS 0 messages are sent or received or
+ * In Java ME, the platform settings are used for SSL connections.
+ *
+ *
+ * A persistence mechanism is used to enable reliable messaging. For
+ * messages sent at qualities of service (QoS) 1 or 2 to be reliably
+ * delivered, messages must be stored (on both the client and server) until
+ * the delivery of the message is complete. If messages are not safely
+ * stored when being delivered then a failure in the client or server can
+ * result in lost messages. A pluggable persistence mechanism is supported
+ * via the {@link MqttClientPersistence} interface. An implementer of this
+ * interface that safely stores messages must be specified in order for
+ * delivery of messages to be reliable. In addition
+ * {@link MqttConnectOptions#setCleanSession(boolean)} must be set to false.
+ * In the event that only QoS 0 messages are sent or received or
* cleanSession is set to true then a safe store is not needed.
*
- * An implementation of file-based persistence is provided in
- * class {@link MqttDefaultFilePersistence} which will work in all Java SE based
- * systems. If no persistence is needed, the persistence parameter
- * can be explicitly set to null
.
+ *
+ * An implementation of file-based persistence is provided in class
+ * {@link MqttDefaultFilePersistence} which will work in all Java SE based
+ * systems. If no persistence is needed, the persistence parameter can be
+ * explicitly set to null
.
+ *
*
- * @param serverURI the address of the server to connect to, specified as a URI. Can be overridden using
- * {@link MqttConnectOptions#setServerURIs(String[])}
- * @param clientId a client identifier that is unique on the server being connected to
- * @param persistence the persistence class to use to store in-flight message. If null then the
- * default persistence mechanism is used
- * @throws MqttException if any other problem was encountered
+ * @param serverURI
+ * the address of the server to connect to, specified as a URI.
+ * Can be overridden using
+ * {@link MqttConnectOptions#setServerURIs(String[])}
+ * @param clientId
+ * a client identifier that is unique on the server being
+ * connected to
+ * @param persistence
+ * the persistence class to use to store in-flight message. If
+ * null then the default persistence mechanism is used
+ * @throws MqttException
+ * if any other problem was encountered
*/
public MqttAsyncClient(String serverURI, String clientId, MqttClientPersistence persistence) throws MqttException {
- this(serverURI,clientId, persistence, new TimerPingSender());
+ this(serverURI, clientId, persistence, new TimerPingSender());
+ }
+
+ public MqttAsyncClient(String serverURI, String clientId, MqttClientPersistence persistence,
+ MqttPingSender pingSender) throws MqttException {
+ this(serverURI, clientId, persistence, pingSender, null);
}
/**
- * Create an MqttAsyncClient that is used to communicate with an MQTT server.
+ * Create an MqttAsyncClient that is used to communicate with an MQTT
+ * server.
*
- * The address of a server can be specified on the constructor. Alternatively
- * a list containing one or more servers can be specified using the
- * {@link MqttConnectOptions#setServerURIs(String[]) setServerURIs} method
- * on MqttConnectOptions.
+ * The address of a server can be specified on the constructor.
+ * Alternatively a list containing one or more servers can be specified
+ * using the {@link MqttConnectOptions#setServerURIs(String[])
+ * setServerURIs} method on MqttConnectOptions.
*
- *
The serverURI
parameter is typically used with the
- * the clientId
parameter to form a key. The key
- * is used to store and reference messages while they are being delivered.
- * Hence the serverURI specified on the constructor must still be specified even if a list
- * of servers is specified on an MqttConnectOptions object.
- * The serverURI on the constructor must remain the same across
- * restarts of the client for delivery of messages to be maintained from a given
- * client to a given server or set of servers.
+ *
+ * The serverURI
parameter is typically used with the the
+ * clientId
parameter to form a key. The key is used to store
+ * and reference messages while they are being delivered. Hence the
+ * serverURI specified on the constructor must still be specified even if a
+ * list of servers is specified on an MqttConnectOptions object. The
+ * serverURI on the constructor must remain the same across restarts of the
+ * client for delivery of messages to be maintained from a given client to a
+ * given server or set of servers.
*
- *
The address of the server to connect to is specified as a URI. Two types of
- * connection are supported tcp://
for a TCP connection and
- * ssl://
for a TCP connection secured by SSL/TLS.
- * For example:
+ *
+ * The address of the server to connect to is specified as a URI. Two types
+ * of connection are supported tcp://
for a TCP connection and
+ * ssl://
for a TCP connection secured by SSL/TLS. For example:
+ *
*
- * tcp://localhost:1883
- * ssl://localhost:8883
+ * tcp://localhost:1883
+ * ssl://localhost:8883
*
*
- * If the port is not specified, it will
- * default to 1883 for tcp://
" URIs, and 8883 for ssl://
URIs.
+ * If the port is not specified, it will default to 1883 for
+ * tcp://
" URIs, and 8883 for ssl://
URIs.
*
*
*
- * A client identifier clientId
must be specified and be less that 65535 characters.
- * It must be unique across all clients connecting to the same
- * server. The clientId is used by the server to store data related to the client,
- * hence it is important that the clientId remain the same when connecting to a server
- * if durable subscriptions or reliable messaging are required.
- *
A convenience method is provided to generate a random client id that
- * should satisfy this criteria - {@link #generateClientId()}. As the client identifier
- * is used by the server to identify a client when it reconnects, the client must use the
- * same identifier between connections if durable subscriptions or reliable
- * delivery of messages is required.
+ * A client identifier clientId
must be specified and be less
+ * that 65535 characters. It must be unique across all clients connecting to
+ * the same server. The clientId is used by the server to store data related
+ * to the client, hence it is important that the clientId remain the same
+ * when connecting to a server if durable subscriptions or reliable
+ * messaging are required.
+ *
+ * A convenience method is provided to generate a random client id that
+ * should satisfy this criteria - {@link #generateClientId()}. As the client
+ * identifier is used by the server to identify a client when it reconnects,
+ * the client must use the same identifier between connections if durable
+ * subscriptions or reliable delivery of messages is required.
*
*
* In Java SE, SSL can be configured in one of several ways, which the
* client will use in the following order:
*
*
- * - Supplying an
SSLSocketFactory
- applications can
- * use {@link MqttConnectOptions#setSocketFactory(SocketFactory)} to supply
- * a factory with the appropriate SSL settings.
- * - SSL Properties - applications can supply SSL settings as a
- * simple Java Properties using {@link MqttConnectOptions#setSSLProperties(Properties)}.
- * - Use JVM settings - There are a number of standard
- * Java system properties that can be used to configure key and trust stores.
+ * - Supplying an
SSLSocketFactory
-
+ * applications can use
+ * {@link MqttConnectOptions#setSocketFactory(SocketFactory)} to supply a
+ * factory with the appropriate SSL settings.
+ * - SSL Properties - applications can supply SSL
+ * settings as a simple Java Properties using
+ * {@link MqttConnectOptions#setSSLProperties(Properties)}.
+ * - Use JVM settings - There are a number of standard
+ * Java system properties that can be used to configure key and trust
+ * stores.
*
*
- * In Java ME, the platform settings are used for SSL connections.
*
- * A persistence mechanism is used to enable reliable messaging.
- * For messages sent at qualities of service (QoS) 1 or 2 to be reliably delivered,
- * messages must be stored (on both the client and server) until the delivery of the message
- * is complete. If messages are not safely stored when being delivered then
- * a failure in the client or server can result in lost messages. A pluggable
- * persistence mechanism is supported via the {@link MqttClientPersistence}
- * interface. An implementer of this interface that safely stores messages
- * must be specified in order for delivery of messages to be reliable. In
- * addition {@link MqttConnectOptions#setCleanSession(boolean)} must be set
- * to false. In the event that only QoS 0 messages are sent or received or
+ * In Java ME, the platform settings are used for SSL connections.
+ *
+ *
+ * A persistence mechanism is used to enable reliable messaging. For
+ * messages sent at qualities of service (QoS) 1 or 2 to be reliably
+ * delivered, messages must be stored (on both the client and server) until
+ * the delivery of the message is complete. If messages are not safely
+ * stored when being delivered then a failure in the client or server can
+ * result in lost messages. A pluggable persistence mechanism is supported
+ * via the {@link MqttClientPersistence} interface. An implementer of this
+ * interface that safely stores messages must be specified in order for
+ * delivery of messages to be reliable. In addition
+ * {@link MqttConnectOptions#setCleanSession(boolean)} must be set to false.
+ * In the event that only QoS 0 messages are sent or received or
* cleanSession is set to true then a safe store is not needed.
*
- * An implementation of file-based persistence is provided in
- * class {@link MqttDefaultFilePersistence} which will work in all Java SE based
- * systems. If no persistence is needed, the persistence parameter
- * can be explicitly set to null
.
+ *
+ * An implementation of file-based persistence is provided in class
+ * {@link MqttDefaultFilePersistence} which will work in all Java SE based
+ * systems. If no persistence is needed, the persistence parameter can be
+ * explicitly set to null
.
+ *
*
- * @param serverURI the address of the server to connect to, specified as a URI. Can be overridden using
- * {@link MqttConnectOptions#setServerURIs(String[])}
- * @param clientId a client identifier that is unique on the server being connected to
- * @param persistence the persistence class to use to store in-flight message. If null then the
- * default persistence mechanism is used
- * @param pingSender Custom {@link MqttPingSender} implementation.
- * @throws IllegalArgumentException if the URI does not start with
- * "tcp://", "ssl://" or "local://"
- * @throws IllegalArgumentException if the clientId is null or is greater than 65535 characters in length
- * @throws MqttException if any other problem was encountered
+ * @param serverURI
+ * the address of the server to connect to, specified as a URI.
+ * Can be overridden using
+ * {@link MqttConnectOptions#setServerURIs(String[])}
+ * @param clientId
+ * a client identifier that is unique on the server being
+ * connected to
+ * @param persistence
+ * the persistence class to use to store in-flight message. If
+ * null then the default persistence mechanism is used
+ * @param pingSender
+ * Custom {@link MqttPingSender} implementation.
+ * @param executorService
+ * used for managing threads. If null then a newFixedThreadPool
+ * is used.
+ * @throws IllegalArgumentException
+ * if the URI does not start with "tcp://", "ssl://" or
+ * "local://"
+ * @throws IllegalArgumentException
+ * if the clientId is null or is greater than 65535 characters
+ * in length
+ * @throws MqttException
+ * if any other problem was encountered
*/
- public MqttAsyncClient(String serverURI, String clientId, MqttClientPersistence persistence, MqttPingSender pingSender) throws MqttException {
+ public MqttAsyncClient(String serverURI, String clientId, MqttClientPersistence persistence,
+ MqttPingSender pingSender, ScheduledExecutorService executorService) throws MqttException {
final String methodName = "MqttAsyncClient";
log.setResourceName(clientId);
- if (clientId == null) { //Support empty client Id, 3.1.1 standard
+ if (clientId == null) { // Support empty client Id, 3.1.1 standard
throw new IllegalArgumentException("Null clientId");
}
// Count characters, surrogate pairs count as one character.
@@ -371,7 +456,7 @@ public MqttAsyncClient(String serverURI, String clientId, MqttClientPersistence
i++;
clientIdLength++;
}
- if ( clientIdLength > 65535) {
+ if (clientIdLength > 65535) {
throw new IllegalArgumentException("ClientId longer than 65535 characters");
}
@@ -385,48 +470,57 @@ public MqttAsyncClient(String serverURI, String clientId, MqttClientPersistence
this.persistence = new MemoryPersistence();
}
+ this.executorService = executorService;
+ if (this.executorService == null) {
+ this.executorService = Executors.newScheduledThreadPool(10);
+ }
+
// @TRACE 101= ClientID={0} ServerURI={1} PersistenceType={2}
- log.fine(CLASS_NAME,methodName,"101",new Object[]{clientId,serverURI,persistence});
+ log.fine(CLASS_NAME, methodName, "101", new Object[] { clientId, serverURI, persistence });
this.persistence.open(clientId, serverURI);
- this.comms = new ClientComms(this, this.persistence, pingSender);
+ this.comms = new ClientComms(this, this.persistence, pingSender, this.executorService);
this.persistence.close();
this.topics = new Hashtable();
}
-
-
/**
- * @param ch the character to check.
+ * @param ch
+ * the character to check.
* @return returns 'true' if the character is a high-surrogate code unit
*/
protected static boolean Character_isHighSurrogate(char ch) {
- return(ch >= MIN_HIGH_SURROGATE) && (ch <= MAX_HIGH_SURROGATE);
+ return (ch >= MIN_HIGH_SURROGATE) && (ch <= MAX_HIGH_SURROGATE);
}
/**
- * Factory method to create an array of network modules, one for
- * each of the supplied URIs
+ * Factory method to create an array of network modules, one for each of the
+ * supplied URIs
*
- * @param address the URI for the server.
- * @param options the {@link MqttConnectOptions} for the connection.
+ * @param address
+ * the URI for the server.
+ * @param options
+ * the {@link MqttConnectOptions} for the connection.
* @return a network module appropriate to the specified address.
- * @throws MqttException if an exception occurs creating the network Modules
- * @throws MqttSecurityException if an issue occurs creating an SSL / TLS Socket
+ * @throws MqttException
+ * if an exception occurs creating the network Modules
+ * @throws MqttSecurityException
+ * if an issue occurs creating an SSL / TLS Socket
*/
- protected NetworkModule[] createNetworkModules(String address, MqttConnectOptions options) throws MqttException, MqttSecurityException {
+ protected NetworkModule[] createNetworkModules(String address, MqttConnectOptions options)
+ throws MqttException, MqttSecurityException {
final String methodName = "createNetworkModules";
// @TRACE 116=URI={0}
- log.fine(CLASS_NAME, methodName, "116", new Object[]{address});
+ log.fine(CLASS_NAME, methodName, "116", new Object[] { address });
NetworkModule[] networkModules = null;
String[] serverURIs = options.getServerURIs();
String[] array = null;
if (serverURIs == null) {
- array = new String[]{address};
+ array = new String[] { address };
} else if (serverURIs.length == 0) {
- array = new String[]{address};
+ array = new String[] { address };
} else {
array = serverURIs;
}
@@ -461,8 +555,22 @@ private NetworkModule createNetworkModule(String address, MqttConnectOptions opt
URI uri;
try {
uri = new URI(address);
+ // If the returned uri contains no host and the address contains underscores,
+ // then it's likely that Java did not parse the URI
+ if(uri.getHost() == null && address.contains("_")){
+ try {
+ final Field hostField = URI.class.getDeclaredField("host");
+ hostField.setAccessible(true);
+ // Get everything after the scheme://
+ String shortAddress = address.substring(uri.getScheme().length() + 3);
+ hostField.set(uri, getHostName(shortAddress));
+
+ } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
+ throw ExceptionHelper.createMqttException(e.getCause());
+ }
+
+ }
} catch (URISyntaxException e) {
- // throw new IllegalArgumentException("Malformed URI: " + address, e); // Cannot use for Java 1.4.2
throw new IllegalArgumentException("Malformed URI: " + address + ", " + e.getMessage());
}
@@ -507,6 +615,7 @@ else if ((factory instanceof SSLSocketFactory) == false) {
// Create the network module...
netModule = new SSLNetworkModule((SSLSocketFactory) factory, host, port, clientId);
((SSLNetworkModule)netModule).setSSLhandshakeTimeout(options.getConnectionTimeout());
+ ((SSLNetworkModule)netModule).setSSLHostnameVerifier(options.getSSLHostnameVerifier());
// Ciphers suites need to be set, if they are available
if (factoryFactory != null) {
String[] enabledCiphers = factoryFactory.getEnabledCipherSuites(null);
@@ -545,7 +654,7 @@ else if ((factory instanceof SSLSocketFactory) == false) {
throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_SOCKET_FACTORY_MISMATCH);
}
- // Create the network module...
+ // Create the network module...
netModule = new WebSocketSecureNetworkModule((SSLSocketFactory) factory, address, host, port, clientId);
((WebSocketSecureNetworkModule)netModule).setSSLhandshakeTimeout(options.getConnectionTimeout());
// Ciphers suites need to be set, if they are available
@@ -556,9 +665,6 @@ else if ((factory instanceof SSLSocketFactory) == false) {
}
}
break;
- case MqttConnectOptions.URI_TYPE_LOCAL :
- netModule = new LocalNetworkModule(address.substring(8));
- break;
default:
// This shouldn't happen, as long as validateURI() has been called.
log.fine(CLASS_NAME,methodName, "119", new Object[] {address});
@@ -567,30 +673,56 @@ else if ((factory instanceof SSLSocketFactory) == false) {
return netModule;
}
- /* (non-Javadoc)
- * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#connect(java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
+ private String getHostName(String uri) {
+ int portIndex = uri.indexOf(':');
+ if (portIndex == -1) {
+ portIndex = uri.indexOf('/');
+ }
+ if (portIndex == -1) {
+ portIndex = uri.length();
+ }
+ return uri.substring(0, portIndex);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.paho.client.mqttv3.IMqttAsyncClient#connect(java.lang.Object,
+ * org.eclipse.paho.client.mqttv3.IMqttActionListener)
*/
public IMqttToken connect(Object userContext, IMqttActionListener callback)
throws MqttException, MqttSecurityException {
return this.connect(new MqttConnectOptions(), userContext, callback);
}
- /* (non-Javadoc)
+ /*
+ * (non-Javadoc)
+ *
* @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#connect()
*/
public IMqttToken connect() throws MqttException, MqttSecurityException {
return this.connect(null, null);
}
- /* (non-Javadoc)
- * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#connect(org.eclipse.paho.client.mqttv3.MqttConnectOptions)
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.paho.client.mqttv3.IMqttAsyncClient#connect(org.eclipse.paho.
+ * client.mqttv3.MqttConnectOptions)
*/
public IMqttToken connect(MqttConnectOptions options) throws MqttException, MqttSecurityException {
- return this.connect(options, null,null);
+ return this.connect(options, null, null);
}
- /* (non-Javadoc)
- * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#connect(org.eclipse.paho.client.mqttv3.MqttConnectOptions, java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.paho.client.mqttv3.IMqttAsyncClient#connect(org.eclipse.paho.
+ * client.mqttv3.MqttConnectOptions, java.lang.Object,
+ * org.eclipse.paho.client.mqttv3.IMqttActionListener)
*/
public IMqttToken connect(MqttConnectOptions options, Object userContext, IMqttActionListener callback)
throws MqttException, MqttSecurityException {
@@ -607,56 +739,35 @@ public IMqttToken connect(MqttConnectOptions options, Object userContext, IMqttA
if (comms.isClosed()) {
throw new MqttException(MqttException.REASON_CODE_CLIENT_CLOSED);
}
- if(options == null){
+ if (options == null) {
options = new MqttConnectOptions();
}
this.connOpts = options;
this.userContext = userContext;
final boolean automaticReconnect = options.isAutomaticReconnect();
- // @TRACE 103=cleanSession={0} connectionTimeout={1} TimekeepAlive={2} userName={3} password={4} will={5} userContext={6} callback={7}
- log.fine(CLASS_NAME,methodName, "103",
- new Object[]{
- Boolean.valueOf(options.isCleanSession()),
- new Integer(options.getConnectionTimeout()),
- new Integer(options.getKeepAliveInterval()),
- options.getUserName(),
- ((null == options.getPassword())?"[null]":"[notnull]"),
- ((null == options.getWillMessage())?"[null]":"[notnull]"),
- userContext,
- callback });
+ // @TRACE 103=cleanSession={0} connectionTimeout={1} TimekeepAlive={2}
+ // userName={3} password={4} will={5} userContext={6} callback={7}
+ log.fine(CLASS_NAME, methodName, "103",
+ new Object[] { Boolean.valueOf(options.isCleanSession()), new Integer(options.getConnectionTimeout()),
+ new Integer(options.getKeepAliveInterval()), options.getUserName(),
+ ((null == options.getPassword()) ? "[null]" : "[notnull]"),
+ ((null == options.getWillMessage()) ? "[null]" : "[notnull]"), userContext, callback });
comms.setNetworkModules(createNetworkModules(serverURI, options));
- comms.setReconnectCallback(new MqttCallbackExtended() {
-
- public void messageArrived(String topic, MqttMessage message) throws Exception {
- }
- public void deliveryComplete(IMqttDeliveryToken token) {
- }
- public void connectComplete(boolean reconnect, String serverURI) {
- }
-
- public void connectionLost(Throwable cause) {
- if(automaticReconnect){
- // Automatic reconnect is set so make sure comms is in resting state
- comms.setRestingState(true);
- reconnecting = true;
- startReconnectCycle();
- }
- }
- });
-
-
-
+ comms.setReconnectCallback(new MqttReconnectCallback(automaticReconnect));
- // Insert our own callback to iterate through the URIs till the connect succeeds
+ // Insert our own callback to iterate through the URIs till the connect
+ // succeeds
MqttToken userToken = new MqttToken(getClientId());
- ConnectActionListener connectActionListener = new ConnectActionListener(this, persistence, comms, options, userToken, userContext, callback, reconnecting);
+ ConnectActionListener connectActionListener = new ConnectActionListener(this, persistence, comms, options,
+ userToken, userContext, callback, reconnecting);
userToken.setActionCallback(connectActionListener);
userToken.setUserContext(this);
- // If we are using the MqttCallbackExtended, set it on the connectActionListener
- if(this.mqttCallback instanceof MqttCallbackExtended){
- connectActionListener.setMqttCallbackExtended((MqttCallbackExtended)this.mqttCallback);
+ // If we are using the MqttCallbackExtended, set it on the
+ // connectActionListener
+ if (this.mqttCallback instanceof MqttCallbackExtended) {
+ connectActionListener.setMqttCallbackExtended((MqttCallbackExtended) this.mqttCallback);
}
comms.setNetworkModuleIndex(0);
@@ -665,34 +776,46 @@ public void connectionLost(Throwable cause) {
return userToken;
}
- /* (non-Javadoc)
- * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnect(java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnect(java.lang.
+ * Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
*/
- public IMqttToken disconnect( Object userContext, IMqttActionListener callback) throws MqttException {
+ public IMqttToken disconnect(Object userContext, IMqttActionListener callback) throws MqttException {
return this.disconnect(QUIESCE_TIMEOUT, userContext, callback);
}
- /* (non-Javadoc)
+ /*
+ * (non-Javadoc)
+ *
* @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnect()
*/
public IMqttToken disconnect() throws MqttException {
return this.disconnect(null, null);
}
- /* (non-Javadoc)
+ /*
+ * (non-Javadoc)
+ *
* @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnect(long)
*/
public IMqttToken disconnect(long quiesceTimeout) throws MqttException {
return this.disconnect(quiesceTimeout, null, null);
}
- /* (non-Javadoc)
- * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnect(long, java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnect(long,
+ * java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
*/
- public IMqttToken disconnect(long quiesceTimeout, Object userContext, IMqttActionListener callback) throws MqttException {
+ public IMqttToken disconnect(long quiesceTimeout, Object userContext, IMqttActionListener callback)
+ throws MqttException {
final String methodName = "disconnect";
// @TRACE 104=> quiesceTimeout={0} userContext={1} callback={2}
- log.fine(CLASS_NAME,methodName, "104",new Object[]{ new Long(quiesceTimeout), userContext, callback});
+ log.fine(CLASS_NAME, methodName, "104", new Object[] { new Long(quiesceTimeout), userContext, callback });
MqttToken token = new MqttToken(getClientId());
token.setActionCallback(callback);
@@ -702,18 +825,19 @@ public IMqttToken disconnect(long quiesceTimeout, Object userContext, IMqttActio
try {
comms.disconnect(disconnect, quiesceTimeout, token);
} catch (MqttException ex) {
- //@TRACE 105=< exception
- log.fine(CLASS_NAME,methodName,"105",null,ex);
+ // @TRACE 105=< exception
+ log.fine(CLASS_NAME, methodName, "105", null, ex);
throw ex;
}
- //@TRACE 108=<
- log.fine(CLASS_NAME,methodName,"108");
+ // @TRACE 108=<
+ log.fine(CLASS_NAME, methodName, "108");
return token;
}
-
+
/*
* (non-Javadoc)
+ *
* @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnectForcibly()
*/
public void disconnectForcibly() throws MqttException {
@@ -722,195 +846,239 @@ public void disconnectForcibly() throws MqttException {
/*
* (non-Javadoc)
- * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnectForcibly(long)
+ *
+ * @see
+ * org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnectForcibly(long)
*/
public void disconnectForcibly(long disconnectTimeout) throws MqttException {
disconnectForcibly(QUIESCE_TIMEOUT, disconnectTimeout);
}
-
+
/*
* (non-Javadoc)
- * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnectForcibly(long, long)
+ *
+ * @see
+ * org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnectForcibly(long,
+ * long)
*/
- public void disconnectForcibly(long quiesceTimeout, long disconnectTimeout) throws MqttException{
+ public void disconnectForcibly(long quiesceTimeout, long disconnectTimeout) throws MqttException {
comms.disconnectForcibly(quiesceTimeout, disconnectTimeout);
}
/**
- * Disconnects from the server forcibly to reset all the states. Could be useful when disconnect attempt failed.
+ * Disconnects from the server forcibly to reset all the states. Could be
+ * useful when disconnect attempt failed.
*
- * Because the client is able to establish the TCP/IP connection to a none MQTT server and it will certainly fail to
- * send the disconnect packet.
- *
- * @param quiesceTimeout the amount of time in milliseconds to allow for existing work to finish before
- * disconnecting. A value of zero or less means the client will not quiesce.
- * @param disconnectTimeout the amount of time in milliseconds to allow send disconnect packet to server.
- * @param sendDisconnectPacket if true, will send the disconnect packet to the server
- * @throws MqttException if any unexpected error
+ * Because the client is able to establish the TCP/IP connection to a none
+ * MQTT server and it will certainly fail to send the disconnect packet.
+ *
+ * @param quiesceTimeout
+ * the amount of time in milliseconds to allow for existing work
+ * to finish before disconnecting. A value of zero or less means
+ * the client will not quiesce.
+ * @param disconnectTimeout
+ * the amount of time in milliseconds to allow send disconnect
+ * packet to server.
+ * @param sendDisconnectPacket
+ * if true, will send the disconnect packet to the server
+ * @throws MqttException
+ * if any unexpected error
*/
- public void disconnectForcibly(long quiesceTimeout, long disconnectTimeout, boolean sendDisconnectPacket) throws MqttException {
- comms.disconnectForcibly(quiesceTimeout, disconnectTimeout, sendDisconnectPacket);
- }
+ public void disconnectForcibly(long quiesceTimeout, long disconnectTimeout, boolean sendDisconnectPacket)
+ throws MqttException {
+ comms.disconnectForcibly(quiesceTimeout, disconnectTimeout, sendDisconnectPacket);
+ }
- /* (non-Javadoc)
+ /*
+ * (non-Javadoc)
+ *
* @see IMqttAsyncClient#isConnected()
*/
public boolean isConnected() {
return comms.isConnected();
}
- /* (non-Javadoc)
+ /*
+ * (non-Javadoc)
+ *
* @see IMqttAsyncClient#getClientId()
*/
public String getClientId() {
return clientId;
}
- /* (non-Javadoc)
+ /*
+ * (non-Javadoc)
+ *
* @see IMqttAsyncClient#getServerURI()
*/
public String getServerURI() {
return serverURI;
}
-
+
/**
- * Returns the currently connected Server URI
- * Implemented due to: https://bugs.eclipse.org/bugs/show_bug.cgi?id=481097
- *
+ * Returns the currently connected Server URI Implemented due to:
+ * https://bugs.eclipse.org/bugs/show_bug.cgi?id=481097
+ *
* Where getServerURI only returns the URI that was provided in
* MqttAsyncClient's constructor, getCurrentServerURI returns the URI of the
- * Server that the client is currently connected to. This would be different in scenarios
- * where multiple server URIs have been provided to the MqttConnectOptions.
- *
+ * Server that the client is currently connected to. This would be different
+ * in scenarios where multiple server URIs have been provided to the
+ * MqttConnectOptions.
+ *
* @return the currently connected server URI
*/
- public String getCurrentServerURI(){
+ public String getCurrentServerURI() {
return comms.getNetworkModules()[comms.getNetworkModuleIndex()].getServerURI();
}
/**
* Get a topic object which can be used to publish messages.
- *
There are two alternative methods that should be used in preference to this one when publishing a message:
+ *
+ * There are two alternative methods that should be used in preference to
+ * this one when publishing a message:
+ *
*
- * - {@link MqttAsyncClient#publish(String, MqttMessage)} to publish a message in a non-blocking manner or
- * - {@link MqttClient#publish(String, MqttMessage)} to publish a message in a blocking manner
+ * - {@link MqttAsyncClient#publish(String, MqttMessage)} to publish a
+ * message in a non-blocking manner or
+ * - {@link MqttClient#publish(String, MqttMessage)} to publish a message
+ * in a blocking manner
*
- * When you build an application,
- * the design of the topic tree should take into account the following principles
- * of topic name syntax and semantics:
+ *
+ * When you build an application, the design of the topic tree should take
+ * into account the following principles of topic name syntax and semantics:
+ *
*
*
- * - A topic must be at least one character long.
- * - Topic names are case sensitive. For example, ACCOUNTS and Accounts are
- * two different topics.
- * - Topic names can include the space character. For example, Accounts
- * payable is a valid topic.
- * - A leading "/" creates a distinct topic. For example, /finance is
- * different from finance. /finance matches "+/+" and "/+", but
- * not "+".
- * - Do not include the null character (Unicode \x0000) in
- * any topic.
+ * - A topic must be at least one character long.
+ * - Topic names are case sensitive. For example, ACCOUNTS and
+ * Accounts are two different topics.
+ * - Topic names can include the space character. For example,
+ * Accounts payable is a valid topic.
+ * - A leading "/" creates a distinct topic. For example,
+ * /finance is different from finance. /finance
+ * matches "+/+" and "/+", but not "+".
+ * - Do not include the null character (Unicode \x0000) in any topic.
*
*
- * The following principles apply to the construction and content of a topic
- * tree:
+ *
+ * The following principles apply to the construction and content of a topic
+ * tree:
+ *
*
*
- * - The length is limited to 64k but within that there are no limits to the
- * number of levels in a topic tree.
- * - There can be any number of root nodes; that is, there can be any number
- * of topic trees.
- *
+ * The length is limited to 64k but within that there are no limits to
+ * the number of levels in a topic tree.
+ * There can be any number of root nodes; that is, there can be any
+ * number of topic trees.
+ *
*
- * @param topic the topic to use, for example "finance/stock/ibm".
- * @return an MqttTopic object, which can be used to publish messages to
- * the topic.
- * @throws IllegalArgumentException if the topic contains a '+' or '#'
- * wildcard character.
+ * @param topic
+ * the topic to use, for example "finance/stock/ibm".
+ * @return an MqttTopic object, which can be used to publish messages to the
+ * topic.
+ * @throws IllegalArgumentException
+ * if the topic contains a '+' or '#' wildcard character.
*/
protected MqttTopic getTopic(String topic) {
- MqttTopic.validate(topic, false/*wildcards NOT allowed*/);
+ MqttTopic.validate(topic, false/* wildcards NOT allowed */);
- MqttTopic result = (MqttTopic)topics.get(topic);
+ MqttTopic result = (MqttTopic) topics.get(topic);
if (result == null) {
result = new MqttTopic(topic, comms);
- topics.put(topic,result);
+ topics.put(topic, result);
}
return result;
}
-
- /* (non-Javadoc)
- * Check and send a ping if needed.
- * By default, client sends PingReq to server to keep the connection to
- * server. For some platforms which cannot use this mechanism, such as Android,
- * developer needs to handle the ping request manually with this method.
- *
- *
- * @throws MqttException for other errors encountered while publishing the message.
+
+ /*
+ * (non-Javadoc) Check and send a ping if needed. By default, client
+ * sends PingReq to server to keep the connection to server. For some
+ * platforms which cannot use this mechanism, such as Android, developer
+ * needs to handle the ping request manually with this method.
+ *
+ * @throws MqttException for other errors encountered while publishing the
+ * message.
*/
- public IMqttToken checkPing(Object userContext, IMqttActionListener callback) throws MqttException{
+ public IMqttToken checkPing(Object userContext, IMqttActionListener callback) throws MqttException {
final String methodName = "ping";
MqttToken token;
- //@TRACE 117=>
- log.fine(CLASS_NAME,methodName,"117");
-
+ // @TRACE 117=>
+ log.fine(CLASS_NAME, methodName, "117");
+
token = comms.checkForActivity();
- //@TRACE 118=<
- log.fine(CLASS_NAME,methodName,"118");
-
+ // @TRACE 118=<
+ log.fine(CLASS_NAME, methodName, "118");
+
return token;
}
-
- /* (non-Javadoc)
- * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang.String, int, java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang.
+ * String, int, java.lang.Object,
+ * org.eclipse.paho.client.mqttv3.IMqttActionListener)
*/
- public IMqttToken subscribe(String topicFilter, int qos, Object userContext, IMqttActionListener callback) throws MqttException {
- return this.subscribe(new String[] {topicFilter}, new int[] {qos}, userContext, callback);
+ public IMqttToken subscribe(String topicFilter, int qos, Object userContext, IMqttActionListener callback)
+ throws MqttException {
+ return this.subscribe(new String[] { topicFilter }, new int[] { qos }, userContext, callback);
}
- /* (non-Javadoc)
- * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang.String, int)
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang.
+ * String, int)
*/
public IMqttToken subscribe(String topicFilter, int qos) throws MqttException {
- return this.subscribe(new String[] {topicFilter}, new int[] {qos}, null, null);
+ return this.subscribe(new String[] { topicFilter }, new int[] { qos }, null, null);
}
- /* (non-Javadoc)
- * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang.String[], int[])
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang.
+ * String[], int[])
*/
public IMqttToken subscribe(String[] topicFilters, int[] qos) throws MqttException {
return this.subscribe(topicFilters, qos, null, null);
}
-
- /* (non-Javadoc)
- * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang.String[], int[], java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang.
+ * String[], int[], java.lang.Object,
+ * org.eclipse.paho.client.mqttv3.IMqttActionListener)
*/
- public IMqttToken subscribe(String[] topicFilters, int[] qos, Object userContext, IMqttActionListener callback) throws MqttException {
+ public IMqttToken subscribe(String[] topicFilters, int[] qos, Object userContext, IMqttActionListener callback)
+ throws MqttException {
final String methodName = "subscribe";
if (topicFilters.length != qos.length) {
throw new IllegalArgumentException();
}
-
- // remove any message handlers for individual topics
+
+ // remove any message handlers for individual topics
for (int i = 0; i < topicFilters.length; ++i) {
this.comms.removeMessageListener(topicFilters[i]);
}
-
+
// Only Generate Log string if we are logging at FINE level
- if(log.isLoggable(Logger.FINE)){
+ if (log.isLoggable(Logger.FINE)) {
StringBuffer subs = new StringBuffer();
- for (int i=0;i0) {
+ for (int i = 0; i < topicFilters.length; i++) {
+ if (i > 0) {
subs.append(", ");
}
subs.append("topic=").append(topicFilters[i]).append(" qos=").append(qos[i]);
-
- //Check if the topic filter is valid before subscribing
- MqttTopic.validate(topicFilters[i], true/*allow wildcards*/);
+
+ // Check if the topic filter is valid before subscribing
+ MqttTopic.validate(topicFilters[i], true/* allow wildcards */);
}
- //@TRACE 106=Subscribe topicFilter={0} userContext={1} callback={2}
- log.fine(CLASS_NAME,methodName,"106",new Object[]{subs.toString(), userContext, callback});
+ // @TRACE 106=Subscribe topicFilter={0} userContext={1} callback={2}
+ log.fine(CLASS_NAME, methodName, "106", new Object[] { subs.toString(), userContext, callback });
}
MqttToken token = new MqttToken(getClientId());
@@ -921,101 +1089,135 @@ public IMqttToken subscribe(String[] topicFilters, int[] qos, Object userContext
MqttSubscribe register = new MqttSubscribe(topicFilters, qos);
comms.sendNoWait(register, token);
- //@TRACE 109=<
- log.fine(CLASS_NAME,methodName,"109");
+ // @TRACE 109=<
+ log.fine(CLASS_NAME, methodName, "109");
return token;
}
-
- /* (non-Javadoc)
- * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang.String, int, java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang.
+ * String, int, java.lang.Object,
+ * org.eclipse.paho.client.mqttv3.IMqttActionListener)
*/
- public IMqttToken subscribe(String topicFilter, int qos, Object userContext, IMqttActionListener callback, IMqttMessageListener messageListener) throws MqttException {
-
- return this.subscribe(new String[] {topicFilter}, new int[] {qos}, userContext, callback, new IMqttMessageListener[] {messageListener});
+ public IMqttToken subscribe(String topicFilter, int qos, Object userContext, IMqttActionListener callback,
+ IMqttMessageListener messageListener) throws MqttException {
+
+ return this.subscribe(new String[] { topicFilter }, new int[] { qos }, userContext, callback,
+ new IMqttMessageListener[] { messageListener });
}
- /* (non-Javadoc)
- * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang.String, int)
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang.
+ * String, int)
*/
- public IMqttToken subscribe(String topicFilter, int qos, IMqttMessageListener messageListener) throws MqttException {
- return this.subscribe(new String[] {topicFilter}, new int[] {qos}, null, null, new IMqttMessageListener[] {messageListener});
+ public IMqttToken subscribe(String topicFilter, int qos, IMqttMessageListener messageListener)
+ throws MqttException {
+ return this.subscribe(new String[] { topicFilter }, new int[] { qos }, null, null,
+ new IMqttMessageListener[] { messageListener });
}
- /* (non-Javadoc)
- * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang.String[], int[])
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang.
+ * String[], int[])
*/
- public IMqttToken subscribe(String[] topicFilters, int[] qos, IMqttMessageListener[] messageListeners) throws MqttException {
+ public IMqttToken subscribe(String[] topicFilters, int[] qos, IMqttMessageListener[] messageListeners)
+ throws MqttException {
return this.subscribe(topicFilters, qos, null, null, messageListeners);
}
-
- public IMqttToken subscribe(String[] topicFilters, int[] qos, Object userContext, IMqttActionListener callback, IMqttMessageListener[] messageListeners) throws MqttException {
-
+
+ public IMqttToken subscribe(String[] topicFilters, int[] qos, Object userContext, IMqttActionListener callback,
+ IMqttMessageListener[] messageListeners) throws MqttException {
+
if ((messageListeners.length != qos.length) || (qos.length != topicFilters.length)) {
throw new IllegalArgumentException();
}
-
+
IMqttToken token = this.subscribe(topicFilters, qos, userContext, callback);
-
+
// add message handlers to the list for this client
for (int i = 0; i < topicFilters.length; ++i) {
this.comms.setMessageListener(topicFilters[i], messageListeners[i]);
}
-
+
return token;
}
- /* (non-Javadoc)
- * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#unsubscribe(java.lang.String, java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.paho.client.mqttv3.IMqttAsyncClient#unsubscribe(java.lang.
+ * String, java.lang.Object,
+ * org.eclipse.paho.client.mqttv3.IMqttActionListener)
*/
- public IMqttToken unsubscribe(String topicFilter, Object userContext, IMqttActionListener callback) throws MqttException {
- return unsubscribe(new String[] {topicFilter}, userContext, callback);
+ public IMqttToken unsubscribe(String topicFilter, Object userContext, IMqttActionListener callback)
+ throws MqttException {
+ return unsubscribe(new String[] { topicFilter }, userContext, callback);
}
- /* (non-Javadoc)
- * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#unsubscribe(java.lang.String)
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.paho.client.mqttv3.IMqttAsyncClient#unsubscribe(java.lang.
+ * String)
*/
public IMqttToken unsubscribe(String topicFilter) throws MqttException {
- return unsubscribe(new String[] {topicFilter}, null, null);
+ return unsubscribe(new String[] { topicFilter }, null, null);
}
- /* (non-Javadoc)
- * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#unsubscribe(java.lang.String[])
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.paho.client.mqttv3.IMqttAsyncClient#unsubscribe(java.lang.
+ * String[])
*/
public IMqttToken unsubscribe(String[] topicFilters) throws MqttException {
return unsubscribe(topicFilters, null, null);
}
- /* (non-Javadoc)
- * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#unsubscribe(java.lang.String[], java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.paho.client.mqttv3.IMqttAsyncClient#unsubscribe(java.lang.
+ * String[], java.lang.Object,
+ * org.eclipse.paho.client.mqttv3.IMqttActionListener)
*/
- public IMqttToken unsubscribe(String[] topicFilters, Object userContext, IMqttActionListener callback) throws MqttException {
+ public IMqttToken unsubscribe(String[] topicFilters, Object userContext, IMqttActionListener callback)
+ throws MqttException {
final String methodName = "unsubscribe";
-
+
// Only Generate Log string if we are logging at FINE level
- if(log.isLoggable(Logger.FINE)){
+ if (log.isLoggable(Logger.FINE)) {
String subs = "";
- for (int i=0;i0) {
- subs+=", ";
+ for (int i = 0; i < topicFilters.length; i++) {
+ if (i > 0) {
+ subs += ", ";
}
- subs+=topicFilters[i];
+ subs += topicFilters[i];
}
-
- //@TRACE 107=Unsubscribe topic={0} userContext={1} callback={2}
- log.fine(CLASS_NAME, methodName,"107",new Object[]{subs, userContext, callback});
+
+ // @TRACE 107=Unsubscribe topic={0} userContext={1} callback={2}
+ log.fine(CLASS_NAME, methodName, "107", new Object[] { subs, userContext, callback });
}
-
- for (int i=0;iWhen cleanSession is set to false, an application must ensure it uses the
- * same client identifier when it reconnects to the server to resume state and maintain
- * assured message delivery.
+ * Returns a randomly generated client identifier based on the the fixed
+ * prefix (paho) and the system time.
+ *
+ * When cleanSession is set to false, an application must ensure it uses the
+ * same client identifier when it reconnects to the server to resume state
+ * and maintain assured message delivery.
+ *
+ *
* @return a generated client identifier
* @see MqttConnectOptions#setCleanSession(boolean)
*/
public static String generateClientId() {
- //length of nanoTime = 15, so total length = 19 < 65535(defined in spec)
- //return CLIENT_ID_PREFIX + System.nanoTime(); // Cannot use for Java 1.4.2
- return CLIENT_ID_PREFIX + (System.currentTimeMillis() * 1000000);
-
+ // length of nanoTime = 15, so total length = 19 < 65535(defined in
+ // spec)
+ return CLIENT_ID_PREFIX + System.nanoTime();
+
}
- /* (non-Javadoc)
+ /*
+ * (non-Javadoc)
+ *
* @see IMqttAsyncClient#getPendingDeliveryTokens()
*/
public IMqttDeliveryToken[] getPendingDeliveryTokens() {
return comms.getPendingDeliveryTokens();
}
- /* (non-Javadoc)
- * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#publish(java.lang.String, byte[], int, boolean, java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.paho.client.mqttv3.IMqttAsyncClient#publish(java.lang.String,
+ * byte[], int, boolean, java.lang.Object,
+ * org.eclipse.paho.client.mqttv3.IMqttActionListener)
*/
- public IMqttDeliveryToken publish(String topic, byte[] payload, int qos,
- boolean retained, Object userContext, IMqttActionListener callback) throws MqttException,
- MqttPersistenceException {
+ public IMqttDeliveryToken publish(String topic, byte[] payload, int qos, boolean retained, Object userContext,
+ IMqttActionListener callback) throws MqttException, MqttPersistenceException {
MqttMessage message = new MqttMessage(payload);
message.setQos(qos);
message.setRetained(retained);
return this.publish(topic, message, userContext, callback);
}
- /* (non-Javadoc)
- * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#publish(java.lang.String, byte[], int, boolean)
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.paho.client.mqttv3.IMqttAsyncClient#publish(java.lang.String,
+ * byte[], int, boolean)
*/
- public IMqttDeliveryToken publish(String topic, byte[] payload, int qos,
- boolean retained) throws MqttException, MqttPersistenceException {
+ public IMqttDeliveryToken publish(String topic, byte[] payload, int qos, boolean retained)
+ throws MqttException, MqttPersistenceException {
return this.publish(topic, payload, qos, retained, null, null);
}
- /* (non-Javadoc)
- * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#publish(java.lang.String, org.eclipse.paho.client.mqttv3.MqttMessage)
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.paho.client.mqttv3.IMqttAsyncClient#publish(java.lang.String,
+ * org.eclipse.paho.client.mqttv3.MqttMessage)
*/
- public IMqttDeliveryToken publish(String topic, MqttMessage message) throws MqttException, MqttPersistenceException {
+ public IMqttDeliveryToken publish(String topic, MqttMessage message)
+ throws MqttException, MqttPersistenceException {
return this.publish(topic, message, null, null);
}
-
- /* (non-Javadoc)
- * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#publish(java.lang.String, org.eclipse.paho.client.mqttv3.MqttMessage, java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.paho.client.mqttv3.IMqttAsyncClient#publish(java.lang.String,
+ * org.eclipse.paho.client.mqttv3.MqttMessage, java.lang.Object,
+ * org.eclipse.paho.client.mqttv3.IMqttActionListener)
*/
- public IMqttDeliveryToken publish(String topic, MqttMessage message, Object userContext, IMqttActionListener callback) throws MqttException,
- MqttPersistenceException {
+ public IMqttDeliveryToken publish(String topic, MqttMessage message, Object userContext,
+ IMqttActionListener callback) throws MqttException, MqttPersistenceException {
final String methodName = "publish";
- //@TRACE 111=< topic={0} message={1}userContext={1} callback={2}
- log.fine(CLASS_NAME,methodName,"111", new Object[] {topic, userContext, callback});
+ // @TRACE 111=< topic={0} message={1}userContext={1} callback={2}
+ log.fine(CLASS_NAME, methodName, "111", new Object[] { topic, userContext, callback });
- //Checks if a topic is valid when publishing a message.
- MqttTopic.validate(topic, false/*wildcards NOT allowed*/);
+ // Checks if a topic is valid when publishing a message.
+ MqttTopic.validate(topic, false/* wildcards NOT allowed */);
MqttDeliveryToken token = new MqttDeliveryToken(getClientId());
token.setActionCallback(callback);
token.setUserContext(userContext);
token.setMessage(message);
- token.internalTok.setTopics(new String[] {topic});
+ token.internalTok.setTopics(new String[] { topic });
MqttPublish pubMsg = new MqttPublish(topic, message);
comms.sendNoWait(pubMsg, token);
- //@TRACE 112=<
- log.fine(CLASS_NAME,methodName,"112");
+ // @TRACE 112=<
+ log.fine(CLASS_NAME, methodName, "112");
return token;
}
/**
* User triggered attempt to reconnect
- * @throws MqttException if there is an issue with reconnecting
+ *
+ * @throws MqttException
+ * if there is an issue with reconnecting
*/
public void reconnect() throws MqttException {
final String methodName = "reconnect";
- //@Trace 500=Attempting to reconnect client: {0}
- log.fine(CLASS_NAME, methodName, "500", new Object[]{this.clientId});
- // Some checks to make sure that we're not attempting to reconnect an already connected client
+ // @Trace 500=Attempting to reconnect client: {0}
+ log.fine(CLASS_NAME, methodName, "500", new Object[] { this.clientId });
+ // Some checks to make sure that we're not attempting to reconnect an
+ // already connected client
if (comms.isConnected()) {
throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_CONNECTED);
}
@@ -1157,69 +1389,47 @@ public void reconnect() throws MqttException {
attemptReconnect();
}
-
-
+
/**
- * Attempts to reconnect the client to the server.
- * If successful it will make sure that there are no further
- * reconnects scheduled. However if the connect fails, the delay will double
- * up to 128 seconds and will re-schedule the reconnect for after the delay.
- *
- * Any thrown exceptions are logged but not acted upon as it is assumed that
+ * Attempts to reconnect the client to the server. If successful it will
+ * make sure that there are no further reconnects scheduled. However if the
+ * connect fails, the delay will double up to 128 seconds and will
+ * re-schedule the reconnect for after the delay.
+ *
+ * Any thrown exceptions are logged but not acted upon as it is assumed that
* they are being thrown due to the server being offline and so reconnect
* attempts will continue.
*/
- private void attemptReconnect(){
- final String methodName = "attemptReconnect";
- //@Trace 500=Attempting to reconnect client: {0}
- log.fine(CLASS_NAME, methodName, "500", new Object[]{this.clientId});
+ private void attemptReconnect() {
+ final String methodName = "attemptReconnect";
+ // @Trace 500=Attempting to reconnect client: {0}
+ log.fine(CLASS_NAME, methodName, "500", new Object[] { this.clientId });
try {
- connect(this.connOpts, this.userContext,new IMqttActionListener() {
-
- public void onSuccess(IMqttToken asyncActionToken) {
- //@Trace 501=Automatic Reconnect Successful: {0}
- log.fine(CLASS_NAME, methodName, "501", new Object[]{asyncActionToken.getClient().getClientId()});
- comms.setRestingState(false);
- stopReconnectCycle();
- }
-
- public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
- //@Trace 502=Automatic Reconnect failed, rescheduling: {0}
- log.fine(CLASS_NAME, methodName, "502", new Object[]{asyncActionToken.getClient().getClientId()});
- if(reconnectDelay < 128000){
- reconnectDelay = reconnectDelay * 2;
- }
- rescheduleReconnectCycle(reconnectDelay);
- }
- });
+ connect(this.connOpts, this.userContext, new MqttReconnectActionListener(methodName));
} catch (MqttSecurityException ex) {
- //@TRACE 804=exception
- log.fine(CLASS_NAME,methodName,"804",null, ex);
+ // @TRACE 804=exception
+ log.fine(CLASS_NAME, methodName, "804", null, ex);
} catch (MqttException ex) {
- //@TRACE 804=exception
- log.fine(CLASS_NAME,methodName,"804",null, ex);
+ // @TRACE 804=exception
+ log.fine(CLASS_NAME, methodName, "804", null, ex);
}
}
-
-
-
- private void startReconnectCycle(){
+ private void startReconnectCycle() {
String methodName = "startReconnectCycle";
- //@Trace 503=Start reconnect timer for client: {0}, delay: {1}
- log.fine(CLASS_NAME, methodName, "503", new Object[]{this.clientId, new Long(reconnectDelay)});
- //reconnectTimer = new Timer("MQTT Reconnect: " + clientId); // Cannot use for Java 1.4.2
- reconnectTimer = new Timer();
+ // @Trace 503=Start reconnect timer for client: {0}, delay: {1}
+ log.fine(CLASS_NAME, methodName, "503", new Object[] { this.clientId, new Long(reconnectDelay) });
+ reconnectTimer = new Timer("MQTT Reconnect: " + clientId);
reconnectTimer.schedule(new ReconnectTask(), reconnectDelay);
}
-
- private void stopReconnectCycle(){
+
+ private void stopReconnectCycle() {
String methodName = "stopReconnectCycle";
- //@Trace 504=Stop reconnect timer for client: {0}
- log.fine(CLASS_NAME, methodName, "504", new Object[]{this.clientId});
- synchronized(clientLock) {
+ // @Trace 504=Stop reconnect timer for client: {0}
+ log.fine(CLASS_NAME, methodName, "504", new Object[] { this.clientId });
+ synchronized (clientLock) {
if (this.connOpts.isAutomaticReconnect()) {
- if(reconnectTimer != null){
+ if (reconnectTimer != null) {
reconnectTimer.cancel();
reconnectTimer = null;
}
@@ -1227,90 +1437,174 @@ private void stopReconnectCycle(){
}
}
}
-
- private void rescheduleReconnectCycle(int delay){
- String methodName = "rescheduleReconnectCycle";
- //@Trace 505=Rescheduling reconnect timer for client: {0}, delay: {1}
- log.fine(CLASS_NAME, methodName, "505", new Object[]{this.clientId, new Long(reconnectDelay)});
- synchronized(clientLock) {
- if(this.connOpts.isAutomaticReconnect()) {
- if (reconnectTimer != null) {
- reconnectTimer.schedule(new ReconnectTask(), delay);
- } else {
- // The previous reconnect timer was cancelled
- reconnectDelay = delay;
- startReconnectCycle();
- }
- }
- }
- }
-
+
private class ReconnectTask extends TimerTask {
private static final String methodName = "ReconnectTask.run";
+
public void run() {
- //@Trace 506=Triggering Automatic Reconnect attempt.
+ // @Trace 506=Triggering Automatic Reconnect attempt.
log.fine(CLASS_NAME, methodName, "506");
attemptReconnect();
}
}
-
+
+ class MqttReconnectCallback implements MqttCallbackExtended {
+
+ final boolean automaticReconnect;
+
+ MqttReconnectCallback(boolean isAutomaticReconnect) {
+ automaticReconnect = isAutomaticReconnect;
+ }
+
+ public void connectionLost(Throwable cause) {
+ if (automaticReconnect) {
+ // Automatic reconnect is set so make sure comms is in resting
+ // state
+ comms.setRestingState(true);
+ reconnecting = true;
+ startReconnectCycle();
+ }
+ }
+
+ public void messageArrived(String topic, MqttMessage message) throws Exception {
+ }
+
+ public void deliveryComplete(IMqttDeliveryToken token) {
+ }
+
+ public void connectComplete(boolean reconnect, String serverURI) {
+ }
+
+ }
+
+ class MqttReconnectActionListener implements IMqttActionListener {
+
+ final String methodName;
+
+ MqttReconnectActionListener(String methodName) {
+ this.methodName = methodName;
+ }
+
+ public void onSuccess(IMqttToken asyncActionToken) {
+ // @Trace 501=Automatic Reconnect Successful: {0}
+ log.fine(CLASS_NAME, methodName, "501", new Object[] { asyncActionToken.getClient().getClientId() });
+ comms.setRestingState(false);
+ stopReconnectCycle();
+ }
+
+ public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
+ // @Trace 502=Automatic Reconnect failed, rescheduling: {0}
+ log.fine(CLASS_NAME, methodName, "502", new Object[] { asyncActionToken.getClient().getClientId() });
+ if (reconnectDelay < 128000) {
+ reconnectDelay = reconnectDelay * 2;
+ }
+ rescheduleReconnectCycle(reconnectDelay);
+ }
+
+ private void rescheduleReconnectCycle(int delay) {
+ String reschedulemethodName = methodName + ":rescheduleReconnectCycle";
+ // @Trace 505=Rescheduling reconnect timer for client: {0}, delay:
+ // {1}
+ log.fine(CLASS_NAME, reschedulemethodName, "505",
+ new Object[] { MqttAsyncClient.this.clientId, String.valueOf(reconnectDelay) });
+ synchronized (clientLock) {
+ if (MqttAsyncClient.this.connOpts.isAutomaticReconnect()) {
+ if (reconnectTimer != null) {
+ reconnectTimer.schedule(new ReconnectTask(), delay);
+ } else {
+ // The previous reconnect timer was cancelled
+ reconnectDelay = delay;
+ startReconnectCycle();
+ }
+ }
+ }
+ }
+
+ }
+
/**
* Sets the DisconnectedBufferOptions for this client
- * @param bufferOpts the {@link DisconnectedBufferOptions}
+ *
+ * @param bufferOpts
+ * the {@link DisconnectedBufferOptions}
*/
public void setBufferOpts(DisconnectedBufferOptions bufferOpts) {
this.comms.setDisconnectedMessageBuffer(new DisconnectedMessageBuffer(bufferOpts));
}
-
-
+
/**
* Returns the number of messages in the Disconnected Message Buffer
+ *
* @return Count of messages in the buffer
*/
- public int getBufferedMessageCount(){
+ public int getBufferedMessageCount() {
return this.comms.getBufferedMessageCount();
}
-
-
+
/**
* Returns a message from the Disconnected Message Buffer
- * @param bufferIndex the index of the message to be retrieved.
+ *
+ * @param bufferIndex
+ * the index of the message to be retrieved.
* @return the message located at the bufferIndex
*/
- public MqttMessage getBufferedMessage(int bufferIndex){
+ public MqttMessage getBufferedMessage(int bufferIndex) {
return this.comms.getBufferedMessage(bufferIndex);
}
-
+
/**
* Deletes a message from the Disconnected Message Buffer
- * @param bufferIndex the index of the message to be deleted.
+ *
+ * @param bufferIndex
+ * the index of the message to be deleted.
*/
- public void deleteBufferedMessage(int bufferIndex){
+ public void deleteBufferedMessage(int bufferIndex) {
this.comms.deleteBufferedMessage(bufferIndex);
}
-
-
+ /**
+ * Returns the current number of outgoing in-flight messages being sent by
+ * the client. Note that this number cannot be guaranteed to be 100%
+ * accurate as some messages may have been sent or queued in the time taken
+ * for this method to return.
+ *
+ * @return the current number of in-flight messages.
+ */
+ public int getInFlightMessageCount() {
+ return this.comms.getActualInFlight();
+ }
- /* (non-Javadoc)
+ /*
+ * (non-Javadoc)
+ *
* @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#close()
*/
public void close() throws MqttException {
+ close(false);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#close()
+ */
+ public void close(boolean force) throws MqttException {
final String methodName = "close";
- //@TRACE 113=<
- log.fine(CLASS_NAME,methodName,"113");
- comms.close();
- //@TRACE 114=>
- log.fine(CLASS_NAME,methodName,"114");
+ // @TRACE 113=<
+ log.fine(CLASS_NAME, methodName, "113");
+ comms.close(force);
+ // @TRACE 114=>
+ log.fine(CLASS_NAME, methodName, "114");
}
/**
* Return a debug object that can be used to help solve problems.
+ *
* @return the {@link Debug} object
*/
public Debug getDebug() {
- return new Debug(clientId,comms);
+ return new Debug(clientId, comms);
}
}
diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttClient.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttClient.java
index 1b5941f94..c87844168 100644
--- a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttClient.java
+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttClient.java
@@ -3,11 +3,11 @@
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
- * and Eclipse Distribution License v1.0 which accompany this distribution.
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
*
- * The Eclipse Public License is available at
+ * The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
- * and the Eclipse Distribution License is available at
+ * and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
@@ -19,6 +19,7 @@
package org.eclipse.paho.client.mqttv3;
import java.util.Properties;
+import java.util.concurrent.ScheduledExecutorService;
import javax.net.SocketFactory;
@@ -31,14 +32,14 @@
*
* This class implements the blocking {@link IMqttClient} client interface where all
* actions block until they have completed (or timed out).
- * This implementation is compatible with all Java SE runtimes from 1.4.2 and up.
+ * This implementation is compatible with all Java SE runtimes from 1.7 and up.
*
* An application can connect to an MQTT server using:
*
* - A plain TCP socket
*
- An secure SSL/TLS socket
*
- *
+ *
* To enable messages to be delivered even across network and client restarts
* messages need to be safely stored until the message has been delivered at the requested
* quality of service. A pluggable persistence mechanism is provided to store the messages.
@@ -228,6 +229,96 @@ public MqttClient(String serverURI, String clientId, MqttClientPersistence persi
aClient = new MqttAsyncClient(serverURI, clientId, persistence);
}
+ /**
+ * Create an MqttClient that can be used to communicate with an MQTT server.
+ *
+ * The address of a server can be specified on the constructor. Alternatively
+ * a list containing one or more servers can be specified using the
+ * {@link MqttConnectOptions#setServerURIs(String[]) setServerURIs} method
+ * on MqttConnectOptions.
+ *
+ *
The serverURI
parameter is typically used with the
+ * the clientId
parameter to form a key. The key
+ * is used to store and reference messages while they are being delivered.
+ * Hence the serverURI specified on the constructor must still be specified even if a list
+ * of servers is specified on an MqttConnectOptions object.
+ * The serverURI on the constructor must remain the same across
+ * restarts of the client for delivery of messages to be maintained from a given
+ * client to a given server or set of servers.
+ *
+ *
The address of the server to connect to is specified as a URI. Two types of
+ * connection are supported tcp://
for a TCP connection and
+ * ssl://
for a TCP connection secured by SSL/TLS.
+ * For example:
+ *
+ *
+ * tcp://localhost:1883
+ * ssl://localhost:8883
+ *
+ * If the port is not specified, it will
+ * default to 1883 for tcp://
" URIs, and 8883 for ssl://
URIs.
+ *
+ *
+ *
+ * A client identifier clientId
must be specified and be less that 65535 characters.
+ * It must be unique across all clients connecting to the same
+ * server. The clientId is used by the server to store data related to the client,
+ * hence it is important that the clientId remain the same when connecting to a server
+ * if durable subscriptions or reliable messaging are required.
+ *
A convenience method is provided to generate a random client id that
+ * should satisfy this criteria - {@link #generateClientId()}. As the client identifier
+ * is used by the server to identify a client when it reconnects, the client must use the
+ * same identifier between connections if durable subscriptions or reliable
+ * delivery of messages is required.
+ *
+ *
+ * In Java SE, SSL can be configured in one of several ways, which the
+ * client will use in the following order:
+ *
+ *
+ * - Supplying an
SSLSocketFactory
- applications can
+ * use {@link MqttConnectOptions#setSocketFactory(SocketFactory)} to supply
+ * a factory with the appropriate SSL settings.
+ * - SSL Properties - applications can supply SSL settings as a
+ * simple Java Properties using {@link MqttConnectOptions#setSSLProperties(Properties)}.
+ * - Use JVM settings - There are a number of standard
+ * Java system properties that can be used to configure key and trust stores.
+ *
+ *
+ * In Java ME, the platform settings are used for SSL connections.
+ *
+ * A persistence mechanism is used to enable reliable messaging.
+ * For messages sent at qualities of service (QoS) 1 or 2 to be reliably delivered,
+ * messages must be stored (on both the client and server) until the delivery of the message
+ * is complete. If messages are not safely stored when being delivered then
+ * a failure in the client or server can result in lost messages. A pluggable
+ * persistence mechanism is supported via the {@link MqttClientPersistence}
+ * interface. An implementer of this interface that safely stores messages
+ * must be specified in order for delivery of messages to be reliable. In
+ * addition {@link MqttConnectOptions#setCleanSession(boolean)} must be set
+ * to false. In the event that only QoS 0 messages are sent or received or
+ * cleanSession is set to true then a safe store is not needed.
+ *
+ * An implementation of file-based persistence is provided in
+ * class {@link MqttDefaultFilePersistence} which will work in all Java SE based
+ * systems. If no persistence is needed, the persistence parameter
+ * can be explicitly set to null
.
+ *
+ * @param serverURI the address of the server to connect to, specified as a URI. Can be overridden using
+ * {@link MqttConnectOptions#setServerURIs(String[])}
+ * @param clientId a client identifier that is unique on the server being connected to
+ * @param persistence the persistence class to use to store in-flight message. If null then the
+ * default persistence mechanism is used
+ * @param executorService used for managing threads. If null then a newFixedThreadPool is used.
+ * @throws IllegalArgumentException if the URI does not start with
+ * "tcp://", "ssl://" or "local://"
+ * @throws IllegalArgumentException if the clientId is null or is greater than 65535 characters in length
+ * @throws MqttException if any other problem was encountered
+ */
+ public MqttClient(String serverURI, String clientId, MqttClientPersistence persistence, ScheduledExecutorService executorService) throws MqttException {
+ aClient = new MqttAsyncClient(serverURI, clientId, persistence, new ScheduledExecutorPingSender(executorService), executorService);
+ }
+
/*
* @see IMqttClient#connect()
*/
@@ -241,7 +332,7 @@ public void connect() throws MqttSecurityException, MqttException {
public void connect(MqttConnectOptions options) throws MqttSecurityException, MqttException {
aClient.connect(options, null, null).waitForCompletion(getTimeToWait());
}
-
+
/*
* @see IMqttClient#connect(MqttConnectOptions)
*/
@@ -267,7 +358,7 @@ public void disconnect(long quiesceTimeout) throws MqttException {
/*
* (non-Javadoc)
- *
+ *
* @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnectForcibly()
*/
public void disconnectForcibly() throws MqttException {
@@ -276,7 +367,7 @@ public void disconnectForcibly() throws MqttException {
/*
* (non-Javadoc)
- *
+ *
* @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnectForcibly(long)
*/
public void disconnectForcibly(long disconnectTimeout) throws MqttException {
@@ -285,7 +376,7 @@ public void disconnectForcibly(long disconnectTimeout) throws MqttException {
/*
* (non-Javadoc)
- *
+ *
* @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnectForcibly(long, long)
*/
public void disconnectForcibly(long quiesceTimeout, long disconnectTimeout) throws MqttException {
@@ -297,7 +388,7 @@ public void disconnectForcibly(long quiesceTimeout, long disconnectTimeout) thro
*
* Because the client is able to establish the TCP/IP connection to a none MQTT server and it will certainly fail to
* send the disconnect packet.
- *
+ *
* @param quiesceTimeout the amount of time in milliseconds to allow for existing work to finish before
* disconnecting. A value of zero or less means the client will not quiesce.
* @param disconnectTimeout the amount of time in milliseconds to allow send disconnect packet to server.
@@ -347,14 +438,14 @@ public void subscribe(String[] topicFilters, int[] qos) throws MqttException {
throw new MqttException(MqttException.REASON_CODE_SUBSCRIBE_FAILED);
}
}
-
+
/* (non-Javadoc)
* @see org.eclipse.paho.client.mqttv3.IMqttClient#subscribe(java.lang.String, int, java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
*/
public void subscribe(String topicFilter, IMqttMessageListener messageListener) throws MqttException {
this.subscribe(new String[] {topicFilter}, new int[] {1}, new IMqttMessageListener[] {messageListener});
}
-
+
/* (non-Javadoc)
* @see org.eclipse.paho.client.mqttv3.IMqttClient#subscribe(java.lang.String, int, java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
*/
@@ -373,23 +464,23 @@ public void subscribe(String topicFilter, int qos, IMqttMessageListener messageL
this.subscribe(new String[] {topicFilter}, new int[] {qos}, new IMqttMessageListener[] {messageListener});
}
-
- public void subscribe(String[] topicFilters, int[] qos, IMqttMessageListener[] messageListeners) throws MqttException {
+
+ public void subscribe(String[] topicFilters, int[] qos, IMqttMessageListener[] messageListeners) throws MqttException {
this.subscribe(topicFilters, qos);
-
+
// add message handlers to the list for this client
for (int i = 0; i < topicFilters.length; ++i) {
aClient.comms.setMessageListener(topicFilters[i], messageListeners[i]);
}
}
-
+
/*
* @see IMqttClient#subscribeWithResponse(String)
*/
public IMqttToken subscribeWithResponse(String topicFilter) throws MqttException {
return this.subscribeWithResponse(new String[] {topicFilter}, new int[] {1});
}
-
+
/*
* @see IMqttClient#subscribeWithResponse(String, IMqttMessageListener)
*/
@@ -450,7 +541,7 @@ public IMqttToken subscribeWithResponse(String[] topicFilters, int[] qos) throws
public IMqttToken subscribeWithResponse(String[] topicFilters, int[] qos, IMqttMessageListener[] messageListeners)
throws MqttException {
IMqttToken tok = this.subscribeWithResponse(topicFilters, qos);
-
+
// add message handlers to the list for this client
for (int i = 0; i < topicFilters.length; ++i) {
aClient.comms.setMessageListener(topicFilters[i], messageListeners[i]);
@@ -532,8 +623,17 @@ public long getTimeToWait() {
* @see org.eclipse.paho.client.mqttv3.IMqttClient#close()
*/
public void close() throws MqttException {
- aClient.close();
+ aClient.close(false);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.paho.client.mqttv3.IMqttClient#close()
+ */
+ public void close(boolean force) throws MqttException {
+ aClient.close(force);
}
+
+
/* (non-Javadoc)
* @see org.eclipse.paho.client.mqttv3.IMqttClient#getClientId()
@@ -555,16 +655,16 @@ public IMqttDeliveryToken[] getPendingDeliveryTokens() {
public String getServerURI() {
return aClient.getServerURI();
}
-
+
/**
* Returns the currently connected Server URI
* Implemented due to: https://bugs.eclipse.org/bugs/show_bug.cgi?id=481097
- *
+ *
* Where getServerURI only returns the URI that was provided in
* MqttAsyncClient's constructor, getCurrentServerURI returns the URI of the
* Server that the client is currently connected to. This would be different in scenarios
* where multiple server URIs have been provided to the MqttConnectOptions.
- *
+ *
* @return the currently connected server URI
*/
public String getCurrentServerURI(){
@@ -591,14 +691,14 @@ public boolean isConnected() {
public void setCallback(MqttCallback callback) {
aClient.setCallback(callback);
}
-
+
/* (non-Javadoc)
* @see org.eclipse.paho.client.mqttv3.IMqttClient#setCallback(org.eclipse.paho.client.mqttv3.MqttCallback)
*/
public void setManualAcks(boolean manualAcks) {
aClient.setManualAcks(manualAcks);
}
-
+
public void messageArrivedComplete(int messageId, int qos) throws MqttException {
aClient.messageArrivedComplete(messageId, qos);
}
@@ -615,7 +715,7 @@ public void messageArrivedComplete(int messageId, int qos) throws MqttException
public static String generateClientId() {
return MqttAsyncClient.generateClientId();
}
-
+
/**
* Will attempt to reconnect to the server after the client has lost connection.
* @throws MqttException if an error occurs attempting to reconnect
@@ -631,5 +731,5 @@ public void reconnect() throws MqttException {
public Debug getDebug() {
return (aClient.getDebug());
}
-
+
}
diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttConnectOptions.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttConnectOptions.java
index 9eb946259..2e16b7a01 100644
--- a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttConnectOptions.java
+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttConnectOptions.java
@@ -20,6 +20,7 @@
import java.util.Properties;
import javax.net.SocketFactory;
+import javax.net.ssl.HostnameVerifier;
import org.eclipse.paho.client.mqttv3.util.Debug;
@@ -73,6 +74,7 @@ public class MqttConnectOptions {
private char[] password;
private SocketFactory socketFactory;
private Properties sslClientProps = null;
+ private HostnameVerifier sslHostnameVerifier = null;
private boolean cleanSession = CLEAN_SESSION_DEFAULT;
private int connectionTimeout = CONNECTION_TIMEOUT_DEFAULT;
private String[] serverURIs = null;
@@ -408,6 +410,27 @@ public void setSSLProperties(Properties props) {
this.sslClientProps = props;
}
+ /**
+ * Returns the HostnameVerifier for the SSL connection.
+ * @return the HostnameVerifier for the SSL connection
+ */
+ public HostnameVerifier getSSLHostnameVerifier() {
+ return sslHostnameVerifier;
+ }
+
+ /**
+ * Sets the HostnameVerifier for the SSL connection. Note that it will be
+ * used after handshake on a connection and you should do actions by
+ * yourserlf when hostname is verified error.
+ *
+ * There is no default HostnameVerifier
+ *
+ * @param hostnameVerifier the {@link HostnameVerifier}
+ */
+ public void setSSLHostnameVerifier(HostnameVerifier hostnameVerifier) {
+ this.sslHostnameVerifier = hostnameVerifier;
+ }
+
/**
* Returns whether the client and server should remember state for the client across reconnects.
* @return the clean session flag
diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttTopic.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttTopic.java
index febec3bfe..404e9610c 100644
--- a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttTopic.java
+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/MqttTopic.java
@@ -3,11 +3,11 @@
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
- * and Eclipse Distribution License v1.0 which accompany this distribution.
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
*
- * The Eclipse Public License is available at
+ * The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
- * and the Eclipse Distribution License is available at
+ * and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
@@ -25,7 +25,7 @@
* Represents a topic destination, used for publish/subscribe messaging.
*/
public class MqttTopic {
-
+
/**
* The forward slash (/) is used to separate each level within a topic tree
* and provide a hierarchical structure to the topic space. The use of the
@@ -45,7 +45,7 @@ public class MqttTopic {
* matches only one topic level.
*/
public static final String SINGLE_LEVEL_WILDCARD = "+";
-
+
/**
* Multi-level wildcard pattern(/#)
*/
@@ -55,15 +55,15 @@ public class MqttTopic {
* Topic wildcards (#+)
*/
public static final String TOPIC_WILDCARDS = MULTI_LEVEL_WILDCARD + SINGLE_LEVEL_WILDCARD;
-
+
//topic name and topic filter length range defined in the spec
private static final int MIN_TOPIC_LEN = 1;
private static final int MAX_TOPIC_LEN = 65535;
private static final char NUL = '\u0000';
-
+
private ClientComms comms;
private String name;
-
+
/**
* @param name The Name of the topic
* @param comms The {@link ClientComms}
@@ -72,12 +72,12 @@ public MqttTopic(String name, ClientComms comms) {
this.comms = comms;
this.name = name;
}
-
+
/**
- * Publishes a message on the topic. This is a convenience method, which will
+ * Publishes a message on the topic. This is a convenience method, which will
* create a new {@link MqttMessage} object with a byte array payload and the
* specified QoS, and then publish it. All other values in the
- * message will be set to the defaults.
+ * message will be set to the defaults.
* @param payload the byte array to use as the payload
* @param qos the Quality of Service. Valid values are 0, 1 or 2.
@@ -96,15 +96,15 @@ public MqttDeliveryToken publish(byte[] payload, int qos, boolean retained) thro
message.setRetained(retained);
return this.publish(message);
}
-
+
/**
- * Publishes the specified message to this topic, but does not wait for delivery
+ * Publishes the specified message to this topic, but does not wait for delivery
* of the message to complete. The returned {@link MqttDeliveryToken token} can be used
- * to track the delivery status of the message. Once this method has
+ * to track the delivery status of the message. Once this method has
* returned cleanly, the message has been accepted for publication by the
- * client. Message delivery will be completed in the background when a connection
+ * client. Message delivery will be completed in the background when a connection
* is available.
- *
+ *
* @param message the message to publish
* @return an MqttDeliveryToken for tracking the delivery of the message
* @throws MqttException if an error occurs publishing the message
@@ -117,23 +117,23 @@ public MqttDeliveryToken publish(MqttMessage message) throws MqttException, Mqtt
token.internalTok.waitUntilSent();
return token;
}
-
+
/**
* Returns the name of the queue or topic.
- *
+ *
* @return the name of this destination.
*/
public String getName() {
return name;
}
-
+
/**
* Create a PUBLISH packet from the specified message.
*/
private MqttPublish createPublish(MqttMessage message) {
return new MqttPublish(this.getName(), message);
}
-
+
/**
* Returns a string representation of this topic.
* @return a string representation of this topic.
@@ -141,21 +141,20 @@ private MqttPublish createPublish(MqttMessage message) {
public String toString() {
return getName();
}
-
+
/**
* Validate the topic name or topic filter
- *
+ *
* @param topicString topic name or filter
* @param wildcardAllowed true if validate topic filter, false otherwise
* @throws IllegalArgumentException if the topic is invalid
*/
- public static void validate(String topicString, boolean wildcardAllowed)
+ public static void validate(String topicString, boolean wildcardAllowed)
throws IllegalArgumentException{
int topicLen = 0;
try {
topicLen = topicString.getBytes("UTF-8").length;
} catch (UnsupportedEncodingException e) {
- // throw new IllegalStateException(e); // Cannot use for Java 1.4.2
throw new IllegalStateException(e.getMessage());
}
@@ -165,10 +164,8 @@ public static void validate(String topicString, boolean wildcardAllowed)
// - Topic Names and Topic Filters are UTF-8 encoded strings, they MUST
// NOT encode to more than 65535 bytes
if (topicLen < MIN_TOPIC_LEN || topicLen > MAX_TOPIC_LEN) {
- //throw new IllegalArgumentException(String.format("Invalid topic length, should be in range[%d, %d]!", // Cannot use for Java 1.4.2
- // new Object[] { new Integer(MIN_TOPIC_LEN), new Integer(MAX_TOPIC_LEN) }));
- String errorMessage = "Invalid topic length, should be in range["+ MIN_TOPIC_LEN + ", " + MAX_TOPIC_LEN + "]!";
- throw new IllegalArgumentException(errorMessage);
+ throw new IllegalArgumentException(String.format("Invalid topic length, should be in range[%d, %d]!",
+ new Object[] { new Integer(MIN_TOPIC_LEN), new Integer(MAX_TOPIC_LEN) }));
}
// *******************************************************************************
@@ -189,8 +186,7 @@ public static void validate(String topicString, boolean wildcardAllowed)
// - The multi-level wildcard must be the last character used within
// the topic tree
if (Strings.countMatches(topicString, MULTI_LEVEL_WILDCARD) > 1
- //|| (topicString.contains(MULTI_LEVEL_WILDCARD) && !topicString// Cannot use for Java 1.4.2
- || ((topicString.indexOf(MULTI_LEVEL_WILDCARD) != -1) && !topicString
+ || (topicString.contains(MULTI_LEVEL_WILDCARD) && !topicString
.endsWith(MULTI_LEVEL_WILDCARD_PATTERN))) {
throw new IllegalArgumentException(
"Invalid usage of multi-level wildcard in topic string: "
@@ -217,7 +213,7 @@ public static void validate(String topicString, boolean wildcardAllowed)
"The topic name MUST NOT contain any wildcard characters (#+)");
}
}
-
+
private static void validateSingleLevelWildcard(String topicString) {
char singleLevelWildcardChar = SINGLE_LEVEL_WILDCARD.charAt(0);
char topicLevelSeparatorChar = TOPIC_LEVEL_SEPARATOR.charAt(0);
@@ -232,43 +228,42 @@ private static void validateSingleLevelWildcard(String topicString) {
if (chars[i] == singleLevelWildcardChar) {
// prev and next can be only '/' or none
if (prev != topicLevelSeparatorChar && prev != NUL || next != topicLevelSeparatorChar && next != NUL) {
-// throw new IllegalArgumentException(String.format( // Cannot use for Java 1.4.2
-// "Invalid usage of single-level wildcard in topic string '%s'!",
-// new Object[] { topicString }));
- String errorMessage = "Invalid usage of single-level wildcard in topic string '" + topicString + "'!";
- throw new IllegalArgumentException(errorMessage);
+ throw new IllegalArgumentException(String.format(
+ "Invalid usage of single-level wildcard in topic string '%s'!",
+ new Object[] { topicString }));
+
}
}
}
}
-
+
/**
* Check the supplied topic name and filter match
- *
+ *
* @param topicFilter topic filter: wildcards allowed
* @param topicName topic name: wildcards not allowed
* @return true if the topic matches the filter
* @throws IllegalArgumentException if the topic name or filter is invalid
*/
- public static boolean isMatched(String topicFilter, String topicName)
+ public static boolean isMatched(String topicFilter, String topicName)
throws IllegalArgumentException {
int curn = 0,
curf = 0;
int curn_end = topicName.length();
int curf_end = topicFilter.length();
-
+
MqttTopic.validate(topicFilter, true);
MqttTopic.validate(topicName, false);
if (topicFilter.equals(topicName)) {
return true;
}
-
+
while (curf < curf_end && curn < curn_end)
{
if (topicName.charAt(curn) == '/' && topicFilter.charAt(curf) != '/')
break;
- if (topicFilter.charAt(curf) != '+' && topicFilter.charAt(curf) != '#' &&
+ if (topicFilter.charAt(curf) != '+' && topicFilter.charAt(curf) != '#' &&
topicFilter.charAt(curf) != topicName.charAt(curn))
break;
if (topicFilter.charAt(curf) == '+')
@@ -285,5 +280,5 @@ else if (topicFilter.charAt(curf) == '#')
return (curn == curn_end) && (curf == curf_end);
}
-
+
}
diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/ScheduledExecutorPingSender.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/ScheduledExecutorPingSender.java
new file mode 100644
index 000000000..511909c9e
--- /dev/null
+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/ScheduledExecutorPingSender.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (c) 2014 IBM Corp.
+ * Copyright (c) 2017 BMW Car IT GmbH.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ */
+
+package org.eclipse.paho.client.mqttv3;
+
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.paho.client.mqttv3.internal.ClientComms;
+import org.eclipse.paho.client.mqttv3.logging.Logger;
+import org.eclipse.paho.client.mqttv3.logging.LoggerFactory;
+
+/**
+ * Default ping sender implementation
+ *
+ * This class implements the {@link IMqttPingSender} pinger interface
+ * allowing applications to send ping packet to server every keep alive interval.
+ *
+ *
+ * @see MqttPingSender
+ */
+public class ScheduledExecutorPingSender implements MqttPingSender {
+ private static final String CLASS_NAME = ScheduledExecutorPingSender.class.getName();
+ private static final Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT, CLASS_NAME);
+
+ private ClientComms comms;
+ private ScheduledExecutorService executorService;
+ private ScheduledFuture scheduledFuture;
+ private String clientid;
+
+ public ScheduledExecutorPingSender(ScheduledExecutorService executorService) {
+ if (executorService == null) {
+ throw new IllegalArgumentException("ExecutorService cannot be null.");
+ }
+ this.executorService = executorService;
+ }
+
+ public void init(ClientComms comms) {
+ if (comms == null) {
+ throw new IllegalArgumentException("ClientComms cannot be null.");
+ }
+ this.comms = comms;
+ clientid = comms.getClient().getClientId();
+ }
+
+ public void start() {
+ final String methodName = "start";
+
+ //@Trace 659=start timer for client:{0}
+ log.fine(CLASS_NAME, methodName, "659", new Object[]{ clientid });
+ //Check ping after first keep alive interval.
+ schedule(comms.getKeepAlive());
+ }
+
+ public void stop() {
+ final String methodName = "stop";
+ //@Trace 661=stop
+ log.fine(CLASS_NAME, methodName, "661", null);
+ if (scheduledFuture != null) {
+ scheduledFuture.cancel(true);
+ }
+ }
+
+ public void schedule(long delayInMilliseconds) {
+ scheduledFuture = executorService.schedule(new PingRunnable(), delayInMilliseconds, TimeUnit.MILLISECONDS);
+ }
+
+ private class PingRunnable implements Runnable {
+ private static final String methodName = "PingTask.run";
+
+ public void run() {
+ String originalThreadName = Thread.currentThread().getName();
+ Thread.currentThread().setName("MQTT Ping: " + clientid);
+ //@Trace 660=Check schedule at {0}
+ log.fine(CLASS_NAME, methodName, "660", new Object[]{ new Long(System.currentTimeMillis()) });
+ comms.checkForActivity();
+ Thread.currentThread().setName(originalThreadName);
+ }
+ }
+}
diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/TimerPingSender.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/TimerPingSender.java
index 2d8e6d2be..4c9321256 100644
--- a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/TimerPingSender.java
+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/TimerPingSender.java
@@ -50,8 +50,7 @@ public void start() {
//@Trace 659=start timer for client:{0}
log.fine(CLASS_NAME, methodName, "659", new Object[]{clientid});
- //timer = new Timer("MQTT Ping: " + clientid); // Cannot use for Java 1.4.2
- timer = new Timer();
+ timer = new Timer("MQTT Ping: " + clientid);
//Check ping after first keep alive interval.
timer.schedule(new PingTask(), comms.getKeepAlive());
}
diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/ClientState.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/ClientState.java
index d2d1e2078..537693713 100644
--- a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/ClientState.java
+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/ClientState.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2009, 2016 IBM Corp.
+ * Copyright (c) 2009, 2017 IBM Corp and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -17,6 +17,7 @@
* James Sutton - Ping Callback (bug 473928)
* Ian Craggs - fix for NPE bug 470718
* James Sutton - Automatic Reconnect & Offline Buffering
+ * Jens Reimann - Fix issue #370
*/
package org.eclipse.paho.client.mqttv3.internal;
@@ -46,6 +47,9 @@
import org.eclipse.paho.client.mqttv3.internal.wire.MqttPubRel;
import org.eclipse.paho.client.mqttv3.internal.wire.MqttPublish;
import org.eclipse.paho.client.mqttv3.internal.wire.MqttSuback;
+import org.eclipse.paho.client.mqttv3.internal.wire.MqttSubscribe;
+import org.eclipse.paho.client.mqttv3.internal.wire.MqttUnsubAck;
+import org.eclipse.paho.client.mqttv3.internal.wire.MqttUnsubscribe;
import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage;
import org.eclipse.paho.client.mqttv3.logging.Logger;
import org.eclipse.paho.client.mqttv3.logging.LoggerFactory;
@@ -480,7 +484,18 @@ private void restoreInflightMessages() {
public void send(MqttWireMessage message, MqttToken token) throws MqttException {
final String methodName = "send";
if (message.isMessageIdRequired() && (message.getMessageId() == 0)) {
- message.setMessageId(getNextMessageId());
+ if(message instanceof MqttPublish && (((MqttPublish) message).getMessage().getQos() != 0)){
+ message.setMessageId(getNextMessageId());
+ }else if(message instanceof MqttPubAck ||
+ message instanceof MqttPubRec ||
+ message instanceof MqttPubRel ||
+ message instanceof MqttPubComp ||
+ message instanceof MqttSubscribe ||
+ message instanceof MqttSuback ||
+ message instanceof MqttUnsubscribe ||
+ message instanceof MqttUnsubAck){
+ message.setMessageId(getNextMessageId());
+ }
}
if (token != null ) {
try {
@@ -1340,7 +1355,9 @@ public int getMaxInFlight(){
*/
protected void close() {
inUseMsgIds.clear();
- pendingMessages.clear();
+ if (pendingMessages != null) {
+ pendingMessages.clear();
+ }
pendingFlows.clear();
outboundQoS2.clear();
outboundQoS1.clear();
diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/CommsCallback.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/CommsCallback.java
index 94e1f9491..bb6d77c46 100644
--- a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/CommsCallback.java
+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/CommsCallback.java
@@ -21,6 +21,9 @@
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.Semaphore;
import org.eclipse.paho.client.mqttv3.IMqttActionListener;
import org.eclipse.paho.client.mqttv3.IMqttMessageListener;
@@ -61,6 +64,9 @@ public class CommsCallback implements Runnable {
private Object spaceAvailable = new Object();
private ClientState clientState;
private boolean manualAcks = false;
+ private String threadName;
+ private final Semaphore runningSemaphore = new Semaphore(1);
+ private Future callbackFuture;
CommsCallback(ClientComms clientComms) {
this.clientComms = clientComms;
@@ -77,8 +83,10 @@ public void setClientState(ClientState clientState) {
/**
* Starts up the Callback thread.
* @param threadName The name of the thread
+ * @param executorService the {@link ExecutorService}
*/
- public void start(String threadName) {
+ public void start(String threadName, ExecutorService executorService) {
+ this.threadName = threadName;
synchronized (lifecycle) {
if (!running) {
// Preparatory work before starting the background thread.
@@ -88,8 +96,7 @@ public void start(String threadName) {
running = true;
quiescing = false;
- callbackThread = new Thread(this, threadName);
- callbackThread.start();
+ callbackFuture = executorService.submit(this);
}
}
}
@@ -101,6 +108,9 @@ public void start(String threadName) {
public void stop() {
final String methodName = "stop";
synchronized (lifecycle) {
+ if (callbackFuture != null) {
+ callbackFuture.cancel(true);
+ }
if (running) {
// @TRACE 700=stopping
log.fine(CLASS_NAME, methodName, "700");
@@ -114,8 +124,10 @@ public void stop() {
workAvailable.notifyAll();
}
// Wait for the thread to finish.
- callbackThread.join();
+ runningSemaphore.acquire();
} catch (InterruptedException ex) {
+ } finally {
+ runningSemaphore.release();
}
}
}
@@ -139,6 +151,16 @@ public void setManualAcks(boolean manualAcks) {
public void run() {
final String methodName = "run";
+ callbackThread = Thread.currentThread();
+ callbackThread.setName(threadName);
+
+ try {
+ runningSemaphore.acquire();
+ } catch (InterruptedException e) {
+ running = false;
+ return;
+ }
+
while (running) {
try {
// If no work is currently available, then wait until there is some...
@@ -196,8 +218,8 @@ public void run() {
log.fine(CLASS_NAME, methodName, "714", null, ex);
running = false;
clientComms.shutdownConnection(null, new MqttException(ex));
-
} finally {
+ runningSemaphore.release();
synchronized (spaceAvailable) {
// Notify the spaceAvailable lock, to say that there's now
// some space on the queue...
@@ -481,4 +503,4 @@ protected boolean deliverMessage(String topicName, int messageId, MqttMessage aM
return delivered;
}
-}
\ No newline at end of file
+}
diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/CommsReceiver.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/CommsReceiver.java
index 25408d877..5bd2db426 100644
--- a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/CommsReceiver.java
+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/CommsReceiver.java
@@ -17,6 +17,9 @@
import java.io.IOException;
import java.io.InputStream;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.Semaphore;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttToken;
@@ -44,6 +47,10 @@ public class CommsReceiver implements Runnable {
private CommsTokenStore tokenStore = null;
private Thread recThread = null;
private volatile boolean receiving;
+ private final Semaphore runningSemaphore = new Semaphore(1);
+ private String threadName;
+ private Future receiverFuture;
+
public CommsReceiver(ClientComms clientComms, ClientState clientState,CommsTokenStore tokenStore, InputStream in) {
this.in = new MqttInputStream(clientState, in);
@@ -55,17 +62,18 @@ public CommsReceiver(ClientComms clientComms, ClientState clientState,CommsToken
/**
* Starts up the Receiver's thread.
- * @param threadName The name of the thread
+ * @param threadName the thread name.
+ * @param executorService used to execute the thread
*/
- public void start(String threadName) {
+ public void start(String threadName, ExecutorService executorService) {
+ this.threadName = threadName;
final String methodName = "start";
//@TRACE 855=starting
log.fine(CLASS_NAME,methodName, "855");
synchronized (lifecycle) {
if (!running) {
running = true;
- recThread = new Thread(this, threadName);
- recThread.start();
+ receiverFuture = executorService.submit(this);
}
}
}
@@ -76,6 +84,9 @@ public void start(String threadName) {
public void stop() {
final String methodName = "stop";
synchronized (lifecycle) {
+ if (receiverFuture != null) {
+ receiverFuture.cancel(true);
+ }
//@TRACE 850=stopping
log.fine(CLASS_NAME,methodName, "850");
if (running) {
@@ -84,14 +95,11 @@ public void stop() {
if (!Thread.currentThread().equals(recThread)) {
try {
// Wait for the thread to finish.
- // It is always a good idea to set a timeout.
- // WARNING: the timeout must be correlated with the
- // socket read timeout.
- // Unfortunately we cannot access the socket here to
- // get its read timeout.
- recThread.join(1500);
+ runningSemaphore.acquire();
}
catch (InterruptedException ex) {
+ } finally {
+ runningSemaphore.release();
}
}
}
@@ -105,9 +113,18 @@ public void stop() {
* Run loop to receive messages from the server.
*/
public void run() {
+ recThread = Thread.currentThread();
+ recThread.setName(threadName);
final String methodName = "run";
MqttToken token = null;
+ try {
+ runningSemaphore.acquire();
+ } catch (InterruptedException e) {
+ running = false;
+ return;
+ }
+
while (running && (in != null)) {
try {
//@TRACE 852=network read message
@@ -166,6 +183,7 @@ public void run() {
}
finally {
receiving = false;
+ runningSemaphore.release();
}
}
diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/CommsSender.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/CommsSender.java
index 6f06254ae..3697a98de 100644
--- a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/CommsSender.java
+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/CommsSender.java
@@ -3,11 +3,11 @@
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
- * and Eclipse Distribution License v1.0 which accompany this distribution.
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
*
- * The Eclipse Public License is available at
+ * The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
- * and the Eclipse Distribution License is available at
+ * and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
@@ -17,6 +17,10 @@
import java.io.IOException;
import java.io.OutputStream;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttToken;
@@ -40,7 +44,11 @@ public class CommsSender implements Runnable {
private ClientComms clientComms = null;
private CommsTokenStore tokenStore = null;
private Thread sendThread = null;
-
+
+ private String threadName;
+ private final Semaphore runningSemaphore = new Semaphore(1);
+ private Future senderFuture;
+
public CommsSender(ClientComms clientComms, ClientState clientState, CommsTokenStore tokenStore, OutputStream out) {
this.out = new MqttOutputStream(clientState, out);
this.clientComms = clientComms;
@@ -48,17 +56,18 @@ public CommsSender(ClientComms clientComms, ClientState clientState, CommsTokenS
this.tokenStore = tokenStore;
log.setResourceName(clientComms.getClient().getClientId());
}
-
+
/**
* Starts up the Sender thread.
- * @param threadName The name of the thread
+ * @param threadName the threadname
+ * @param executorService used to execute the thread
*/
- public void start(String threadName) {
+ public void start(String threadName, ExecutorService executorService) {
+ this.threadName = threadName;
synchronized (lifecycle) {
if (!running) {
running = true;
- sendThread = new Thread(this, threadName);
- sendThread.start();
+ senderFuture = executorService.submit(this);
}
}
}
@@ -68,22 +77,26 @@ public void start(String threadName) {
*/
public void stop() {
final String methodName = "stop";
-
+
synchronized (lifecycle) {
+ if (senderFuture != null) {
+ senderFuture.cancel(true);
+ }
//@TRACE 800=stopping sender
log.fine(CLASS_NAME,methodName,"800");
if (running) {
running = false;
if (!Thread.currentThread().equals(sendThread)) {
try {
- while(sendThread.isAlive()){
+ while (running) {
// first notify get routine to finish
clientState.notifyQueueLock();
// Wait for the thread to finish.
- sendThread.join(100);
+ runningSemaphore.tryAcquire(100, TimeUnit.MILLISECONDS);
}
- }
- catch (InterruptedException ex) {
+ } catch (InterruptedException ex) {
+ } finally {
+ runningSemaphore.release();
}
}
}
@@ -92,54 +105,69 @@ public void stop() {
log.fine(CLASS_NAME,methodName,"801");
}
}
-
+
public void run() {
+ sendThread = Thread.currentThread();
+ sendThread.setName(threadName);
final String methodName = "run";
MqttWireMessage message = null;
- while (running && (out != null)) {
- try {
- message = clientState.get();
- if (message != null) {
- //@TRACE 802=network send key={0} msg={1}
- log.fine(CLASS_NAME,methodName,"802", new Object[] {message.getKey(),message});
-
- if (message instanceof MqttAck) {
- out.write(message);
- out.flush();
- } else {
- MqttToken token = tokenStore.getToken(message);
- // While quiescing the tokenstore can be cleared so need
- // to check for null for the case where clear occurs
- // while trying to send a message.
- if (token != null) {
- synchronized (token) {
- out.write(message);
- try {
- out.flush();
- } catch (IOException ex) {
- // The flush has been seen to fail on disconnect of a SSL socket
- // as disconnect is in progress this should not be treated as an error
- if (!(message instanceof MqttDisconnect)) {
- throw ex;
+
+ try {
+ runningSemaphore.acquire();
+ } catch (InterruptedException e) {
+ running = false;
+ return;
+ }
+
+ try {
+ while (running && (out != null)) {
+ try {
+ message = clientState.get();
+ if (message != null) {
+ //@TRACE 802=network send key={0} msg={1}
+ log.fine(CLASS_NAME,methodName,"802", new Object[] {message.getKey(),message});
+
+ if (message instanceof MqttAck) {
+ out.write(message);
+ out.flush();
+ } else {
+ MqttToken token = tokenStore.getToken(message);
+ // While quiescing the tokenstore can be cleared so need
+ // to check for null for the case where clear occurs
+ // while trying to send a message.
+ if (token != null) {
+ synchronized (token) {
+ out.write(message);
+ try {
+ out.flush();
+ } catch (IOException ex) {
+ // The flush has been seen to fail on disconnect of a SSL socket
+ // as disconnect is in progress this should not be treated as an error
+ if (!(message instanceof MqttDisconnect)) {
+ throw ex;
+ }
}
+ clientState.notifySent(message);
}
- clientState.notifySent(message);
}
}
- }
- } else { // null message
- //@TRACE 803=get message returned null, stopping}
- log.fine(CLASS_NAME,methodName,"803");
+ } else { // null message
+ //@TRACE 803=get message returned null, stopping}
+ log.fine(CLASS_NAME,methodName,"803");
- running = false;
+ running = false;
+ }
+ } catch (MqttException me) {
+ handleRunException(message, me);
+ } catch (Exception ex) {
+ handleRunException(message, ex);
}
- } catch (MqttException me) {
- handleRunException(message, me);
- } catch (Exception ex) {
- handleRunException(message, ex);
- }
- } // end while
-
+ } // end while
+ } finally {
+ running = false;
+ runningSemaphore.release();
+ }
+
//@TRACE 805=<
log.fine(CLASS_NAME, methodName,"805");
diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/ConnectActionListener.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/ConnectActionListener.java
index 53b199f29..aa6bb8d53 100644
--- a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/ConnectActionListener.java
+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/ConnectActionListener.java
@@ -92,9 +92,7 @@ public void onSuccess(IMqttToken token) {
userToken.internalTok.notifyComplete();
userToken.internalTok.setClient(this.client); // fix bug 469527 - maybe should be set elsewhere?
- if(reconnect){
- comms.notifyReconnect();
- }
+ comms.notifyConnect();
if (userCallback != null) {
userToken.setUserContext(userContext);
diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/LocalNetworkModule.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/LocalNetworkModule.java
deleted file mode 100644
index 29c34557b..000000000
--- a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/LocalNetworkModule.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2009, 2014 IBM Corp.
- *
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * and Eclipse Distribution License v1.0 which accompany this distribution.
- *
- * The Eclipse Public License is available at
- * http://www.eclipse.org/legal/epl-v10.html
- * and the Eclipse Distribution License is available at
- * http://www.eclipse.org/org/documents/edl-v10.php.
- *
- * Contributors:
- * Dave Locke - initial API and implementation and/or initial documentation
- */
-package org.eclipse.paho.client.mqttv3.internal;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.lang.reflect.Method;
-
-import org.eclipse.paho.client.mqttv3.MqttException;
-
-
-/**
- * Special comms class that allows an MQTT client to use a non TCP / optimised
- * mechanism to talk to an MQTT server when running in the same JRE instance as the
- * MQTT server.
- *
- * This class checks for the existence of the optimised comms adatper class i.e. the one
- * that provides the optimised communication mechanism. If not available the request
- * to connect using the optimised mechanism is rejected.
- *
- * The only known server that implements this is the microbroker:- an MQTT server that
- * ships with a number of IBM products.
- */
-public class LocalNetworkModule implements NetworkModule {
- private Class localListener;
- private String brokerName;
- private Object localAdapter;
-
- public LocalNetworkModule(String brokerName) {
- this.brokerName = brokerName;
- }
-
- public void start() throws IOException, MqttException{
- if (!ExceptionHelper.isClassAvailable("com.ibm.mqttdirect.modules.local.bindings.localListener")) {
- throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_SERVER_CONNECT_ERROR);
- }
- try {
- localListener = Class.forName("com.ibm.mqttdirect.modules.local.bindings.localListener");
- Method connect_m = localListener.getMethod("connect", new Class[]{ java.lang.String.class });
- localAdapter = connect_m.invoke(null,new Object[]{ brokerName });
- } catch(Exception e) {
- }
- if(localAdapter == null) {
- throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_SERVER_CONNECT_ERROR);
- }
- }
-
- public InputStream getInputStream() throws IOException {
- InputStream stream = null;
- try {
- Method m = localListener.getMethod("getClientInputStream",new Class[]{});
- stream = (InputStream)m.invoke(this.localAdapter,new Object[]{});
- } catch(Exception e) {
- }
- return stream;
- }
-
- public OutputStream getOutputStream() throws IOException {
- OutputStream stream = null;
- try {
- Method m = localListener.getMethod("getClientOutputStream",new Class[]{});
- stream = (OutputStream)m.invoke(this.localAdapter,new Object[]{});
- } catch(Exception e) {
- }
- return stream;
- }
-
- public void stop() throws IOException {
- if (localAdapter != null) {
- try {
- Method m = localListener.getMethod("close",new Class[]{});
- m.invoke(this.localAdapter,new Object[]{});
- } catch(Exception e) {
- }
- }
- }
-
- public String getServerURI() {
- return "local://" + brokerName;
- }
-}
diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/SSLNetworkModule.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/SSLNetworkModule.java
index f0a78cd17..a20f3c6b3 100644
--- a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/SSLNetworkModule.java
+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/SSLNetworkModule.java
@@ -17,6 +17,8 @@
import java.io.IOException;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
@@ -33,6 +35,7 @@ public class SSLNetworkModule extends TCPNetworkModule {
private String[] enabledCiphers;
private int handshakeTimeoutSecs;
+ private HostnameVerifier hostnameVerifier;
private String host;
private int port;
@@ -88,6 +91,14 @@ public void setSSLhandshakeTimeout(int timeout) {
this.handshakeTimeoutSecs = timeout;
}
+ public HostnameVerifier getSSLHostnameVerifier() {
+ return hostnameVerifier;
+ }
+
+ public void setSSLHostnameVerifier(HostnameVerifier hostnameVerifier) {
+ this.hostnameVerifier = hostnameVerifier;
+ }
+
public void start() throws IOException, MqttException {
super.start();
setEnabledCiphers(enabledCiphers);
@@ -95,6 +106,10 @@ public void start() throws IOException, MqttException {
// RTC 765: Set a timeout to avoid the SSL handshake being blocked indefinitely
socket.setSoTimeout(this.handshakeTimeoutSecs*1000);
((SSLSocket)socket).startHandshake();
+ if (hostnameVerifier != null) {
+ SSLSession session = ((SSLSocket)socket).getSession();
+ hostnameVerifier.verify(host, session);
+ }
// reset timeout to default value
socket.setSoTimeout(soTimeout);
}
diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/TCPNetworkModule.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/TCPNetworkModule.java
index 9a8e90765..51fb34677 100644
--- a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/TCPNetworkModule.java
+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/TCPNetworkModule.java
@@ -3,11 +3,11 @@
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
- * and Eclipse Distribution License v1.0 which accompany this distribution.
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
*
- * The Eclipse Public License is available at
+ * The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
- * and the Eclipse Distribution License is available at
+ * and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
@@ -25,13 +25,14 @@
import java.net.SocketAddress;
import javax.net.SocketFactory;
+import javax.net.ssl.SSLSocketFactory;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.logging.Logger;
import org.eclipse.paho.client.mqttv3.logging.LoggerFactory;
/**
- * A network module for connecting over TCP.
+ * A network module for connecting over TCP.
*/
public class TCPNetworkModule implements NetworkModule {
private static final String CLASS_NAME = TCPNetworkModule.class.getName();
@@ -42,7 +43,7 @@ public class TCPNetworkModule implements NetworkModule {
private String host;
private int port;
private int conTimeout;
-
+
/**
* Constructs a new TCPNetworkModule using the specified host and
* port. The supplied SocketFactory is used to supply the network
@@ -57,7 +58,7 @@ public TCPNetworkModule(SocketFactory factory, String host, int port, String res
this.factory = factory;
this.host = host;
this.port = port;
-
+
}
/**
@@ -72,15 +73,18 @@ public void start() throws IOException, MqttException {
// socket = factory.createSocket(host, port, localAddr, 0);
// @TRACE 252=connect to host {0} port {1} timeout {2}
log.fine(CLASS_NAME,methodName, "252", new Object[] {host, new Integer(port), new Long(conTimeout*1000)});
- SocketAddress sockaddr = new InetSocketAddress(InetAddress.getByName(host), port);
- socket = factory.createSocket();
- // Set a read timeout on the socket.
- // If you change the value here you should also change
- // the value in CommsReceiver.stop().
- socket.setSoTimeout(1000);
- socket.connect(sockaddr, conTimeout*1000);
+ SocketAddress sockaddr = new InetSocketAddress(host, port);
+ if (factory instanceof SSLSocketFactory) {
+ // SNI support
+ Socket tempsocket = new Socket();
+ tempsocket.connect(sockaddr, conTimeout*1000);
+ socket = ((SSLSocketFactory)factory).createSocket(tempsocket, host, port, true);
+ } else {
+ socket = factory.createSocket();
+ socket.connect(sockaddr, conTimeout*1000);
+ }
- // SetTcpNoDelay was originally set ot true disabling Nagle's algorithm.
+ // SetTcpNoDelay was originally set ot true disabling Nagle's algorithm.
// This should not be required.
// socket.setTcpNoDelay(true); // TCP_NODELAY on, which means we do not use Nagle's algorithm
}
@@ -94,7 +98,7 @@ public void start() throws IOException, MqttException {
public InputStream getInputStream() throws IOException {
return socket.getInputStream();
}
-
+
public OutputStream getOutputStream() throws IOException {
return socket.getOutputStream();
}
@@ -127,7 +131,7 @@ public void stop() throws IOException {
socket.close();
}
}
-
+
/**
* Set the maximum time to wait for a socket to be established
* @param timeout The connection timeout
diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/websocket/ExtendedByteArrayOutputStream.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/websocket/ExtendedByteArrayOutputStream.java
new file mode 100644
index 000000000..2caee99e9
--- /dev/null
+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/websocket/ExtendedByteArrayOutputStream.java
@@ -0,0 +1,49 @@
+package org.eclipse.paho.client.mqttv3.internal.websocket;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+
+import org.eclipse.paho.client.mqttv3.internal.TCPNetworkModule;
+
+class ExtendedByteArrayOutputStream extends ByteArrayOutputStream {
+
+ final WebSocketNetworkModule webSocketNetworkModule;
+ final WebSocketSecureNetworkModule webSocketSecureNetworkModule;
+
+ ExtendedByteArrayOutputStream(WebSocketNetworkModule module) {
+ this.webSocketNetworkModule = module;
+ this.webSocketSecureNetworkModule = null;
+ }
+
+ ExtendedByteArrayOutputStream(WebSocketSecureNetworkModule module) {
+ this.webSocketNetworkModule = null;
+ this.webSocketSecureNetworkModule = module;
+ }
+
+ public void flush() throws IOException {
+ final ByteBuffer byteBuffer;
+ synchronized (this) {
+ byteBuffer = ByteBuffer.wrap(toByteArray());
+ reset();
+ }
+ WebSocketFrame frame = new WebSocketFrame((byte)0x02, true, byteBuffer.array());
+ byte[] rawFrame = frame.encodeFrame();
+ getSocketOutputStream().write(rawFrame);
+ getSocketOutputStream().flush();
+
+ }
+
+ OutputStream getSocketOutputStream() throws IOException {
+
+ if(webSocketNetworkModule != null ){
+ return webSocketNetworkModule.getSocketOutputStream();
+ }
+ if(webSocketSecureNetworkModule != null){
+ return webSocketSecureNetworkModule.getSocketOutputStream();
+ }
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketHandshake.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketHandshake.java
index 1738d45e8..92be90137 100644
--- a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketHandshake.java
+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketHandshake.java
@@ -3,11 +3,11 @@
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
- * and Eclipse Distribution License v1.0 which accompany this distribution.
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
*
- * The Eclipse Public License is available at
+ * The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
- * and the Eclipse Distribution License is available at
+ * and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
@@ -28,11 +28,12 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
+import java.util.UUID;
/**
* Helper class to execute a WebSocket Handshake.
*/
public class WebSocketHandshake {
-
+
// Do not change: https://tools.ietf.org/html/rfc6455#section-1.3
private static final String ACCEPT_SALT = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
private static final String SHA1_PROTOCOL = "SHA1";
@@ -51,8 +52,8 @@ public class WebSocketHandshake {
String uri;
String host;
int port;
-
-
+
+
public WebSocketHandshake(InputStream input, OutputStream output, String uri, String host, int port){
this.input = input;
this.output = output;
@@ -61,19 +62,20 @@ public WebSocketHandshake(InputStream input, OutputStream output, String uri, St
this.port = port;
}
-
+
/**
* Executes a Websocket Handshake.
* Will throw an IOException if the handshake fails
* @throws IOException thrown if an exception occurs during the handshake
*/
public void execute() throws IOException {
- String key = "mqtt3-" + (System.currentTimeMillis()/1000);
- String b64Key = Base64.encode(key);
+ byte[] key = new byte[16];
+ System.arraycopy(UUID.randomUUID().toString().getBytes(), 0, key, 0, 16);
+ String b64Key = Base64.encodeBytes(key);
sendHandshakeRequest(b64Key);
receiveHandshakeResponse(b64Key);
}
-
+
/**
* Builds and sends the HTTP Header GET Request
* for the socket.
@@ -84,11 +86,9 @@ private void sendHandshakeRequest(String key) throws IOException{
try {
String path = "/mqtt";
URI srvUri = new URI(uri);
- //if (srvUri.getRawPath() != null && !srvUri.getRawPath().isEmpty()) { // Cannot use for Java 1.4.2
- if (srvUri.getRawPath() != null && srvUri.getRawPath().length() != 0) {
+ if (srvUri.getRawPath() != null && !srvUri.getRawPath().isEmpty()) {
path = srvUri.getRawPath();
- //if (srvUri.getRawQuery() != null && !srvUri.getRawQuery().isEmpty()) { // Cannot use for Java 1.4.2
- if (srvUri.getRawQuery() != null && srvUri.getRawQuery().length() != 0) {
+ if (srvUri.getRawQuery() != null && !srvUri.getRawQuery().isEmpty()) {
path += "?" + srvUri.getRawQuery();
}
}
@@ -105,7 +105,7 @@ private void sendHandshakeRequest(String key) throws IOException{
pw.print("Upgrade: websocket" + LINE_SEPARATOR);
pw.print("Connection: Upgrade" + LINE_SEPARATOR);
pw.print("Sec-WebSocket-Key: " + key + LINE_SEPARATOR);
- pw.print("Sec-WebSocket-Protocol: mqttv3.1" + LINE_SEPARATOR);
+ pw.print("Sec-WebSocket-Protocol: mqtt" + LINE_SEPARATOR);
pw.print("Sec-WebSocket-Version: 13" + LINE_SEPARATOR);
String userInfo = srvUri.getUserInfo();
@@ -116,11 +116,10 @@ private void sendHandshakeRequest(String key) throws IOException{
pw.print(LINE_SEPARATOR);
pw.flush();
} catch (URISyntaxException e) {
- // throw new IllegalStateException(e.getMessage()); // Cannot use for Java 1.4.2
throw new IllegalStateException(e.getMessage());
}
}
-
+
/**
* Receives the Handshake response and verifies that it is valid.
* @param key Base64 encoded key
@@ -145,8 +144,7 @@ private void receiveHandshakeResponse(String key) throws IOException {
}
String upgradeHeader = (String) headerMap.get(HTTP_HEADER_UPGRADE);
- //if(!upgradeHeader.toLowerCase().contains(HTTP_HEADER_UPGRADE_WEBSOCKET)){ // Cannot use for Java 1.4.2
- if(upgradeHeader.toLowerCase().indexOf(HTTP_HEADER_UPGRADE_WEBSOCKET) == -1){
+ if(upgradeHeader == null || !upgradeHeader.toLowerCase().contains(HTTP_HEADER_UPGRADE_WEBSOCKET)){
throw new IOException("WebSocket Response header: Incorrect upgrade.");
}
@@ -158,7 +156,7 @@ private void receiveHandshakeResponse(String key) throws IOException {
if(!headerMap.containsKey(HTTP_HEADER_SEC_WEBSOCKET_ACCEPT)){
throw new IOException("WebSocket Response header: Missing Sec-WebSocket-Accept");
}
-
+
try {
verifyWebSocketKey(key, (String)headerMap.get(HTTP_HEADER_SEC_WEBSOCKET_ACCEPT));
} catch (NoSuchAlgorithmException e) {
@@ -168,7 +166,7 @@ private void receiveHandshakeResponse(String key) throws IOException {
}
}
-
+
/**
* Returns a Hashmap of HTTP headers
* @param ArrayList of headers
@@ -183,7 +181,7 @@ private Map getHeaders(ArrayList headers){
}
return headerMap;
}
-
+
/**
* Verifies that the Accept key provided is correctly built from the
* original key sent.
@@ -199,9 +197,9 @@ private void verifyWebSocketKey(String key, String accept) throws NoSuchAlgorith
String encodedSha1Bytes = Base64.encodeBytes(sha1Bytes).trim();
if(!encodedSha1Bytes.equals(accept.trim())){
throw new HandshakeFailedException();
- }
+ }
}
-
+
/**
* Returns the sha1 byte array of the provided string.
* @param input
diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketNetworkModule.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketNetworkModule.java
index 67fe408c5..5524a3158 100644
--- a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketNetworkModule.java
+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketNetworkModule.java
@@ -46,21 +46,7 @@ public class WebSocketNetworkModule extends TCPNetworkModule {
* This allows us to encode the MQTT payload into a WebSocket
* Frame before passing it through to the real socket.
*/
- private ByteArrayOutputStream outputStream = new ByteArrayOutputStream(){
-
- public void flush() throws IOException {
- final ByteBuffer byteBuffer;
- synchronized (this) {
- byteBuffer = ByteBuffer.wrap(toByteArray());
- reset();
- }
- WebSocketFrame frame = new WebSocketFrame((byte)0x02, true, byteBuffer.array());
- byte[] rawFrame = frame.encodeFrame();
- getSocketOutputStream().write(rawFrame);
- getSocketOutputStream().flush();
-
- }
- };
+ private ByteArrayOutputStream outputStream = new ExtendedByteArrayOutputStream(this);
public WebSocketNetworkModule(SocketFactory factory, String uri, String host, int port, String resourceContext){
super(factory, host, port, resourceContext);
@@ -80,11 +66,11 @@ public void start() throws IOException, MqttException {
webSocketReceiver.start("webSocketReceiver");
}
- private OutputStream getSocketOutputStream() throws IOException {
+ OutputStream getSocketOutputStream() throws IOException {
return super.getOutputStream();
}
- private InputStream getSocketInputStream() throws IOException {
+ InputStream getSocketInputStream() throws IOException {
return super.getInputStream();
}
diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketSecureNetworkModule.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketSecureNetworkModule.java
index f2b9dcbac..8224103cd 100644
--- a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketSecureNetworkModule.java
+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketSecureNetworkModule.java
@@ -46,21 +46,7 @@ public class WebSocketSecureNetworkModule extends SSLNetworkModule{
* This allows us to encode the MQTT payload into a WebSocket
* Frame before passing it through to the real socket.
*/
- private ByteArrayOutputStream outputStream = new ByteArrayOutputStream(){
-
- public void flush() throws IOException {
- final ByteBuffer byteBuffer;
- synchronized (this) {
- byteBuffer = ByteBuffer.wrap(toByteArray());
- reset();
- }
- WebSocketFrame frame = new WebSocketFrame((byte)0x02, true, byteBuffer.array());
- byte[] rawFrame = frame.encodeFrame();
- getSocketOutputStream().write(rawFrame);
- getSocketOutputStream().flush();
-
- }
- };
+ private ByteArrayOutputStream outputStream = new ExtendedByteArrayOutputStream(this);
public WebSocketSecureNetworkModule(SSLSocketFactory factory, String uri, String host, int port, String clientId) {
super(factory, host, port, clientId);
@@ -80,11 +66,11 @@ public void start() throws IOException, MqttException {
}
- private OutputStream getSocketOutputStream() throws IOException {
+ OutputStream getSocketOutputStream() throws IOException {
return super.getOutputStream();
}
- private InputStream getSocketInputStream() throws IOException {
+ InputStream getSocketInputStream() throws IOException {
return super.getInputStream();
}
@@ -112,6 +98,8 @@ public void stop() throws IOException {
public String getServerURI() {
return "wss://" + host + ":" + port;
}
+
+
}
diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttOutputStream.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttOutputStream.java
index 412bf35fe..95b9cea9d 100644
--- a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttOutputStream.java
+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttOutputStream.java
@@ -87,8 +87,8 @@ public void write(MqttWireMessage message) throws IOException, MqttException {
clientState.notifySentBytes(length);
}
- // @TRACE 500= sent {0}
- log.fine(CLASS_NAME, methodName, "500", new Object[]{message});
+ // @TRACE 529= sent {0}
+ log.fine(CLASS_NAME, methodName, "529", new Object[]{message});
}
}
diff --git a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/persist/MqttDefaultFilePersistence.java b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/persist/MqttDefaultFilePersistence.java
index 33e03949c..e86e7e9d4 100644
--- a/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/persist/MqttDefaultFilePersistence.java
+++ b/org.eclipse.paho.client.mqttv3/src/main/java/org/eclipse/paho/client/mqttv3/persist/MqttDefaultFilePersistence.java
@@ -298,5 +298,6 @@ public void clear() throws MqttPersistenceException {
for (int i=0; i= MIN && ch < (MAX +1);
- }
+
/**
* Checks if a CharSequence is empty ("") or null.
diff --git a/org.eclipse.paho.client.mqttv3/src/main/resources/org/eclipse/paho/client/mqttv3/internal/nls/logcat.properties b/org.eclipse.paho.client.mqttv3/src/main/resources/org/eclipse/paho/client/mqttv3/internal/nls/logcat.properties
index e3e8957ee..b7d07546f 100644
--- a/org.eclipse.paho.client.mqttv3/src/main/resources/org/eclipse/paho/client/mqttv3/internal/nls/logcat.properties
+++ b/org.eclipse.paho.client.mqttv3/src/main/resources/org/eclipse/paho/client/mqttv3/internal/nls/logcat.properties
@@ -80,6 +80,7 @@
516=Restoring all buffered messages.
517=Un-Persisting Buffered message key={0}
518=Failed to Un-Persist Buffered message key={0}
+529=Sent {0}
600=>
601=key={0} message={1}
602=key={0} exception
diff --git a/org.eclipse.paho.jmeclient/org.eclipse.paho.jmeclient.mqttv3/META-INF/MANIFEST.MF b/org.eclipse.paho.jmeclient/org.eclipse.paho.jmeclient.mqttv3/META-INF/MANIFEST.MF
index 80420bf77..cb7578b0a 100644
--- a/org.eclipse.paho.jmeclient/org.eclipse.paho.jmeclient.mqttv3/META-INF/MANIFEST.MF
+++ b/org.eclipse.paho.jmeclient/org.eclipse.paho.jmeclient.mqttv3/META-INF/MANIFEST.MF
@@ -1,7 +1,7 @@
Manifest-Version: 1.0
Bundle-Name: MQTTv3_JMEClient Plug-in
Bundle-SymbolicName: org.eclipse.paho.jmeclient.mqttv3;singleton:=true
-Bundle-Version: 1.1.1
+Bundle-Version: 1.2.0
Bundle-ManifestVersion: 2
Import-Package: javax.net;resolution:=optional,
javax.net.ssl;resolution:=optional
diff --git a/org.eclipse.paho.sample.utility/pom.xml b/org.eclipse.paho.sample.utility/pom.xml
index f9bd9a1f9..2671696a4 100644
--- a/org.eclipse.paho.sample.utility/pom.xml
+++ b/org.eclipse.paho.sample.utility/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.paho
java-parent
- 1.1.1
+ 1.2.0
org.eclipse.paho.mqtt.utility
@@ -71,7 +71,7 @@
org.eclipse.paho
org.eclipse.paho.client.mqttv3
- 1.1.1
+ 1.2.0
diff --git a/org.eclipse.paho.ui/org.eclipse.paho.ui.app/org.eclipse.paho.ui.app.product b/org.eclipse.paho.ui/org.eclipse.paho.ui.app/org.eclipse.paho.ui.app.product
index 1f6de8b13..1e76afd57 100644
--- a/org.eclipse.paho.ui/org.eclipse.paho.ui.app/org.eclipse.paho.ui.app.product
+++ b/org.eclipse.paho.ui/org.eclipse.paho.ui.app/org.eclipse.paho.ui.app.product
@@ -1,7 +1,7 @@
-
+
diff --git a/org.eclipse.paho.ui/org.eclipse.paho.ui.app/pom.xml b/org.eclipse.paho.ui/org.eclipse.paho.ui.app/pom.xml
index 2522f0bb5..999d41c52 100644
--- a/org.eclipse.paho.ui/org.eclipse.paho.ui.app/pom.xml
+++ b/org.eclipse.paho.ui/org.eclipse.paho.ui.app/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.paho
org.eclipse.paho.ui
- 1.1.1
+ 1.2.0
org.eclipse.paho.ui.app
diff --git a/org.eclipse.paho.ui/org.eclipse.paho.ui.core/META-INF/MANIFEST.MF b/org.eclipse.paho.ui/org.eclipse.paho.ui.core/META-INF/MANIFEST.MF
index 0cb563ab7..f67d081d9 100644
--- a/org.eclipse.paho.ui/org.eclipse.paho.ui.core/META-INF/MANIFEST.MF
+++ b/org.eclipse.paho.ui/org.eclipse.paho.ui.core/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %bundle.name
Bundle-SymbolicName: org.eclipse.paho.ui.core;singleton:=true
-Bundle-Version: 1.1.1
+Bundle-Version: 1.2.0
Bundle-Activator: org.eclipse.paho.mqtt.ui.Activator
Bundle-Vendor: %bundle.provider
Require-Bundle: org.eclipse.ui,
diff --git a/org.eclipse.paho.ui/org.eclipse.paho.ui.core/pom.xml b/org.eclipse.paho.ui/org.eclipse.paho.ui.core/pom.xml
index 37ebf33b2..44ad19f09 100644
--- a/org.eclipse.paho.ui/org.eclipse.paho.ui.core/pom.xml
+++ b/org.eclipse.paho.ui/org.eclipse.paho.ui.core/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.paho
org.eclipse.paho.ui
- 1.1.1
+ 1.2.0
org.eclipse.paho.ui.core
diff --git a/org.eclipse.paho.ui/org.eclipse.paho.ui.plugin/pom.xml b/org.eclipse.paho.ui/org.eclipse.paho.ui.plugin/pom.xml
index f72daf294..f63f2382f 100644
--- a/org.eclipse.paho.ui/org.eclipse.paho.ui.plugin/pom.xml
+++ b/org.eclipse.paho.ui/org.eclipse.paho.ui.plugin/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.paho
org.eclipse.paho.ui
- 1.1.1
+ 1.2.0
org.eclipse.paho.ui.plugin
@@ -87,7 +87,7 @@
org.eclipse.paho
org.eclipse.paho.ui.core
- 1.1.1
+ 1.2.0
diff --git a/org.eclipse.paho.ui/pom.xml b/org.eclipse.paho.ui/pom.xml
index 894b41294..7759d6e92 100644
--- a/org.eclipse.paho.ui/pom.xml
+++ b/org.eclipse.paho.ui/pom.xml
@@ -4,7 +4,7 @@
org.eclipse.paho
java-parent
- 1.1.1
+ 1.2.0
org.eclipse.paho.ui
diff --git a/pom.xml b/pom.xml
index 1cdb0c176..6916f2111 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,13 +4,13 @@
org.eclipse.paho
java-parent
pom
- 1.1.1
+ 1.2.0
- 1.4
+ 1.7
- 1.6
+ 1.7
UTF-8
${maven.build.timestamp}
0.20.0
@@ -171,6 +171,11 @@
tycho-pack200b-plugin
${tycho.version}
+
+ org.eluder.coveralls
+ coveralls-maven-plugin
+ 4.3.0
+
diff --git a/test/tls-testing/Dockerfile b/test/tls-testing/Dockerfile
new file mode 100644
index 000000000..78270feaf
--- /dev/null
+++ b/test/tls-testing/Dockerfile
@@ -0,0 +1,3 @@
+FROM eclipse-mosquitto:latest
+COPY mosquitto-docker.conf /mosquitto/config/mosquitto.conf
+COPY keys /mosquitto/config/keys
diff --git a/test/tls-testing/keys/all-ca.crt b/test/tls-testing/keys/all-ca.crt
new file mode 100644
index 000000000..592d8e588
--- /dev/null
+++ b/test/tls-testing/keys/all-ca.crt
@@ -0,0 +1,74 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1 (0x1)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=GB, ST=Derbyshire, L=Derby, O=Paho Project, OU=Testing, CN=Root CA
+ Validity
+ Not Before: Jul 29 19:21:30 2013 GMT
+ Not After : Jul 28 19:21:30 2018 GMT
+ Subject: C=GB, ST=Derbyshire, O=Paho Project, OU=Testing, CN=Signing CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (1024 bit)
+ Modulus:
+ 00:dc:26:78:40:ae:b2:ad:2f:26:12:0a:d5:b1:18:
+ 80:16:d8:88:be:0b:42:ce:32:ad:12:d5:f5:78:1b:
+ 35:28:f2:13:1b:05:09:fb:7e:d7:d9:a1:8a:0d:4a:
+ fe:95:37:d4:16:75:83:e4:6a:44:34:33:57:2e:49:
+ ba:bc:b4:cf:d0:c0:87:e0:bc:f0:60:76:14:00:d6:
+ eb:cb:f6:db:b3:43:f1:c8:4d:4a:0a:bb:e0:37:7c:
+ 8e:93:1f:a0:87:68:59:fe:0c:25:40:f3:7c:fd:71:
+ 90:55:ef:de:18:b4:08:86:c9:75:c2:99:2f:ce:12:
+ bf:c5:5e:cf:5f:f1:06:53:07
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ 29:4D:6E:C7:F2:F7:71:72:DA:27:9C:9C:AB:DA:07:1D:47:9C:D8:41
+ X509v3 Authority Key Identifier:
+ keyid:4A:2B:69:D6:31:1D:A3:68:E8:46:6F:FB:4B:F3:8E:B6:8D:51:0E:BF
+
+ X509v3 Basic Constraints:
+ CA:TRUE
+ Signature Algorithm: sha1WithRSAEncryption
+ 48:ec:d7:80:8a:8f:82:a6:42:b1:89:2c:b9:4b:6d:0a:37:b8:
+ 72:19:05:de:75:80:0c:d6:41:97:b2:d7:fe:99:cb:7e:c4:0e:
+ 77:97:09:a8:9f:87:ff:0b:de:3f:1c:dc:1e:fe:09:36:a7:f5:
+ 54:9a:85:4e:fb:6f:27:fe:0f:29:45:61:8d:07:c6:0c:da:37:
+ 3d:a3:69:4b:82:71:e6:24:e0:87:a6:ee:d5:87:61:dd:8f:08:
+ fe:33:a6:1f:ae:b2:ae:1f:d8:2c:20:c8:a6:fc:33:0e:82:68:
+ 80:23:61:10:ad:5c:1d:80:d6:b1:5f:e4:af:66:6d:63:10:e4:
+ 96:e4
+-----BEGIN CERTIFICATE-----
+MIICkzCCAfygAwIBAgIBATANBgkqhkiG9w0BAQUFADBtMQswCQYDVQQGEwJHQjET
+MBEGA1UECAwKRGVyYnlzaGlyZTEOMAwGA1UEBwwFRGVyYnkxFTATBgNVBAoMDFBh
+aG8gUHJvamVjdDEQMA4GA1UECwwHVGVzdGluZzEQMA4GA1UEAwwHUm9vdCBDQTAe
+Fw0xMzA3MjkxOTIxMzBaFw0xODA3MjgxOTIxMzBaMGAxCzAJBgNVBAYTAkdCMRMw
+EQYDVQQIDApEZXJieXNoaXJlMRUwEwYDVQQKDAxQYWhvIFByb2plY3QxEDAOBgNV
+BAsMB1Rlc3RpbmcxEzARBgNVBAMMClNpZ25pbmcgQ0EwgZ8wDQYJKoZIhvcNAQEB
+BQADgY0AMIGJAoGBANwmeECusq0vJhIK1bEYgBbYiL4LQs4yrRLV9XgbNSjyExsF
+Cft+19mhig1K/pU31BZ1g+RqRDQzVy5Jury0z9DAh+C88GB2FADW68v227ND8chN
+Sgq74Dd8jpMfoIdoWf4MJUDzfP1xkFXv3hi0CIbJdcKZL84Sv8Vez1/xBlMHAgMB
+AAGjUDBOMB0GA1UdDgQWBBQpTW7H8vdxctonnJyr2gcdR5zYQTAfBgNVHSMEGDAW
+gBRKK2nWMR2jaOhGb/tL8462jVEOvzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEB
+BQUAA4GBAEjs14CKj4KmQrGJLLlLbQo3uHIZBd51gAzWQZey1/6Zy37EDneXCaif
+h/8L3j8c3B7+CTan9VSahU77byf+DylFYY0HxgzaNz2jaUuCceYk4Iem7tWHYd2P
+CP4zph+usq4f2CwgyKb8Mw6CaIAjYRCtXB2A1rFf5K9mbWMQ5Jbk
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICqDCCAhGgAwIBAgIJAKrzwmdXIUxsMA0GCSqGSIb3DQEBBQUAMG0xCzAJBgNV
+BAYTAkdCMRMwEQYDVQQIDApEZXJieXNoaXJlMQ4wDAYDVQQHDAVEZXJieTEVMBMG
+A1UECgwMUGFobyBQcm9qZWN0MRAwDgYDVQQLDAdUZXN0aW5nMRAwDgYDVQQDDAdS
+b290IENBMB4XDTEzMDcyOTE5MjEyOVoXDTIzMDcyNzE5MjEyOVowbTELMAkGA1UE
+BhMCR0IxEzARBgNVBAgMCkRlcmJ5c2hpcmUxDjAMBgNVBAcMBURlcmJ5MRUwEwYD
+VQQKDAxQYWhvIFByb2plY3QxEDAOBgNVBAsMB1Rlc3RpbmcxEDAOBgNVBAMMB1Jv
+b3QgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKbPzEEWCKsjjwjJ787u
+Q32k5EdqoDddMEjSVbZNSNEwUew1L7O8NTbmtCEeVFQjOLAdmdiF3rQbXHV+Zew0
+jt2g4vtPpl1GOG6jA/6YznKAyQdvGCdYfGZUN2tN+mbtVxWqkHZitQDQGaSHnx24
+NX649La2uyFy+7l9o8++xPONAgMBAAGjUDBOMB0GA1UdDgQWBBRKK2nWMR2jaOhG
+b/tL8462jVEOvzAfBgNVHSMEGDAWgBRKK2nWMR2jaOhGb/tL8462jVEOvzAMBgNV
+HRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAEd+gW86/W+fisz5PFHAeEw7zn9q
+dzLHm7+QZgNLZ9h7/ZbhObRUFMRtU2xm4amyh85h7hUE5R2E2uW2OXumic7/D4ZD
+6unjr4m5jwVWDTqTUYIcNSriyoDWAVlPfOWaU5NyUhqS1DM28tvOWVHVLCxmVcZl
+tJQqo5eHbQ/+Hjfx
+-----END CERTIFICATE-----
diff --git a/test/tls-testing/keys/server/server-mitm.crt b/test/tls-testing/keys/server/server-mitm.crt
new file mode 100644
index 000000000..7f21fd309
--- /dev/null
+++ b/test/tls-testing/keys/server/server-mitm.crt
@@ -0,0 +1,61 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 2 (0x2)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=GB, ST=Derbyshire, O=Paho Project, OU=Testing, CN=Signing CA
+ Validity
+ Not Before: Aug 19 14:03:57 2013 GMT
+ Not After : Aug 18 14:03:57 2018 GMT
+ Subject: C=GB, ST=Nottinghamshire, L=Nottingham, O=Server, OU=Production, CN=m2m.example.org
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (1024 bit)
+ Modulus:
+ 00:9f:f2:ee:40:bb:b6:e9:f9:2f:c1:79:ef:9b:a7:
+ 39:27:15:09:bd:48:44:b6:df:1e:92:eb:dd:1d:4e:
+ 36:d9:3a:06:80:d3:d1:33:c3:d7:6b:70:01:76:a0:
+ 53:2b:31:8e:96:6c:72:3a:ae:df:52:19:12:9b:ae:
+ 55:0b:0a:49:f4:52:b4:34:4c:24:39:45:f6:35:89:
+ 42:f8:92:86:82:9f:a4:fb:fd:db:b1:19:60:6f:fc:
+ 1e:22:a7:02:ec:61:07:57:f8:8c:cb:24:64:d7:89:
+ 39:ee:d0:da:40:04:55:16:8c:a0:78:38:7f:df:59:
+ 3e:75:6a:53:01:39:f8:3f:f5
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ Netscape Comment:
+ OpenSSL Generated Certificate
+ X509v3 Subject Key Identifier:
+ 22:EC:63:4C:22:BF:83:04:5D:37:AA:46:49:5D:03:39:AE:50:56:95
+ X509v3 Authority Key Identifier:
+ keyid:29:4D:6E:C7:F2:F7:71:72:DA:27:9C:9C:AB:DA:07:1D:47:9C:D8:41
+
+ Signature Algorithm: sha1WithRSAEncryption
+ b6:b6:65:ec:fe:35:7d:b9:ce:45:2d:be:78:e2:55:79:a0:b0:
+ 4c:df:07:43:9f:ce:2f:77:61:b8:a0:43:f4:e4:33:51:a1:2d:
+ f7:d9:cb:1f:b9:ba:a5:33:2e:18:e1:51:29:26:d6:32:93:3d:
+ f9:19:ac:31:1a:90:1c:1a:56:8b:96:d3:a4:0b:ae:0a:e3:00:
+ 09:82:81:3f:d7:3a:52:de:9c:84:d7:bb:83:d1:0a:f2:28:be:
+ 1d:a2:a8:4b:0d:05:7a:e5:ca:cf:f7:23:7d:b3:65:0a:23:cb:
+ 6a:78:a6:8e:4d:29:2c:b2:5d:98:96:10:b7:cc:fd:04:04:d8:
+ 7f:7b
+-----BEGIN CERTIFICATE-----
+MIICzTCCAjagAwIBAgIBAjANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJHQjET
+MBEGA1UECAwKRGVyYnlzaGlyZTEVMBMGA1UECgwMUGFobyBQcm9qZWN0MRAwDgYD
+VQQLDAdUZXN0aW5nMRMwEQYDVQQDDApTaWduaW5nIENBMB4XDTEzMDgxOTE0MDM1
+N1oXDTE4MDgxODE0MDM1N1owfDELMAkGA1UEBhMCR0IxGDAWBgNVBAgMD05vdHRp
+bmdoYW1zaGlyZTETMBEGA1UEBwwKTm90dGluZ2hhbTEPMA0GA1UECgwGU2VydmVy
+MRMwEQYDVQQLDApQcm9kdWN0aW9uMRgwFgYDVQQDDA9tMm0uZXhhbXBsZS5vcmcw
+gZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAJ/y7kC7tun5L8F575unOScVCb1I
+RLbfHpLr3R1ONtk6BoDT0TPD12twAXagUysxjpZscjqu31IZEpuuVQsKSfRStDRM
+JDlF9jWJQviShoKfpPv927EZYG/8HiKnAuxhB1f4jMskZNeJOe7Q2kAEVRaMoHg4
+f99ZPnVqUwE5+D/1AgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZIAYb4QgENBB8W
+HU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBQi7GNMIr+D
+BF03qkZJXQM5rlBWlTAfBgNVHSMEGDAWgBQpTW7H8vdxctonnJyr2gcdR5zYQTAN
+BgkqhkiG9w0BAQUFAAOBgQC2tmXs/jV9uc5FLb544lV5oLBM3wdDn84vd2G4oEP0
+5DNRoS332csfubqlMy4Y4VEpJtYykz35GawxGpAcGlaLltOkC64K4wAJgoE/1zpS
+3pyE17uD0QryKL4doqhLDQV65crP9yN9s2UKI8tqeKaOTSkssl2YlhC3zP0EBNh/
+ew==
+-----END CERTIFICATE-----
diff --git a/test/tls-testing/keys/server/server-mitm.key b/test/tls-testing/keys/server/server-mitm.key
new file mode 100644
index 000000000..cbde88546
--- /dev/null
+++ b/test/tls-testing/keys/server/server-mitm.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQCf8u5Au7bp+S/Bee+bpzknFQm9SES23x6S690dTjbZOgaA09Ez
+w9drcAF2oFMrMY6WbHI6rt9SGRKbrlULCkn0UrQ0TCQ5RfY1iUL4koaCn6T7/dux
+GWBv/B4ipwLsYQdX+IzLJGTXiTnu0NpABFUWjKB4OH/fWT51alMBOfg/9QIDAQAB
+AoGBAJq4AOM5MQxOcHV0n+Qu88fsP8mKuH00gnyYDwEkAUZLLLM9MaYjEKPqal/3
+YbvsagLzz+MX+QmPG0LgdX8CatmV+Y1Ot1aSUykB3y4xJ0K4r5UdgXq3VjQHe0Es
+HcFEfd4i+U/TV2XAMi1T70XDE/B1tL7pR8PH1Ut/+jtL4RRBAkEAzwv4zQPcvc5k
+oGt6EHhOZCMtdMNhzmlJM6cqOTkzxtAzPo7f18UZ4xVfnL3RVgzDI9ygxQUq2fGn
+JP2Y6vd1uQJBAMXEPpjMhb9Z6shK9piEs9trhkuO0rGbCrf1lSnYTTdzojtGM0pV
+WiHf61sNq3aB8fvyIBlPkomNhGPjVIdkOh0CQBU1O7n8UEsc7g/Eko9HU3hMZiaU
+sNsMy9O/psKPym7t+qHIzsLyTJGHKfELPHsc+Ywd2FX2Doqehylx1I0U5fkCQQC/
+OjT09r5U+pMFPUcYzyhCgMQIh3+/cJV6r6KeJaL0fYFfI1AxDjrrCmO/VYZ4PspH
+viW/k6u0Cfz7wTNY/1cZAkBGSivOyQ3Hocj/6aWPWgagMeEwdS9/+7xyeQunigV6
+zf+0RkX0jzHv2uvrpVECt97BLBOmKdQFogKw24XQ9+B4
+-----END RSA PRIVATE KEY-----
diff --git a/test/tls-testing/keys/server/server.crt b/test/tls-testing/keys/server/server.crt
new file mode 100644
index 000000000..bb02ae1ad
--- /dev/null
+++ b/test/tls-testing/keys/server/server.crt
@@ -0,0 +1,61 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1 (0x1)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=GB, ST=Derbyshire, O=Paho Project, OU=Testing, CN=Signing CA
+ Validity
+ Not Before: Aug 19 14:03:57 2013 GMT
+ Not After : Aug 18 14:03:57 2018 GMT
+ Subject: C=GB, ST=Nottinghamshire, L=Nottingham, O=Server, OU=Production, CN=m2m.eclipse.org
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (1024 bit)
+ Modulus:
+ 00:a1:17:38:47:00:53:78:7d:b5:28:46:08:76:ed:
+ df:c0:87:6b:01:7d:5f:40:e6:36:c7:33:bd:e6:c5:
+ 15:0c:55:60:b8:4d:05:2f:19:0b:29:d6:d6:b9:e7:
+ 15:a3:2f:1b:e1:61:49:87:23:db:c1:ad:1d:5c:8a:
+ 4c:2f:1e:60:0b:1f:16:fa:37:a6:e0:76:ff:d2:b2:
+ 84:dd:b1:be:f5:39:7a:bc:40:0d:1b:fb:08:12:31:
+ d4:97:89:d0:23:3b:6b:24:35:95:72:d4:bb:f9:f6:
+ b4:8b:3e:98:f5:76:17:0e:11:27:f3:a3:8a:6f:02:
+ e8:3e:1d:2a:b0:20:62:ac:17
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ Netscape Comment:
+ OpenSSL Generated Certificate
+ X509v3 Subject Key Identifier:
+ 10:9A:A2:BA:80:4A:70:31:1B:EB:92:A0:77:FA:1F:56:A1:7C:E7:C8
+ X509v3 Authority Key Identifier:
+ keyid:29:4D:6E:C7:F2:F7:71:72:DA:27:9C:9C:AB:DA:07:1D:47:9C:D8:41
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 6d:33:28:7c:11:50:1c:c9:10:81:61:e1:bd:ff:5f:ec:d6:90:
+ 6c:48:7f:a2:30:7c:55:db:f1:11:4e:58:ca:e2:51:b2:91:c3:
+ da:6a:22:48:e6:6f:5e:6d:39:b6:e5:ab:ec:1a:bb:2a:d3:be:
+ a0:06:8b:f6:a2:00:a9:5f:22:8c:54:31:b0:99:c2:6b:b4:92:
+ 9b:f1:08:29:e8:93:0a:1d:e3:e5:7e:0e:9d:ce:b6:cc:fd:29:
+ 8a:5f:e5:df:54:62:7e:53:d8:65:69:c5:88:a5:26:c2:25:5f:
+ 73:9a:79:3c:56:a6:23:fc:8d:60:93:bc:3d:76:f2:cc:18:89:
+ 5a:4d
+-----BEGIN CERTIFICATE-----
+MIICzTCCAjagAwIBAgIBATANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJHQjET
+MBEGA1UECAwKRGVyYnlzaGlyZTEVMBMGA1UECgwMUGFobyBQcm9qZWN0MRAwDgYD
+VQQLDAdUZXN0aW5nMRMwEQYDVQQDDApTaWduaW5nIENBMB4XDTEzMDgxOTE0MDM1
+N1oXDTE4MDgxODE0MDM1N1owfDELMAkGA1UEBhMCR0IxGDAWBgNVBAgMD05vdHRp
+bmdoYW1zaGlyZTETMBEGA1UEBwwKTm90dGluZ2hhbTEPMA0GA1UECgwGU2VydmVy
+MRMwEQYDVQQLDApQcm9kdWN0aW9uMRgwFgYDVQQDDA9tMm0uZWNsaXBzZS5vcmcw
+gZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKEXOEcAU3h9tShGCHbt38CHawF9
+X0DmNsczvebFFQxVYLhNBS8ZCynW1rnnFaMvG+FhSYcj28GtHVyKTC8eYAsfFvo3
+puB2/9KyhN2xvvU5erxADRv7CBIx1JeJ0CM7ayQ1lXLUu/n2tIs+mPV2Fw4RJ/Oj
+im8C6D4dKrAgYqwXAgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZIAYb4QgENBB8W
+HU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBQQmqK6gEpw
+MRvrkqB3+h9WoXznyDAfBgNVHSMEGDAWgBQpTW7H8vdxctonnJyr2gcdR5zYQTAN
+BgkqhkiG9w0BAQUFAAOBgQBtMyh8EVAcyRCBYeG9/1/s1pBsSH+iMHxV2/ERTljK
+4lGykcPaaiJI5m9ebTm25avsGrsq076gBov2ogCpXyKMVDGwmcJrtJKb8Qgp6JMK
+HePlfg6dzrbM/SmKX+XfVGJ+U9hlacWIpSbCJV9zmnk8VqYj/I1gk7w9dvLMGIla
+TQ==
+-----END CERTIFICATE-----
diff --git a/test/tls-testing/keys/server/server.key b/test/tls-testing/keys/server/server.key
new file mode 100644
index 000000000..86987daae
--- /dev/null
+++ b/test/tls-testing/keys/server/server.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQChFzhHAFN4fbUoRgh27d/Ah2sBfV9A5jbHM73mxRUMVWC4TQUv
+GQsp1ta55xWjLxvhYUmHI9vBrR1cikwvHmALHxb6N6bgdv/SsoTdsb71OXq8QA0b
++wgSMdSXidAjO2skNZVy1Lv59rSLPpj1dhcOESfzo4pvAug+HSqwIGKsFwIDAQAB
+AoGAF+3CD1HuFDzgFw5oHXpyPUAgVgVQi+xmgKqf79KgsMdR6a6PMZLPxLRECzNX
+VHkf9Beme2uJLjjP3rowXONvMptVj2U6IQPic4J7H/xIso4q4eqYz2Z3zH6ou95/
+MzqdlGx5Xrq68eZO4Hjjf440DVBURKTcBen6aiH1+LhNBlkCQQDUA3Uwfj2iylP1
+K92rME5eBT3TDQ+nFCN5gidOlzI3tGJLIYmff9vd5FMZ7+Wq94OPEWFSjOCDRYl1
+QptebqvLAkEAwoMhcoPaf8Ho9FgRQ/67ttea8f2aRWfMJ2TbzkXOzqAkIho0/i2B
+mP17Y/Ut8ABxzi0OWvc5nLzQyJ38IvgPZQJAFJMhVEtr/9hSvYA8StitQkKP9WNX
+GYfCyY/v8JiLrww6XcArcucdlzMJPYMibq0Gf2PYZgJ150dgR5jnKwricQJAcjm7
+bJpNt/TRrpwBF6MB7S/UCAPbkqKP2xp5kjqgZerZDUXClPl4P7Wrvll+7o/0RRVk
+drr0FD01hnpwVMDDmQJAYWRBkvXHx/u6N1VHutwW9KtpC5ONxydDwhjv5DS07gjq
+xir50wrdoaYKwgPxE7jRRASDjJ75aHUhQSC99Q22EQ==
+-----END RSA PRIVATE KEY-----
diff --git a/test/tls-testing/keys/test-root-ca.crt b/test/tls-testing/keys/test-root-ca.crt
new file mode 100644
index 000000000..92dd58365
--- /dev/null
+++ b/test/tls-testing/keys/test-root-ca.crt
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICqDCCAhGgAwIBAgIJAKrzwmdXIUxsMA0GCSqGSIb3DQEBBQUAMG0xCzAJBgNV
+BAYTAkdCMRMwEQYDVQQIDApEZXJieXNoaXJlMQ4wDAYDVQQHDAVEZXJieTEVMBMG
+A1UECgwMUGFobyBQcm9qZWN0MRAwDgYDVQQLDAdUZXN0aW5nMRAwDgYDVQQDDAdS
+b290IENBMB4XDTEzMDcyOTE5MjEyOVoXDTIzMDcyNzE5MjEyOVowbTELMAkGA1UE
+BhMCR0IxEzARBgNVBAgMCkRlcmJ5c2hpcmUxDjAMBgNVBAcMBURlcmJ5MRUwEwYD
+VQQKDAxQYWhvIFByb2plY3QxEDAOBgNVBAsMB1Rlc3RpbmcxEDAOBgNVBAMMB1Jv
+b3QgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKbPzEEWCKsjjwjJ787u
+Q32k5EdqoDddMEjSVbZNSNEwUew1L7O8NTbmtCEeVFQjOLAdmdiF3rQbXHV+Zew0
+jt2g4vtPpl1GOG6jA/6YznKAyQdvGCdYfGZUN2tN+mbtVxWqkHZitQDQGaSHnx24
+NX649La2uyFy+7l9o8++xPONAgMBAAGjUDBOMB0GA1UdDgQWBBRKK2nWMR2jaOhG
+b/tL8462jVEOvzAfBgNVHSMEGDAWgBRKK2nWMR2jaOhGb/tL8462jVEOvzAMBgNV
+HRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAEd+gW86/W+fisz5PFHAeEw7zn9q
+dzLHm7+QZgNLZ9h7/ZbhObRUFMRtU2xm4amyh85h7hUE5R2E2uW2OXumic7/D4ZD
+6unjr4m5jwVWDTqTUYIcNSriyoDWAVlPfOWaU5NyUhqS1DM28tvOWVHVLCxmVcZl
+tJQqo5eHbQ/+Hjfx
+-----END CERTIFICATE-----
diff --git a/test/tls-testing/keys/test-signing-ca.crt b/test/tls-testing/keys/test-signing-ca.crt
new file mode 100644
index 000000000..3a10151d6
--- /dev/null
+++ b/test/tls-testing/keys/test-signing-ca.crt
@@ -0,0 +1,57 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1 (0x1)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=GB, ST=Derbyshire, L=Derby, O=Paho Project, OU=Testing, CN=Root CA
+ Validity
+ Not Before: Jul 29 19:21:30 2013 GMT
+ Not After : Jul 28 19:21:30 2018 GMT
+ Subject: C=GB, ST=Derbyshire, O=Paho Project, OU=Testing, CN=Signing CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (1024 bit)
+ Modulus:
+ 00:dc:26:78:40:ae:b2:ad:2f:26:12:0a:d5:b1:18:
+ 80:16:d8:88:be:0b:42:ce:32:ad:12:d5:f5:78:1b:
+ 35:28:f2:13:1b:05:09:fb:7e:d7:d9:a1:8a:0d:4a:
+ fe:95:37:d4:16:75:83:e4:6a:44:34:33:57:2e:49:
+ ba:bc:b4:cf:d0:c0:87:e0:bc:f0:60:76:14:00:d6:
+ eb:cb:f6:db:b3:43:f1:c8:4d:4a:0a:bb:e0:37:7c:
+ 8e:93:1f:a0:87:68:59:fe:0c:25:40:f3:7c:fd:71:
+ 90:55:ef:de:18:b4:08:86:c9:75:c2:99:2f:ce:12:
+ bf:c5:5e:cf:5f:f1:06:53:07
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ 29:4D:6E:C7:F2:F7:71:72:DA:27:9C:9C:AB:DA:07:1D:47:9C:D8:41
+ X509v3 Authority Key Identifier:
+ keyid:4A:2B:69:D6:31:1D:A3:68:E8:46:6F:FB:4B:F3:8E:B6:8D:51:0E:BF
+
+ X509v3 Basic Constraints:
+ CA:TRUE
+ Signature Algorithm: sha1WithRSAEncryption
+ 48:ec:d7:80:8a:8f:82:a6:42:b1:89:2c:b9:4b:6d:0a:37:b8:
+ 72:19:05:de:75:80:0c:d6:41:97:b2:d7:fe:99:cb:7e:c4:0e:
+ 77:97:09:a8:9f:87:ff:0b:de:3f:1c:dc:1e:fe:09:36:a7:f5:
+ 54:9a:85:4e:fb:6f:27:fe:0f:29:45:61:8d:07:c6:0c:da:37:
+ 3d:a3:69:4b:82:71:e6:24:e0:87:a6:ee:d5:87:61:dd:8f:08:
+ fe:33:a6:1f:ae:b2:ae:1f:d8:2c:20:c8:a6:fc:33:0e:82:68:
+ 80:23:61:10:ad:5c:1d:80:d6:b1:5f:e4:af:66:6d:63:10:e4:
+ 96:e4
+-----BEGIN CERTIFICATE-----
+MIICkzCCAfygAwIBAgIBATANBgkqhkiG9w0BAQUFADBtMQswCQYDVQQGEwJHQjET
+MBEGA1UECAwKRGVyYnlzaGlyZTEOMAwGA1UEBwwFRGVyYnkxFTATBgNVBAoMDFBh
+aG8gUHJvamVjdDEQMA4GA1UECwwHVGVzdGluZzEQMA4GA1UEAwwHUm9vdCBDQTAe
+Fw0xMzA3MjkxOTIxMzBaFw0xODA3MjgxOTIxMzBaMGAxCzAJBgNVBAYTAkdCMRMw
+EQYDVQQIDApEZXJieXNoaXJlMRUwEwYDVQQKDAxQYWhvIFByb2plY3QxEDAOBgNV
+BAsMB1Rlc3RpbmcxEzARBgNVBAMMClNpZ25pbmcgQ0EwgZ8wDQYJKoZIhvcNAQEB
+BQADgY0AMIGJAoGBANwmeECusq0vJhIK1bEYgBbYiL4LQs4yrRLV9XgbNSjyExsF
+Cft+19mhig1K/pU31BZ1g+RqRDQzVy5Jury0z9DAh+C88GB2FADW68v227ND8chN
+Sgq74Dd8jpMfoIdoWf4MJUDzfP1xkFXv3hi0CIbJdcKZL84Sv8Vez1/xBlMHAgMB
+AAGjUDBOMB0GA1UdDgQWBBQpTW7H8vdxctonnJyr2gcdR5zYQTAfBgNVHSMEGDAW
+gBRKK2nWMR2jaOhGb/tL8462jVEOvzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEB
+BQUAA4GBAEjs14CKj4KmQrGJLLlLbQo3uHIZBd51gAzWQZey1/6Zy37EDneXCaif
+h/8L3j8c3B7+CTan9VSahU77byf+DylFYY0HxgzaNz2jaUuCceYk4Iem7tWHYd2P
+CP4zph+usq4f2CwgyKb8Mw6CaIAjYRCtXB2A1rFf5K9mbWMQ5Jbk
+-----END CERTIFICATE-----
diff --git a/test/tls-testing/mosquitto-docker.conf b/test/tls-testing/mosquitto-docker.conf
new file mode 100644
index 000000000..ebc2a99f9
--- /dev/null
+++ b/test/tls-testing/mosquitto-docker.conf
@@ -0,0 +1,49 @@
+log_type error
+log_type warning
+log_type notice
+log_type information
+#log_type debug
+
+#log_dest file /var/log/mosquitto/tls-testing.log
+
+allow_anonymous true
+
+#message_size_limit 5000000
+
+# non-SSL listeners
+listener 1883
+listener 18883
+
+# listener for mutual authentication
+listener 18884
+cafile /mosquitto/config/keys/all-ca.crt
+certfile /mosquitto/config/keys/server/server.crt
+keyfile /mosquitto/config/keys/server/server.key
+require_certificate true
+#tls_version tlsv1
+
+# server authentication - no client authentication
+listener 18885
+cafile /mosquitto/config/keys/all-ca.crt
+certfile /mosquitto/config/keys/server/server.crt
+keyfile /mosquitto/config/keys/server/server.key
+require_certificate false
+#tls_version tlsv1
+
+listener 18886
+cafile /mosquitto/config/keys/all-ca.crt
+certfile /mosquitto/config/keys/server/server.crt
+keyfile /mosquitto/config/keys/server/server.key
+require_certificate false
+#ciphers ADH-DES-CBC-SHA
+#tls_version tlsv1
+
+# server authentication - no client authentication - uses fake hostname to
+# simulate mitm attack. Clients should refuse to connect to this listener.
+listener 18887
+#cafile /mosquitto/config/keys/all-ca.crt
+cafile /mosquitto/config/keys/server/server.crt
+certfile /mosquitto/config/keys/server/server-mitm.crt
+keyfile /mosquitto/config/keys/server/server-mitm.key
+require_certificate true
+#tls_version tlsv1
diff --git a/test/tls-testing/mosquitto.conf b/test/tls-testing/mosquitto.conf
new file mode 100644
index 000000000..e0f429404
--- /dev/null
+++ b/test/tls-testing/mosquitto.conf
@@ -0,0 +1,54 @@
+log_type error
+log_type warning
+log_type notice
+log_type information
+#log_type debug
+
+#log_dest file /var/log/mosquitto/tls-testing.log
+
+allow_anonymous true
+password_file test/tls-testing/mosquitto.pw
+
+#message_size_limit 5000000
+
+# non-SSL listeners
+listener 1883
+listener 18883
+
+# listener for mutual authentication
+listener 18884
+cafile test/tls-testing/keys/all-ca.crt
+certfile test/tls-testing/keys/server/server.crt
+keyfile test/tls-testing/keys/server/server.key
+require_certificate true
+use_identity_as_username false
+#tls_version tlsv1
+
+# server authentication - no client authentication
+listener 18885
+cafile test/tls-testing/keys/all-ca.crt
+certfile test/tls-testing/keys/server/server.crt
+keyfile test/tls-testing/keys/server/server.key
+require_certificate false
+#tls_version tlsv1
+
+listener 18886
+cafile test/tls-testing/keys/all-ca.crt
+certfile test/tls-testing/keys/server/server.crt
+keyfile test/tls-testing/keys/server/server.key
+require_certificate false
+#ciphers ADH-DES-CBC-SHA
+#tls_version tlsv1
+
+# server authentication - no client authentication - uses fake hostname to
+# simulate mitm attack. Clients should refuse to connect to this listener.
+listener 18887
+#cafile test/tls-testing/keys/all-ca.crt
+cafile test/tls-testing/keys/server/server.crt
+certfile test/tls-testing/keys/server/server-mitm.crt
+keyfile test/tls-testing/keys/server/server-mitm.key
+require_certificate true
+#tls_version tlsv1
+
+listener 8080
+protocol websockets
diff --git a/test/tls-testing/mosquitto.pw b/test/tls-testing/mosquitto.pw
new file mode 100644
index 000000000..cb50c892e
--- /dev/null
+++ b/test/tls-testing/mosquitto.pw
@@ -0,0 +1,4 @@
+test_user:$6$PY1pf+FdPmONSyhb$gC0yIykOSqj5x2rY7odzhNqLrQJ1S7dpQAgvxK+DXKquw1Yp5whSSgfcv24xTIotm3KCt1D6vcISOq/m3OhGEg==
+testuser:$6$VsVwgoeK2yQI5QTo$2fSqD6qUgMvfSOQQxRm7aQH1sA2oqGmdbNFM86GBbjWja7sN5wxOQHIjk/B8PeOqmMOee5UWX2faXy+X9fPZ4w==
+Admin:$6$OAKIhJgkrZUTDSbN$AmKUUznotPbGl5XaFbs8wqw4OyWFkjOgxJfJ4C/9vaknsouvR4RmsEozCee2SPwFaXSR52QRype/9+iSvU7y5w==
+username:$6$djEZwC47JvyetTxD$8x/J3ClUe38pcxE55hRw/uTpulVOCH6yXwJzJI6SPgmIdSGXAPtoEFLhKeXzoN20P1W30NUpgcIRI716lCYWVw==
diff --git a/travis-install.sh b/travis-install.sh
new file mode 100755
index 000000000..36ecff689
--- /dev/null
+++ b/travis-install.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+if [ "$TRAVIS_OS_NAME" == "linux" ]; then
+ pwd
+ sudo service mosquitto stop
+ mosquitto -h
+ mosquitto -c test/tls-testing/mosquitto.conf &
+fi
+
+if [ "$TRAVIS_OS_NAME" == "osx" ]; then
+ pwd
+ brew update
+ brew install openssl mosquitto
+ brew services stop mosquitto
+ /usr/local/sbin/mosquitto -c test/tls-testing/mosquitto.conf &
+fi