Java tutorial
/** * Copyright (c) 2015-2016 Dishant Langayan * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Dishant Langayan */ package com.dishant.iot.smarthome.stats.poller.mqtt; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.dishant.iot.smarthome.SmarthomeException; import com.dishant.iot.smarthome.data.DataTransportListener; import com.dishant.iot.smarthome.data.mqtt.MqttDataTransport; import com.dishant.iot.smarthome.stats.Metric; import com.dishant.iot.smarthome.stats.poller.Poller; import com.dishant.iot.smarthome.stats.poller.PollerListener; import com.dishant.iot.smarthome.util.ValidationUtil; public class MqttPoller implements Poller, DataTransportListener { private static final Logger s_logger = LoggerFactory.getLogger(MqttPoller.class); private static final String PUBLISH_TOPICPREFIX_PROP_NAME = "stats.publish.topicPrefix"; private static final String PUBLISH_QOS_PROP_NAME = "stats.publish.qos"; private static final String SUBSCRIBE_TOPICPREFIX_PROP_NAME = "stats.subscribe.topicPrefix"; private static final String SUBSCRIBE_QOS_PROP_NAME = "stats.subscribe.qos"; private static final String AWS_THINGS_PROP_NAME = "stats.things"; private static final String MQTT_TIMEOUT_PROP_NAME = "mqtt.timeout"; private Map<String, Object> m_properties = new HashMap<String, Object>(); private List<String> m_things = new ArrayList<String>(); private PollerListener m_listener; private JSONParser m_jsonParser = new JSONParser(); private MqttDataTransport m_dataTransport; private String m_pubTopicPrefix = ""; private String m_subTopicPrefix = ""; private int m_pubQos = 0; private int m_subQos = 0; private long m_timeout = 0; @Override public void activate(Map<String, Object> properties) throws SmarthomeException { s_logger.info("Activating MqttPoller..."); // Configuration m_properties = properties; try { if (m_properties == null || m_properties.size() == 0) { throw new IllegalArgumentException("Configuration is null or empty - please ensure you " + "have specificed the correct configuration file."); } // // Validate and set required properties for publishing & subscribing m_pubTopicPrefix = (String) m_properties.get(PUBLISH_TOPICPREFIX_PROP_NAME); ValidationUtil.notEmptyOrNull(m_pubTopicPrefix, PUBLISH_TOPICPREFIX_PROP_NAME); m_subTopicPrefix = (String) m_properties.get(SUBSCRIBE_TOPICPREFIX_PROP_NAME); ValidationUtil.notEmptyOrNull(m_subTopicPrefix, SUBSCRIBE_TOPICPREFIX_PROP_NAME); m_pubQos = Integer.parseInt((String) m_properties.get(PUBLISH_QOS_PROP_NAME)); ValidationUtil.notNegative(m_pubQos, PUBLISH_QOS_PROP_NAME); m_subQos = Integer.parseInt((String) m_properties.get(SUBSCRIBE_QOS_PROP_NAME)); ValidationUtil.notNegative(m_subQos, SUBSCRIBE_QOS_PROP_NAME); // AWS Things from which to collect sensor data String things = (String) m_properties.get(AWS_THINGS_PROP_NAME); ValidationUtil.notEmptyOrNull(things, AWS_THINGS_PROP_NAME); String[] thingsList = things.split(","); for (String thing : thingsList) { m_things.add(thing.trim()); } m_timeout = Integer.parseInt((String) m_properties.get(MQTT_TIMEOUT_PROP_NAME)); ValidationUtil.notNegative(m_timeout, MQTT_TIMEOUT_PROP_NAME); } catch (Exception e) { s_logger.error("Invalid MqttPoller configuration"); throw new SmarthomeException("Invalid MqttPoller configuration", e); } // // Connect to remote mqtt broker try { s_logger.debug("Connecting to MQTT data transport..."); m_dataTransport = new MqttDataTransport(m_properties, this); m_dataTransport.connect(); } catch (Exception e) { throw new SmarthomeException("Connection to MQTT data transport failed", e); } // // Subscribe to receive get updates from AWS-IoT thing String topic = ""; try { for (String thing : m_things) { topic = m_subTopicPrefix + thing + "/shadow/get/#"; s_logger.info("Subscribing to AWS thing shawdow topic: " + topic); m_dataTransport.subscribe(topic, m_subQos); } } catch (Exception e) { throw new SmarthomeException("Failed to subscribe to AWS thing shawdow topic: " + topic, e); } s_logger.info("Activating MqttPoller... Done"); } @Override public void deactivate() { s_logger.info("Deactivating MqttPoller..."); try { if (m_dataTransport != null && m_dataTransport.isConnected()) { m_dataTransport.disconnect(m_timeout); } } catch (Exception e) { e.printStackTrace(); } s_logger.info("Deactivating MqttPoller... Done"); } @Override public void runCollection() { if (m_dataTransport != null && !m_dataTransport.isConnected()) { s_logger.error("No connected data transport - ignoring collection"); return; } // // Send the get request for each thing String topic = ""; try { for (String thing : m_things) { topic = m_pubTopicPrefix + thing + "/shadow/get"; s_logger.debug("Publishing to AWS thing shawdow topic: " + topic); m_dataTransport.publish(topic, "{}".getBytes(), m_pubQos, false); } } catch (Exception e) { s_logger.error("Failed to publish to AWS thing shawdow topic: " + topic, e); } } @Override public void setPollerListener(PollerListener listener) { m_listener = listener; } @Override public void removePollerListener(PollerListener listener) { m_listener = null; } private void notifyListener(Metric metric) { if (m_listener != null) { try { m_listener.handleMetric(metric); } catch (Throwable t) { s_logger.error("Unexpected Throwable", t); } } else { s_logger.warn("No registered listener. Missing handleMetric()"); } } // // DataTransportListener implementation // @Override public void onConnectionEstablished() { s_logger.info("MqttPoller connected!"); } @Override public void onDisconnecting() { s_logger.info("MqttPoller disconnecting from MQTT broker"); } @Override public void onDisconnected() { s_logger.info("MqttPoller disconnected"); } @Override public void onConnectionLost(Throwable cause) { s_logger.warn("Connection to MQTT broker lost!" + cause.getMessage()); } @Override public void onMessageArrived(String topic, byte[] payload, int qos, boolean retained) { // Check if the get request was rejected if (!topic.startsWith(m_subTopicPrefix) && topic.endsWith("/get/rejected")) { // log the error only try { JSONObject obj = (JSONObject) m_jsonParser.parse(new String(payload)); String code = (String) obj.get("code"); String message = (String) obj.get("message"); String timestamp = (String) obj.get("timestamp"); String clientToken = (String) obj.get("clientToken"); s_logger.warn("AWS IoT thing get rejected - code: {} message: {} timestamp: {} clientToken: {}", new Object[] { code, message, timestamp, clientToken }); } catch (ParseException e) { s_logger.error("Error while parsing AWS IoT thing get rejected message for topic: " + topic, e); } } // // Parse the message a create a metric object // See link // http://docs.aws.amazon.com/iot/latest/developerguide/thing-shadow-document-syntax.html#thing-shadow-example-response-json // for more details on the format of the response message try { JSONObject obj = (JSONObject) m_jsonParser.parse(new String(payload)); JSONObject stateObj = (JSONObject) obj.get("state"); JSONObject reportedObj = (JSONObject) stateObj.get("reported"); Iterator<?> iter = reportedObj.keySet().iterator(); while (iter.hasNext()) { String key = (String) iter.next(); Metric metric = new Metric(); metric.setMeasurement(key); metric.setValue(reportedObj.get(key)); // Get the thing name from the topic String thingName = topic.substring(m_subTopicPrefix.length(), topic.lastIndexOf("/shadow/get/accepted")); metric.setThingName(thingName); metric.setTimestamp(System.currentTimeMillis()); s_logger.debug("Received metric: " + metric.toString()); // Notify listener notifyListener(metric); } } catch (ParseException e) { s_logger.error("Error while parsing AWS IoT thing get accepted message for topic: " + topic, e); } } @Override public void onMessageConfirmed(int messageId, String topic) { } }