From 15f5197de00302333daff8d09548241c626236fa Mon Sep 17 00:00:00 2001 From: Gus Class Date: Fri, 22 Sep 2017 17:50:08 -0700 Subject: [PATCH 1/5] Final additions in private beta --- .../iot/examples/DeviceRegistryExample.java | 56 +++++++++++++------ .../DeviceRegistryExampleOptions.java | 1 + 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/iot/api-client/manager/src/main/java/com/example/cloud/iot/examples/DeviceRegistryExample.java b/iot/api-client/manager/src/main/java/com/example/cloud/iot/examples/DeviceRegistryExample.java index 2415db8aa74..ccadaec80c7 100644 --- a/iot/api-client/manager/src/main/java/com/example/cloud/iot/examples/DeviceRegistryExample.java +++ b/iot/api-client/manager/src/main/java/com/example/cloud/iot/examples/DeviceRegistryExample.java @@ -20,16 +20,10 @@ import com.google.api.client.json.JsonFactory; import com.google.api.client.json.jackson2.JacksonFactory; import com.google.api.client.util.Charsets; -import com.google.api.services.cloudiot.v1beta1.CloudIot; -import com.google.api.services.cloudiot.v1beta1.CloudIotScopes; -import com.google.api.services.cloudiot.v1beta1.model.Device; -import com.google.api.services.cloudiot.v1beta1.model.DeviceConfig; -import com.google.api.services.cloudiot.v1beta1.model.DeviceConfigData; -import com.google.api.services.cloudiot.v1beta1.model.DeviceCredential; -import com.google.api.services.cloudiot.v1beta1.model.DeviceRegistry; -import com.google.api.services.cloudiot.v1beta1.model.ModifyCloudToDeviceConfigRequest; -import com.google.api.services.cloudiot.v1beta1.model.NotificationConfig; -import com.google.api.services.cloudiot.v1beta1.model.PublicKeyCredential; +import com.google.api.services.cloudiot.v1.CloudIot; +import com.google.api.services.cloudiot.v1.CloudIotScopes; +import com.google.api.services.cloudiot.v1.model.*; +import com.google.api.services.cloudiot.v1.model.DeviceConfig; import com.google.cloud.Role; import com.google.cloud.pubsub.v1.TopicAdminClient; import com.google.common.io.Files; @@ -116,9 +110,11 @@ public static void createRegistry(String cloudRegion, String projectId, String r final String fullPubsubPath = "projects/" + projectId + "/topics/" + pubsubTopicPath; DeviceRegistry registry = new DeviceRegistry(); - NotificationConfig notificationConfig = new NotificationConfig(); + EventNotificationConfig notificationConfig = new EventNotificationConfig (); notificationConfig.setPubsubTopicName(fullPubsubPath); - registry.setEventNotificationConfig(notificationConfig); + List notificationConfigs = new ArrayList(); + notificationConfigs.add(notificationConfig); + registry.setEventNotificationConfigs(notificationConfigs); registry.setId(registryName); DeviceRegistry reg = service.projects().locations().registries().create(projectPath, @@ -331,6 +327,28 @@ public static Device getDevice(String deviceId, String projectId, String cloudRe return service.projects().locations().registries().devices().get(devicePath).execute(); } + /** Retrieves device metadata from a registry. **/ + public static List getDeviceStates( + String deviceId, String projectId, String cloudRegion, String registryName) + throws GeneralSecurityException, IOException { + GoogleCredential credential = + GoogleCredential.getApplicationDefault().createScoped(CloudIotScopes.all()); + JsonFactory jsonFactory = JacksonFactory.getDefaultInstance(); + HttpRequestInitializer init = new RetryHttpInitializerWrapper(credential); + CloudIot service = new CloudIot(GoogleNetHttpTransport.newTrustedTransport(), jsonFactory, + init); + + String registryPath = "projects/" + projectId + "/locations/" + cloudRegion + "/registries/" + + registryName; + + String devicePath = registryPath + "/devices/" + deviceId; + System.out.println("Retrieving device " + devicePath); + + ListDeviceStatesResponse resp = service.projects().locations().registries().devices().states(). + list(devicePath).execute(); + return resp.getDeviceStates(); + } + /** Retrieves registry metadata from a project. **/ public static DeviceRegistry getRegistry( String projectId, String cloudRegion, String registryName) @@ -376,7 +394,7 @@ public static void listDeviceConfigs( for (DeviceConfig config : deviceConfigs) { System.out.println("Config version: " + config.getVersion()); - System.out.println("Contents: " + config.getData().getBinaryData()); + System.out.println("Contents: " + config.getBinaryData()); System.out.println(); } } @@ -432,10 +450,8 @@ public static void modifyCloudToDeviceConfig(String deviceId, String configData, + "/registries/" + registryName; final String devicePath = registryPath + "/devices/" + deviceId; ModifyCloudToDeviceConfigRequest request = new ModifyCloudToDeviceConfigRequest(); - DeviceConfigData data = new DeviceConfigData(); - data.setBinaryData(DatatypeConverter.printBase64Binary(configData.getBytes(Charsets.UTF_8))); request.setVersionToUpdate(0L); // 0L indicates update all versions - request.setData(data); + request.setBinaryData(DatatypeConverter.printBase64Binary(configData.getBytes(Charsets.UTF_8))); DeviceConfig config = service .projects() @@ -575,6 +591,14 @@ public static void main(String[] args) throws Exception { options.registryName) .toPrettyString()); break; + case "get-device-state": + System.out.println("Get device state"); + List states = getDeviceStates(options.deviceId, options.projectId, + options.cloudRegion, options.registryName); + for (DeviceState state: states) { + System.out.println(state.toPrettyString()); + } + break; case "get-registry": System.out.println("Get registry"); System.out.println(getRegistry(options.projectId, options.cloudRegion, diff --git a/iot/api-client/manager/src/main/java/com/example/cloud/iot/examples/DeviceRegistryExampleOptions.java b/iot/api-client/manager/src/main/java/com/example/cloud/iot/examples/DeviceRegistryExampleOptions.java index 2ff9e68de7c..cc3e5de8ef7 100644 --- a/iot/api-client/manager/src/main/java/com/example/cloud/iot/examples/DeviceRegistryExampleOptions.java +++ b/iot/api-client/manager/src/main/java/com/example/cloud/iot/examples/DeviceRegistryExampleOptions.java @@ -60,6 +60,7 @@ public static DeviceRegistryExampleOptions fromFlags(String[] args) { + "\n\tdelete-device" + "\n\tdelete-registry" + "\n\tget-device" + + "\n\tget-device-state" + "\n\tget-registry" + "\n\tlist-devices" + "\n\tlist-registries" From 2fdad0273d79959e7f61d4c7faebbc131e60b266 Mon Sep 17 00:00:00 2001 From: Gus Class Date: Mon, 25 Sep 2017 16:59:45 -0700 Subject: [PATCH 2/5] Final updates in private beta --- iot/api-client/manager/README.md | 9 +- iot/api-client/manager/pom.xml | 7 + .../manager/resources/ec_public.pem | 4 + iot/api-client/manager/resources/rsa_cert.pem | 18 +++ .../iot/examples/DeviceRegistryExample.java | 81 ++--------- .../example/cloud/iot/examples/ManagerIT.java | 130 +++++++++++++++--- 6 files changed, 163 insertions(+), 86 deletions(-) create mode 100644 iot/api-client/manager/resources/ec_public.pem create mode 100644 iot/api-client/manager/resources/rsa_cert.pem diff --git a/iot/api-client/manager/README.md b/iot/api-client/manager/README.md index 1c80a53f695..862defe3fca 100644 --- a/iot/api-client/manager/README.md +++ b/iot/api-client/manager/README.md @@ -96,7 +96,14 @@ Create a device registry: java -cp target/cloudiot-manager-demo-1.0-jar-with-dependencies.jar \ com.example.cloud.iot.examples.DeviceRegistryExample \ -project_id=blue-jet-123 -pubsub_topic=hello-java \ - -registry_name=hello-java -command=create-registry \ + -registry_name=hello-java -command=create-registry + +Delete a device registry: + + java -cp target/cloudiot-manager-demo-1.0-jar-with-dependencies.jar \ + com.example.cloud.iot.examples.DeviceRegistryExample \ + -project_id=blue-jet-123 -pubsub_topic=hello-java \ + -registry_name=hello-java -command=delete-registry Get a device registry: diff --git a/iot/api-client/manager/pom.xml b/iot/api-client/manager/pom.xml index 7769b851300..6d678e875c3 100644 --- a/iot/api-client/manager/pom.xml +++ b/iot/api-client/manager/pom.xml @@ -37,11 +37,13 @@ + com.google.cloud google-cloud-pubsub @@ -52,6 +54,11 @@ google-oauth-client 1.22.0 + + com.google.guava + guava + 23.0 + com.google.api-client google-api-client diff --git a/iot/api-client/manager/resources/ec_public.pem b/iot/api-client/manager/resources/ec_public.pem new file mode 100644 index 00000000000..d14a5b6fc3f --- /dev/null +++ b/iot/api-client/manager/resources/ec_public.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAECfwA4OrF9Pcr1W5mXUa+Dx8hpPYd ++pQ5153zNtSSaeEnA/4hrY2AKxUHmKIPJXYRkZrxTxsFElkkpLcoi/CUNQ== +-----END PUBLIC KEY----- diff --git a/iot/api-client/manager/resources/rsa_cert.pem b/iot/api-client/manager/resources/rsa_cert.pem new file mode 100644 index 00000000000..ccaf7eded0a --- /dev/null +++ b/iot/api-client/manager/resources/rsa_cert.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC+DCCAeCgAwIBAgIJAJW4zZX4mjtpMA0GCSqGSIb3DQEBCwUAMBExDzANBgNV +BAMMBnVudXNlZDAeFw0xNzA5MjUyMjM3MzhaFw0xNzEwMjUyMjM3MzhaMBExDzAN +BgNVBAMMBnVudXNlZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKaB +voBJf6cr+Q+S1l/n5Bp0i7BEYeeCnUta+MIZle38y0E3TzXQvjMr8uk081RcWqFq +wkRjY+OM7hLIMbs3C+Qvg6uxbjaJM0LE+gjwnU8Wg76Y4jjhl+tPYP9njWxRZF0d +7WGRMpaztKpukEpgUszC75YM9XVQCal6m3eegu5BraiXrAjngGOAninBe56jhw/b +HIqF85PXczI9BbUoJeq4VycRsdUa2dJSqMxKoF7T2blYLiLBFTyo72ZF6m8SAIzv +eMw78pgtwJK4ZIzONrSe2PaPtctyRmFQBGnnZaMGi3ToiYgQ/pQ4LkuPN1sCCv7y +n3ljavj+QM+IiM5DvZMCAwEAAaNTMFEwHQYDVR0OBBYEFFK2kAhd7rYZInQxdk8T +MZrXXBCrMB8GA1UdIwQYMBaAFFK2kAhd7rYZInQxdk8TMZrXXBCrMA8GA1UdEwEB +/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAKCB1nQ2rFsnQpRRtvjDqlI03Opc +8sfHQ4GxJ3L7ZuXx6MzH37k0+g5/dgGYhRM+Zm9fDnxD6a1c5fek+0iGGIHOg1Cw +9lwqZN15w7SXxMiwxDVYoMBvx7JQEZSFeMfP3ZcKdwSaFRYXNRtbeC45VS70MwhM +CgqqkGDi2hM/JGYxv+UCvIm5+JrF+4SGOFtZeIT8mayq+ZOiD3+Sqo1++mRNXkSr +C8+QUjxW9y2CObE6d7Y/fryfO0mlWUnJS8Ed5H+12GqFWc7HudV2EIPS4RgthdZs +odK3woxB+18j28C1toSaSzzUtaS6hxo+vNsRqfXeK9hCm3RxU3bWRUuQj1U= +-----END CERTIFICATE----- diff --git a/iot/api-client/manager/src/main/java/com/example/cloud/iot/examples/DeviceRegistryExample.java b/iot/api-client/manager/src/main/java/com/example/cloud/iot/examples/DeviceRegistryExample.java index ccadaec80c7..5df7a424942 100644 --- a/iot/api-client/manager/src/main/java/com/example/cloud/iot/examples/DeviceRegistryExample.java +++ b/iot/api-client/manager/src/main/java/com/example/cloud/iot/examples/DeviceRegistryExample.java @@ -22,8 +22,13 @@ import com.google.api.client.util.Charsets; import com.google.api.services.cloudiot.v1.CloudIot; import com.google.api.services.cloudiot.v1.CloudIotScopes; -import com.google.api.services.cloudiot.v1.model.*; +import com.google.api.services.cloudiot.v1.model.Device; import com.google.api.services.cloudiot.v1.model.DeviceConfig; +import com.google.api.services.cloudiot.v1.model.DeviceCredential; +import com.google.api.services.cloudiot.v1.model.DeviceRegistry; +import com.google.api.services.cloudiot.v1.model.EventNotificationConfig; +import com.google.api.services.cloudiot.v1.model.ListDeviceStatesResponse; +import com.google.api.services.cloudiot.v1.model.PublicKeyCredential; import com.google.cloud.Role; import com.google.cloud.pubsub.v1.TopicAdminClient; import com.google.common.io.Files; @@ -74,7 +79,7 @@ public class DeviceRegistryExample { /** Creates a topic and grants the IoT service account access. */ - public static void createIotTopic(String projectId, String topicId) throws Exception { + public static Topic createIotTopic(String projectId, String topicId) throws Exception { // Create a new topic final TopicName topicName = TopicName.create(projectId, topicId); @@ -92,6 +97,7 @@ public static void createIotTopic(String projectId, String topicId) throws Excep topicAdminClient.setIamPolicy(topicName.toString(), updatedPolicy); System.out.println("Setup topic / policy for: " + topic.getName()); + return topic; } } @@ -110,7 +116,7 @@ public static void createRegistry(String cloudRegion, String projectId, String r final String fullPubsubPath = "projects/" + projectId + "/topics/" + pubsubTopicPath; DeviceRegistry registry = new DeviceRegistry(); - EventNotificationConfig notificationConfig = new EventNotificationConfig (); + EventNotificationConfig notificationConfig = new EventNotificationConfig(); notificationConfig.setPubsubTopicName(fullPubsubPath); List notificationConfigs = new ArrayList(); notificationConfigs.add(notificationConfig); @@ -342,10 +348,10 @@ public static List getDeviceStates( + registryName; String devicePath = registryPath + "/devices/" + deviceId; - System.out.println("Retrieving device " + devicePath); + System.out.println("Retrieving device states " + devicePath); - ListDeviceStatesResponse resp = service.projects().locations().registries().devices().states(). - list(devicePath).execute(); + ListDeviceStatesResponse resp = service.projects().locations().registries().devices().states() + .list(devicePath).execute(); return resp.getDeviceStates(); } @@ -627,68 +633,5 @@ public static void main(String[] args) throws Exception { System.out.println("Wrong, wrong, wrong. Usage is like this:"); // TODO: break; } - - - /* - // Simple example of interacting with the Cloud IoT API. - String registryName = "cloudiot_device_manager_example_registry_" + System.currentTimeMillis(); - - // Create a new registry with the above name. - DeviceRegistryExample registry = - new DeviceRegistryExample( - options.projectId, options.cloudRegion, registryName, options.pubsubTopic); - - // List the devices in the registry. Since we haven't created any yet, this should be empty. - registry.listDevices(); - - // Create a device that is authenticated using RSA. - String rs256deviceId = "rs256-device"; - registry.createDeviceWithRs256(rs256deviceId, options.rsaCertificateFile); - - // Create a device without an authentication credential. We'll patch it to use elliptic curve - // cryptography. - String es256deviceId = "es256-device"; - registry.createDeviceWithNoAuth(es256deviceId); - - // List the devices again. This should show the above two devices. - registry.listDevices(); - - // Give the device without an authentication credential an elliptic curve credential. - registry.patchEs256ForAuth(es256deviceId, options.ecPublicKeyFile); - - // List the devices in the registry again, still showing the two devices. - registry.listDevices(); - - // List the device configs for the RSA authenticated device. Since we haven't pushed any, this - // list will only contain the default empty config. - registry.listDeviceConfigs(rs256deviceId); - - // Push two new configs to the device. - registry.modifyCloudToDeviceConfig(rs256deviceId, "config v1"); - registry.modifyCloudToDeviceConfig(rs256deviceId, "config v2"); - - // List the configs again. This will show the two configs that we just pushed. - registry.listDeviceConfigs(rs256deviceId); - - // Delete the elliptic curve device. - registry.deleteDevice(es256deviceId); - - // Since we deleted the elliptic curve device, this will only show the RSA device. - registry.listDevices(); - - try { - // Try to delete the registry. However, since the registry is not empty, this will fail and - // throw an exception. - registry.deleteRegistry(); - } catch (IOException e) { - System.out.println("Exception: " + e.getMessage()); - } - - // Delete the RSA device. The registry is now empty. - registry.deleteDevice(rs256deviceId); - - // Since the registry has no devices in it, the delete will succeed. - registry.deleteRegistry(); - */ } } diff --git a/iot/api-client/manager/src/test/java/com/example/cloud/iot/examples/ManagerIT.java b/iot/api-client/manager/src/test/java/com/example/cloud/iot/examples/ManagerIT.java index 0f3e8616781..3716bb2517c 100644 --- a/iot/api-client/manager/src/test/java/com/example/cloud/iot/examples/ManagerIT.java +++ b/iot/api-client/manager/src/test/java/com/example/cloud/iot/examples/ManagerIT.java @@ -16,11 +16,14 @@ package com.example.cloud.iot.examples; +import com.google.cloud.pubsub.v1.TopicAdminClient; +import com.google.pubsub.v1.Topic; + import java.io.ByteArrayOutputStream; -import java.io.IOException; import java.io.PrintStream; import org.junit.After; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -34,54 +37,149 @@ public class ManagerIT { private ByteArrayOutputStream bout; private PrintStream out; private DeviceRegistryExample app; + + private static final String CLOUD_REGION = "us-central1"; + private static final String ES_PATH = "resources/ec_public.pem"; private static final String PROJECT_ID = System.getenv("GOOGLE_CLOUD_PROJECT"); - private static final String BUCKET = PROJECT_ID; + private static final String REGISTRY_ID = "java-reg-" + (System.currentTimeMillis() / 1000L); + private static final String RSA_PATH = "resources/rsa_cert.pem"; + private static final String TOPIC_ID = "java-pst-" + (System.currentTimeMillis() / 1000L); + + private static Topic topic; @Before - public void setUp() throws IOException { + public void setUp() throws Exception { bout = new ByteArrayOutputStream(); out = new PrintStream(bout); System.setOut(out); - app = new DeviceRegistryExample(); - // TODO: Create IoT PubSub Topic - // TODO: Create Registry } @After - public void tearDown() { - // TODO: Delete registry - // TODO: Remove PubSub Topic + public void tearDown() throws Exception { System.setOut(null); } @Test public void testCreateDeleteUnauthDevice() throws Exception { - // TODO: Implement when library is live + final String deviceName = "noauth-device"; + topic = DeviceRegistryExample.createIotTopic( + PROJECT_ID, + TOPIC_ID); + + DeviceRegistryExample.createRegistry(CLOUD_REGION, PROJECT_ID, REGISTRY_ID, TOPIC_ID); + DeviceRegistryExample.createDeviceWithNoAuth(deviceName, PROJECT_ID, CLOUD_REGION, REGISTRY_ID); + + String got = bout.toString(); + Assert.assertTrue(got.contains("Created device: {")); + + DeviceRegistryExample.deleteDevice(deviceName, PROJECT_ID, CLOUD_REGION, REGISTRY_ID); + DeviceRegistryExample.deleteRegistry(CLOUD_REGION, PROJECT_ID, REGISTRY_ID); + try (TopicAdminClient topicAdminClient = TopicAdminClient.create()) { + topicAdminClient.deleteTopic(topic.getNameAsTopicName()); + } } @Test public void testCreateDeleteEsDevice() throws Exception { - // TODO: Implement when library is live + final String deviceName = "es-device"; + topic = DeviceRegistryExample.createIotTopic( + PROJECT_ID, + TOPIC_ID); + DeviceRegistryExample.createRegistry(CLOUD_REGION, PROJECT_ID, REGISTRY_ID, TOPIC_ID); + DeviceRegistryExample.createDeviceWithEs256(deviceName, ES_PATH, PROJECT_ID, CLOUD_REGION, + REGISTRY_ID); + DeviceRegistryExample.getDeviceStates(deviceName, PROJECT_ID, CLOUD_REGION, REGISTRY_ID); + + String got = bout.toString(); + Assert.assertTrue(got.contains("Created device: {")); + + DeviceRegistryExample.deleteDevice(deviceName, PROJECT_ID, CLOUD_REGION, REGISTRY_ID); + DeviceRegistryExample.deleteRegistry(CLOUD_REGION, PROJECT_ID, REGISTRY_ID); + try (TopicAdminClient topicAdminClient = TopicAdminClient.create()) { + topicAdminClient.deleteTopic(topic.getNameAsTopicName()); + } } @Test public void testCreateDeleteRsaDevice() throws Exception { - // TODO: Implement when library is live + final String deviceName = "rsa-device"; + topic = DeviceRegistryExample.createIotTopic( + PROJECT_ID, + TOPIC_ID); + DeviceRegistryExample.createRegistry(CLOUD_REGION, PROJECT_ID, REGISTRY_ID, TOPIC_ID); + DeviceRegistryExample.createDeviceWithRs256( + deviceName, RSA_PATH, PROJECT_ID, CLOUD_REGION, REGISTRY_ID); + DeviceRegistryExample.getDeviceStates(deviceName, PROJECT_ID, CLOUD_REGION, REGISTRY_ID); + + String got = bout.toString(); + Assert.assertTrue(got.contains("Created device: {")); + + DeviceRegistryExample.deleteDevice(deviceName, PROJECT_ID, CLOUD_REGION, REGISTRY_ID); + DeviceRegistryExample.deleteRegistry(CLOUD_REGION, PROJECT_ID, REGISTRY_ID); + try (TopicAdminClient topicAdminClient = TopicAdminClient.create()) { + topicAdminClient.deleteTopic(topic.getNameAsTopicName()); + } } @Test public void testCreateGetDevice() throws Exception { - // TODO: Implement when library is live + final String deviceName = "rsa-device"; + topic = DeviceRegistryExample.createIotTopic( + PROJECT_ID, + TOPIC_ID); + DeviceRegistryExample.createRegistry(CLOUD_REGION, PROJECT_ID, REGISTRY_ID, TOPIC_ID); + DeviceRegistryExample.createDeviceWithRs256( + deviceName, RSA_PATH, PROJECT_ID, CLOUD_REGION, REGISTRY_ID); + DeviceRegistryExample.getDevice(deviceName, PROJECT_ID, CLOUD_REGION, REGISTRY_ID); + + String got = bout.toString(); + Assert.assertTrue(got.contains("Created device: {")); + Assert.assertTrue(got.contains("Retrieving device")); + + DeviceRegistryExample.deleteDevice(deviceName, PROJECT_ID, CLOUD_REGION, REGISTRY_ID); + DeviceRegistryExample.deleteRegistry(CLOUD_REGION, PROJECT_ID, REGISTRY_ID); + try (TopicAdminClient topicAdminClient = TopicAdminClient.create()) { + topicAdminClient.deleteTopic(topic.getNameAsTopicName()); + } } - @Test public void testCreateListDevices() throws Exception { - // TODO: Implement when library is live + final String deviceName = "rsa-device"; + topic = DeviceRegistryExample.createIotTopic( + PROJECT_ID, + TOPIC_ID); + DeviceRegistryExample.createRegistry(CLOUD_REGION, PROJECT_ID, REGISTRY_ID, TOPIC_ID); + DeviceRegistryExample.createDeviceWithRs256( + deviceName, RSA_PATH, PROJECT_ID, CLOUD_REGION, REGISTRY_ID); + DeviceRegistryExample.listDevices(PROJECT_ID, CLOUD_REGION, REGISTRY_ID); + + String got = bout.toString(); + Assert.assertTrue(got.contains("Created device: {")); + Assert.assertTrue(got.contains("Found")); + + DeviceRegistryExample.deleteDevice(deviceName, PROJECT_ID, CLOUD_REGION, REGISTRY_ID); + DeviceRegistryExample.deleteRegistry(CLOUD_REGION, PROJECT_ID, REGISTRY_ID); + try (TopicAdminClient topicAdminClient = TopicAdminClient.create()) { + topicAdminClient.deleteTopic(topic.getNameAsTopicName()); + } } @Test public void testCreateGetRegistry() throws Exception { - // TODO: Implement when library is live + + topic = DeviceRegistryExample.createIotTopic( + PROJECT_ID, + TOPIC_ID); + DeviceRegistryExample.createRegistry(CLOUD_REGION, PROJECT_ID, REGISTRY_ID, TOPIC_ID); + DeviceRegistryExample.getRegistry(PROJECT_ID, CLOUD_REGION, REGISTRY_ID); + + String got = bout.toString(); + Assert.assertFalse(got.contains("eventNotificationConfigs")); + + DeviceRegistryExample.deleteRegistry(CLOUD_REGION, PROJECT_ID, REGISTRY_ID); + try (TopicAdminClient topicAdminClient = TopicAdminClient.create()) { + topicAdminClient.deleteTopic(topic.getNameAsTopicName()); + } } } From 2f5aafc00ad279c176c8175b6f1db0d1ba319337 Mon Sep 17 00:00:00 2001 From: Gus Class Date: Mon, 25 Sep 2017 17:13:19 -0700 Subject: [PATCH 3/5] Adds tests, state support, and HTTP client --- iot/api-client/http_example/README.md | 60 ++++++++ iot/api-client/http_example/pom.xml | 47 ++++++ .../cloud/iot/examples/HttpExample.java | 139 ++++++++++++++++++ .../cloud/iot/examples/MqttExample.java | 40 ++--- .../iot/examples/MqttExampleOptions.java | 11 ++ 5 files changed, 280 insertions(+), 17 deletions(-) create mode 100644 iot/api-client/http_example/README.md create mode 100644 iot/api-client/http_example/pom.xml create mode 100644 iot/api-client/http_example/src/main/java/com/google/cloud/iot/examples/HttpExample.java diff --git a/iot/api-client/http_example/README.md b/iot/api-client/http_example/README.md new file mode 100644 index 00000000000..079a72c81f5 --- /dev/null +++ b/iot/api-client/http_example/README.md @@ -0,0 +1,60 @@ +# Cloud IoT Core Java HTTP example + +This sample app publishes data to Cloud Pub/Sub using the HTTP bridge provided +as part of Google Cloud IoT Core. + +Note that before you can run the sample, you must configure a Google Cloud +PubSub topic for Cloud IoT Core and register a device as described in the +[parent README](../README.md). + +## Setup + +Run the following command to install the dependencies using Maven: + + mvn clean compile + +## Running the sample + +The following command summarizes the sample usage: + +``` + mvn exec:java \ + -Dexec.mainClass="com.google.cloud.iot.examples.HttpExample" \ + -Dexec.args="-project_id=my-iot-project \ + -registry_id=my-registry \ + -device_id=my-device \ + -private_key_file=rsa_private_pkcs8 \ + -algorithm=RS256" +``` + +For example, if your project ID is `blue-jet-123`, your service account +credentials are stored in your home folder in creds.json and you have generated +your credentials using the [`generate_keys.sh`](../generate_keys.sh) script +provided in the parent folder, you can run the sample as: + +``` + mvn exec:java \ + -Dexec.mainClass="com.google.cloud.iot.examples.HttpExample" \ + -Dexec.args="-project_id=blue-jet-123 \ + -registry_id=my-registry \ + -device_id=my-java-device \ + -private_key_file=../rsa_private_pkcs8 \ + -algorithm=RS256" +``` + +## Reading the messages written by the sample client + +1. Create a subscription to your topic. + +``` + gcloud beta pubsub subscriptions create \ + projects/your-project-id/subscriptions/my-subscription \ + --topic device-events +``` + +2. Read messages published to the topic + +``` + gcloud beta pubsub subscriptions pull --auto-ack \ + projects/my-iot-project/subscriptions/my-subscription +``` diff --git a/iot/api-client/http_example/pom.xml b/iot/api-client/http_example/pom.xml new file mode 100644 index 00000000000..e977cc847bf --- /dev/null +++ b/iot/api-client/http_example/pom.xml @@ -0,0 +1,47 @@ + + 4.0.0 + com.google.cloud.iot.examples + cloudiot-http-example + jar + 1.0 + cloudiot-http-example + http://maven.apache.org + + + 1.7 + 1.7 + + + + + doc-samples + com.google.cloud + 1.0.0 + ../../../ + + + + + io.jsonwebtoken + jjwt + 0.7.0 + + + joda-time + joda-time + 2.1 + + + commons-cli + commons-cli + 1.3 + + + org.json + json + 20090211 + + + + diff --git a/iot/api-client/http_example/src/main/java/com/google/cloud/iot/examples/HttpExample.java b/iot/api-client/http_example/src/main/java/com/google/cloud/iot/examples/HttpExample.java new file mode 100644 index 00000000000..514783a49fd --- /dev/null +++ b/iot/api-client/http_example/src/main/java/com/google/cloud/iot/examples/HttpExample.java @@ -0,0 +1,139 @@ +package com.google.cloud.iot.examples; + +import io.jsonwebtoken.JwtBuilder; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.KeyFactory; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Base64; +import org.joda.time.DateTime; +import org.json.JSONObject; + +/** + * Java sample of connecting to Google Cloud IoT Core vice via HTTP, using JWT. + * + *

This example connects to Google Cloud IoT Core via HTTP Bridge, using a JWT for device + * authentication. After connecting, by default the device publishes 100 messages at a rate of one + * per second, and then exits. You can change The behavior to set state instead of events by using + * flag -message_type to 'state'. + * + *

To run this example, follow the instructions in the README located in the sample's parent + * folder. + */ +public class HttpExample { + /** Create a Cloud IoT Core JWT for the given project id, signed with the given private key. */ + private static String createJwtRsa(String projectId, String privateKeyFile) throws Exception { + DateTime now = new DateTime(); + // Create a JWT to authenticate this device. The device will be disconnected after the token + // expires, and will have to reconnect with a new token. The audience field should always be set + // to the GCP project id. + JwtBuilder jwtBuilder = + Jwts.builder() + .setIssuedAt(now.toDate()) + .setExpiration(now.plusMinutes(20).toDate()) + .setAudience(projectId); + + byte[] keyBytes = Files.readAllBytes(Paths.get(privateKeyFile)); + PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes); + KeyFactory kf = KeyFactory.getInstance("RSA"); + + return jwtBuilder.signWith(SignatureAlgorithm.RS256, kf.generatePrivate(spec)).compact(); + } + + private static String createJwtEs(String projectId, String privateKeyFile) throws Exception { + DateTime now = new DateTime(); + // Create a JWT to authenticate this device. The device will be disconnected after the token + // expires, and will have to reconnect with a new token. The audience field should always be set + // to the GCP project id. + JwtBuilder jwtBuilder = + Jwts.builder() + .setIssuedAt(now.toDate()) + .setExpiration(now.plusMinutes(20).toDate()) + .setAudience(projectId); + + byte[] keyBytes = Files.readAllBytes(Paths.get(privateKeyFile)); + PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes); + KeyFactory kf = KeyFactory.getInstance("ES256"); + + return jwtBuilder.signWith(SignatureAlgorithm.ES256, kf.generatePrivate(spec)).compact(); + } + + public static void main(String[] args) throws Exception { + HttpExampleOptions options = HttpExampleOptions.fromFlags(args); + if (options == null) { + // Could not parse the flags. + System.exit(1); + } + + // Build the resource path of the device that is going to be authenticated. + String devicePath = + String.format( + "projects/%s/locations/%s/registries/%s/devices/%s", + options.projectId, options.cloudRegion, options.registryId, options.deviceId); + + // This describes the operation that is going to be perform with the device. + String urlSuffix = options.messageType.equals("event") ? "publishEvent" : "setState"; + + String urlPath = + String.format( + "%s/%s/%s:%s", options.httpBridgeAddress, options.apiVersion, devicePath, urlSuffix); + URL url = new URL(urlPath); + System.out.format("Using URL: '%s'\n", urlPath); + + // Create the corresponding JWT depending on the selected algorithm. + String token; + if (options.algorithm.equals("RS256")) { + token = createJwtRsa(options.projectId, options.privateKeyFile); + } else if (options.algorithm.equals("ES256")) { + token = createJwtEs(options.projectId, options.privateKeyFile); + } else { + throw new IllegalArgumentException( + "Invalid algorithm " + options.algorithm + ". Should be one of 'RS256' or 'ES256'."); + } + + // Data sent through the wire has to be base64 encoded. + Base64.Encoder encoder = Base64.getEncoder(); + + // Publish numMessages messages to the HTTP bridge. + for (int i = 1; i <= options.numMessages; ++i) { + String payload = String.format("%s/%s-payload-%d", options.registryId, options.deviceId, i); + System.out.format( + "Publishing %s message %d/%d: '%s'\n", + options.messageType, i, options.numMessages, payload); + String encPayload = encoder.encodeToString(payload.getBytes("UTF-8")); + + HttpURLConnection httpCon = (HttpURLConnection) url.openConnection(); + httpCon.setDoOutput(true); + httpCon.setRequestMethod("POST"); + + // Adding headers. + httpCon.setRequestProperty("Authorization", String.format("Bearer %s", token)); + httpCon.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); + + // Adding the post data. The structure of the data send depends on whether it is event or a + // state message. + JSONObject data = new JSONObject(); + if (options.messageType.equals("event")) { + data.put("binary_data", encPayload); + } else { + JSONObject state = new JSONObject(); + state.put("binary_data", encPayload); + data.put("state", state); + } + httpCon.getOutputStream().write(data.toString().getBytes("UTF-8")); + httpCon.getOutputStream().close(); + + // This will perform the connection as well. + System.out.println(httpCon.getResponseCode()); + System.out.println(httpCon.getResponseMessage()); + + // Send events every second; states, every 5. + Thread.sleep(options.messageType.equals("event") ? 1000 : 5000); + } + System.out.println("Finished loop successfully. Goodbye!"); + } +} diff --git a/iot/api-client/mqtt_example/src/main/java/com/google/cloud/iot/examples/MqttExample.java b/iot/api-client/mqtt_example/src/main/java/com/google/cloud/iot/examples/MqttExample.java index 91e8c2246cc..59bf85df20f 100644 --- a/iot/api-client/mqtt_example/src/main/java/com/google/cloud/iot/examples/MqttExample.java +++ b/iot/api-client/mqtt_example/src/main/java/com/google/cloud/iot/examples/MqttExample.java @@ -3,30 +3,29 @@ import io.jsonwebtoken.JwtBuilder; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.KeyFactory; +import java.security.spec.PKCS8EncodedKeySpec; import org.eclipse.paho.client.mqttv3.MqttClient; import org.eclipse.paho.client.mqttv3.MqttConnectOptions; import org.eclipse.paho.client.mqttv3.MqttMessage; import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; import org.joda.time.DateTime; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.security.KeyFactory; -import java.security.PrivateKey; -import java.security.spec.PKCS8EncodedKeySpec; - /** * Java sample of connecting to Google Cloud IoT Core vice via MQTT, using JWT. * *

This example connects to Google Cloud IoT Core via MQTT, using a JWT for device - * authentication. After connecting, by default the device publishes 100 messages to - * the device's MQTT topic at a rate of one per second, and then exits. + * authentication. After connecting, by default the device publishes 100 messages to the device's + * MQTT topic at a rate of one per second, and then exits. To set state instead of publishing + * telemetry events, set the `-message_type` flag to `state.` * - *

To run this example, first create your credentials and register your device as - * described in the README located in the sample's parent folder. + *

To run this example, first create your credentials and register your device as described in + * the README located in the sample's parent folder. * - *

After you have registered your device and generated your credentials, compile and - * run with the corresponding algorithm flag, for example: + *

After you have registered your device and generated your credentials, compile and run with the + * corresponding algorithm flag, for example: * *

  *   $ mvn compile
@@ -86,12 +85,12 @@ public static void main(String[] args) throws Exception {
     // Build the connection string for Google's Cloud IoT Core MQTT server. Only SSL
     // connections are accepted. For server authentication, the JVM's root certificates
     // are used.
-    String mqttServerAddress =
+    final String mqttServerAddress =
         String.format("ssl://%s:%s", options.mqttBridgeHostname, options.mqttBridgePort);
 
     // Create our MQTT client. The mqttClientId is a unique string that identifies this device. For
     // Google Cloud IoT Core, it must be in the format below.
-    String mqttClientId =
+    final String mqttClientId =
         String.format(
             "projects/%s/locations/%s/registries/%s/devices/%s",
             options.projectId, options.cloudRegion, options.registryId, options.deviceId);
@@ -123,22 +122,29 @@ public static void main(String[] args) throws Exception {
     MqttClient client = new MqttClient(mqttServerAddress, mqttClientId, new MemoryPersistence());
     client.connect(connectOptions);
 
+    // Publish to the events or state topic based on the flag.
+    String subTopic = options.messageType.equals("event") ? "events" : options.messageType;
+
     // The MQTT topic that this device will publish telemetry data to. The MQTT topic name is
     // required to be in the format below. Note that this is not the same as the device registry's
     // Cloud Pub/Sub topic.
-    String mqttTopic = String.format("/devices/%s/events", options.deviceId);
+    String mqttTopic = String.format("/devices/%s/%s", options.deviceId, subTopic);
 
     // Publish numMessages messages to the MQTT bridge, at a rate of 1 per second.
     for (int i = 1; i <= options.numMessages; ++i) {
       String payload = String.format("%s/%s-payload-%d", options.registryId, options.deviceId, i);
-      System.out.format("Publishing message %d/%d: '%s'\n", i, options.numMessages, payload);
+      System.out.format(
+          "Publishing %s message %d/%d: '%s'\n",
+          options.messageType, i, options.numMessages, payload);
 
       // Publish "payload" to the MQTT topic. qos=1 means at least once delivery. Cloud IoT Core
       // also supports qos=0 for at most once delivery.
       MqttMessage message = new MqttMessage(payload.getBytes());
       message.setQos(1);
       client.publish(mqttTopic, message);
-      Thread.sleep(1000);
+
+      // Send events every second. State should not be updated as often
+      Thread.sleep(options.messageType.equals("event") ? 1000 : 5000);
     }
     // Disconnect the client and finish the run.
     client.disconnect();
diff --git a/iot/api-client/mqtt_example/src/main/java/com/google/cloud/iot/examples/MqttExampleOptions.java b/iot/api-client/mqtt_example/src/main/java/com/google/cloud/iot/examples/MqttExampleOptions.java
index 472ee15bb47..d08d3c05311 100644
--- a/iot/api-client/mqtt_example/src/main/java/com/google/cloud/iot/examples/MqttExampleOptions.java
+++ b/iot/api-client/mqtt_example/src/main/java/com/google/cloud/iot/examples/MqttExampleOptions.java
@@ -18,6 +18,7 @@ public class MqttExampleOptions {
   int numMessages = 100;
   String mqttBridgeHostname = "mqtt.googleapis.com";
   short mqttBridgePort = 8883;
+  String messageType = "event";
 
   /** Construct an MqttExampleOptions class from command line flags. */
   public static MqttExampleOptions fromFlags(String[] args) {
@@ -93,6 +94,13 @@ public static MqttExampleOptions fromFlags(String[] args) {
             .hasArg()
             .desc("MQTT bridge port.")
             .build());
+    options.addOption(
+        Option.builder()
+            .type(String.class)
+            .longOpt("message_type")
+            .hasArg()
+            .desc("Indicates whether the message is a telemetry event or a device state message")
+            .build());
 
     CommandLineParser parser = new DefaultParser();
     CommandLine commandLine;
@@ -118,6 +126,9 @@ public static MqttExampleOptions fromFlags(String[] args) {
         res.mqttBridgePort =
             ((Number) commandLine.getParsedOptionValue("mqtt_bridge_port")).shortValue();
       }
+      if (commandLine.hasOption("message_type")) {
+        res.messageType = commandLine.getOptionValue("message_type");
+      }
       return res;
     } catch (ParseException e) {
       System.err.println(e.getMessage());

From b2845e3880da96b308e28850c0a5ac5b43611c6c Mon Sep 17 00:00:00 2001
From: Gus Class 
Date: Tue, 26 Sep 2017 12:37:34 -0700
Subject: [PATCH 4/5] Feedback from review

---
 iot/api-client/http_example/pom.xml           |  15 ++
 .../cloud/iot/examples/HttpExample.java       |  23 ++-
 .../iot/examples/HttpExampleOptions.java      | 151 ++++++++++++++++++
 iot/api-client/manager/pom.xml                |   2 +-
 .../iot/examples/DeviceRegistryExample.java   |   2 +
 iot/api-client/mqtt_example/pom.xml           |  15 ++
 .../cloud/iot/examples/MqttExample.java       |  76 +++++----
 .../iot/examples/MqttExampleOptions.java      |  14 ++
 8 files changed, 268 insertions(+), 30 deletions(-)
 create mode 100644 iot/api-client/http_example/src/main/java/com/google/cloud/iot/examples/HttpExampleOptions.java

diff --git a/iot/api-client/http_example/pom.xml b/iot/api-client/http_example/pom.xml
index e977cc847bf..a60c66ba065 100644
--- a/iot/api-client/http_example/pom.xml
+++ b/iot/api-client/http_example/pom.xml
@@ -1,3 +1,18 @@
+
 
   4.0.0
diff --git a/iot/api-client/http_example/src/main/java/com/google/cloud/iot/examples/HttpExample.java b/iot/api-client/http_example/src/main/java/com/google/cloud/iot/examples/HttpExample.java
index 514783a49fd..d7dd27fa482 100644
--- a/iot/api-client/http_example/src/main/java/com/google/cloud/iot/examples/HttpExample.java
+++ b/iot/api-client/http_example/src/main/java/com/google/cloud/iot/examples/HttpExample.java
@@ -1,3 +1,17 @@
+/**
+ * Copyright 2017, Google, Inc.
+ *
+ * 

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + *

http://www.apache.org/licenses/LICENSE-2.0 + * + *

Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.google.cloud.iot.examples; import io.jsonwebtoken.JwtBuilder; @@ -131,8 +145,13 @@ public static void main(String[] args) throws Exception { System.out.println(httpCon.getResponseCode()); System.out.println(httpCon.getResponseMessage()); - // Send events every second; states, every 5. - Thread.sleep(options.messageType.equals("event") ? 1000 : 5000); + if (options.messageType.equals("event")) { + // Frequently send event payloads (every second) + Thread.sleep(1000); + } else { + // Update state with low frequency (once every 5 seconds) + Thread.sleep(5000); + } } System.out.println("Finished loop successfully. Goodbye!"); } diff --git a/iot/api-client/http_example/src/main/java/com/google/cloud/iot/examples/HttpExampleOptions.java b/iot/api-client/http_example/src/main/java/com/google/cloud/iot/examples/HttpExampleOptions.java new file mode 100644 index 00000000000..8d028da8df0 --- /dev/null +++ b/iot/api-client/http_example/src/main/java/com/google/cloud/iot/examples/HttpExampleOptions.java @@ -0,0 +1,151 @@ +/** + * Copyright 2017, Google, Inc. + * + *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + *

http://www.apache.org/licenses/LICENSE-2.0 + * + *

Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.iot.examples; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; + +/** Command line options for the HTTP example. */ +public class HttpExampleOptions { + String projectId; + String registryId; + String deviceId; + String privateKeyFile; + String algorithm; + String cloudRegion = "us-central1"; + int numMessages = 100; + String httpBridgeAddress = "https://cloudiot-device.googleapis.com"; + String apiVersion = "v1beta1"; + String messageType = "event"; + + /** Construct an HttpExampleOptions class from command line flags. */ + public static HttpExampleOptions fromFlags(String[] args) { + Options options = new Options(); + // Required arguments + options.addOption( + Option.builder() + .type(String.class) + .longOpt("project_id") + .hasArg() + .desc("GCP cloud project name.") + .required() + .build()); + options.addOption( + Option.builder() + .type(String.class) + .longOpt("registry_id") + .hasArg() + .desc("Cloud IoT Core registry id.") + .required() + .build()); + options.addOption( + Option.builder() + .type(String.class) + .longOpt("device_id") + .hasArg() + .desc("Cloud IoT Core device id.") + .required() + .build()); + options.addOption( + Option.builder() + .type(String.class) + .longOpt("private_key_file") + .hasArg() + .desc("Path to private key file.") + .required() + .build()); + options.addOption( + Option.builder() + .type(String.class) + .longOpt("algorithm") + .hasArg() + .desc("Encryption algorithm to use to generate the JWT. Either 'RS256' or 'ES256'.") + .required() + .build()); + + // Optional arguments. + options.addOption( + Option.builder() + .type(String.class) + .longOpt("cloud_region") + .hasArg() + .desc("GCP cloud region.") + .build()); + options.addOption( + Option.builder() + .type(Number.class) + .longOpt("num_messages") + .hasArg() + .desc("Number of messages to publish.") + .build()); + options.addOption( + Option.builder() + .type(String.class) + .longOpt("http_bridge_address") + .hasArg() + .desc("HTTP bridge address.") + .build()); + options.addOption( + Option.builder() + .type(String.class) + .longOpt("api_version") + .hasArg() + .desc("The version to use of the API.") + .build()); + options.addOption( + Option.builder() + .type(String.class) + .longOpt("message_type") + .hasArg() + .desc("Indicates whether message is a telemetry event or a device state message") + .build()); + + CommandLineParser parser = new DefaultParser(); + CommandLine commandLine; + try { + commandLine = parser.parse(options, args); + HttpExampleOptions res = new HttpExampleOptions(); + + res.projectId = commandLine.getOptionValue("project_id"); + res.registryId = commandLine.getOptionValue("registry_id"); + res.deviceId = commandLine.getOptionValue("device_id"); + res.privateKeyFile = commandLine.getOptionValue("private_key_file"); + res.algorithm = commandLine.getOptionValue("algorithm"); + if (commandLine.hasOption("cloud_region")) { + res.cloudRegion = commandLine.getOptionValue("cloud_region"); + } + if (commandLine.hasOption("num_messages")) { + res.numMessages = ((Number) commandLine.getParsedOptionValue("num_messages")).intValue(); + } + if (commandLine.hasOption("http_bridge_address")) { + res.httpBridgeAddress = commandLine.getOptionValue("http_bridge_address"); + } + if (commandLine.hasOption("api_version")) { + res.apiVersion = commandLine.getOptionValue("api_version"); + } + if (commandLine.hasOption("message_type")) { + res.messageType = commandLine.getOptionValue("message_type"); + } + return res; + } catch (ParseException e) { + System.err.println(e.getMessage()); + return null; + } + } +} diff --git a/iot/api-client/manager/pom.xml b/iot/api-client/manager/pom.xml index 6d678e875c3..3aca3831f66 100644 --- a/iot/api-client/manager/pom.xml +++ b/iot/api-client/manager/pom.xml @@ -47,7 +47,7 @@ com.google.cloud google-cloud-pubsub - 0.21.1-beta + 0.24.0-beta com.google.oauth-client diff --git a/iot/api-client/manager/src/main/java/com/example/cloud/iot/examples/DeviceRegistryExample.java b/iot/api-client/manager/src/main/java/com/example/cloud/iot/examples/DeviceRegistryExample.java index 5df7a424942..9e63932fe49 100644 --- a/iot/api-client/manager/src/main/java/com/example/cloud/iot/examples/DeviceRegistryExample.java +++ b/iot/api-client/manager/src/main/java/com/example/cloud/iot/examples/DeviceRegistryExample.java @@ -26,8 +26,10 @@ import com.google.api.services.cloudiot.v1.model.DeviceConfig; import com.google.api.services.cloudiot.v1.model.DeviceCredential; import com.google.api.services.cloudiot.v1.model.DeviceRegistry; +import com.google.api.services.cloudiot.v1.model.DeviceState; import com.google.api.services.cloudiot.v1.model.EventNotificationConfig; import com.google.api.services.cloudiot.v1.model.ListDeviceStatesResponse; +import com.google.api.services.cloudiot.v1.model.ModifyCloudToDeviceConfigRequest; import com.google.api.services.cloudiot.v1.model.PublicKeyCredential; import com.google.cloud.Role; import com.google.cloud.pubsub.v1.TopicAdminClient; diff --git a/iot/api-client/mqtt_example/pom.xml b/iot/api-client/mqtt_example/pom.xml index 09b95f4966e..90cfdaa7396 100644 --- a/iot/api-client/mqtt_example/pom.xml +++ b/iot/api-client/mqtt_example/pom.xml @@ -1,3 +1,18 @@ + 4.0.0 diff --git a/iot/api-client/mqtt_example/src/main/java/com/google/cloud/iot/examples/MqttExample.java b/iot/api-client/mqtt_example/src/main/java/com/google/cloud/iot/examples/MqttExample.java index 59bf85df20f..c74b38b0330 100644 --- a/iot/api-client/mqtt_example/src/main/java/com/google/cloud/iot/examples/MqttExample.java +++ b/iot/api-client/mqtt_example/src/main/java/com/google/cloud/iot/examples/MqttExample.java @@ -1,3 +1,17 @@ +/** + * Copyright 2017, Google, Inc. + * + *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + *

http://www.apache.org/licenses/LICENSE-2.0 + * + *

Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.google.cloud.iot.examples; import io.jsonwebtoken.JwtBuilder; @@ -120,34 +134,42 @@ public static void main(String[] args) throws Exception { // Create a client, and connect to the Google MQTT bridge. MqttClient client = new MqttClient(mqttServerAddress, mqttClientId, new MemoryPersistence()); - client.connect(connectOptions); - - // Publish to the events or state topic based on the flag. - String subTopic = options.messageType.equals("event") ? "events" : options.messageType; - - // The MQTT topic that this device will publish telemetry data to. The MQTT topic name is - // required to be in the format below. Note that this is not the same as the device registry's - // Cloud Pub/Sub topic. - String mqttTopic = String.format("/devices/%s/%s", options.deviceId, subTopic); - - // Publish numMessages messages to the MQTT bridge, at a rate of 1 per second. - for (int i = 1; i <= options.numMessages; ++i) { - String payload = String.format("%s/%s-payload-%d", options.registryId, options.deviceId, i); - System.out.format( - "Publishing %s message %d/%d: '%s'\n", - options.messageType, i, options.numMessages, payload); - - // Publish "payload" to the MQTT topic. qos=1 means at least once delivery. Cloud IoT Core - // also supports qos=0 for at most once delivery. - MqttMessage message = new MqttMessage(payload.getBytes()); - message.setQos(1); - client.publish(mqttTopic, message); - - // Send events every second. State should not be updated as often - Thread.sleep(options.messageType.equals("event") ? 1000 : 5000); + try { + client.connect(connectOptions); + + // Publish to the events or state topic based on the flag. + String subTopic = options.messageType.equals("event") ? "events" : options.messageType; + + // The MQTT topic that this device will publish telemetry data to. The MQTT topic name is + // required to be in the format below. Note that this is not the same as the device registry's + // Cloud Pub/Sub topic. + String mqttTopic = String.format("/devices/%s/%s", options.deviceId, subTopic); + + // Publish numMessages messages to the MQTT bridge, at a rate of 1 per second. + for (int i = 1; i <= options.numMessages; ++i) { + String payload = String.format("%s/%s-payload-%d", options.registryId, options.deviceId, i); + System.out.format( + "Publishing %s message %d/%d: '%s'\n", + options.messageType, i, options.numMessages, payload); + + // Publish "payload" to the MQTT topic. qos=1 means at least once delivery. Cloud IoT Core + // also supports qos=0 for at most once delivery. + MqttMessage message = new MqttMessage(payload.getBytes()); + message.setQos(1); + client.publish(mqttTopic, message); + + if (options.messageType.equals("event")) { + // Send telemetry events every second + Thread.sleep(1000); + } else { + // Note: Update Device state less frequently than with telemetry events + Thread.sleep(5000); + } + } + } finally { + // Disconnect the client and finish the run. + client.disconnect(); } - // Disconnect the client and finish the run. - client.disconnect(); System.out.println("Finished loop successfully. Goodbye!"); } } diff --git a/iot/api-client/mqtt_example/src/main/java/com/google/cloud/iot/examples/MqttExampleOptions.java b/iot/api-client/mqtt_example/src/main/java/com/google/cloud/iot/examples/MqttExampleOptions.java index d08d3c05311..102368acb7a 100644 --- a/iot/api-client/mqtt_example/src/main/java/com/google/cloud/iot/examples/MqttExampleOptions.java +++ b/iot/api-client/mqtt_example/src/main/java/com/google/cloud/iot/examples/MqttExampleOptions.java @@ -1,3 +1,17 @@ +/** + * Copyright 2017, Google, Inc. + * + *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + *

http://www.apache.org/licenses/LICENSE-2.0 + * + *

Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.google.cloud.iot.examples; import org.apache.commons.cli.CommandLine; From 8a0a0057cc1cb27c87750607db9c9a621fa10dee Mon Sep 17 00:00:00 2001 From: Gus Class Date: Tue, 26 Sep 2017 13:36:34 -0700 Subject: [PATCH 5/5] Updates to snapshot version of sample library dependency --- iot/api-client/manager/README.md | 2 +- iot/api-client/manager/pom.xml | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/iot/api-client/manager/README.md b/iot/api-client/manager/README.md index 862defe3fca..daf4c47125a 100644 --- a/iot/api-client/manager/README.md +++ b/iot/api-client/manager/README.md @@ -11,7 +11,7 @@ Manually install [the provided client library](https://cloud.google.com/iot/reso for Cloud IoT Core to Maven: mvn install:install-file -Dfile=cloud-iot-core-library.jar -DgroupId=com.example.apis \ - -DartifactId=google-api-services-cloudiot -Dversion=v1beta1-rev20170418-1.22.0-SNAPSHOT \ + -DartifactId=google-api-services-cloudiot -Dversion=v1beta1-rev20170926-1.22.0-SNAPSHOT \ -Dpackaging=jar Run the following command to install the libraries and build the sample with diff --git a/iot/api-client/manager/pom.xml b/iot/api-client/manager/pom.xml index 3aca3831f66..8c060ff5f91 100644 --- a/iot/api-client/manager/pom.xml +++ b/iot/api-client/manager/pom.xml @@ -37,13 +37,11 @@ - com.google.cloud google-cloud-pubsub