/** * Publish data to the IBM Internet of Things Foundation.<br> * This method allows QoS to be passed as an argument * * @param event Name of the dataset under which to publish the data * @param data Object to be added to the payload as the dataset * @param qos Quality of Service - should be 0, 1 or 2 * @return Whether the send was successful. */ public boolean publishEvent(String event, Object data, int qos) { if (!isConnected()) { return false; } final String METHOD = "publishEvent(2)"; JsonObject payload = new JsonObject(); String timestamp = ISO8601_DATE_FORMAT.format(new Date()); payload.addProperty("ts", timestamp); JsonElement dataElement = gson.toJsonTree(data); payload.add("d", dataElement); String topic = "iot-2/evt/" + event + "/fmt/json"; LoggerUtility.fine(CLASS_NAME, METHOD, "Topic = " + topic); LoggerUtility.fine(CLASS_NAME, METHOD, "Payload = " + payload.toString()); MqttMessage msg = new MqttMessage(payload.toString().getBytes(Charset.forName("UTF-8"))); msg.setQos(qos); msg.setRetained(false); try { mqttAsyncClient.publish(topic, msg).waitForCompletion(); } catch (MqttPersistenceException e) { e.printStackTrace(); return false; } catch (MqttException e) { e.printStackTrace(); return false; } return true; }
private void setDeviceData(DeviceData deviceData) throws Exception { final String METHOD = "setDeviceData"; if (deviceData == null) { LoggerUtility.log( Level.FINE, CLASS_NAME, METHOD, "Could not create Managed Client " + "without DeviceInformations !"); throw new Exception("Could not create Managed Client without DeviceInformations !"); } String typeId = deviceData.getTypeId(); String deviceId = deviceData.getDeviceId(); if (typeId == null || deviceId == null) { LoggerUtility.log( Level.FINE, CLASS_NAME, METHOD, "Could not create Managed Client " + "without Device Type or Device ID!"); throw new Exception( "Could not create Managed Client without Device Type or Device ID!, " + "Please specify the same in DeviceData"); } this.deviceData = deviceData; }
@Override public void messageArrived(String topic, MqttMessage message) throws Exception { final String METHOD = "messageArrived"; if (topic.equals(ServerTopic.RESPONSE.getName())) { LoggerUtility.log( Level.FINE, CLASS_NAME, METHOD, "Received response from IoT Foundation, topic (" + topic + ")"); String responsePayload = new String(message.getPayload(), "UTF-8"); JsonObject jsonResponse = new JsonParser().parse(responsePayload).getAsJsonObject(); try { String reqId = jsonResponse.get("reqId").getAsString(); LoggerUtility.fine(CLASS_NAME, METHOD, "reqId (" + reqId + "): " + jsonResponse.toString()); MqttMessage sentMsg = requests.remove(reqId); if (sentMsg != null) { queue.put(jsonResponse); } } catch (Exception e) { if (jsonResponse.get("reqId") == null) { LoggerUtility.warn( CLASS_NAME, METHOD, "The response " + "does not contain 'reqId' field (" + responsePayload + ")"); } else { LoggerUtility.log(Level.SEVERE, CLASS_NAME, METHOD, "Unexpected exception", e); } } } else { LoggerUtility.warn(CLASS_NAME, METHOD, "Unknown topic (" + topic + ")"); } }
/** * Constructor that creates a ManagedDevice object, but does not connect to IBM IoT Foundation * connect yet * * @param options List of options to connect to IBM IoT Foundation Connect * @param deviceData The Device Model * @throws Exception If the essential parameters are not set */ public ManagedDevice(Properties options, DeviceData deviceData) throws Exception { super(options); final String METHOD = "constructor"; if (deviceData == null) { LoggerUtility.log( Level.FINE, CLASS_NAME, METHOD, "Could not create Managed Client " + "without DeviceInformations !"); throw new Exception("Could not create Managed Client without DeviceInformations !"); } String typeId = this.getDeviceType(); String deviceId = this.getDeviceId(); if (typeId == null || deviceId == null) { LoggerUtility.log( Level.FINE, CLASS_NAME, METHOD, "Could not create Managed Client " + "without Device Type or Device ID !"); throw new Exception( "Could not create Managed Client without Device Type or Device ID!, " + "Please specify the same in properties"); } deviceData.setTypeId(typeId); deviceData.setDeviceId(deviceId); this.deviceData = deviceData; }
/** * UnSubscribe the library from the given topics * * <p>This method is used by the library to unsubscribe each of the topic where IBM IoT Foundation * will send the DM requests * * @param topics topics to be unsubscribed * @throws MqttException */ public void unsubscribe(String[] topics) throws MqttException { final String METHOD = "unsubscribe#2"; LoggerUtility.fine(CLASS_NAME, METHOD, "Topics(" + topics + ")"); if (isConnected()) { if (mqttAsyncClient != null) { mqttAsyncClient.unsubscribe(topics); } else if (mqttClient != null) { mqttClient.unsubscribe(topics); } } else { LoggerUtility.warn( CLASS_NAME, METHOD, "Will not unsubscribe from topics(" + topics + ") because MQTT client is not connected."); } }
/** * Moves the device from managed state to unmanaged state * * <p>A device uses this request when it no longer needs to be managed. This means IoTF will no * longer send new device management requests to this device and device management requests from * this device will be rejected apart from a Manage device request * * @return True if the unmanage command is successful * @throws MqttException */ public boolean unmanage() throws MqttException { final String METHOD = "unmanage"; boolean success = false; DeviceTopic topic = DeviceTopic.UNMANAGE; JsonObject jsonPayload = new JsonObject(); JsonObject jsonResponse = sendAndWait(topic, jsonPayload, REGISTER_TIMEOUT_VALUE); if (jsonResponse != null && jsonResponse.get("rc").getAsInt() == ResponseCode.DM_SUCCESS.getCode()) { success = true; } terminate(); DMListener.stop(this); DMRequestHandler.clearRequestHandlers(this); this.deviceData.terminateHandlers(); this.supportsDeviceActions = false; this.supportsFirmwareActions = false; if (responseSubscription != null) { this.unsubscribe(this.responseSubscription); responseSubscription = null; } LoggerUtility.log(Level.FINE, CLASS_NAME, METHOD, "Success (" + success + ")"); if (success) { bManaged = false; } return success; }
/** * Subscribe the given listeners to the given topics * * <p>This method is used by the library to subscribe to each of the topic where IBM IoT * Foundation will send the DM requests * * @param topics List of topics to be subscribed * @param qos Quality of Service for the subscription * @param listeners The list of IMqttMessageListeners for the given topics * @throws MqttException */ public void subscribe(String[] topics, int[] qos, IMqttMessageListener[] listeners) throws MqttException { final String METHOD = "subscribe#2"; LoggerUtility.fine(CLASS_NAME, METHOD, "Topics(" + topics + ")"); if (isConnected()) { if (mqttAsyncClient != null) { mqttAsyncClient.subscribe(topics, qos, listeners); } else if (mqttClient != null) { mqttClient.subscribe(topics, qos, listeners); } } else { LoggerUtility.warn( CLASS_NAME, METHOD, "Will not subscribe to topics(" + topics + ") because MQTT client is not connected."); } }
/** * Subscribe the given listener to the given topic * * <p>This method is used by the library to subscribe to each of the topic where IBM IoT * Foundation will send the DM requests * * @param topic topic to be subscribed * @param qos Quality of Service for the subscription * @param listener The IMqttMessageListener for the given topic * @throws MqttException */ public void subscribe(ServerTopic topic, int qos, IMqttMessageListener listener) throws MqttException { final String METHOD = "subscribe"; LoggerUtility.fine(CLASS_NAME, METHOD, "Topic(" + topic + ")"); if (isConnected()) { if (mqttAsyncClient != null) { mqttAsyncClient.subscribe(topic.getName(), qos, listener); } else if (mqttClient != null) { mqttClient.subscribe(topic.getName(), qos, listener); } } else { LoggerUtility.warn( CLASS_NAME, METHOD, "Will not subscribe to topic(" + topic + ") because MQTT client is not connected."); } }
/** * This method just connects to the IBM Internet of Things Foundation, Device needs to make a call * to manage() to participate in Device Management activities. * * <p>This method does nothing if the device is already connected */ public void connect() { final String METHOD = "connect"; if (this.isConnected()) { LoggerUtility.log(Level.WARNING, CLASS_NAME, METHOD, "Device is already connected"); return; } super.connect(); }
private boolean registerDevice(long lifetime) { final String METHOD = "registerDevice"; boolean success = false; String organization = getOrgId(); if (organization == null || ("quickstart").equals(organization)) { LoggerUtility.log( Level.SEVERE, CLASS_NAME, METHOD, "Unable to create ManagedClient instance. " + "QuickStart devices do not support device management"); throw new RuntimeException( "Unable to create ManagedClient instance. " + "QuickStart devices do not support device management"); } try { success = this.manage(lifetime); if (success) { LoggerUtility.log(Level.INFO, CLASS_NAME, METHOD, "Device is connected as managed device"); } else { LoggerUtility.log( Level.WARNING, CLASS_NAME, METHOD, "Device is failed to connect as managed device"); } } catch (MqttException ex) { LoggerUtility.log( Level.SEVERE, CLASS_NAME, METHOD, "Connecting the device as managed device " + "operation is Failed, Exception: " + ex.getMessage()); RuntimeException e = new RuntimeException( "Connecting the device as managed device " + "operation is Failed, Exception: " + ex.getMessage()); e.initCause(ex); throw e; } return success; }
@Override public void run() { final String METHOD = "run"; running = true; LoggerUtility.log(Level.FINE, CLASS_NAME, METHOD, "Running..."); while (running) { try { JsonObject o = publishQueue.take(); if (o.equals(dummy)) { LoggerUtility.log(Level.FINE, CLASS_NAME, METHOD, "It is time to quit."); } else { publish(o); } } catch (Exception e) { LoggerUtility.log(Level.SEVERE, CLASS_NAME, METHOD, e.toString()); e.printStackTrace(); running = false; } } LoggerUtility.log(Level.FINE, CLASS_NAME, METHOD, "Exiting..."); }
/** * Publish the Device management response to IBm IoT Foundation * * <p>This method is used by the library to respond to each of the Device Management commands from * IBM IoT Foundation * * @param topic Topic where the response to be published * @param payload the Payload * @param qos The Quality Of Service * @throws MqttException */ public void publish(DeviceTopic topic, JsonObject payload, int qos) throws MqttException { final String METHOD = "publish3"; JsonObject jsonPubMsg = new JsonObject(); jsonPubMsg.addProperty("topic", topic.getName()); jsonPubMsg.add("qos", new JsonPrimitive(qos)); jsonPubMsg.add("payload", payload); publishQueue.add(jsonPubMsg); LoggerUtility.log( Level.FINE, CLASS_NAME, METHOD, ": Queued Topic(" + topic + ") qos=" + qos + " payload (" + payload.toString() + ")"); }
/** * This method reconnects when the connection is lost due to n/w interruption and this method is * called only when the connection is established originally by the library code. * * <p>This method does the following activities, 1. Checks whether the device was in a managed * state before disconnecting 2. Calculates the lifetime that we need to send in the manage * request, */ @Override protected void reconnect() { String METHOD = "reconnect"; IMqttDeliveryToken[] tokens = this.mqttAsyncClient.getPendingDeliveryTokens(); super.connect(); responseSubscription = null; if (this.isConnected() && this.bManaged == true) { long lifetime = 0; if (dormantTime != null) { Date currentTime = new Date(); lifetime = (dormantTime.getTime() - currentTime.getTime()) / 1000; if (lifetime < 0) { lifetime = 0; } } try { LoggerUtility.log(Level.FINE, CLASS_NAME, METHOD, "lifetime (" + lifetime + ")"); this.manage(lifetime); if (tokens != null) { LoggerUtility.log(Level.FINE, CLASS_NAME, METHOD, "Republishing messages start"); for (int i = 0; i < tokens.length; i++) { try { MqttMessage msg = tokens[i].getMessage(); this.mqttAsyncClient.publish(tokens[i].getTopics()[0], msg); } catch (MqttException e) { e.printStackTrace(); } } LoggerUtility.log(Level.FINE, CLASS_NAME, METHOD, "Republishing messages End"); } } catch (MqttException e) { e.printStackTrace(); } } }
private void publish(JsonObject jsonPubMsg) throws MqttException, UnsupportedEncodingException { final String METHOD = "publish1"; String topic = jsonPubMsg.get("topic").getAsString(); int qos = jsonPubMsg.get("qos").getAsInt(); JsonObject payload = jsonPubMsg.getAsJsonObject("payload"); LoggerUtility.log( Level.FINE, CLASS_NAME, METHOD, ": Topic(" + topic + ") qos=" + qos + " payload (" + payload.toString() + ")"); MqttMessage message = new MqttMessage(); message.setPayload(payload.toString().getBytes("UTF-8")); message.setQos(qos); publish(DeviceTopic.get(topic), message); }
/** The Device client does not currently support subscriptions. */ public void messageArrived(String topic, MqttMessage msg) throws Exception { final String METHOD = "messageArrived"; if (commandCallback != null) { /* Only check whether the message is a command if a callback * has been defined, otherwise it is a waste of time * as without a callback there is nothing to process the generated * command. */ Matcher matcher = COMMAND_PATTERN.matcher(topic); if (matcher.matches()) { String command = matcher.group(1); String format = matcher.group(2); Command cmd = new Command(command, format, msg); LoggerUtility.fine(CLASS_NAME, METHOD, "Event received: " + cmd.toString()); commandCallback.processCommand(cmd); } } }
/** * A completed deliver does not guarantee that the message is received by the service because * devices send messages with Quality of Service (QoS) 0. <br> * The message count represents the number of messages that were sent by the device without an * error on from the perspective of the device. * * @param token MQTT delivery token */ public void deliveryComplete(IMqttDeliveryToken token) { final String METHOD = "deliveryComplete"; LoggerUtility.fine(CLASS_NAME, METHOD, "token " + token.getMessageId()); messageCount++; }
/** * If we lose connection trigger the connect logic to attempt to reconnect to the IBM Internet * of Things Foundation. * * @param exception Throwable which caused the connection to get lost */ public void connectionLost(Throwable exception) { final String METHOD = "connectionLost"; LoggerUtility.info(CLASS_NAME, METHOD, exception.getMessage()); reconnect(); }
/** * Send a device manage request to IoT Foundation * * <p>A device uses this request to become a managed device. It should be the first device * management request sent by the device after connecting to the Internet of Things Foundation. It * would be usual for a device management agent to send this whenever is starts or restarts. * * <p>This method connects the device to IoT Foundation connect if its not connected already * * @param lifetime The length of time in seconds within which the device must send another Manage * device request. if set to 0, the managed device will not become dormant. When set, the * minimum supported setting is 3600 (1 hour). * @return True if successful * @throws MqttException */ public boolean manage(long lifetime) throws MqttException { final String METHOD = "manage"; LoggerUtility.log(Level.FINE, CLASS_NAME, METHOD, "lifetime value (" + lifetime + ")"); boolean success = false; DeviceTopic topic = DeviceTopic.MANAGE; if (!this.isConnected()) { this.connect(); } JsonObject jsonPayload = new JsonObject(); if (deviceData.getDeviceInfo() != null || deviceData.getMetadata() != null) { JsonObject supports = new JsonObject(); supports.add("deviceActions", new JsonPrimitive(this.supportsDeviceActions)); supports.add("firmwareActions", new JsonPrimitive(this.supportsFirmwareActions)); JsonObject data = new JsonObject(); data.add("supports", supports); if (deviceData.getDeviceInfo() != null) { data.add("deviceInfo", deviceData.getDeviceInfo().toJsonObject()); } if (deviceData.getMetadata() != null) { data.add("metadata", deviceData.getMetadata().getMetadata()); } data.add("lifetime", new JsonPrimitive(lifetime)); jsonPayload.add("d", data); } else { LoggerUtility.log( Level.SEVERE, CLASS_NAME, METHOD, "Cannot send manage request " + "as either deviceInfo or metadata is not set !!"); return false; } JsonObject jsonResponse = sendAndWait(topic, jsonPayload, REGISTER_TIMEOUT_VALUE); if (jsonResponse != null && jsonResponse.get("rc").getAsInt() == ResponseCode.DM_SUCCESS.getCode()) { DMListener.start(this); DMRequestHandler.setRequestHandlers(this); publishQueue = new LinkedBlockingQueue<JsonObject>(); Thread t = new Thread(this); t.start(); /* * set the dormant time to a local variable, in case if the connection is * lost due to n/w interruption, we need to send another manage request * with the dormant time as the lifetime */ if (lifetime > 0) { Date currentTime = new Date(); dormantTime = new Date(currentTime.getTime() + (lifetime * 1000)); } success = true; } LoggerUtility.log(Level.FINE, CLASS_NAME, METHOD, "Success (" + success + ")"); bManaged = success; return success; }
protected IMqttDeliveryToken publish(DeviceTopic topic, MqttMessage message) throws MqttException { final String METHOD = "publish"; IMqttDeliveryToken token = null; LoggerUtility.fine(CLASS_NAME, METHOD, "Topic(" + topic + ")"); while (true) { if (isConnected()) { try { if (this.mqttAsyncClient != null) { token = mqttAsyncClient.publish(topic.getName(), message); } else if (mqttClient != null) { mqttClient.publish(topic.getName(), message); } } catch (MqttException ex) { String payload = null; try { payload = new String(message.getPayload(), "UTF-8"); } catch (UnsupportedEncodingException e1) { } if (this.mqttAsyncClient.isConnected() == false) { LoggerUtility.log( Level.WARNING, CLASS_NAME, METHOD, " Connection Lost retrying to publish MSG :" + payload + " on topic " + topic + " every 5 seconds"); // wait for 5 seconds and retry try { Thread.sleep(5 * 1000); continue; } catch (InterruptedException e) { } } else { throw ex; } } if (isConnected() == false) { LoggerUtility.log( Level.WARNING, CLASS_NAME, METHOD, "MQTT got disconnected " + "after publish to Topic(" + topic + ")"); } return token; } else { LoggerUtility.warn( CLASS_NAME, METHOD, ": Will not publish to topic(" + topic + ") because MQTT client is not connected."); try { Thread.sleep(5 * 1000); continue; } catch (InterruptedException e) { } } } }
/** * Send the message and waits for the response from IBM IoT Foundation * * <p> * * <p>This method is used by the library to send following messages to IBM IoT Foundation * * <ul class="simple"> * <li>Manage * <li>Unmanage * <li>Location update * <li>Diagnostic update/clear * </ul> * * @param topic Topic where the message to be sent * @param jsonPayload The message * @param timeout How long to wait for the resonse * @return response in Json format * @throws MqttException */ public JsonObject sendAndWait(DeviceTopic topic, JsonObject jsonPayload, long timeout) throws MqttException { final String METHOD = "sendAndWait"; String uuid = UUID.randomUUID().toString(); jsonPayload.add("reqId", new JsonPrimitive(uuid)); LoggerUtility.fine( CLASS_NAME, METHOD, "Topic (" + topic + ") payload (" + jsonPayload.toString() + ") reqId (" + uuid + ")"); if (responseSubscription == null) { responseSubscription = ServerTopic.RESPONSE; subscribe(responseSubscription, 1, this); } MqttMessage message = new MqttMessage(); try { message.setPayload(jsonPayload.toString().getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { LoggerUtility.log( Level.SEVERE, CLASS_NAME, METHOD, "Error setting payload for topic: " + topic, e); return null; } message.setQos(1); requests.put(uuid, message); publish(topic, message); JsonObject jsonResponse = null; while (jsonResponse == null) { try { jsonResponse = queue.poll(timeout, TimeUnit.MILLISECONDS); if (jsonResponse == null) { break; } if (jsonResponse.get("reqId").getAsString().equals(uuid)) { LoggerUtility.fine( CLASS_NAME, METHOD, "" + "This response is for me reqId:" + jsonResponse.toString()); break; } else { // This response is not for our request, put it back to the queue. LoggerUtility.warn( CLASS_NAME, METHOD, "This response is NOT for me reqId:" + jsonResponse.toString()); queue.add(jsonResponse); jsonResponse = null; } } catch (InterruptedException e) { break; } } if (jsonResponse == null) { LoggerUtility.warn( CLASS_NAME, METHOD, "NO RESPONSE from IoTF for request: " + jsonPayload.toString()); LoggerUtility.warn(CLASS_NAME, METHOD, "Connected(" + isConnected() + ")"); } return jsonResponse; }