Java tutorial
/** * This file was automatically generated by the Mule Development Kit */ package com.angrygiant.mule.mqtt; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.eclipse.paho.client.mqttv3.*; import org.mule.api.ConnectionExceptionCode; import org.mule.api.annotations.Module; import org.mule.api.ConnectionException; import org.mule.api.annotations.Configurable; import org.mule.api.annotations.Processor; import org.mule.api.annotations.Source; import org.mule.api.annotations.lifecycle.Start; import org.mule.api.annotations.lifecycle.Stop; import org.mule.api.annotations.param.Default; import org.mule.api.annotations.param.Optional; import org.mule.api.annotations.param.OutboundHeaders; import org.mule.api.annotations.param.Payload; import org.mule.api.callback.SourceCallback; import javax.annotation.PreDestroy; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectOutput; import java.io.ObjectOutputStream; import java.util.HashMap; import java.util.Map; /** * Mule MQTT Module * <p/> * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com * <p/> * <p> * The software in this package is published under the terms of the CPAL v1.0 * license, a copy of which has been included with this distribution in the * LICENSE.md file. * </p> * <p> * Created with IntelliJ IDEA. * User: dmiller@angrygiant.com * Date: 9/21/12 * Time: 9:57 AM * </p> * <p> * Module definition for publishing and subscribing to a given MQTT broker server. * </p> * * @author dmiller@angrygiant.com */ @Module(name = "mqtt", schemaVersion = "0.2.1") public class MqttModule { private static final Logger logger = Logger.getLogger(MqttModule.class); public static final int MQTT_DEFAULT_PORT = 1883; public static final String MQTT_DEFAULT_HOST = "localhost"; /** * Broker Host Name: Host of the MQTT broker, defaults to 'localhost' */ @Configurable @Optional @Default("localhost") private String brokerHostName = MQTT_DEFAULT_HOST; /** * Broker port number, defaults to 1883 */ @Configurable @Optional @Default("1883") private int brokerPort = MQTT_DEFAULT_PORT; /** * Client ID (required): client identifier for the broker */ @Configurable private String clientId; /** * Clean Session - refer to Eclipse Paho documentation */ @Configurable @Optional @Default("true") private boolean cleanSession; /** * Username to log into broker with */ @Configurable @Optional private String username; /** * Password to log into broker with */ @Configurable @Optional private String password; /** * Connection Timeout - refer to Eclipse Paho documentation */ @Configurable @Optional @Default("30") private int connectionTimeout = 30; /** * Last Will and Testimate Topic - refer to Eclipse Paho documentation */ @Configurable @Optional private String lwtTopicName; /** * Last Will and Testimate Message - refer to Eclipse Paho documentation */ @Configurable @Optional private String lwtMessage; /** * Last Will and Testimate QOS - refer to Eclipse Paho documentation */ @Configurable @Optional @Default("2") private int lwtQos; /** * Last Will and Testimate Retention - refer to Eclipse Paho documentation */ @Configurable @Optional @Default("false") private boolean lwtRetained; /** * Keep Alive Interval - refer to Eclipse Paho documentation */ @Configurable @Optional @Default("60") private int keepAliveInterval = 60; /** * File Persistence Location - directory on the machine where message persistence can be stored to disk. */ @Configurable @Optional private String persistenceLocation; /** * Milliseconds of delay before subscription to a topic occurs. Gives the client time to connect. */ @Configurable @Optional @Default("500") private long subscriptionDelay; /** * Private variables not used by Mule directly */ private MqttClient client; private MqttConnectOptions connectOptions = new MqttConnectOptions(); /** * Initialize the connector by creating a client */ @Start public void initialize() throws ConnectionException { this.client = connectClient(getClientId()); try { this.client.disconnect(); } catch (MqttException e) { logger.debug("ERROR: Pre-emptive disconnect created an error", e); } } /** * Convenience method that connects to a given MQTT broker. Returns the MqttClient object * for later use with publish and subscribe. * * @param clientId * @return MqttClient * @throws ConnectionException */ private MqttClient connectClient(String clientId) throws ConnectionException { logger.warn("Creating client with ID of " + clientId); String brokerUrl = "tcp://" + getBrokerHostName() + ":" + getBrokerPort(); MqttDefaultFilePersistence filePersistence = null; if (getPersistenceLocation() != null || StringUtils.isNotBlank(getPersistenceLocation())) { try { logger.debug("Attempting to set up file persistence for client..."); filePersistence = new MqttDefaultFilePersistence(getPersistenceLocation()); } catch (MqttPersistenceException e) { logger.error("Error creating file persistence for messages...persistence will be IGNORED!!!", e); filePersistence = null; } } setupConnectOptions(); try { if (filePersistence == null) { logger.debug("Creating client without file persistence"); this.client = new MqttClient(brokerUrl, clientId, null); } else { logger.debug("Creating client with file persistence enabled"); this.client = new MqttClient(brokerUrl, clientId, filePersistence); } } catch (MqttException e) { logger.error("Error creating client to MQTT broker:", e); throw new ConnectionException(ConnectionExceptionCode.UNKNOWN, null, "Mule has issues with the MQTT client", e); } if (StringUtils.isNotBlank(this.getLwtTopicName())) { logger.debug("Setting up last will information..."); MqttTopic lwtTopic = this.client.getTopic(this.getLwtTopicName()); this.connectOptions.setWill(lwtTopic, this.getLwtMessage().getBytes(), this.getLwtQos(), false); } return this.client; } /** * Method that sets up the MqttConnectOptions class for use. This reads the settings given via * the mqtt:config element. */ private void setupConnectOptions() { connectOptions.setCleanSession(isCleanSession()); connectOptions.setConnectionTimeout(getConnectionTimeout()); connectOptions.setKeepAliveInterval(getKeepAliveInterval()); if (getPassword() != null || StringUtils.isNotBlank(getPassword())) { connectOptions.setPassword(getPassword().toCharArray()); } connectOptions.setUserName(getUsername()); } /** * Stop the client prior to departure * * @throws MqttException */ @Stop public void stopConnector() throws MqttException { if (this.client.isConnected()) { logger.info("Diconnecting from MQTT broker..."); this.client.disconnect(); } this.client = null; this.connectOptions = null; } /** * Publish processor - used to publish a message to a given topic on the MQTT broker. * <p/> * <p/> * {@sample.xml ../../../doc/mqtt-connector.xml.sample mqtt:publish} * * @param topicName topic to publish message to * @param message string message to publish (if blank/null, uses messagePayload) * @param qos qos level to use when publishing message * @param messagePayload injects passed payload into this object (for possible use) * @param outboundHeaders injected outbound headers map so we can set them prior to leaving * @return delivered byte[] payload of MqttMessage */ @Processor public byte[] publish(String topicName, String message, @Optional @Default("1") int qos, @Payload Object messagePayload, @OutboundHeaders Map<String, Object> outboundHeaders) throws MqttException, IOException { byte[] payloadBytes; logger.debug("Connecting to Broker..."); if (!this.client.isConnected()) { this.client.connect(); logger.info( "Connected to " + getBrokerHostName() + " on port " + getBrokerPort() + " as " + getClientId()); } else { logger.debug("Already connected to Broker"); } if (qos < 0 || qos > 2) { logger.warn("QOS is invalid, setting to default value of 2"); qos = 2; } logger.debug("Decipher whether we're using String or Message payload"); if (StringUtils.isNotBlank(message)) { logger.debug("Using the string message..."); payloadBytes = message.getBytes(); } else if (messagePayload instanceof byte[]) { logger.debug("Message payload is a byte array, using it directly..."); payloadBytes = (byte[]) messagePayload; } else { logger.info("Converting message payload to a byte array..."); ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutput out = new ObjectOutputStream(bos); try { out.writeObject(messagePayload); payloadBytes = bos.toByteArray(); } finally { bos.close(); out.close(); } } logger.debug("Retrieving topic '" + topicName + "'"); MqttTopic topic = this.client.getTopic(topicName); logger.debug("Preparing message..."); MqttMessage mqttMessage = new MqttMessage(payloadBytes); mqttMessage.setQos(qos); logger.debug("publishing message to broker..."); MqttDeliveryToken token = topic.publish(mqttMessage); logger.trace("Waiting for default timeout of 5minutes..."); token.waitForCompletion(900000); if (outboundHeaders == null) { outboundHeaders = new HashMap<String, Object>(); } outboundHeaders.put(MqttTopicListener.TOPIC_NAME, topic.getName()); outboundHeaders.put(MqttTopicListener.MESSAGE_DUPLICATE, mqttMessage.isDuplicate()); outboundHeaders.put(MqttTopicListener.MESSAGE_RETAIN, mqttMessage.isRetained()); outboundHeaders.put(MqttTopicListener.MESSAGE_QOS, mqttMessage.getQos()); outboundHeaders.put(MqttTopicListener.CLIENT_ID, this.client.getClientId()); outboundHeaders.put(MqttTopicListener.CLIENT_URI, this.client.getServerURI()); return mqttMessage.getPayload(); } /** * Publish processor - used to publish a message to a given topic on the MQTT broker. * <p/> * <p/> * {@sample.xml ../../../doc/mqtt-connector.xml.sample mqtt:subscribe} * * @param subscriberId required subscriber id to use for subscription * @param topicName topic to publish message to * @param filter topic filter string, comma delimited if multiple (takes precedence over topic name) * @param qos qos level to use when publishing message * @param callback qos level to use when publishing message * @return */ @Source public void subscribe(String subscriberId, @Optional String topicName, @Optional String filter, @Optional @Default("1") int qos, final SourceCallback callback) throws ConnectionException { logger.info("Creating new client for topic subscription"); MqttClient subscriberClient = connectClient(subscriberId); String[] filters; int[] qoss; logger.info("Deciding whether filter or name is used..."); if (StringUtils.isNotBlank(filter)) { logger.info("Building filters list"); filters = filter.split(","); logger.debug("I have " + filters.length + " filters defined. Creating matching queue of qos levels"); qoss = new int[filters.length]; for (int i = 0; i < filters.length; i++) { qoss[i] = qos; } } else { filters = null; qoss = null; } try { subscriberClient.disconnect(); } catch (MqttException e) { logger.warn("Pre-emptive disconnect called before subscription, errors occurred: " + e); } try { subscriberClient.setCallback(new MqttTopicListener(subscriberClient, callback, topicName, connectOptions, getSubscriptionDelay(), qos)); subscriberClient.connect(connectOptions); Thread.sleep(getSubscriptionDelay()); if (filters != null) { logger.debug("Subscribing to filters..."); subscriberClient.subscribe(filters, qoss); } else { logger.debug("Subscribing to topic name..."); subscriberClient.subscribe(topicName, qos); } } catch (MqttException e) { logger.error("MQTT Exceptions occurred subscribing", e); throw new ConnectionException(ConnectionExceptionCode.UNKNOWN, null, "Subscription Error", e); } catch (InterruptedException ie) { logger.error("Interrupt exception occurred sleeping before subscribing..."); throw new ConnectionException(ConnectionExceptionCode.UNKNOWN, null, "Interrupt Error", ie); } logger.info("Done subscribing to topic " + topicName); } //Getters and Setters public String getBrokerHostName() { return brokerHostName; } public void setBrokerHostName(String brokerHostName) { this.brokerHostName = brokerHostName; } public int getBrokerPort() { return brokerPort; } public void setBrokerPort(int brokerPort) { this.brokerPort = brokerPort; } public String getClientId() { return clientId; } public void setClientId(String clientId) { this.clientId = clientId; } public boolean isCleanSession() { return cleanSession; } public void setCleanSession(boolean cleanSession) { this.cleanSession = cleanSession; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public int getConnectionTimeout() { return connectionTimeout; } public void setConnectionTimeout(int connectionTimeout) { this.connectionTimeout = connectionTimeout; } public String getLwtTopicName() { return lwtTopicName; } public void setLwtTopicName(String lwtTopicName) { this.lwtTopicName = lwtTopicName; } public String getLwtMessage() { return lwtMessage; } public void setLwtMessage(String lwtMessage) { this.lwtMessage = lwtMessage; } public int getKeepAliveInterval() { return keepAliveInterval; } public void setKeepAliveInterval(int keepAliveInterval) { this.keepAliveInterval = keepAliveInterval; } public String getPersistenceLocation() { return persistenceLocation; } public void setPersistenceLocation(String persistenceLocation) { this.persistenceLocation = persistenceLocation; } public int getLwtQos() { return lwtQos; } public void setLwtQos(int lwtQos) { this.lwtQos = lwtQos; } public boolean isLwtRetained() { return lwtRetained; } public void setLwtRetained(boolean lwtRetained) { this.lwtRetained = lwtRetained; } public long getSubscriptionDelay() { return subscriptionDelay; } public void setSubscriptionDelay(long subscriptionDelay) { this.subscriptionDelay = subscriptionDelay; } }