├── .classpath
├── .gitignore
├── .project
├── .settings
├── org.eclipse.core.resources.prefs
├── org.eclipse.jdt.core.prefs
└── org.eclipse.m2e.core.prefs
├── README.md
├── plugin.xml
├── pom.xml
└── src
├── main
└── java
│ └── io
│ └── inventit
│ └── dev
│ └── mqtt
│ └── paho
│ ├── MqttWebSocketAsyncClient.java
│ ├── PahoConsoleLogger.java
│ └── WebSocketNetworkModule.java
└── test
└── java
└── io
└── inventit
└── dev
└── mqtt
└── paho
├── JettyStrErrLogUtils.java
├── MqttWebSocketAsyncClientInContainerTest.java
├── MqttWebSocketAsyncClientTest.java
└── WebSocketNetworkModuleInContainerTest.java
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 | /pom.xml.versionsBackup
3 | .idea
4 | *.iml
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | mqtt-websocket-java
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 | org.eclipse.m2e.core.maven2Builder
15 |
16 |
17 |
18 |
19 |
20 | org.eclipse.jdt.core.javanature
21 | org.eclipse.m2e.core.maven2Nature
22 |
23 |
24 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.core.resources.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | encoding//src/main/java=UTF-8
3 | encoding//src/main/resources=UTF-8
4 | encoding//src/test/java=UTF-8
5 | encoding//src/test/resources=UTF-8
6 | encoding/=UTF-8
7 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
3 | org.eclipse.jdt.core.compiler.compliance=1.6
4 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
5 | org.eclipse.jdt.core.compiler.source=1.6
6 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.m2e.core.prefs:
--------------------------------------------------------------------------------
1 | activeProfiles=
2 | eclipse.preferences.version=1
3 | resolveWorkspaceProjects=true
4 | version=1
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Announcement
2 |
3 | Eclipse Paho Java client has WebSocket support. You can use Paho Java client for using MQTT over WebSocket.
4 |
5 | Please refer to the project page at https://eclipse.org/paho/clients/java/
6 |
7 | The project is EOL. Thank you for all our project users and contributors.
8 |
9 | Daisuke Baba
10 |
11 | ---
12 |
13 | # MQTT over WebSocket library for Java
14 |
15 | This library offers MQTT client functionality over WebSocket transport with [Paho](http://www.eclipse.org/paho/) library and [Jetty](http://www.eclipse.org/jetty/) library.
16 |
17 | # Supported MQTT Version
18 |
19 | 1. MQTT v3.1 (with Sub-Protocol: `mqttv3.1`)
20 | 1. MQTT v3.1.1 (with Sub-Protocol: `mqtt`) ... DEFAULT
21 |
22 | # Supported Paho MQTT library version and Jetty WebSocket Client version
23 |
24 | 1. [Paho org.eclipse.paho.mqtt.java 1.0.2](http://git.eclipse.org/c/paho/org.eclipse.paho.mqtt.java.git/tag/?id=v1.0.2)
25 | 1. [Jetty websocket-client 9.2.14.v20151106](http://www.eclipse.org/jetty/documentation/current/jetty-websocket-client-api.html)
26 |
27 | # Supported JDK/JRE Version
28 |
29 | JDK/JRE 1.7+ is required as Jetty no longer supports JDK/JRE 1.6.
30 |
31 | However, [`mqtt-websocket-jdk16-android`](https://github.com/inventit/mqtt-websocket-jdk16-android) library works on JDK/JRE 1.6 environment.
32 |
33 | # HTTP Proxy Unsupported
34 |
35 | Jetty9 WebSocket client doesn't support HTTP proxy. Therefore, this library won't work with HTTP proxy.
36 |
37 | # Dependencies
38 |
39 | The following libraries are requried as well.
40 |
41 | | GroupId | ArtifactId | Version |
42 | |---------------------------|----------------|----------------|
43 | |org.eclipse.jetty |jetty-io |9.2.14.v20151106|
44 | |org.eclipse.jetty |jetty-util |9.2.14.v20151106|
45 | |org.eclipse.jetty.websocket|websocket-api |9.2.14.v20151106|
46 | |org.eclipse.jetty.websocket|websocket-common|9.2.14.v20151106|
47 |
48 | ## maven pom.xml settings
49 |
50 | Adds the following elements to your pom.xml if you're using maven.
51 |
52 | ```
53 |
54 | io.inventit.dev
55 | mqtt-websocket-java
56 | 1.0.1
57 |
58 |
59 | org.eclipse.jetty.websocket
60 | websocket-client
61 | 9.2.14.v20151106
62 |
63 |
64 | org.eclipse.paho
65 | org.eclipse.paho.client.mqttv3
66 | 1.0.2
67 |
68 | ```
69 |
70 | # How to install
71 |
72 | ## Downloading the jar file
73 |
74 | curl -O -L https://github.com/inventit/mqtt-websocket-java/releases/download/1.0.1/mqtt-websocket-java-1.0.1.jar
75 |
76 | Or you can create a jar file by yourself (see below).
77 |
78 | ## Installing into your mvn local repository
79 |
80 | mvn install:install-file -Dfile=mqtt-websocket-java-1.0.1.jar \
81 | -DgroupId=io.inventit.dev -DartifactId=mqtt-websocket-java \
82 | -Dversion=1.0.1 -Dpackaging=jar
83 |
84 | # How to use
85 | You can use this library as the same manner as Paho's library but use `MqttWebSocketAsyncClient` instead of Paho's classes such as `MqttClient` and `MqttAsyncClient`.
86 |
87 | The `MqttWebSocketAsyncClient` supports the following URI schimes:
88 |
89 | 1. `ws://:` ... for a plain WebSocket
90 | 1. `wss://:` ... for a WebSocket with SSL/TLS
91 | 1. `tcp://:` ... for a plain TCP MQTT socket
92 | 1. `ssl://:` ... for a secure SSL/TLS MQTT socket
93 |
94 | Here is sample code to use `MqttWebSocketAsyncClient`.
95 |
96 | // Plain MQTT
97 | // final String uriString = "tcp://your-mqtt-broker:1883";
98 |
99 | // MQTT over WebSocket
100 | final String uriString = "wss://your-ws-broker/mqtt";
101 |
102 | // Credentials
103 | final String clientId = "your-client-id";
104 | final String userName = "your-user-name";
105 | final String password = "your-password";
106 |
107 | final IMqttAsyncClient client = new MqttWebSocketAsyncClient(
108 | uriString, clientId, new MemoryPersistence());
109 | final MqttConnectOptions options = new MqttConnectOptions();
110 | options.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1_1);
111 | options.setCleanSession(true);
112 | options.setUserName(userName);
113 | options.setPassword(password.toCharArray());
114 | client.connect(options);
115 | client.setCallback(new MqttCallback() {
116 |
117 | @Override
118 | public void messageArrived(String topic, MqttMessage message)
119 | throws Exception {
120 | }
121 |
122 | @Override
123 | public void deliveryComplete(IMqttDeliveryToken token) {
124 | }
125 |
126 | @Override
127 | public void connectionLost(Throwable cause) {
128 | }
129 | });
130 |
131 | # How to build
132 |
133 | Install maven then run the following command on the project root directory.
134 |
135 | $ mvn clean package
136 |
137 | Then you'll get `mqtt-websocket-java-.jar` under the `target` directory.
138 |
139 | # Source Code License
140 |
141 | All program source codes are available under the MIT style License.
142 |
143 | Copyright (c) 2014 Inventit Inc.
144 |
145 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
146 |
147 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
148 |
149 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
150 |
151 | # Change History
152 |
153 | [1.0.1 : November 29, 2014](https://github.com/inventit/mqtt-websocket-java/releases/tag/1.0.1)
154 |
155 | * Upgrades Jetty 9 library
156 | * Adds a new factory method for WebSocketNetworkModule instance
157 | * Adds new constructors with a new paramter for specifying the logger name to MqttWebSocketAsyncClient
158 | * Releases a JDK1.6 class version (50) jar as well in order for Android app to include this library (**Note that Jetty 9 itself doesn't support Android and JDK 1.6**)
159 |
160 | [1.0.0 : July 30, 2014](https://github.com/inventit/mqtt-websocket-java/releases/tag/1.0.0)
161 |
162 | * Initial
163 |
--------------------------------------------------------------------------------
/plugin.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 | mqtt-websocket-java
7 | MQTT over WebScoket for Java with Paho and Jetty
8 | MIT
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 | 4.0.0
6 |
7 | io.inventit.dev
8 | mqtt-websocket-java
9 | 1.0.1
10 | MQTT over WebScoket for Java with Paho and Jetty
11 | https://github.com/inventit/mqtt-websocket-java
12 |
13 |
14 | MIT
15 | https://github.com/inventit/mqtt-websocket-java/blob/master/README.md#source-code-license
16 |
17 |
18 |
19 | https://github.com/inventit/mqtt-websocket-java
20 | scm:git:git://github.com/inventit/mqtt-websocket-java.git
21 | scm:git:git@github.com:inventit/mqtt-websocket-java.git
22 |
23 |
24 | https://github.com/inventit/mqtt-websocket-java/issues
25 | GitHub Issues
26 |
27 |
28 |
29 |
30 | 1.7
31 | UTF-8
32 |
33 |
34 |
35 |
36 |
37 | maven-compiler-plugin
38 | 3.1
39 |
40 | UTF-8
41 | 1.6
42 | ${target.jdk}
43 |
44 |
45 |
46 | maven-surefire-plugin
47 | 2.17
48 |
49 | 0
50 | -Dfile.encoding=UTF-8 -Xmx512m -XX:MaxPermSize=256m
51 |
52 | **/*InContainerTest.java
53 |
54 |
55 |
56 |
57 | org.codehaus.mojo
58 | cobertura-maven-plugin
59 | 2.5.1
60 |
61 |
62 | false
63 |
64 |
65 |
66 |
67 | pre-site
68 |
69 | clean
70 | check
71 |
72 |
73 |
74 |
75 |
76 | maven-clean-plugin
77 |
78 |
79 |
80 | ${basedir}
81 |
82 | **/*.log*
83 |
84 |
85 | .hg/**/*
86 | .git/**/*
87 | **/src/test/resources/logs/*
88 |
89 | false
90 |
91 |
92 |
93 |
94 |
95 | maven-javadoc-plugin
96 | 2.9
97 |
98 |
99 |
100 | UTF-8
101 |
102 | http://java.sun.com/javase/6/docs/api/
103 | http://download.oracle.com/javaee/5/api/
104 |
105 |
106 |
107 |
108 | maven-jar-plugin
109 |
110 | ${jdk.classifier}
111 |
112 | false
113 |
114 | true
115 |
116 |
117 | ${buildNumber}
118 | InventIt Inc. dev.inventit.io
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 | org.eclipse.jetty.websocket
129 | websocket-client
130 | 9.2.14.v20151106
131 |
132 |
133 | org.eclipse.paho
134 | org.eclipse.paho.client.mqttv3
135 | 1.0.2
136 |
137 |
138 | junit
139 | junit
140 | 4.11
141 | test
142 |
143 |
144 | org.mockito
145 | mockito-all
146 | 1.9.5
147 | test
148 |
149 |
150 | org.easytesting
151 | fest-assert-core
152 | 2.0M10
153 | test
154 |
155 |
156 | org.jmock
157 | jmock
158 | 2.6.0
159 | test
160 |
161 |
162 |
163 |
164 | jdk16
165 |
166 | 1.6
167 |
168 |
169 | jdk16
170 | 1.6
171 |
172 |
173 |
174 |
175 |
--------------------------------------------------------------------------------
/src/main/java/io/inventit/dev/mqtt/paho/MqttWebSocketAsyncClient.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2014 Inventit Inc.
3 | */
4 | package io.inventit.dev.mqtt.paho;
5 |
6 | import java.net.URI;
7 |
8 | import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
9 | import org.eclipse.paho.client.mqttv3.MqttClientPersistence;
10 | import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
11 | import org.eclipse.paho.client.mqttv3.MqttException;
12 | import org.eclipse.paho.client.mqttv3.MqttPingSender;
13 | import org.eclipse.paho.client.mqttv3.MqttSecurityException;
14 | import org.eclipse.paho.client.mqttv3.TimerPingSender;
15 | import org.eclipse.paho.client.mqttv3.internal.NetworkModule;
16 | import org.eclipse.paho.client.mqttv3.logging.Logger;
17 | import org.eclipse.paho.client.mqttv3.logging.LoggerFactory;
18 | import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence;
19 |
20 | public class MqttWebSocketAsyncClient extends MqttAsyncClient {
21 |
22 | private static final String CLASS_NAME = MqttWebSocketAsyncClient.class
23 | .getName();
24 | private final Logger log;
25 |
26 | private final String serverURI;
27 |
28 | /**
29 | * Create a dummy URI in order to by-pass MqttConnectOptions.validateURI()
30 | * validation.
31 | *
32 | * @param original
33 | * @return
34 | */
35 | protected static String createDummyURI(String original) {
36 | if (!original.startsWith("ws:") && !original.startsWith("wss:")) {
37 | return original;
38 | }
39 | final URI uri = URI.create(original);
40 | return "tcp://DUMMY-" + uri.getHost() + ":"
41 | + (uri.getPort() > 0 ? uri.getPort() : 80);
42 | }
43 |
44 | protected static boolean isDummyURI(String uri) {
45 | return uri.startsWith("tcp://DUMMY-");
46 | }
47 |
48 | public MqttWebSocketAsyncClient(String serverURI, String clientId,
49 | MqttClientPersistence persistence, MqttPingSender pingSender,
50 | String loggerName) throws MqttException {
51 |
52 | super(createDummyURI(serverURI), clientId, persistence, pingSender);
53 | this.serverURI = serverURI;
54 |
55 | final String methodName = "MqttWebSocketAsyncClient";
56 |
57 | this.log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT,
58 | (loggerName == null || loggerName.length() == 0) ? CLASS_NAME
59 | : loggerName);
60 |
61 | // @TRACE 101= ClientID={0} ServerURI={1} PersistenceType={2}
62 | if (log.isLoggable(Logger.FINE)) {
63 | log.fine(CLASS_NAME, methodName, "101", new Object[] { clientId,
64 | serverURI, persistence });
65 | }
66 | }
67 |
68 | public MqttWebSocketAsyncClient(String serverURI, String clientId,
69 | MqttClientPersistence persistence, String loggerName)
70 | throws MqttException {
71 | this(serverURI, clientId, persistence, new TimerPingSender(),
72 | loggerName);
73 | }
74 |
75 | public MqttWebSocketAsyncClient(String serverURI, String clientId,
76 | MqttClientPersistence persistence) throws MqttException {
77 | this(serverURI, clientId, persistence, null);
78 | }
79 |
80 | public MqttWebSocketAsyncClient(String serverURI, String clientId,
81 | String loggerName) throws MqttException {
82 | this(serverURI, clientId, new MqttDefaultFilePersistence(), loggerName);
83 | }
84 |
85 | public MqttWebSocketAsyncClient(String serverURI, String clientId)
86 | throws MqttException {
87 | this(serverURI, clientId, (String) null);
88 | }
89 |
90 | /**
91 | * Same as super{@link #createNetworkModules(String, MqttConnectOptions)}
92 | */
93 | @Override
94 | protected NetworkModule[] createNetworkModules(String address,
95 | MqttConnectOptions options) throws MqttException,
96 | MqttSecurityException {
97 | final String methodName = "createNetworkModules";
98 | // @TRACE 116=URI={0}
99 | if (log.isLoggable(Logger.FINE)) {
100 | log.fine(CLASS_NAME, methodName, "116", new Object[] { address });
101 | }
102 | NetworkModule[] networkModules = null;
103 | String[] serverURIs = options.getServerURIs();
104 | String[] array = null;
105 | if (serverURIs == null) {
106 | array = new String[] { address };
107 | } else if (serverURIs.length == 0) {
108 | array = new String[] { address };
109 | } else {
110 | array = serverURIs;
111 | }
112 |
113 | networkModules = new NetworkModule[array.length];
114 | for (int i = 0; i < array.length; i++) {
115 | networkModules[i] = createNetworkModule(array[i], options);
116 | }
117 |
118 | log.fine(CLASS_NAME, methodName, "108");
119 | return networkModules;
120 | }
121 |
122 | /**
123 | * Factory method to create the correct network module, based on the
124 | * supplied address URI.
125 | *
126 | * @param address
127 | * the URI for the server.
128 | * @param options
129 | * MQTT connect options
130 | * @return a network module appropriate to the specified address.
131 | */
132 | protected NetworkModule createNetworkModule(String input,
133 | MqttConnectOptions options) throws MqttException,
134 | MqttSecurityException {
135 | final String address = isDummyURI(input) ? this.serverURI : input;
136 | if (!address.startsWith("ws:") && !address.startsWith("wss:")) {
137 | return super.createNetworkModules(address, options)[0];
138 | }
139 |
140 | final String methodName = "createNetworkModule";
141 | // @TRACE 115=URI={0}
142 | if (log.isLoggable(Logger.FINE)) {
143 | log.fine(CLASS_NAME, methodName, "115", new Object[] { address });
144 | }
145 |
146 | final String subProtocol;
147 | if (options.getMqttVersion() == MqttConnectOptions.MQTT_VERSION_3_1) {
148 | // http://wiki.eclipse.org/Paho/Paho_Websockets#Ensuring_implementations_can_inter-operate
149 | subProtocol = "mqttv3.1";
150 | } else {
151 | // http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/cs01/mqtt-v3.1.1-cs01.html#_Toc388534418
152 | subProtocol = "mqtt";
153 | }
154 | return newWebSocketNetworkModule(URI.create(address), subProtocol,
155 | options);
156 | }
157 |
158 | /**
159 | * A factory method for instantiating a {@link NetworkModule} with websocket
160 | * support. Subclasses is able to extend this method in order to create an
161 | * arbitrary {@link NetworkModule} class instance.
162 | *
163 | * @param uri
164 | * @param subProtocol
165 | * Either `mqtt` for MQTT v3 or `mqttv3.1` for MQTT v3.1
166 | * @param options
167 | * @return
168 | */
169 | protected NetworkModule newWebSocketNetworkModule(URI uri,
170 | String subProtocol, MqttConnectOptions options) {
171 | final WebSocketNetworkModule netModule = new WebSocketNetworkModule(
172 | uri, subProtocol, getClientId());
173 | netModule.setConnectTimeout(options.getConnectionTimeout());
174 | return netModule;
175 | }
176 |
177 | }
178 |
--------------------------------------------------------------------------------
/src/main/java/io/inventit/dev/mqtt/paho/PahoConsoleLogger.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2014 Inventit Inc.
3 | */
4 | package io.inventit.dev.mqtt.paho;
5 |
6 | import java.text.MessageFormat;
7 | import java.util.ResourceBundle;
8 |
9 | import org.eclipse.paho.client.mqttv3.logging.Logger;
10 | import org.eclipse.paho.client.mqttv3.logging.LoggerFactory;
11 |
12 | public class PahoConsoleLogger implements Logger {
13 |
14 | public static void enableLog() {
15 | LoggerFactory.setLogger(PahoConsoleLogger.class.getName());
16 | }
17 |
18 | private ResourceBundle messageCatalog;
19 |
20 | @Override
21 | public void initialise(ResourceBundle messageCatalog, String loggerID,
22 | String resourceName) {
23 | this.messageCatalog = messageCatalog;
24 | }
25 |
26 | @Override
27 | public void setResourceName(String logContext) {
28 | }
29 |
30 | @Override
31 | public boolean isLoggable(int level) {
32 | return true;
33 | }
34 |
35 | @Override
36 | public void severe(String sourceClass, String sourceMethod, String msg) {
37 | System.err.println("[SEVERE]" + sourceClass + ":" + sourceMethod + ":"
38 | + msg);
39 | }
40 |
41 | @Override
42 | public void severe(String sourceClass, String sourceMethod, String msg,
43 | Object[] inserts) {
44 | System.err.println("[SEVERE]" + sourceClass + ":" + sourceMethod + ":"
45 | + formatMessage(msg, inserts));
46 | }
47 |
48 | @Override
49 | public void severe(String sourceClass, String sourceMethod, String msg,
50 | Object[] inserts, Throwable thrown) {
51 | System.err.println("[SEVERE]" + sourceClass + ":" + sourceMethod + ":"
52 | + formatMessage(msg, inserts));
53 | thrown.printStackTrace();
54 | }
55 |
56 | @Override
57 | public void warning(String sourceClass, String sourceMethod, String msg) {
58 | System.err.println("[WARN]" + sourceClass + ":" + sourceMethod);
59 | }
60 |
61 | @Override
62 | public void warning(String sourceClass, String sourceMethod, String msg,
63 | Object[] inserts) {
64 | System.err.println("[WARN]" + sourceClass + ":" + sourceMethod + ":"
65 | + formatMessage(msg, inserts));
66 | }
67 |
68 | @Override
69 | public void warning(String sourceClass, String sourceMethod, String msg,
70 | Object[] inserts, Throwable thrown) {
71 | System.err.println("[WARN]" + sourceClass + ":" + sourceMethod + ":"
72 | + formatMessage(msg, inserts));
73 | thrown.printStackTrace();
74 | }
75 |
76 | @Override
77 | public void info(String sourceClass, String sourceMethod, String msg) {
78 | System.err.println("[INFO]" + sourceClass + ":" + sourceMethod + ":"
79 | + msg);
80 | }
81 |
82 | @Override
83 | public void info(String sourceClass, String sourceMethod, String msg,
84 | Object[] inserts) {
85 | System.err.println("[INFO]" + sourceClass + ":" + sourceMethod + ":"
86 | + formatMessage(msg, inserts));
87 | }
88 |
89 | @Override
90 | public void info(String sourceClass, String sourceMethod, String msg,
91 | Object[] inserts, Throwable thrown) {
92 | System.err.println("[INFO]" + sourceClass + ":" + sourceMethod + ":"
93 | + formatMessage(msg, inserts));
94 | thrown.printStackTrace();
95 | }
96 |
97 | @Override
98 | public void config(String sourceClass, String sourceMethod, String msg) {
99 | System.err.println("[CONFIG]" + sourceClass + ":" + sourceMethod + ":"
100 | + msg);
101 | }
102 |
103 | @Override
104 | public void config(String sourceClass, String sourceMethod, String msg,
105 | Object[] inserts) {
106 | System.err.println("[CONFIG]" + sourceClass + ":" + sourceMethod + ":"
107 | + formatMessage(msg, inserts));
108 | }
109 |
110 | @Override
111 | public void config(String sourceClass, String sourceMethod, String msg,
112 | Object[] inserts, Throwable thrown) {
113 | System.err.println("[CONFIG]" + sourceClass + ":" + sourceMethod + ":"
114 | + formatMessage(msg, inserts));
115 | thrown.printStackTrace();
116 | }
117 |
118 | @Override
119 | public void fine(String sourceClass, String sourceMethod, String msg) {
120 | System.err.println("[FINE]" + sourceClass + ":" + sourceMethod + ":"
121 | + msg);
122 | }
123 |
124 | @Override
125 | public void fine(String sourceClass, String sourceMethod, String msg,
126 | Object[] inserts) {
127 | System.err.println("[FINE]" + sourceClass + ":" + sourceMethod + ":"
128 | + formatMessage(msg, inserts));
129 | }
130 |
131 | @Override
132 | public void fine(String sourceClass, String sourceMethod, String msg,
133 | Object[] inserts, Throwable ex) {
134 | System.err.println("[FINE]" + sourceClass + ":" + sourceMethod + ":"
135 | + formatMessage(msg, inserts));
136 | ex.printStackTrace();
137 | }
138 |
139 | @Override
140 | public void finer(String sourceClass, String sourceMethod, String msg) {
141 | System.err.println("[FINER]" + sourceClass + ":" + sourceMethod + ":"
142 | + msg);
143 | }
144 |
145 | @Override
146 | public void finer(String sourceClass, String sourceMethod, String msg,
147 | Object[] inserts) {
148 | System.err.println("[FINER]" + sourceClass + ":" + sourceMethod + ":"
149 | + formatMessage(msg, inserts));
150 | }
151 |
152 | @Override
153 | public void finer(String sourceClass, String sourceMethod, String msg,
154 | Object[] inserts, Throwable ex) {
155 | System.err.println("[FINER]" + sourceClass + ":" + sourceMethod + ":"
156 | + formatMessage(msg, inserts));
157 | ex.printStackTrace();
158 | }
159 |
160 | @Override
161 | public void finest(String sourceClass, String sourceMethod, String msg) {
162 | System.err.println("[FINEST]" + sourceClass + ":" + sourceMethod + ":"
163 | + msg);
164 | }
165 |
166 | @Override
167 | public void finest(String sourceClass, String sourceMethod, String msg,
168 | Object[] inserts) {
169 | System.err.println("[FINEST]" + sourceClass + ":" + sourceMethod + ":"
170 | + formatMessage(msg, inserts));
171 | }
172 |
173 | @Override
174 | public void finest(String sourceClass, String sourceMethod, String msg,
175 | Object[] inserts, Throwable ex) {
176 | System.err.println("[FINEST]" + sourceClass + ":" + sourceMethod + ":"
177 | + formatMessage(msg, inserts));
178 | ex.printStackTrace();
179 | }
180 |
181 | @Override
182 | public void log(int level, String sourceClass, String sourceMethod,
183 | String msg, Object[] inserts, Throwable thrown) {
184 | switch (level) {
185 | case Logger.CONFIG:
186 | config(sourceClass, sourceMethod, msg, inserts, thrown);
187 | break;
188 | default:
189 | case Logger.FINE:
190 | fine(sourceClass, sourceMethod, msg, inserts, thrown);
191 | break;
192 | case Logger.FINER:
193 | finer(sourceClass, sourceMethod, msg, inserts, thrown);
194 | break;
195 | case Logger.FINEST:
196 | finest(sourceClass, sourceMethod, msg, inserts, thrown);
197 | break;
198 | case Logger.INFO:
199 | info(sourceClass, sourceMethod, msg, inserts, thrown);
200 | break;
201 | case Logger.WARNING:
202 | warning(sourceClass, sourceMethod, msg, inserts, thrown);
203 | break;
204 | case Logger.SEVERE:
205 | severe(sourceClass, sourceMethod, msg, inserts, thrown);
206 | break;
207 | }
208 | }
209 |
210 | @Override
211 | public void trace(int level, String sourceClass, String sourceMethod,
212 | String msg, Object[] inserts, Throwable thrown) {
213 | log(level, sourceClass, sourceMethod, msg, inserts, thrown);
214 | }
215 |
216 | @Override
217 | public String formatMessage(String msg, Object[] inserts) {
218 | if (!messageCatalog.containsKey(msg)) {
219 | return MessageFormat.format(msg, inserts);
220 | }
221 | final String message = messageCatalog.getString(msg);
222 | if (inserts == null) {
223 | return message;
224 | } else {
225 | return MessageFormat.format(message, inserts);
226 | }
227 | }
228 |
229 | @Override
230 | public void dumpTrace() {
231 | }
232 |
233 | }
234 |
--------------------------------------------------------------------------------
/src/main/java/io/inventit/dev/mqtt/paho/WebSocketNetworkModule.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2014 Inventit Inc.
3 | */
4 | package io.inventit.dev.mqtt.paho;
5 |
6 | import java.io.ByteArrayOutputStream;
7 | import java.io.IOException;
8 | import java.io.InputStream;
9 | import java.io.OutputStream;
10 | import java.io.PipedInputStream;
11 | import java.io.PipedOutputStream;
12 | import java.net.ConnectException;
13 | import java.net.URI;
14 | import java.nio.ByteBuffer;
15 | import java.util.concurrent.Future;
16 |
17 | import javax.net.ssl.SSLEngine;
18 | import javax.net.ssl.SSLParameters;
19 |
20 | import org.eclipse.jetty.util.ssl.SslContextFactory;
21 | import org.eclipse.jetty.websocket.api.Session;
22 | import org.eclipse.jetty.websocket.api.WebSocketAdapter;
23 | import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
24 | import org.eclipse.jetty.websocket.client.WebSocketClient;
25 | import org.eclipse.paho.client.mqttv3.MqttException;
26 | import org.eclipse.paho.client.mqttv3.internal.NetworkModule;
27 | import org.eclipse.paho.client.mqttv3.logging.Logger;
28 | import org.eclipse.paho.client.mqttv3.logging.LoggerFactory;
29 |
30 | /**
31 | * A network module for connecting over WebScoket with Jetty 9.
32 | */
33 | public class WebSocketNetworkModule extends WebSocketAdapter implements
34 | NetworkModule {
35 | private static final String CLASS_NAME = WebSocketNetworkModule.class
36 | .getName();
37 | private static final Logger log = LoggerFactory.getLogger(
38 | LoggerFactory.MQTT_CLIENT_MSG_CAT, CLASS_NAME);
39 |
40 | /**
41 | * WebSocket URI
42 | */
43 | private final URI uri;
44 |
45 | /**
46 | * Sub-Protocol
47 | */
48 | private final String subProtocol;
49 |
50 | /**
51 | * A stream for outgoing data
52 | */
53 | private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream() {
54 | @Override
55 | public void flush() throws IOException {
56 | final ByteBuffer byteBuffer;
57 | synchronized (this) {
58 | byteBuffer = ByteBuffer.wrap(toByteArray());
59 | reset();
60 | }
61 | // Asynchronous call
62 | getRemote().sendBytes(byteBuffer);
63 | getRemote().flush();
64 | }
65 | };
66 |
67 | /**
68 | * A pair of streams for incoming data
69 | */
70 | private final PipedOutputStream receiverStream = new PipedOutputStream();
71 | private final PipedInputStream inputStream;
72 |
73 | private WebSocketClient client;
74 | private int conTimeout;
75 |
76 | /**
77 | * Constructs a new WebSocketNetworkModule using the specified URI.
78 | *
79 | * @param uri
80 | * @param subProtocol
81 | * @param resourceContext
82 | */
83 | public WebSocketNetworkModule(URI uri, String subProtocol,
84 | String resourceContext) {
85 | log.setResourceName(resourceContext);
86 | this.uri = uri;
87 | this.subProtocol = subProtocol;
88 | try {
89 | this.inputStream = new PipedInputStream(receiverStream);
90 | } catch (IOException unexpected) {
91 | throw new IllegalStateException(unexpected);
92 | }
93 | }
94 |
95 | /**
96 | * A factory method for {@link ClientUpgradeRequest} class
97 | *
98 | * @return
99 | */
100 | protected ClientUpgradeRequest createClientUpgradeRequest() {
101 | final ClientUpgradeRequest request = new ClientUpgradeRequest();
102 | // you can manipulate the request by overriding this method.
103 | return request;
104 | }
105 |
106 | /**
107 | * A factory method for {@link WebSocketClient} class
108 | *
109 | * @return
110 | */
111 | protected WebSocketClient createWebSocketClient() {
112 | final WebSocketClient client = new WebSocketClient(
113 | createSslContextFactory());
114 | // you can manipulate the client by overriding this method.
115 | return client;
116 | }
117 |
118 | /**
119 | * A factory method for {@link SslContextFactory} class, used for
120 | * instantiating a WebSocketClient()
121 | *
122 | * @return
123 | */
124 | protected SslContextFactory createSslContextFactory() {
125 | return new SslContextFactory(true){
126 | @Override
127 | public void customize(SSLEngine sslEngine) {
128 | SSLParameters sslParams = sslEngine.getSSLParameters();
129 | //sslParams.setEndpointIdentificationAlgorithm(_endpointIdentificationAlgorithm);
130 | sslEngine.setSSLParameters(sslParams);
131 |
132 | if (getWantClientAuth())
133 | sslEngine.setWantClientAuth(getWantClientAuth());
134 | if (getNeedClientAuth())
135 | sslEngine.setNeedClientAuth(getNeedClientAuth());
136 |
137 | sslEngine.setEnabledCipherSuites(selectCipherSuites(
138 | sslEngine.getEnabledCipherSuites(),
139 | sslEngine.getSupportedCipherSuites()));
140 |
141 | sslEngine.setEnabledProtocols(selectProtocols(sslEngine.getEnabledProtocols(),sslEngine.getSupportedProtocols()));
142 | }
143 | };
144 | }
145 |
146 | /**
147 | * Starts the module, by creating a TCP socket to the server.
148 | */
149 | @Override
150 | public void start() throws IOException, MqttException {
151 | final String methodName = "start";
152 | try {
153 | // @TRACE 252=connect to host {0} port {1} timeout {2}
154 | if (log.isLoggable(Logger.FINE)) {
155 | log.fine(
156 | CLASS_NAME,
157 | methodName,
158 | "252",
159 | new Object[] { uri.toString(),
160 | Integer.valueOf(uri.getPort()),
161 | Long.valueOf(conTimeout * 1000) });
162 | }
163 | client = createWebSocketClient();
164 | client.setConnectTimeout(conTimeout * 1000);
165 | if (client.isStarted() == false) {
166 | client.start();
167 | }
168 |
169 | final ClientUpgradeRequest request = createClientUpgradeRequest();
170 | request.setSubProtocols(subProtocol);
171 | final Future future = client.connect(this, uri, request);
172 | // Replays the same behavior as Socket.connect().
173 | // blocks until the connection is established or some error occurs.
174 | future.get();
175 |
176 | } catch (ConnectException ex) {
177 | // @TRACE 250=Failed to create TCP socket
178 | log.fine(CLASS_NAME, methodName, "250", null, ex);
179 | throw new MqttException(
180 | MqttException.REASON_CODE_SERVER_CONNECT_ERROR, ex);
181 |
182 | } catch (Exception ex) {
183 | // @TRACE 250=Failed to create TCP socket
184 | log.fine(CLASS_NAME, methodName, "250", null, ex);
185 | throw new MqttException(MqttException.REASON_CODE_UNEXPECTED_ERROR,
186 | ex);
187 | }
188 | }
189 |
190 | @Override
191 | public InputStream getInputStream() throws IOException {
192 | return inputStream;
193 | }
194 |
195 | @Override
196 | public OutputStream getOutputStream() throws IOException {
197 | return outputStream;
198 | }
199 |
200 | /**
201 | * Stops the module, by closing the web socket.
202 | */
203 | @Override
204 | public void stop() throws IOException {
205 | try {
206 | client.stop();
207 | } catch (IOException e) {
208 | throw e;
209 | } catch (Exception e) {
210 | throw new IllegalStateException(e);
211 | }
212 | }
213 |
214 | /**
215 | * Set the maximum time in seconds to wait for a socket to be established
216 | *
217 | * @param timeout
218 | * in seconds
219 | */
220 | public void setConnectTimeout(int timeout) {
221 | this.conTimeout = timeout;
222 | }
223 |
224 | @Override
225 | public void onWebSocketBinary(byte[] payload, int offset, int len) {
226 | try {
227 | this.receiverStream.write(payload, offset, len);
228 | this.receiverStream.flush();
229 | } catch (IOException e) {
230 | log.fine(CLASS_NAME, "onWebSocketError", "401", null, e);
231 | throw new IllegalStateException(e);
232 | }
233 | }
234 |
235 | @Override
236 | public void onWebSocketError(Throwable cause) {
237 | if (log.isLoggable(Logger.FINE)) {
238 | log.fine(CLASS_NAME, "onWebSocketError", "401", null, cause);
239 | }
240 | }
241 |
242 | @Override
243 | public void onWebSocketConnect(Session sess) {
244 | super.onWebSocketConnect(sess);
245 | if (log.isLoggable(Logger.FINE)) {
246 | log.fine(CLASS_NAME, "onWebSocketConnect", "116",
247 | new Object[] { uri.toString() + ", WebSocket CONNECTED." });
248 | }
249 | }
250 |
251 | @Override
252 | public void onWebSocketClose(int statusCode, String reason) {
253 | super.onWebSocketClose(statusCode, reason);
254 | if (log.isLoggable(Logger.FINE)) {
255 | log.fine(CLASS_NAME, "onWebSocketConnect", "116",
256 | new Object[] { uri.toString() + ", WebSocket CLOSED." });
257 | }
258 | }
259 |
260 | }
261 |
--------------------------------------------------------------------------------
/src/test/java/io/inventit/dev/mqtt/paho/JettyStrErrLogUtils.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2014 Inventit Inc.
3 | */
4 | package io.inventit.dev.mqtt.paho;
5 |
6 | public class JettyStrErrLogUtils {
7 |
8 | private JettyStrErrLogUtils() {
9 | }
10 |
11 | public static void enableLog() {
12 | System.setProperty("org.eclipse.jetty.util.log.class",
13 | "org.eclipse.jetty.util.log.StrErrLog");
14 | System.setProperty("org.eclipse.jetty.LEVEL", "INFO");
15 | System.setProperty("org.eclipse.jetty.websocket.LEVEL", "DEBUG");
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/test/java/io/inventit/dev/mqtt/paho/MqttWebSocketAsyncClientInContainerTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2014 Inventit Inc.
3 | */
4 | package io.inventit.dev.mqtt.paho;
5 |
6 | import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
7 | import org.eclipse.paho.client.mqttv3.IMqttToken;
8 | import org.eclipse.paho.client.mqttv3.MqttCallback;
9 | import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
10 | import org.eclipse.paho.client.mqttv3.MqttMessage;
11 | import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
12 | import org.junit.Before;
13 | import org.junit.Test;
14 |
15 | /**
16 | * This test is skipped when building with maven.
17 | */
18 | public class MqttWebSocketAsyncClientInContainerTest {
19 |
20 | @Before
21 | public void setUp() throws Exception {
22 | JettyStrErrLogUtils.enableLog();
23 | PahoConsoleLogger.enableLog();
24 | }
25 |
26 | @Test
27 | public void test() throws Exception {
28 | // Plain MQTT
29 | // final String uriString = "tcp://your-mqtt-broker:1883";
30 |
31 | // MQTT over WebSocket
32 | final String uriString = "wss://your-ws-broker/mqtt";
33 |
34 | // Credentials
35 | final String clientId = "123";
36 | final String userName = "";
37 | final String password = "";
38 |
39 | final MqttWebSocketAsyncClient client = new MqttWebSocketAsyncClient(
40 | uriString, clientId, new MemoryPersistence());
41 | final MqttConnectOptions options = new MqttConnectOptions();
42 | options.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1_1);
43 | options.setCleanSession(true);
44 | options.setUserName(userName);
45 | options.setPassword(password.toCharArray());
46 | final IMqttToken token = client.connect(options);
47 | client.setCallback(new MqttCallback() {
48 |
49 | @Override
50 | public void messageArrived(String topic, MqttMessage message)
51 | throws Exception {
52 | System.out.println("messageArrived:" + topic + "/" + message);
53 | }
54 |
55 | @Override
56 | public void deliveryComplete(IMqttDeliveryToken token) {
57 | System.out.println("deliveryComplete:" + token);
58 | }
59 |
60 | @Override
61 | public void connectionLost(Throwable cause) {
62 | System.out.println("connectionLost");
63 | cause.printStackTrace();
64 | }
65 | });
66 |
67 | try {
68 | final long start = System.currentTimeMillis();
69 | while (client.isConnected() == false
70 | && System.currentTimeMillis() - start < 5000) {
71 | Thread.sleep(100);
72 | }
73 | System.out.println("OK!");
74 | } finally {
75 | System.out.println(token.isComplete());
76 | client.disconnect();
77 | client.close();
78 | }
79 | }
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/src/test/java/io/inventit/dev/mqtt/paho/MqttWebSocketAsyncClientTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2014 Inventit Inc.
3 | */
4 | package io.inventit.dev.mqtt.paho;
5 |
6 | import static org.junit.Assert.*;
7 |
8 | import java.net.URI;
9 |
10 | import org.junit.Test;
11 |
12 | public class MqttWebSocketAsyncClientTest {
13 |
14 | @Test
15 | public void test_createDummyURI() {
16 | final URI uri = URI.create(MqttWebSocketAsyncClient
17 | .createDummyURI("ws://server.address:123/path"));
18 | assertEquals("tcp", uri.getScheme());
19 | assertEquals("DUMMY-server.address", uri.getHost());
20 | assertEquals(123, uri.getPort());
21 | assertEquals("", uri.getPath());
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/test/java/io/inventit/dev/mqtt/paho/WebSocketNetworkModuleInContainerTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2014 Inventit Inc.
3 | */
4 | package io.inventit.dev.mqtt.paho;
5 |
6 | import java.net.URI;
7 |
8 | import org.junit.Test;
9 |
10 | public class WebSocketNetworkModuleInContainerTest {
11 |
12 | @Test
13 | public void test_connection() throws Exception {
14 | final String uriString = "ws://your.broker.com:80/mqtt";
15 | final String clientId = "my-client-id";
16 |
17 | final WebSocketNetworkModule module = new WebSocketNetworkModule(
18 | URI.create(uriString), "mqttv3.1", clientId);
19 | module.start();
20 | System.out.println("OK!");
21 |
22 | module.getOutputStream().write("test".getBytes());
23 | module.getOutputStream().flush();
24 | module.stop();
25 | System.out.println("END");
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------