package cgl.iotcloud.transport.mqtt; import org.fusesource.mqtt.client.*; import org.fusesource.mqtt.codec.MQTTFrame; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.URISyntaxException; import java.util.concurrent.BlockingQueue; public class MQTTProducer { private static Logger LOG = LoggerFactory.getLogger(MQTTProducer.class); private CallbackConnection connection; private String url; private int port; private BlockingQueue messages; private String queueName; private boolean trace = false; private QoS qoS; private boolean run = true; public MQTTProducer(String url, int port, BlockingQueue messages, String queueName) { this(url, port, messages, queueName, QoS.AT_MOST_ONCE); } public MQTTProducer(String url, int port, BlockingQueue messages, String queueName, QoS qoS) { this.url = url; this.messages = messages; this.queueName = queueName; this.qoS = qoS; this.port = port; } public void setTrace(boolean trace) { this.trace = trace; } private enum State { INIT, CONNECTED, TOPIC_CONNECTED, DISCONNECTED, } private State state = State.INIT; public void open() { MQTT mqtt = new MQTT(); try { if (port != -1) { mqtt.setHost(url, port); } else { mqtt.setHost(url); } } catch (URISyntaxException e) { String msg = "Invalid URL for the MQTT Broker"; LOG.error(msg, e); throw new RuntimeException(msg, e); } if (trace) { mqtt.setTracer(new Tracer() { @Override public void onReceive(MQTTFrame frame) { LOG.info("recv: " + frame); } @Override public void onSend(MQTTFrame frame) { LOG.info("send: " + frame); } @Override public void debug(String message, Object... args) { LOG.info(String.format("debug: " + message, args)); } }); } connection = mqtt.callbackConnection(); connection.getDispatchQueue().execute(new Runnable() { @Override public void run() { connection.connect(new Callback<Void>() { public void onFailure(Throwable value) { state = State.INIT; String s = "Failed to connect to the broker"; LOG.error(s, value); throw new RuntimeException(s, value); } // Once we connect.. public void onSuccess(Void v) { LOG.debug("Connection established"); state = State.CONNECTED; } }); } }); Thread workerThread = new Thread(new Worker()); workerThread.start(); } public void close() { run = false; if (connection != null && (state == State.CONNECTED || state == State.TOPIC_CONNECTED)) { // To disconnect.. connection.getDispatchQueue().execute(new Runnable() { @Override public void run() { connection.disconnect(new Callback<Void>() { public void onSuccess(Void v) { // called once the connection is disconnected. state = State.DISCONNECTED; } public void onFailure(Throwable value) { // Disconnects never fail. state = State.DISCONNECTED; } }); } }); } } private class Worker implements Runnable { @Override public void run() { int errorCount = 0; while (run) { try { if (state == State.CONNECTED) { try { final Object input = messages.take(); connection.getDispatchQueue().execute(new Runnable() { @Override public void run() { if (input instanceof byte []) { connection.publish(queueName, (byte []) input, qoS, false, null); } else { throw new RuntimeException("Expepected byte array after conversion"); } } }); } catch (InterruptedException e) { LOG.error("Exception occurred in the worker listening for consumer changes", e); } } else { Thread.sleep(100); } } catch (Throwable t) { errorCount++; if (errorCount <= 3) { LOG.error("Error occurred " + errorCount + " times.. trying to continue the worker", t); } else { LOG.error("Error occurred " + errorCount + " times.. terminating the worker", t); run = false; } } } String message = "Unexpected notification type"; LOG.error(message); throw new RuntimeException(message); } } }