Java tutorial
/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package com.arm.iot.event.hub.responder; import com.arm.connector.bridge.json.JSONGenerator; import com.arm.connector.bridge.json.JSONGeneratorFactory; import com.arm.connector.bridge.json.JSONParser; import com.microsoft.azure.iot.service.sdk.DeliveryAcknowledgement; import com.microsoft.azure.iot.service.sdk.FeedbackReceiver; import com.microsoft.azure.iot.service.sdk.IotHubServiceClientProtocol; import com.microsoft.azure.iot.service.sdk.Message; import com.microsoft.azure.iot.service.sdk.ServiceClient; import java.io.IOException; import com.microsoft.eventhubs.client.Constants; import com.microsoft.eventhubs.client.EventHubClient; import com.microsoft.eventhubs.client.EventHubEnqueueTimeFilter; import com.microsoft.eventhubs.client.EventHubException; import com.microsoft.eventhubs.client.EventHubMessage; import com.microsoft.eventhubs.client.EventHubReceiver; import com.microsoft.eventhubs.client.ConnectionStringBuilder; import java.io.UnsupportedEncodingException; import java.net.URISyntaxException; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import org.bouncycastle.jce.provider.BouncyCastleProvider; /** * This application processes input telemetry from the mbed-ethernet-sample project (monotonic counter resource) and sends a message back to * the device (via IoTEventHub and the mbed Connector bridge) to toggle the LED resource on or off depending on whether the counter resource * is even or odd */ public class IoTEventHubResponder { private static EventHubClient client; private static long now = System.currentTimeMillis(); // Configuration/Connection Parameters - you need to change these public static final String connectionString = "[Enter your IoTHub OWNER Connection String here]"; public static final String deviceId = "[mbed Connector Endpoint Name goes here]"; public static final String policyKey = "[SharedAccessKey part of your OWNER SAS Token goes here... you leave off the SharedAccessKey= though though...]"; public static final String namespace = "[Your IoTHub namespace value]"; public static final String name = "[Your IoTHub qualified name goes here]"; public static final String dm_fota_data = "{}"; //FOTA manifest/image // You should not have to change these... public static final String policyName = "iothubowner"; public static final String counter_resource_uri = "/123/0/4567"; public static final String accelerometer_resource_uri = "/888/0/7700"; public static final String led_resource_uri = "/311/0/5850"; public static final String temp_resource_uri = "/303/0/5700"; // Device Management URIs public static final String dm_passphrase = "arm1234"; // Passphrase for permitting Actions... see main.cpp in endpoint public static final String dm_firmware_version_resource_uri = "/3/0/3"; // Firmware Version public static final String dm_deregister_action_resource_uri = "/86/0/0"; // Action: De-Register device public static final String dm_reboot_action_resource_uri = "/3/0/3"; // Action: Reboot device public static final String dm_reset_action_resource_uri = "/3/0/4"; // Action: Reset device public static final String dm_fota_manifest_resource_uri = "/5/0/1"; // Manifest: FOTA URL public static final String dm_fota_action_resource_uri = "/5/0/2"; // Action: FOTA device // sleep time between receives (MS) public static final int receiveSleepMS = 1000; // 1 second /** Choose iotHubServiceClientProtocol */ private static final IotHubServiceClientProtocol protocol = IotHubServiceClientProtocol.AMQPS; private static ServiceClient serviceClient = null; private static FeedbackReceiver feedbackReceiver = null; protected static void openServiceClient() throws Exception { System.out.println("Creating ServiceClient..."); serviceClient = ServiceClient.createFromConnectionString(connectionString, protocol); CompletableFuture<Void> future = serviceClient.openAsync(); future.get(); System.out.println("********* Successfully created an ServiceClient."); } protected static void closeServiceClient() throws ExecutionException, InterruptedException, IOException { serviceClient.close(); CompletableFuture<Void> future = serviceClient.closeAsync(); future.get(); serviceClient = null; System.out.println("********* Successfully closed ServiceClient."); } protected static void openFeedbackReceiver() throws ExecutionException, InterruptedException { if (serviceClient != null) { feedbackReceiver = serviceClient.getFeedbackReceiver(deviceId); if (feedbackReceiver != null) { CompletableFuture<Void> future = feedbackReceiver.openAsync(); future.get(); System.out.println("********* Successfully opened FeedbackReceiver..."); } } } protected static void closeFeedbackReceiver() throws ExecutionException, InterruptedException { CompletableFuture<Void> future = feedbackReceiver.closeAsync(); future.get(); feedbackReceiver = null; System.out.println("********* Successfully closed FeedbackReceiver."); } // Message Receiver private static class MessageReceiver implements Runnable { public volatile boolean stopThread = false; private String partitionId; private JSONGeneratorFactory m_json_factory = null; private JSONGenerator m_json_generator = null; private JSONParser m_json_parser = null; public MessageReceiver(String partitionId) { this.partitionId = partitionId; // JSON Factory this.m_json_factory = JSONGeneratorFactory.getInstance(); // create the JSON Generator this.m_json_generator = this.m_json_factory.newJsonGenerator(); // create the JSON Parser this.m_json_parser = this.m_json_factory.newJsonParser(); } // send CoAP GET request for a given URL private void dispatchGETRequest(String ep_name, String uri) { // CoAP Verb: GET String coap_verb = "get"; // Temperature JSON message for the bridge to parse... String message = "{ \"path\":\"" + uri + "\", \"ep\":\"" + ep_name + "\", \"coap_verb\": \"" + coap_verb + "\" }"; // Send this message this.sendMessage(coap_verb, ep_name, message); } // send CoAP POST command to the special device management resources to invoke actions private void dispatchDeviceManagementAction(String ep_name, String uri, String passphrase) { // default CoAP verb is POST this.dispatchDeviceManagementAction(ep_name, uri, passphrase, "post", null); } // send CoAP POST command to the special device management resources to invoke actions private void dispatchDeviceManagementAction(String ep_name, String uri, String passphrase, String options) { // default CoAP verb is POST this.dispatchDeviceManagementAction(ep_name, uri, passphrase, "post", options); } // send CoAP POST command to the special device management resources to invoke actions private void dispatchDeviceManagementAction(String ep_name, String uri, String passphrase, String coap_verb, String options) { // DEBUG Add 10 to Counter System.out.println( "Invoking (" + coap_verb + ") Action to: " + ep_name + " URI: " + uri + " Options: " + options); // options for the mDS/mDC rest call String opts = ""; if (options != null && options.length() > 0) { opts = ",\"options\":\"" + options + "\""; } // Add 10 to Counter JSON message for bridge to parse (new_value is the passphrase to permit the action) String message = "{ \"path\":\"" + uri + "\",\"new_value\": \"" + passphrase + "\",\"ep\":\"" + ep_name + "\", \"coap_verb\": \"" + coap_verb + "\"" + opts + " }"; // Send this message this.sendMessage(coap_verb, ep_name, message); } // Device Management: De-Register the device private void deregisterDevice(String ep_name, String passphrase) { this.dispatchDeviceManagementAction(ep_name, dm_deregister_action_resource_uri, passphrase); } // Device Management: Reboot the device private void rebootDevice(String ep_name, String passphrase) { this.dispatchDeviceManagementAction(ep_name, dm_reboot_action_resource_uri, passphrase); } // Device Management: Reset the device private void resetDevice(String ep_name, String passphrase) { this.dispatchDeviceManagementAction(ep_name, dm_reset_action_resource_uri, passphrase); } // Device Management: Get the current Firmware version from the device private void getDeviceFirmwareVersion(String ep_name) { // dispatch the GET.. answer will come back via observation/notification response... this.dispatchGETRequest(ep_name, dm_firmware_version_resource_uri); } // Device Management: FOTA the device: set manifest private void fotaSetManifest(String ep_name, String fota_manifest, String passphrase) { // first we PUT the manifest (via CoAP PUT) this.dispatchDeviceManagementAction(ep_name, dm_fota_manifest_resource_uri, fota_manifest, "put", null); // we now wait for the PUT to complete... then we will POST to execute... see processPutResponse() } // Device Management: FOTA the device: invoke private void fotaInvokeFOTA(Map response) { // Check for FOTA invocation readiness... String uri = (String) response.get("path"); if (uri != null && uri.equalsIgnoreCase(dm_fota_manifest_resource_uri)) { // DEBUG System.out.println("Invoking FOTA(POST) for Device: " + (String) response.get("ep")); // then we POST to invoke the FOTA action using the passphrase to permit the action this.dispatchDeviceManagementAction((String) response.get("ep"), dm_fota_action_resource_uri, dm_passphrase, "post", "noResp=true"); } } // send CoAP POST to add 10 to the counter: "Add 10 to Counter" private void dispatchAdd10ToCounter(String ep_name, String uri) { // DEBUG Add 10 to Counter System.out.println("Add 10 to Counter: " + ep_name + " URI: " + uri); // CoAP Verb: POST String coap_verb = "post"; // Add 10 to Counter JSON message for bridge to parse String message = "{ \"path\":\"" + uri + "\",\"new_value\": \"10\",\"ep\":\"" + ep_name + "\", \"coap_verb\": \"" + coap_verb + "\" }"; // Send this message this.sendMessage(coap_verb, ep_name, message); } // send CoAP PUT to set the counter to 8: "Set Counter to 8" private void dispatchSetCounterTo8(String ep_name, String uri) { // DEBUG Set Counter to 8 System.out.println("Setting Counter to 8: " + ep_name + " URI: " + uri); // Set Counter to 8 this.dispatchSetCounterToValue(ep_name, uri, "8"); } // send CoAP PUT to set the counter to : "Reset Counter" private void dispatchResetCounter(String ep_name, String uri) { // DEBUG Reset Counter System.out.println("Resetting Counter: " + ep_name + " URI: " + uri); // Set Counter to 0 this.dispatchSetCounterToValue(ep_name, uri, "0"); } // send CoAP PUT to set the counter to a new value private void dispatchSetCounterToValue(String ep_name, String uri, String value) { // CoAP Verb: GET String coap_verb = "put"; // Temperature JSON message for the bridge to parse... // { "path":"/123/0/4567", "new_value":"8", "ep":"cc69e7c5-c24f-43cf-8365-8d23bb01c707", "coap_verb":"put" } String message = "{ \"path\":\"" + uri + "\",\"new_value\": \"" + value + "\",\"ep\":\"" + ep_name + "\", \"coap_verb\": \"" + coap_verb + "\" }"; // Send this message this.sendMessage(coap_verb, ep_name, message); } // process a CoAP GET Response @SuppressWarnings("empty-statement") private void processGetResponse(Map response) { // DEBUG System.out.println("GET RESPONSE: " + response); // Do fun and interesting stuff... ; } // process a CoAP PUT Response @SuppressWarnings("empty-statement") private void processPutResponse(Map response) { // DEBUG System.out.println("PUT RESPONSE: " + response); // check and Process FOTA readiness fotaInvokeFOTA(response); } // process a CoAP Observation private void processObservation(Map observation) { boolean led_off_request_temp = false; // DEBUG We are processing an observation System.out.println("Processing Observation: " + observation); // Get the path... if it is the monotonic counter resource, we will process it... String path = (String) observation.get("path"); if (path != null && path.contentEquals(IoTEventHubResponder.accelerometer_resource_uri) == true) { try { // Get the Accelerometer JSON object Map accel_json = (Map) observation.get("value"); // DEBUG System.out.println("processObservation: ACCEL: " + accel_json); } catch (Exception ex) { System.out.println("processObservation: Exception: " + ex.getMessage()); } } if (path != null && path.contentEquals(IoTEventHubResponder.counter_resource_uri) == true) { try { // get the monotonic counter value (an integer) (quick-json has an issue of always using Strings...) String counter_str = (String) observation.get("value"); Integer counter = Integer.parseInt(counter_str); // CoAP put() verb will be used HashMap<String, String> messageProperties = new HashMap<>(); messageProperties.put("coap_verb", "put"); // if the counter is even, turn LED ON... otherwise, turn LED OFF.. we can set the CoAP verb in the JSON itself... String commandMessage = null; if (counter % 2 == 0) { // DEBUG System.out.println("processObservation: Turning LED ON"); // turn ON commandMessage = "{ \"path\":\"" + led_resource_uri + "\", \"new_value\":\"1\", \"ep\":\"" + deviceId + "\", \"coap_verb\": \"" + messageProperties.get("coap_verb") + "\" }"; // we will issue a GET on the temperature directly (simply as an example of issuing a get()) led_off_request_temp = false; } else { // DEBUG System.out.println("processObservation: Turning LED OFF"); // turn OFF commandMessage = "{ \"path\":\"" + led_resource_uri + "\", \"new_value\":\"0\", \"ep\":\"" + deviceId + "\", \"coap_verb\": \"" + messageProperties.get("coap_verb") + "\" }"; // we will issue a GET on the temperature directly (simply as an example of issuing a get()) led_off_request_temp = true; } // DEBUG System.out.println("processObservation: Sending LED message: " + commandMessage + " to device: " + deviceId); // send the message this.sendMessage("put", deviceId, commandMessage); // ***** NodeRED Flow processing ***** // now do a GET on temperature if the counter value is ODD if (led_off_request_temp == true) { // DEBUG System.out.println( "processObservation: Dispatching CoAP GET of Temperature resource from device: " + deviceId); this.dispatchGETRequest(deviceId, temp_resource_uri); } // Get the Firmware Version if (counter == 3) { // DEBUG System.out.println( "processObservation: Dispatching CoAP GET for Firmware Version of the device: " + deviceId); this.getDeviceFirmwareVersion(deviceId); } // Check the counter value if (counter == 5) { // Set Counter Value to 8 this.dispatchSetCounterTo8(deviceId, counter_resource_uri); // TEST: Data Management actions //this.deregisterDevice(deviceId, dm_passphrase); //this.rebootDevice(deviceId, dm_passphrase); //this.resetDevice(deviceId, dm_passphrase); this.fotaSetManifest(deviceId, dm_fota_data, dm_passphrase); } if (counter == 10) { // Add 10 to Counter this.dispatchAdd10ToCounter(deviceId, counter_resource_uri); } if (counter > 30) { // Reset Counter this.dispatchResetCounter(deviceId, counter_resource_uri); // TEST: Data Management actions //this.deregisterDevice(deviceId, dm_passphrase); this.rebootDevice(deviceId, dm_passphrase); //this.resetDevice(deviceId, dm_passphrase); //this.fotaSetManifest(deviceId,dm_fota_data,dm_passphrase); } // ***** NodeRED Flow processing ***** } catch (NumberFormatException ex) { System.out.println("processObservation: Exception: " + ex.getMessage()); } } } // send a message private void sendMessage(String verb, String ep_name, String commandMessage) { try { // CoAP GET requested HashMap<String, String> messageProperties = new HashMap<>(); messageProperties.put("coap_verb", verb); // create the message to send to the device Message messageToSend = new Message(commandMessage); messageToSend.setDeliveryAcknowledgement(DeliveryAcknowledgement.Full); // Setting standard properties messageToSend.setMessageId(java.util.UUID.randomUUID().toString()); Date now = new Date(); messageToSend.setExpiryTimeUtc(new Date(now.getTime() + 60 * 1000)); messageToSend.setCorrelationId(java.util.UUID.randomUUID().toString()); messageToSend.setUserId(java.util.UUID.randomUUID().toString()); // set the message properties messageToSend.clearCustomProperties(); messageToSend.setProperties(messageProperties); // send the message and its properties back through IoTEventHub CompletableFuture<Void> completableFuture = serviceClient.sendAsync(ep_name, messageToSend); completableFuture.get(); } catch (UnsupportedEncodingException | InterruptedException | ExecutionException ex) { System.out.println("sendMessage: Exception: " + ex.getMessage()); } } // process an input event private void processEvent(String message) { // parse the message Map parsed = this.parseMessage(message.replace("\"\"", "\" \"")); // jsonParser has issues... // Get the CoAP verb String verb = (String) parsed.get("verb"); if (verb != null && verb.equalsIgnoreCase("get") == true) { // We are processing a GET response this.processGetResponse(parsed); } else if (verb != null && verb.equalsIgnoreCase("put") == true) { // We are processing a PUT response this.processPutResponse(parsed); } else { // We are processing an observation (DEFAULT) this.processObservation(parsed); } } // parse the JSON string private Map parseMessage(String json) { return this.m_json_parser.parseJson(json); } @Override public void run() { try { EventHubReceiver receiver = client.getConsumerGroup(null).createReceiver(partitionId, new EventHubEnqueueTimeFilter(now), Constants.DefaultAmqpCredits); System.out.println("Created receiver on partition " + partitionId); while (!stopThread) { EventHubMessage message = EventHubMessage.parseAmqpMessage(receiver.receive(receiveSleepMS)); if (message != null) { // DEBUG //System.out.println("Received: " + partitionId + " (" + message.getOffset() + " | " // + message.getSequence() + " | " + message.getEnqueuedTimestamp() // + ") => " + message.getDataAsString()); // process event this.processEvent(message.getDataAsString()); } } receiver.close(); } catch (EventHubException e) { System.out.println("Exception: " + e.getMessage()); } } } // main method() public static void main(String[] args) throws IOException, URISyntaxException, Exception { // enable PEMReader() from BouncyCastle java.security.Security.addProvider(new BouncyCastleProvider()); try { ConnectionStringBuilder csb = new ConnectionStringBuilder(IoTEventHubResponder.policyName, IoTEventHubResponder.policyKey, IoTEventHubResponder.namespace); IoTEventHubResponder.client = EventHubClient.create(csb.getConnectionString(), IoTEventHubResponder.name); } catch (EventHubException e) { System.out.println("Exception: " + e.getMessage()); } // DEBUG Announcement System.out.println("Starting IoTEventHubResponder. Listening for Device: " + IoTEventHubResponder.deviceId); IoTEventHubResponder.openServiceClient(); IoTEventHubResponder.openFeedbackReceiver(); MessageReceiver mr0 = new MessageReceiver("0"); MessageReceiver mr1 = new MessageReceiver("1"); Thread t0 = new Thread(mr0); Thread t1 = new Thread(mr1); t0.start(); t1.start(); System.out.println("Press ENTER to exit."); System.in.read(); mr0.stopThread = true; mr1.stopThread = true; IoTEventHubResponder.client.close(); IoTEventHubResponder.closeFeedbackReceiver(); IoTEventHubResponder.closeServiceClient(); } }