package org.apache.axis2.transport.mqtt; /* * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.eclipse.paho.client.mqttv3.*; import java.io.IOException; import java.sql.Timestamp; public class MqttAsyncCallback implements MqttCallback { int state = BEGIN; static final int BEGIN = 0; static final int CONNECTED = 1; static final int PUBLISHED = 2; static final int SUBSCRIBED = 3; static final int DISCONNECTED = 4; static final int FINISH = 5; static final int ERROR = 6; static final int DISCONNECT = 7; private MqttConnectOptions conOpt; private Log log = LogFactory.getLog(MqttAsyncCallback.class); // Private instance variables private MqttAsyncClient client; private String brokerUrl; private Throwable ex = null; private final Object waiter = new Object(); private boolean donext = false; public void setConOpt(MqttConnectOptions conOpt) { this.conOpt = conOpt; } public MqttAsyncCallback(MqttAsyncClient clientAsync) throws MqttException { client = clientAsync; // Set this wrapper as the callback handler client.setCallback(this); } /** * Publish / send a message to an MQTT server * * @param topicName the name of the topic to publish to * @param message the set of bytes to send to the MQTT server * @throws MqttException */ public void publish(String topicName, MqttMessage message) throws Throwable { // Use a state machine to decide which step to do next. State change occurs // when a notification is received that an MQTT action has completed while (state != FINISH) { switch (state) { case BEGIN: // Connect using a non-blocking connect MqttConnector con = new MqttConnector(); con.doConnect(); break; case CONNECTED: // Publish using a non-blocking publisher Publisher pub = new Publisher(); pub.doPublish(topicName, message); break; case PUBLISHED: state = DISCONNECT; donext = true; break; case DISCONNECT: Disconnector disc = new Disconnector(); disc.doDisconnect(); break; case ERROR: throw ex; case DISCONNECTED: state = FINISH; donext = true; break; } waitForStateChange(10000); } } /** * Wait for a maximum amount of time for a state change event to occur * * @param maxTTW maximum time to wait in milliseconds * @throws MqttException */ private void waitForStateChange(int maxTTW) throws MqttException { synchronized (waiter) { if (!donext) { try { waiter.wait(maxTTW); } catch (InterruptedException e) { log.warn("Timed out while waiting for the senders callback", e); } if (ex != null) { throw (MqttException) ex; } } donext = false; } } /** * Subscribe to a topic on an MQTT server * Once subscribed this method waits for the messages to arrive from the server * that match the subscription. It continues listening for messages until the enter key is * pressed. * * @param topicName to subscribe to (can be wild carded) * @param qos the maximum quality of service to receive messages at for this subscription * @throws MqttException */ public void subscribe(String topicName, int qos) throws Throwable { // Use a state machine to decide which step to do next. State change occurs // when a notification is received that an MQTT action has completed while (state != FINISH) { switch (state) { case BEGIN: // Connect using a non-blocking connect MqttConnector con = new MqttConnector(); con.doConnect(); break; case CONNECTED: // Subscribe using a non-blocking subscribe Subscriber sub = new Subscriber(); sub.doSubscribe(topicName, qos); break; case SUBSCRIBED: // Block until Enter is pressed allowing messages to arrive log.info("Press <Enter> to exit"); try { System.in.read(); } catch (IOException e) { //If we can't read we'll just exit } state = DISCONNECT; donext = true; break; case DISCONNECT: Disconnector disc = new Disconnector(); disc.doDisconnect(); break; case ERROR: throw ex; case DISCONNECTED: state = FINISH; donext = true; break; } waitForStateChange(10000); } } public void connectionLost(Throwable throwable) { //ignoring for the moment... } public void messageArrived(String s, MqttMessage mqttMessage) throws Exception { throw new IllegalStateException(); } public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) { if (log.isDebugEnabled()) { log.debug("Message delivered: " + iMqttDeliveryToken.toString()); } } /** * Connect in a non-blocking way and then sit back and wait to be * notified that the action has completed. */ public class MqttConnector { public MqttConnector() { } public void doConnect() { // Connect to the server // Get a token and setup an asynchronous listener on the token which // will be notified once the connect completes if (log.isDebugEnabled()) { log.debug("Connecting with client ID " + client.getClientId()); } IMqttActionListener conListener = new IMqttActionListener() { public void onSuccess(IMqttToken asyncActionToken) { state = CONNECTED; carryOn(); } public void onFailure(IMqttToken asyncActionToken, Throwable exception) { ex = exception; state = ERROR; log.warn("Client " + client.getClientId() + " connection failed." + exception); carryOn(); } public void carryOn() { synchronized (waiter) { donext = true; waiter.notifyAll(); } } }; try { // Connect using a non-blocking connect client.connect(conOpt, "Connect sample context", conListener); } catch (MqttException e) { // If though it is a non-blocking connect an exception can be // thrown if validation of parms fails or other checks such // as already connected fail. state = ERROR; donext = true; ex = e; } } } /** * Publish in a non-blocking way and then sit back and wait to be * notified that the action has completed. */ public class Publisher { public void doPublish(String topicName, MqttMessage message) { // Send / publish a message to the server // Get a token and setup an asynchronous listener on the token which // will be notified once the message has been delivered // MqttMessage message = new MqttMessage(payload); //message.setQos(qos); String time = new Timestamp(System.currentTimeMillis()).toString(); // Setup a listener object to be notified when the publish completes. IMqttActionListener pubListener = new IMqttActionListener() { public void onSuccess(IMqttToken asyncActionToken) { state = PUBLISHED; carryOn(); } public void onFailure(IMqttToken asyncActionToken, Throwable exception) { ex = exception; state = ERROR; log.warn("Message publishing failed" + exception); carryOn(); } public void carryOn() { synchronized (waiter) { donext = true; waiter.notifyAll(); } } }; try { // Publish the message client.publish(topicName, message, "Pub sample context", pubListener); } catch (MqttException e) { state = ERROR; donext = true; ex = e; } } } /** * Subscribe in a non-blocking way and then sit back and wait to be * notified that the action has completed. */ public class Subscriber { public void doSubscribe(String topicName, int qos) { // Make a subscription // Get a token and setup an asynchronous listener on the token which // will be notified once the subscription is in place. if (log.isDebugEnabled()) { log.debug("Subscribing to topic \"" + topicName + "\" qos " + qos); } IMqttActionListener subListener = new IMqttActionListener() { public void onSuccess(IMqttToken asyncActionToken) { state = SUBSCRIBED; carryOn(); } public void onFailure(IMqttToken asyncActionToken, Throwable exception) { ex = exception; state = ERROR; log.warn("Topic subscription failed" + exception); carryOn(); } public void carryOn() { synchronized (waiter) { donext = true; waiter.notifyAll(); } } }; try { client.subscribe(topicName, qos, "Subscribe sample context", subListener); } catch (MqttException e) { state = ERROR; donext = true; ex = e; } } } /** * Disconnect in a non-blocking way and then sit back and wait to be * notified that the action has completed. */ public class Disconnector { public void doDisconnect() { // Disconnect the client if (log.isDebugEnabled()) { log.debug("Disconnecting client " + client.getClientId()); } IMqttActionListener discListener = new IMqttActionListener() { public void onSuccess(IMqttToken asyncActionToken) { state = DISCONNECTED; carryOn(); } public void onFailure(IMqttToken asyncActionToken, Throwable exception) { ex = exception; state = ERROR; log.warn("Disconnection failed" + exception); carryOn(); } public void carryOn() { synchronized (waiter) { donext = true; waiter.notifyAll(); } } }; try { client.disconnect("Disconnect sample context", discListener); } catch (MqttException e) { state = ERROR; donext = true; ex = e; } } } }