PageRenderTime 52ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/org.eclipse.paho.sample.mqttv3app/src/main/java/org/eclipse/paho/sample/mqttv3app/SampleAsyncCallBack.java

http://github.com/eclipse/paho.mqtt.java
Java | 655 lines | 416 code | 58 blank | 181 comment | 37 complexity | c22763d10dde7d314647833ec9811c2d MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception
  1. /*******************************************************************************
  2. * Copyright (c) 2009, 2014 IBM Corp.
  3. *
  4. * All rights reserved. This program and the accompanying materials
  5. * are made available under the terms of the Eclipse Public License v2.0
  6. * and Eclipse Distribution License v1.0 which accompany this distribution.
  7. *
  8. * The Eclipse Public License is available at
  9. * https://www.eclipse.org/legal/epl-2.0
  10. * and the Eclipse Distribution License is available at
  11. * https://www.eclipse.org/org/documents/edl-v10.php
  12. *
  13. * Contributors:
  14. * Dave Locke - initial API and implementation and/or initial documentation
  15. */
  16. package org.eclipse.paho.sample.mqttv3app;
  17. import java.io.IOException;
  18. import java.sql.Timestamp;
  19. import java.util.Arrays;
  20. import org.eclipse.paho.client.mqttv3.IMqttActionListener;
  21. import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
  22. import org.eclipse.paho.client.mqttv3.IMqttToken;
  23. import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
  24. import org.eclipse.paho.client.mqttv3.MqttCallback;
  25. import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
  26. import org.eclipse.paho.client.mqttv3.MqttException;
  27. import org.eclipse.paho.client.mqttv3.MqttMessage;
  28. import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence;
  29. /**
  30. * A sample application that demonstrates how to use the Paho MQTT v3.1 Client API in
  31. * non-blocking callback/notification mode.
  32. *
  33. * It can be run from the command line in one of two modes:
  34. * - as a publisher, sending a single message to a topic on the server
  35. * - as a subscriber, listening for messages from the server
  36. *
  37. * There are three versions of the sample that implement the same features
  38. * but do so using using different programming styles:
  39. * <ol>
  40. * <li>Sample which uses the API which blocks until the operation completes</li>
  41. * <li>SampleAsyncWait shows how to use the asynchronous API with waiters that block until
  42. * an action completes</li>
  43. * <li>SampleAsyncCallBack (this one) shows how to use the asynchronous API where events are
  44. * used to notify the application when an action completes<li>
  45. * </ol>
  46. *
  47. * If the application is run with the -h parameter then info is displayed that
  48. * describes all of the options / parameters.
  49. */
  50. public class SampleAsyncCallBack implements MqttCallback {
  51. int state = BEGIN;
  52. static final int BEGIN = 0;
  53. static final int CONNECTED = 1;
  54. static final int PUBLISHED = 2;
  55. static final int SUBSCRIBED = 3;
  56. static final int DISCONNECTED = 4;
  57. static final int FINISH = 5;
  58. static final int ERROR = 6;
  59. static final int DISCONNECT = 7;
  60. /**
  61. * The main entry point of the sample.
  62. *
  63. * This method handles parsing the arguments specified on the
  64. * command-line before performing the specified action.
  65. */
  66. public static void main(String[] args) {
  67. // Default settings:
  68. boolean quietMode = false;
  69. String action = "publish";
  70. String topic = "";
  71. String message = "Message from async callback Paho MQTTv3 Java client sample";
  72. int qos = 2;
  73. String broker = "m2m.eclipse.org";
  74. int port = 1883;
  75. String clientId = null;
  76. String subTopic = "Sample/#";
  77. String pubTopic = "Sample/Java/v3";
  78. boolean cleanSession = true; // Non durable subscriptions
  79. boolean ssl = false;
  80. String password = null;
  81. String userName = null;
  82. // Parse the arguments -
  83. for (int i=0; i<args.length; i++) {
  84. // Check this is a valid argument
  85. if (args[i].length() == 2 && args[i].startsWith("-")) {
  86. char arg = args[i].charAt(1);
  87. // Handle arguments that take no-value
  88. switch(arg) {
  89. case 'h': case '?': printHelp(); return;
  90. case 'q': quietMode = true; continue;
  91. }
  92. // Now handle the arguments that take a value and
  93. // ensure one is specified
  94. if (i == args.length -1 || args[i+1].charAt(0) == '-') {
  95. System.out.println("Missing value for argument: "+args[i]);
  96. printHelp();
  97. return;
  98. }
  99. switch(arg) {
  100. case 'a': action = args[++i]; break;
  101. case 't': topic = args[++i]; break;
  102. case 'm': message = args[++i]; break;
  103. case 's': qos = Integer.parseInt(args[++i]); break;
  104. case 'b': broker = args[++i]; break;
  105. case 'p': port = Integer.parseInt(args[++i]); break;
  106. case 'i': clientId = args[++i]; break;
  107. case 'c': cleanSession = Boolean.valueOf(args[++i]).booleanValue(); break;
  108. case 'k': System.getProperties().put("javax.net.ssl.keyStore", args[++i]); break;
  109. case 'w': System.getProperties().put("javax.net.ssl.keyStorePassword", args[++i]); break;
  110. case 'r': System.getProperties().put("javax.net.ssl.trustStore", args[++i]); break;
  111. case 'v': ssl = Boolean.valueOf(args[++i]).booleanValue(); break;
  112. case 'u': userName = args[++i]; break;
  113. case 'z': password = args[++i]; break;
  114. default:
  115. System.out.println("Unrecognised argument: "+args[i]);
  116. printHelp();
  117. return;
  118. }
  119. } else {
  120. System.out.println("Unrecognised argument: "+args[i]);
  121. printHelp();
  122. return;
  123. }
  124. }
  125. // Validate the provided arguments
  126. if (!action.equals("publish") && !action.equals("subscribe")) {
  127. System.out.println("Invalid action: "+action);
  128. printHelp();
  129. return;
  130. }
  131. if (qos < 0 || qos > 2) {
  132. System.out.println("Invalid QoS: "+qos);
  133. printHelp();
  134. return;
  135. }
  136. if (topic.equals("")) {
  137. // Set the default topic according to the specified action
  138. if (action.equals("publish")) {
  139. topic = pubTopic;
  140. } else {
  141. topic = subTopic;
  142. }
  143. }
  144. String protocol = "tcp://";
  145. if (ssl) {
  146. protocol = "ssl://";
  147. }
  148. String url = protocol + broker + ":" + port;
  149. if (clientId == null || clientId.equals("")) {
  150. clientId = "SampleJavaV3_"+action;
  151. }
  152. // With a valid set of arguments, the real work of
  153. // driving the client API can begin
  154. try {
  155. // Create an instance of the Sample client wrapper
  156. SampleAsyncCallBack sampleClient = new SampleAsyncCallBack(url,clientId,cleanSession, quietMode,userName,password);
  157. // Perform the specified action
  158. if (action.equals("publish")) {
  159. sampleClient.publish(topic,qos,message.getBytes());
  160. } else if (action.equals("subscribe")) {
  161. sampleClient.subscribe(topic,qos);
  162. }
  163. } catch(MqttException me) {
  164. // Display full details of any exception that occurs
  165. System.out.println("reason "+me.getReasonCode());
  166. System.out.println("msg "+me.getMessage());
  167. System.out.println("loc "+me.getLocalizedMessage());
  168. System.out.println("cause "+me.getCause());
  169. System.out.println("excep "+me);
  170. me.printStackTrace();
  171. } catch (Throwable th) {
  172. System.out.println("Throwable caught "+th);
  173. th.printStackTrace();
  174. }
  175. }
  176. // Private instance variables
  177. MqttAsyncClient client;
  178. String brokerUrl;
  179. private boolean quietMode;
  180. private MqttConnectOptions conOpt;
  181. private boolean clean;
  182. Throwable ex = null;
  183. Object waiter = new Object();
  184. boolean donext = false;
  185. private String password;
  186. private String userName;
  187. /**
  188. * Constructs an instance of the sample client wrapper
  189. * @param brokerUrl the url to connect to
  190. * @param clientId the client id to connect with
  191. * @param cleanSession clear state at end of connection or not (durable or non-durable subscriptions)
  192. * @param quietMode whether debug should be printed to standard out
  193. * @param userName the username to connect with
  194. * @param password the password for the user
  195. * @throws MqttException
  196. */
  197. public SampleAsyncCallBack(String brokerUrl, String clientId, boolean cleanSession,
  198. boolean quietMode, String userName, String password) throws MqttException {
  199. this.brokerUrl = brokerUrl;
  200. this.quietMode = quietMode;
  201. this.clean = cleanSession;
  202. this.password = password;
  203. this.userName = userName;
  204. //This sample stores in a temporary directory... where messages temporarily
  205. // stored until the message has been delivered to the server.
  206. //..a real application ought to store them somewhere
  207. // where they are not likely to get deleted or tampered with
  208. String tmpDir = System.getProperty("java.io.tmpdir");
  209. MqttDefaultFilePersistence dataStore = new MqttDefaultFilePersistence(tmpDir);
  210. try {
  211. // Construct the object that contains connection parameters
  212. // such as cleanSession and LWT
  213. conOpt = new MqttConnectOptions();
  214. conOpt.setCleanSession(clean);
  215. if(password != null ) {
  216. conOpt.setPassword(this.password.toCharArray());
  217. }
  218. if(userName != null) {
  219. conOpt.setUserName(this.userName);
  220. }
  221. // Construct the MqttClient instance
  222. client = new MqttAsyncClient(this.brokerUrl,clientId, dataStore);
  223. // Set this wrapper as the callback handler
  224. client.setCallback(this);
  225. } catch (MqttException e) {
  226. e.printStackTrace();
  227. log("Unable to set up client: "+e.toString());
  228. System.exit(1);
  229. }
  230. }
  231. /**
  232. * Publish / send a message to an MQTT server
  233. * @param topicName the name of the topic to publish to
  234. * @param qos the quality of service to delivery the message at (0,1,2)
  235. * @param payload the set of bytes to send to the MQTT server
  236. * @throws MqttException
  237. */
  238. public void publish(String topicName, int qos, byte[] payload) throws Throwable {
  239. // Use a state machine to decide which step to do next. State change occurs
  240. // when a notification is received that an MQTT action has completed
  241. while (state != FINISH) {
  242. switch (state) {
  243. case BEGIN:
  244. // Connect using a non-blocking connect
  245. MqttConnector con = new MqttConnector();
  246. con.doConnect();
  247. break;
  248. case CONNECTED:
  249. // Publish using a non-blocking publisher
  250. Publisher pub = new Publisher();
  251. pub.doPublish(topicName, qos, payload);
  252. break;
  253. case PUBLISHED:
  254. state = DISCONNECT;
  255. donext = true;
  256. break;
  257. case DISCONNECT:
  258. Disconnector disc = new Disconnector();
  259. disc.doDisconnect();
  260. break;
  261. case ERROR:
  262. throw ex;
  263. case DISCONNECTED:
  264. state = FINISH;
  265. donext = true;
  266. break;
  267. }
  268. // if (state != FINISH) {
  269. // Wait until notified about a state change and then perform next action
  270. waitForStateChange(10000);
  271. // }
  272. }
  273. }
  274. /**
  275. * Wait for a maximum amount of time for a state change event to occur
  276. * @param maxTTW maximum time to wait in milliseconds
  277. * @throws MqttException
  278. */
  279. private void waitForStateChange(int maxTTW ) throws MqttException {
  280. synchronized (waiter) {
  281. if (!donext ) {
  282. try {
  283. waiter.wait(maxTTW);
  284. } catch (InterruptedException e) {
  285. log("timed out");
  286. e.printStackTrace();
  287. }
  288. if (ex != null) {
  289. throw (MqttException)ex;
  290. }
  291. }
  292. donext = false;
  293. }
  294. }
  295. /**
  296. * Subscribe to a topic on an MQTT server
  297. * Once subscribed this method waits for the messages to arrive from the server
  298. * that match the subscription. It continues listening for messages until the enter key is
  299. * pressed.
  300. * @param topicName to subscribe to (can be wild carded)
  301. * @param qos the maximum quality of service to receive messages at for this subscription
  302. * @throws MqttException
  303. */
  304. public void subscribe(String topicName, int qos) throws Throwable {
  305. // Use a state machine to decide which step to do next. State change occurs
  306. // when a notification is received that an MQTT action has completed
  307. while (state != FINISH) {
  308. switch (state) {
  309. case BEGIN:
  310. // Connect using a non-blocking connect
  311. MqttConnector con = new MqttConnector();
  312. con.doConnect();
  313. break;
  314. case CONNECTED:
  315. // Subscribe using a non-blocking subscribe
  316. Subscriber sub = new Subscriber();
  317. sub.doSubscribe(topicName, qos);
  318. break;
  319. case SUBSCRIBED:
  320. // Block until Enter is pressed allowing messages to arrive
  321. log("Press <Enter> to exit");
  322. try {
  323. System.in.read();
  324. } catch (IOException e) {
  325. //If we can't read we'll just exit
  326. }
  327. state = DISCONNECT;
  328. donext = true;
  329. break;
  330. case DISCONNECT:
  331. Disconnector disc = new Disconnector();
  332. disc.doDisconnect();
  333. break;
  334. case ERROR:
  335. throw ex;
  336. case DISCONNECTED:
  337. state = FINISH;
  338. donext = true;
  339. break;
  340. }
  341. // if (state != FINISH && state != DISCONNECT) {
  342. waitForStateChange(10000);
  343. }
  344. // }
  345. }
  346. /**
  347. * Utility method to handle logging. If 'quietMode' is set, this method does nothing
  348. * @param message the message to log
  349. */
  350. void log(String message) {
  351. if (!quietMode) {
  352. System.out.println(message);
  353. }
  354. }
  355. /****************************************************************/
  356. /* Methods to implement the MqttCallback interface */
  357. /****************************************************************/
  358. /**
  359. * @see MqttCallback#connectionLost(Throwable)
  360. */
  361. public void connectionLost(Throwable cause) {
  362. // Called when the connection to the server has been lost.
  363. // An application may choose to implement reconnection
  364. // logic at this point. This sample simply exits.
  365. log("Connection to " + brokerUrl + " lost!" + cause);
  366. System.exit(1);
  367. }
  368. /**
  369. * @see MqttCallback#deliveryComplete(IMqttDeliveryToken)
  370. */
  371. public void deliveryComplete(IMqttDeliveryToken token) {
  372. // Called when a message has been delivered to the
  373. // server. The token passed in here is the same one
  374. // that was returned from the original call to publish.
  375. // This allows applications to perform asynchronous
  376. // delivery without blocking until delivery completes.
  377. //
  378. // This sample demonstrates asynchronous deliver, registering
  379. // a callback to be notified on each call to publish.
  380. //
  381. // The deliveryComplete method will also be called if
  382. // the callback is set on the client
  383. //
  384. // note that token.getTopics() returns an array so we convert to a string
  385. // before printing it on the console
  386. log("Delivery complete callback: Publish Completed "+Arrays.toString(token.getTopics()));
  387. }
  388. /**
  389. * @see MqttCallback#messageArrived(String, MqttMessage)
  390. */
  391. public void messageArrived(String topic, MqttMessage message) throws MqttException {
  392. // Called when a message arrives from the server that matches any
  393. // subscription made by the client
  394. String time = new Timestamp(System.currentTimeMillis()).toString();
  395. System.out.println("Time:\t" +time +
  396. " Topic:\t" + topic +
  397. " Message:\t" + new String(message.getPayload()) +
  398. " QoS:\t" + message.getQos());
  399. }
  400. /****************************************************************/
  401. /* End of MqttCallback methods */
  402. /****************************************************************/
  403. static void printHelp() {
  404. System.out.println(
  405. "Syntax:\n\n" +
  406. " SampleAsyncCallBack [-h] [-a publish|subscribe] [-t <topic>] [-m <message text>]\n" +
  407. " [-s 0|1|2] -b <hostname|IP address>] [-p <brokerport>] [-i <clientID>]\n\n" +
  408. " -h Print this help text and quit\n" +
  409. " -q Quiet mode (default is false)\n" +
  410. " -a Perform the relevant action (default is publish)\n" +
  411. " -t Publish/subscribe to <topic> instead of the default\n" +
  412. " (publish: \"Sample/Java/v3\", subscribe: \"Sample/#\")\n" +
  413. " -m Use <message text> instead of the default\n" +
  414. " (\"Message from MQTTv3 Java client\")\n" +
  415. " -s Use this QoS instead of the default (2)\n" +
  416. " -b Use this name/IP address instead of the default (m2m.eclipse.org)\n" +
  417. " -p Use this port instead of the default (1883)\n\n" +
  418. " -i Use this client ID instead of SampleJavaV3_<action>\n" +
  419. " -c Connect to the server with a clean session (default is false)\n" +
  420. " \n\n Security Options \n" +
  421. " -u Username \n" +
  422. " -z Password \n" +
  423. " \n\n SSL Options \n" +
  424. " -v SSL enabled; true - (default is false) " +
  425. " -k Use this JKS format key store to verify the client\n" +
  426. " -w Passpharse to verify certificates in the keys store\n" +
  427. " -r Use this JKS format keystore to verify the server\n" +
  428. " If javax.net.ssl properties have been set only the -v flag needs to be set\n" +
  429. "Delimit strings containing spaces with \"\"\n\n" +
  430. "Publishers transmit a single message then disconnect from the server.\n" +
  431. "Subscribers remain connected to the server and receive appropriate\n" +
  432. "messages until <enter> is pressed.\n\n"
  433. );
  434. }
  435. /**
  436. * Connect in a non-blocking way and then sit back and wait to be
  437. * notified that the action has completed.
  438. */
  439. public class MqttConnector {
  440. public MqttConnector() {
  441. }
  442. public void doConnect() {
  443. // Connect to the server
  444. // Get a token and setup an asynchronous listener on the token which
  445. // will be notified once the connect completes
  446. log("Connecting to "+brokerUrl + " with client ID "+client.getClientId());
  447. IMqttActionListener conListener = new IMqttActionListener() {
  448. public void onSuccess(IMqttToken asyncActionToken) {
  449. log("Connected");
  450. state = CONNECTED;
  451. carryOn();
  452. }
  453. public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
  454. ex = exception;
  455. state = ERROR;
  456. log ("connect failed" +exception);
  457. carryOn();
  458. }
  459. public void carryOn() {
  460. synchronized (waiter) {
  461. donext=true;
  462. waiter.notifyAll();
  463. }
  464. }
  465. };
  466. try {
  467. // Connect using a non-blocking connect
  468. client.connect(conOpt,"Connect sample context", conListener);
  469. } catch (MqttException e) {
  470. // If though it is a non-blocking connect an exception can be
  471. // thrown if validation of parms fails or other checks such
  472. // as already connected fail.
  473. state = ERROR;
  474. donext = true;
  475. ex = e;
  476. }
  477. }
  478. }
  479. /**
  480. * Publish in a non-blocking way and then sit back and wait to be
  481. * notified that the action has completed.
  482. */
  483. public class Publisher {
  484. public void doPublish(String topicName, int qos, byte[] payload) {
  485. // Send / publish a message to the server
  486. // Get a token and setup an asynchronous listener on the token which
  487. // will be notified once the message has been delivered
  488. MqttMessage message = new MqttMessage(payload);
  489. message.setQos(qos);
  490. String time = new Timestamp(System.currentTimeMillis()).toString();
  491. log("Publishing at: "+time+ " to topic \""+topicName+"\" qos "+qos);
  492. // Setup a listener object to be notified when the publish completes.
  493. //
  494. IMqttActionListener pubListener = new IMqttActionListener() {
  495. public void onSuccess(IMqttToken asyncActionToken) {
  496. log("Publish Completed");
  497. state = PUBLISHED;
  498. carryOn();
  499. }
  500. public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
  501. ex = exception;
  502. state = ERROR;
  503. log ("Publish failed" +exception);
  504. carryOn();
  505. }
  506. public void carryOn() {
  507. synchronized (waiter) {
  508. donext=true;
  509. waiter.notifyAll();
  510. }
  511. }
  512. };
  513. try {
  514. // Publish the message
  515. client.publish(topicName, message, "Pub sample context", pubListener);
  516. } catch (MqttException e) {
  517. state = ERROR;
  518. donext = true;
  519. ex = e;
  520. }
  521. }
  522. }
  523. /**
  524. * Subscribe in a non-blocking way and then sit back and wait to be
  525. * notified that the action has completed.
  526. */
  527. public class Subscriber {
  528. public void doSubscribe(String topicName, int qos) {
  529. // Make a subscription
  530. // Get a token and setup an asynchronous listener on the token which
  531. // will be notified once the subscription is in place.
  532. log("Subscribing to topic \""+topicName+"\" qos "+qos);
  533. IMqttActionListener subListener = new IMqttActionListener() {
  534. public void onSuccess(IMqttToken asyncActionToken) {
  535. log("Subscribe Completed");
  536. state = SUBSCRIBED;
  537. carryOn();
  538. }
  539. public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
  540. ex = exception;
  541. state = ERROR;
  542. log ("Subscribe failed" +exception);
  543. carryOn();
  544. }
  545. public void carryOn() {
  546. synchronized (waiter) {
  547. donext=true;
  548. waiter.notifyAll();
  549. }
  550. }
  551. };
  552. try {
  553. client.subscribe(topicName, qos, "Subscribe sample context", subListener);
  554. } catch (MqttException e) {
  555. state = ERROR;
  556. donext = true;
  557. ex = e;
  558. }
  559. }
  560. }
  561. /**
  562. * Disconnect in a non-blocking way and then sit back and wait to be
  563. * notified that the action has completed.
  564. */
  565. public class Disconnector {
  566. public void doDisconnect() {
  567. // Disconnect the client
  568. log("Disconnecting");
  569. IMqttActionListener discListener = new IMqttActionListener() {
  570. public void onSuccess(IMqttToken asyncActionToken) {
  571. log("Disconnect Completed");
  572. state = DISCONNECTED;
  573. carryOn();
  574. }
  575. public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
  576. ex = exception;
  577. state = ERROR;
  578. log ("Disconnect failed" +exception);
  579. carryOn();
  580. }
  581. public void carryOn() {
  582. synchronized (waiter) {
  583. donext=true;
  584. waiter.notifyAll();
  585. }
  586. }
  587. };
  588. try {
  589. client.disconnect("Disconnect sample context", discListener);
  590. } catch (MqttException e) {
  591. state = ERROR;
  592. donext = true;
  593. ex = e;
  594. }
  595. }
  596. }
  597. }