From 59a55dab1e08839598cc04753d50be520f9cb4ca Mon Sep 17 00:00:00 2001 From: Gus Class Date: Tue, 16 May 2017 16:38:09 -0700 Subject: [PATCH 1/4] Initial commit of sample --- iot/api/README.md | 38 ++ iot/api/generate_keys.sh | 24 ++ iot/api/manager/README.md | 36 ++ iot/api/manager/pom.xml | 82 +++++ .../maven-metadata.xml | 11 + .../maven-metadata.xml | 25 ++ .../iot/examples/DeviceRegistryExample.java | 338 ++++++++++++++++++ .../DeviceRegistryExampleOptions.java | 100 ++++++ .../examples/RetryHttpInitializerWrapper.java | 100 ++++++ iot/api/mqtt_example/README.md | 52 +++ iot/api/mqtt_example/pom.xml | 42 +++ .../cloud/iot/examples/MqttExample.java | 132 +++++++ .../iot/examples/MqttExampleOptions.java | 127 +++++++ iot/api/scripts/README.rst | 115 ++++++ iot/api/scripts/README.rst.in | 22 ++ iot/api/scripts/iam.py | 57 +++ iot/api/scripts/requirements.txt | 1 + 17 files changed, 1302 insertions(+) create mode 100644 iot/api/README.md create mode 100755 iot/api/generate_keys.sh create mode 100644 iot/api/manager/README.md create mode 100644 iot/api/manager/pom.xml create mode 100644 iot/api/manager/repo/com/google/apis/google-api-services-cloudiot/maven-metadata.xml create mode 100644 iot/api/manager/repo/com/google/apis/google-api-services-cloudiot/v1beta1-rev20170418-1.22.0-SNAPSHOT/maven-metadata.xml create mode 100644 iot/api/manager/src/main/java/com/google/cloud/iot/examples/DeviceRegistryExample.java create mode 100644 iot/api/manager/src/main/java/com/google/cloud/iot/examples/DeviceRegistryExampleOptions.java create mode 100644 iot/api/manager/src/main/java/com/google/cloud/iot/examples/RetryHttpInitializerWrapper.java create mode 100644 iot/api/mqtt_example/README.md create mode 100644 iot/api/mqtt_example/pom.xml create mode 100644 iot/api/mqtt_example/src/main/java/com/google/cloud/iot/examples/MqttExample.java create mode 100644 iot/api/mqtt_example/src/main/java/com/google/cloud/iot/examples/MqttExampleOptions.java create mode 100644 iot/api/scripts/README.rst create mode 100644 iot/api/scripts/README.rst.in create mode 100644 iot/api/scripts/iam.py create mode 100644 iot/api/scripts/requirements.txt diff --git a/iot/api/README.md b/iot/api/README.md new file mode 100644 index 00000000000..e4406401d81 --- /dev/null +++ b/iot/api/README.md @@ -0,0 +1,38 @@ +# Cloud IoT Core NodeJS Samples +This folder contains NodeJS samples that demonstrate an overview of the +Google Cloud IoT Core platform. + +## Quickstart + +1. Install the gCloud CLI as described in [the device manager guide](https://cloud-dot-devsite.googleplex.com/iot/docs/device_manager_guide). +2. Create a PubSub topic: + + gcloud beta pubsub topics create projects/my-iot-project/topics/device-events + +3. Add the special account `cloud-iot@system.gserviceaccount.com` to that +PubSub topic from the [Cloud Developer Console](https://console.cloud.google.com) +or by using the helper script in the /scripts folder. + +4. Create a registry: + + gcloud alpha iot registries create my-registry \ + --project=my-iot-project \ + --region=us-central1 \ + --pubsub-topic=projects/my-iot-project/topics/device-events + +5. Use the `generate_keys.sh` script to generate your signing keys: + + ./generate_keys.sh + +6. Create a device. + + gcloud alpha iot devices create my-java-device \ + --project=my-iot-project \ + --region=us-central1 \ + --registry=my-registry \ + --public-key path=rsa_cert.pem,type=rs256 + +7. Connect a sample device using the sample app in the `mqtt_example` folder. +8. Learn how to manage devices programatically with the sample app in the +`manager` folder. + diff --git a/iot/api/generate_keys.sh b/iot/api/generate_keys.sh new file mode 100755 index 00000000000..e9beedc3dd6 --- /dev/null +++ b/iot/api/generate_keys.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# 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. + +openssl req -x509 -newkey rsa:2048 -keyout rsa_private.pem -nodes -out \ + rsa_cert.pem -subj "/CN=unused" +openssl ecparam -genkey -name prime256v1 -noout -out ec_private.pem +openssl ec -in ec_private.pem -pubout -out ec_public.pem +openssl pkcs8 -topk8 -inform PEM -outform DER -in rsa_private.pem \ + -nocrypt > rsa_private_pkcs8 +openssl pkcs8 -topk8 -inform PEM -outform DER -in ec_private.pem \ + -nocrypt > ec_private_pkcs8 diff --git a/iot/api/manager/README.md b/iot/api/manager/README.md new file mode 100644 index 00000000000..7632c866c10 --- /dev/null +++ b/iot/api/manager/README.md @@ -0,0 +1,36 @@ +# Cloud IoT Core Java Device Management example + +This sample app demonstrates device management for Google Cloud IoT Core. + +Note that before you can run the sample, you must configure a Google Cloud +PubSub topic for Cloud IoT as described in the parent README. + +## Setup + +Run the following command to install the libraries and build the sample with +Maven: + +mvn clean compile assembly:single + +## Running the sample + +The following command summarizes the sample usage: + + mvn exec:java \ + -Dexec.mainClass="com.google.cloud.iot.examples.DeviceRegistryExample" \ + -Dexec.args="-project_id=my-project-id \ + -pubsub_topic=projects/my-project-id/topics/my-topic-id \ + -ec_public_key_file=/path/to/ec_public.pem \ + -rsa_certificate_file=/path/to/rsa_cert.pem" + +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 shell script provided in the parent folder, you can +run the sample as: + + mvn exec:java \ + -Dexec.mainClass="com.google.cloud.iot.examples.DeviceRegistryExample" \ + -Dexec.args="-project_id=blue-jet-123 \ + -pubsub_topic=projects/blue-jet-123/topics/device-events \ + -ec_public_key_file=../ec_public.pem \ + -rsa_certificate_file=../rsa_cert.pem" diff --git a/iot/api/manager/pom.xml b/iot/api/manager/pom.xml new file mode 100644 index 00000000000..d2dc277b82d --- /dev/null +++ b/iot/api/manager/pom.xml @@ -0,0 +1,82 @@ + + + 4.0.0 + com.example.cloud + cloudiot-manager-demo + jar + 1.0 + cloudiot-manager-demo + http://maven.apache.org + + + + + + project.local + project + file:./repo + + + + + + 1.7 + 1.7 + + + + + com.google.apis + google-api-services-cloudiot + v1beta1-rev20170418-1.22.0-SNAPSHOT + + + com.google.oauth-client + google-oauth-client + 1.22.0 + + + com.google.api-client + google-api-client + 1.22.0 + + + commons-cli + commons-cli + 1.3 + + + + + + + maven-assembly-plugin + + + + com.example.cloudiot.Manage + + + + jar-with-dependencies + + + + + + diff --git a/iot/api/manager/repo/com/google/apis/google-api-services-cloudiot/maven-metadata.xml b/iot/api/manager/repo/com/google/apis/google-api-services-cloudiot/maven-metadata.xml new file mode 100644 index 00000000000..fb8b8c01301 --- /dev/null +++ b/iot/api/manager/repo/com/google/apis/google-api-services-cloudiot/maven-metadata.xml @@ -0,0 +1,11 @@ + + + com.google.apis + google-api-services-cloudiot + + + v1beta1-rev20170418-1.22.0-SNAPSHOT + + 20170502215244 + + diff --git a/iot/api/manager/repo/com/google/apis/google-api-services-cloudiot/v1beta1-rev20170418-1.22.0-SNAPSHOT/maven-metadata.xml b/iot/api/manager/repo/com/google/apis/google-api-services-cloudiot/v1beta1-rev20170418-1.22.0-SNAPSHOT/maven-metadata.xml new file mode 100644 index 00000000000..30cc78dd286 --- /dev/null +++ b/iot/api/manager/repo/com/google/apis/google-api-services-cloudiot/v1beta1-rev20170418-1.22.0-SNAPSHOT/maven-metadata.xml @@ -0,0 +1,25 @@ + + + com.google.apis + google-api-services-cloudiot + v1beta1-rev20170418-1.22.0-SNAPSHOT + + + 20170502.215244 + 1 + + 20170502215244 + + + jar + v1beta1-rev20170418-1.22.0-20170502.215244-1 + 20170502215244 + + + pom + v1beta1-rev20170418-1.22.0-20170502.215244-1 + 20170502215244 + + + + diff --git a/iot/api/manager/src/main/java/com/google/cloud/iot/examples/DeviceRegistryExample.java b/iot/api/manager/src/main/java/com/google/cloud/iot/examples/DeviceRegistryExample.java new file mode 100644 index 00000000000..0061db51ebd --- /dev/null +++ b/iot/api/manager/src/main/java/com/google/cloud/iot/examples/DeviceRegistryExample.java @@ -0,0 +1,338 @@ +/** + * 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 com.google.api.client.googleapis.auth.oauth2.GoogleCredential; +import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; +import com.google.api.client.http.HttpRequestInitializer; +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.common.io.Files; +import java.io.File; +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import javax.xml.bind.DatatypeConverter; + +/** + * Example of using Cloud IoT device manager API to administer devices, registries and projects. + * + *

This example uses the Device Manager API to create, retrieve, disable, list and delete Cloud + * IoT devices and registries, using both RSA and eliptic curve keys for authentication. + * + *

To start, follow the instructions on the Developer Guide at cloud.google.com/iot to create a + * service_account.json file. Also create a Cloud Pub/Sub topic as discussed in the guide. After + * doing this, you should have service_account.json in your directory. Tell gcloud about this file + * by setting it in an environment variable: + * + *

+ * 
+ * $ export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service_account.json
+ * 
+ * 
+ * + *

Before running the example, we have to create private and public keys, as described in + * cloud.google.com/iot. Since we are interacting with the device manager, we will only use the + * public keys. The private keys are used to sign JWTs to authenticate devices. See the MQTT client + * example in the developer guide for an example of this. + * + *

Finally, compile and run the example with: + * + *

+ * 
+ * $ mvn clean compile assembly:single
+ * $ mvn exec:java \
+ *       -Dexec.mainClass="com.google.cloud.iot.examples.DeviceRegistryExample" \
+ *       -Dexec.args="-project_id=my-project-id \
+ *                    -pubsub_topic=projects/my-project-id/topics/my-topic-id \
+ *                    -ec_public_key_file=/path/to/ec_public.pem \
+ *                    -rsa_certificate_file=/path/to/rsa_cert.pem"
+ * 
+ * 
+ */ +public class DeviceRegistryExample { + // CloudIot service for administering Cloud IoT devices, registries and projects. + private CloudIot service; + // Path to the project and location: "projects/my-project-id/locations/us-central1" + private String projectPath; + // Path to the registry: "projects/my-project-id/location/us-central1/registries/my-registry-id" + private String registryPath; + + /** Construct a new registry with the given name and pubsub topic inside the given project. */ + public DeviceRegistryExample( + String projectId, String location, String registryName, String pubsubTopicPath) + throws IOException, GeneralSecurityException { + GoogleCredential credential = + GoogleCredential.getApplicationDefault().createScoped(CloudIotScopes.all()); + JsonFactory jsonFactory = JacksonFactory.getDefaultInstance(); + HttpRequestInitializer init = new RetryHttpInitializerWrapper(credential); + service = new CloudIot(GoogleNetHttpTransport.newTrustedTransport(), jsonFactory, init); + projectPath = "projects/" + projectId + "/locations/" + location; + registryPath = projectPath + "/registries/" + registryName; + + NotificationConfig notificationConfig = new NotificationConfig(); + notificationConfig.setPubsubTopicName(pubsubTopicPath); + + DeviceRegistry registry = new DeviceRegistry(); + registry.setEventNotificationConfig(notificationConfig); + registry.setId(registryName); + service.projects().locations().registries().create(projectPath, registry).execute(); + } + + /** Delete this registry from Cloud IoT. */ + public void delete() throws IOException { + System.out.println("Deleting: " + registryPath); + service.projects().locations().registries().delete(registryPath).execute(); + } + + /** Print all of the devices in this registry to standard out. */ + public void listDevices() throws IOException { + List devices = + service + .projects() + .locations() + .registries() + .devices() + .list(registryPath) + .execute() + .getDevices(); + + if (devices != null) { + System.out.println("Found " + devices.size() + " devices"); + for (Device d : devices) { + System.out.println("Id: " + d.getId()); + if (d.getConfig() != null) { + // Note that this will show the device config in Base64 encoded format. + System.out.println("Config: " + d.getConfig().toPrettyString()); + } + System.out.println(); + } + } else { + System.out.println("Registry has no devices."); + } + } + + private DeviceCredential createDeviceCredential(String publicKeyFilePath, String keyFormat) + throws IOException { + PublicKeyCredential publicKeyCredential = new PublicKeyCredential(); + String key = Files.toString(new File(publicKeyFilePath), Charsets.UTF_8); + publicKeyCredential.setKey(key); + publicKeyCredential.setFormat(keyFormat); + + DeviceCredential credential = new DeviceCredential(); + credential.setPublicKey(publicKeyCredential); + return credential; + } + + /** + * Create a new device in this registry with the given id. The device will be authenticated with + * the given public key file. + */ + private void createDevice(String deviceId, List credentials) + throws IOException { + System.out.println("Creating device with id: " + deviceId); + Device device = new Device(); + device.setId(deviceId); + device.setCredentials(credentials); + Device createdDevice = + service + .projects() + .locations() + .registries() + .devices() + .create(registryPath, device) + .execute(); + + System.out.println("Created device: " + createdDevice.toPrettyString()); + } + + /** Create a device that is authenticated using RS256. */ + public void createDeviceWithRs256(String deviceId, String certificateFilePath) + throws IOException { + createDevice( + deviceId, Arrays.asList(createDeviceCredential(certificateFilePath, "RSA_X509_PEM"))); + } + + /** Create a device that is authenticated using ES256. */ + public void createDeviceWithEs256(String deviceId, String publicKeyFilePath) throws IOException { + createDevice(deviceId, Arrays.asList(createDeviceCredential(publicKeyFilePath, "ES256_PEM"))); + } + + /** + * Create a device that has no credentials. + * + *

This is a valid way to construct a device, however until it is patched with a credential the + * device will not be able to connect to Cloud IoT. + */ + public void createDeviceWithNoAuth(String deviceId) throws IOException { + createDevice(deviceId, new ArrayList()); + } + + /** Patch the device to add an ES256 key for authentication. */ + public void patchEs256ForAuth(String deviceId, String publicKeyFilePath) throws IOException { + String devicePath = registryPath + "/devices/" + deviceId; + PublicKeyCredential publicKeyCredential = new PublicKeyCredential(); + String key = Files.toString(new File(publicKeyFilePath), Charsets.UTF_8); + publicKeyCredential.setKey(key); + publicKeyCredential.setFormat("ES256_PEM"); + + DeviceCredential credential = new DeviceCredential(); + credential.setPublicKey(publicKeyCredential); + + Device device = new Device(); + device.setCredentials(Arrays.asList(credential)); + + Device patchedDevice = + service + .projects() + .locations() + .registries() + .devices() + .patch(devicePath, device) + .setFields("credentials") + .execute(); + + System.out.println("Patched device is " + patchedDevice.toPrettyString()); + } + + /** Delete the given device from the registry. */ + public void deleteDevice(String deviceId) throws IOException { + String devicePath = registryPath + "/devices/" + deviceId; + System.out.println("Deleting device " + devicePath); + service.projects().locations().registries().devices().delete(devicePath).execute(); + } + + /** List all of the configs for the given device. */ + public void listDeviceConfigs(String deviceId) throws IOException { + String devicePath = registryPath + "/devices/" + deviceId; + System.out.println("Listing device configs for " + devicePath); + List deviceConfigs = + service + .projects() + .locations() + .registries() + .devices() + .configVersions() + .list(devicePath) + .execute() + .getDeviceConfigs(); + + for (DeviceConfig config : deviceConfigs) { + System.out.println("Config version: " + config.getVersion()); + System.out.println("Contents: " + config.getData().getBinaryData()); + System.out.println(); + } + } + + /** Modify the latest cloud to device config for the given device, with the config data. */ + public void modifyCloudToDeviceConfig(String deviceId, String configData) throws IOException { + 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); + request.setData(data); + DeviceConfig config = + service + .projects() + .locations() + .registries() + .devices() + .modifyCloudToDeviceConfig(devicePath, request) + .execute(); + + System.out.println("Created device config: " + config.toPrettyString()); + } + + public static void main(String[] args) throws IOException, GeneralSecurityException { + DeviceRegistryExampleOptions options = DeviceRegistryExampleOptions.fromFlags(args); + if (options == null) { + // Could not parse. + return; + } + + // 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.delete(); + } 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.delete(); + } +} diff --git a/iot/api/manager/src/main/java/com/google/cloud/iot/examples/DeviceRegistryExampleOptions.java b/iot/api/manager/src/main/java/com/google/cloud/iot/examples/DeviceRegistryExampleOptions.java new file mode 100644 index 00000000000..34eb9e9f590 --- /dev/null +++ b/iot/api/manager/src/main/java/com/google/cloud/iot/examples/DeviceRegistryExampleOptions.java @@ -0,0 +1,100 @@ +/** + * 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 Device Manager example */ +public class DeviceRegistryExampleOptions { + String projectId; + String pubsubTopic; + String ecPublicKeyFile = "ec_public.pem"; + String rsaCertificateFile = "rsa_cert.pem"; + String cloudRegion = "us-central1"; + + /** Construct an DeviceRegistryExampleOptions class from command line flags. */ + public static DeviceRegistryExampleOptions 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("pubsub_topic") + .hasArg() + .desc( + "Pub/Sub topic to create registry with, i.e. 'projects/project-id/topics/topic-id'") + .required() + .build()); + + // Optional arguments. + options.addOption( + Option.builder() + .type(String.class) + .longOpt("ec_public_key_file") + .hasArg() + .desc("Path to ES256 public key file.") + .build()); + options.addOption( + Option.builder() + .type(String.class) + .longOpt("rsa_certificate_file") + .hasArg() + .desc("Path to RS256 certificate file.") + .build()); + options.addOption( + Option.builder() + .type(String.class) + .longOpt("cloud_region") + .hasArg() + .desc("GCP cloud region.") + .build()); + + CommandLineParser parser = new DefaultParser(); + CommandLine commandLine; + try { + commandLine = parser.parse(options, args); + DeviceRegistryExampleOptions res = new DeviceRegistryExampleOptions(); + + res.projectId = commandLine.getOptionValue("project_id"); + res.pubsubTopic = commandLine.getOptionValue("pubsub_topic"); + + if (commandLine.hasOption("ec_public_key_file")) { + res.ecPublicKeyFile = commandLine.getOptionValue("ec_public_key_file"); + } + if (commandLine.hasOption("rsa_certificate_file")) { + res.rsaCertificateFile = commandLine.getOptionValue("rsa_certificate_file"); + } + if (commandLine.hasOption("cloud_region")) { + res.cloudRegion = commandLine.getOptionValue("cloud_region"); + } + return res; + } catch (ParseException e) { + System.err.println(e.getMessage()); + return null; + } + } +} diff --git a/iot/api/manager/src/main/java/com/google/cloud/iot/examples/RetryHttpInitializerWrapper.java b/iot/api/manager/src/main/java/com/google/cloud/iot/examples/RetryHttpInitializerWrapper.java new file mode 100644 index 00000000000..7d499f22d32 --- /dev/null +++ b/iot/api/manager/src/main/java/com/google/cloud/iot/examples/RetryHttpInitializerWrapper.java @@ -0,0 +1,100 @@ +/** + * 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 com.google.api.client.auth.oauth2.Credential; +import com.google.api.client.http.HttpBackOffIOExceptionHandler; +import com.google.api.client.http.HttpBackOffUnsuccessfulResponseHandler; +import com.google.api.client.http.HttpRequest; +import com.google.api.client.http.HttpRequestInitializer; +import com.google.api.client.http.HttpResponse; +import com.google.api.client.http.HttpUnsuccessfulResponseHandler; +import com.google.api.client.util.ExponentialBackOff; +import com.google.api.client.util.Sleeper; +import com.google.common.base.Preconditions; +import java.io.IOException; +import java.util.logging.Logger; + +/** + * RetryHttpInitializerWrapper will automatically retry upon RPC failures, preserving the + * auto-refresh behavior of the Google Credentials. + */ +public class RetryHttpInitializerWrapper implements HttpRequestInitializer { + + /** A private logger. */ + private static final Logger LOG = Logger.getLogger(RetryHttpInitializerWrapper.class.getName()); + + /** One minutes in milliseconds. */ + private static final int ONE_MINUTE_MILLIS = 60 * 1000; + + /** + * Intercepts the request for filling in the "Authorization" header field, as well as recovering + * from certain unsuccessful error codes wherein the Credential must refresh its token for a + * retry. + */ + private final Credential wrappedCredential; + + /** A sleeper; you can replace it with a mock in your test. */ + private final Sleeper sleeper; + + /** + * A constructor. + * + * @param wrappedCredential Credential which will be wrapped and used for providing auth header. + */ + public RetryHttpInitializerWrapper(final Credential wrappedCredential) { + this(wrappedCredential, Sleeper.DEFAULT); + } + + /** + * A protected constructor only for testing. + * + * @param wrappedCredential Credential which will be wrapped and used for providing auth header. + * @param sleeper Sleeper for easy testing. + */ + RetryHttpInitializerWrapper(final Credential wrappedCredential, final Sleeper sleeper) { + this.wrappedCredential = Preconditions.checkNotNull(wrappedCredential); + this.sleeper = sleeper; + } + + /** Initializes the given request. */ + @Override + public final void initialize(final HttpRequest request) { + request.setReadTimeout(2 * ONE_MINUTE_MILLIS); // 2 minutes read timeout + final HttpUnsuccessfulResponseHandler backoffHandler = + new HttpBackOffUnsuccessfulResponseHandler(new ExponentialBackOff()).setSleeper(sleeper); + request.setInterceptor(wrappedCredential); + request.setUnsuccessfulResponseHandler( + new HttpUnsuccessfulResponseHandler() { + @Override + public boolean handleResponse( + final HttpRequest request, final HttpResponse response, final boolean supportsRetry) + throws IOException { + if (wrappedCredential.handleResponse(request, response, supportsRetry)) { + // If credential decides it can handle it, the return code or message indicated + // something specific to authentication, and no backoff is desired. + return true; + } else if (backoffHandler.handleResponse(request, response, supportsRetry)) { + // Otherwise, we defer to the judgment of our internal backoff handler. + LOG.info("Retrying " + request.getUrl().toString()); + return true; + } else { + return false; + } + } + }); + request.setIOExceptionHandler( + new HttpBackOffIOExceptionHandler(new ExponentialBackOff()).setSleeper(sleeper)); + } +} diff --git a/iot/api/mqtt_example/README.md b/iot/api/mqtt_example/README.md new file mode 100644 index 00000000000..b8d7239b60f --- /dev/null +++ b/iot/api/mqtt_example/README.md @@ -0,0 +1,52 @@ +# Cloud IoT Core Java MQTT example + +This sample app publishes data to Cloud Pub/Sub using the MQTT 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. + +## 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.MqttExample" \ + -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 shell script provided in the parent folder, you can +run the sample as: + + mvn exec:java \ + -Dexec.mainClass="com.google.cloud.iot.examples.MqttExample" \ + -Dexec.args="-project_id=blue-jet-123 \ + -registry_id=my-registry \ + -device_id=my-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/mqtt_example/pom.xml b/iot/api/mqtt_example/pom.xml new file mode 100644 index 00000000000..f98e0953aed --- /dev/null +++ b/iot/api/mqtt_example/pom.xml @@ -0,0 +1,42 @@ + + 4.0.0 + com.google.cloud.iot.examples + cloudiot-mqtt-example + jar + 1.0 + cloudiot-mqtt-example + http://maven.apache.org + + 1.7 + 1.7 + + + + Eclipse Paho Repo + https://repo.eclipse.org/content/repositories/paho-releases/ + + + + + org.eclipse.paho + org.eclipse.paho.client.mqttv3 + 1.0.2 + + + io.jsonwebtoken + jjwt + 0.7.0 + + + joda-time + joda-time + 2.1 + + + commons-cli + commons-cli + 1.3 + + + diff --git a/iot/api/mqtt_example/src/main/java/com/google/cloud/iot/examples/MqttExample.java b/iot/api/mqtt_example/src/main/java/com/google/cloud/iot/examples/MqttExample.java new file mode 100644 index 00000000000..69b7df1ca0c --- /dev/null +++ b/iot/api/mqtt_example/src/main/java/com/google/cloud/iot/examples/MqttExample.java @@ -0,0 +1,132 @@ +package com.google.cloud.iot.examples; + +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.PrivateKey; +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; + +/** + * 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. + * + *

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 ompile and run with the + * corresponding algorithm flag, for example: + * + *

+ *   $ mvn compile
+ *   $ mvn exec:java -Dexec.mainClass="com.google.cloud.iot.examples.MqttExample" \
+ *       -Dexec.args="-project_id=my-project-id \
+ *       -registry_id=my-registry-id \
+ *       -device_id=my-device-id \
+ *       -private_key_file=/path/to/private_pkcs8 \
+ *       -algorithm=RS256"
+ * 
+ */ +public class MqttExample { + /** Load a PKCS8 encoded keyfile from the given path. */ + private static PrivateKey loadKeyFile(String filename, String algorithm) throws Exception { + byte[] keyBytes = Files.readAllBytes(Paths.get(filename)); + PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes); + KeyFactory kf = KeyFactory.getInstance(algorithm); + return kf.generatePrivate(spec); + } + + /** Create a Cloud IoT Core JWT for the given project id, signed with the given private key */ + private static String createJwt(String projectId, String privateKeyFile, String algorithm) + 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); + + if (algorithm.equals("RS256")) { + PrivateKey privateKey = loadKeyFile(privateKeyFile, "RSA"); + return jwtBuilder.signWith(SignatureAlgorithm.RS256, privateKey).compact(); + } else if (algorithm.equals("ES256")) { + PrivateKey privateKey = loadKeyFile(privateKeyFile, "EC"); + return jwtBuilder.signWith(SignatureAlgorithm.ES256, privateKey).compact(); + } else { + throw new IllegalArgumentException( + "Invalid algorithm " + algorithm + ". Should be one of 'RS256' or 'ES256'."); + } + } + + public static void main(String[] args) throws Exception { + MqttExampleOptions options = MqttExampleOptions.fromFlags(args); + if (options == null) { + // Could not parse. + System.exit(1); + } + + // 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 = + 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 = + String.format( + "projects/%s/locations/%s/registries/%s/devices/%s", + options.projectId, options.cloudRegion, options.registryId, options.deviceId); + + MqttConnectOptions connectOptions = new MqttConnectOptions(); + // Note that the the Google Cloud IoT Core only supports MQTT 3.1.1, and Paho requires that we + // explictly set this. If you don't set MQTT version, the server will immediately close its + // connection to your device. + connectOptions.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1_1); + + // With Google Cloud IoT Core, the username field is ignored, however it must be set for the Paho + // client library to send the password field. The password field is used to transmit a JWT to + // authorize the device. + connectOptions.setUserName("unused"); + connectOptions.setPassword( + createJwt(options.projectId, options.privateKeyFile, options.algorithm).toCharArray()); + + // Create a client, and connect to the Google MQTT bridge. + MqttClient client = new MqttClient(mqttServerAddress, mqttClientId, new MemoryPersistence()); + client.connect(connectOptions); + + // 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); + + // 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); + + // 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); + } + // Disconnect the client and finish the run. + client.disconnect(); + System.out.println("Finished loop successfully. Goodbye!"); + } +} diff --git a/iot/api/mqtt_example/src/main/java/com/google/cloud/iot/examples/MqttExampleOptions.java b/iot/api/mqtt_example/src/main/java/com/google/cloud/iot/examples/MqttExampleOptions.java new file mode 100644 index 00000000000..d27e64579b4 --- /dev/null +++ b/iot/api/mqtt_example/src/main/java/com/google/cloud/iot/examples/MqttExampleOptions.java @@ -0,0 +1,127 @@ +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 MQTT example */ +public class MqttExampleOptions { + String projectId; + String registryId; + String deviceId; + String privateKeyFile; + String algorithm; + String cloudRegion = "us-central1"; + int numMessages = 100; + String mqttBridgeHostname = "mqtt.googleapis.com"; + short mqttBridgePort = 8883; + + /** Construct an MqttExampleOptions class from command line flags. */ + public static MqttExampleOptions 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("mqtt_bridge_hostname") + .hasArg() + .desc("MQTT bridge hostname.") + .build()); + options.addOption( + Option.builder() + .type(Number.class) + .longOpt("mqtt_bridge_port") + .hasArg() + .desc("MQTT bridge port.") + .build()); + + CommandLineParser parser = new DefaultParser(); + CommandLine commandLine; + try { + commandLine = parser.parse(options, args); + MqttExampleOptions res = new MqttExampleOptions(); + + 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("mqtt_bridge_hostname")) { + res.mqttBridgeHostname = commandLine.getOptionValue("mqtt_bridge_hostname"); + } + if (commandLine.hasOption("mqtt_bridge_port")) { + res.mqttBridgePort = + ((Number) commandLine.getParsedOptionValue("mqtt_bridge_port")).shortValue(); + } + return res; + } catch (ParseException e) { + System.err.println(e.getMessage()); + return null; + } + } +} diff --git a/iot/api/scripts/README.rst b/iot/api/scripts/README.rst new file mode 100644 index 00000000000..2d48c4a9aba --- /dev/null +++ b/iot/api/scripts/README.rst @@ -0,0 +1,115 @@ +.. This file is automatically generated. Do not edit this file directly. + +Google Cloud IoT Core Python Samples +=============================================================================== + +This directory contains samples for Google Cloud IoT Core. `Google Cloud IoT Core`_ is a fully-managed, globally distributed solution for managing devices and sending / receiving messages from devices. Set the `GOOGLE_CLOUD_PROJECT` environment variable and call the script with your topic ID. + + + + +.. _Google Cloud IoT Core: https://cloud.google.com/iot/ + +Setup +------------------------------------------------------------------------------- + + +Authentication +++++++++++++++ + +Authentication is typically done through `Application Default Credentials`_, +which means you do not have to change the code to authenticate as long as +your environment has credentials. You have a few options for setting up +authentication: + +#. When running locally, use the `Google Cloud SDK`_ + + .. code-block:: bash + + gcloud auth application-default login + + +#. When running on App Engine or Compute Engine, credentials are already + set-up. However, you may need to configure your Compute Engine instance + with `additional scopes`_. + +#. You can create a `Service Account key file`_. This file can be used to + authenticate to Google Cloud Platform services from any environment. To use + the file, set the ``GOOGLE_APPLICATION_CREDENTIALS`` environment variable to + the path to the key file, for example: + + .. code-block:: bash + + export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service_account.json + +.. _Application Default Credentials: https://cloud.google.com/docs/authentication#getting_credentials_for_server-centric_flow +.. _additional scopes: https://cloud.google.com/compute/docs/authentication#using +.. _Service Account key file: https://developers.google.com/identity/protocols/OAuth2ServiceAccount#creatinganaccount + +Install Dependencies +++++++++++++++++++++ + +#. Install `pip`_ and `virtualenv`_ if you do not already have them. + +#. Create a virtualenv. Samples are compatible with Python 2.7 and 3.4+. + + .. code-block:: bash + + $ virtualenv env + $ source env/bin/activate + +#. Install the dependencies needed to run the samples. + + .. code-block:: bash + + $ pip install -r requirements.txt + +.. _pip: https://pip.pypa.io/ +.. _virtualenv: https://virtualenv.pypa.io/ + +Samples +------------------------------------------------------------------------------- + +PubSub helper ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + + +To run this sample: + +.. code-block:: bash + + $ python iam.py + + usage: iam.py [-h] topic_name + + This application demonstrates how programatically grant access to the + Cloud IoT Core service account on a given PubSub topic. + + For more information, see https://cloud.google.com/iot. + + positional arguments: + topic_name The PubSub topic to grant Cloud IoT Core access to + + optional arguments: + -h, --help show this help message and exit + + + + +The client library +------------------------------------------------------------------------------- + +This sample uses the `Google Cloud Client Library for Python`_. +You can read the documentation for more details on API usage and use GitHub +to `browse the source`_ and `report issues`_. + +.. Google Cloud Client Library for Python: + https://googlecloudplatform.github.io/google-cloud-python/ +.. browse the source: + https://github.com/GoogleCloudPlatform/google-cloud-python +.. report issues: + https://github.com/GoogleCloudPlatform/google-cloud-python/issues + + +.. _Google Cloud SDK: https://cloud.google.com/sdk/ \ No newline at end of file diff --git a/iot/api/scripts/README.rst.in b/iot/api/scripts/README.rst.in new file mode 100644 index 00000000000..dfd077ff41b --- /dev/null +++ b/iot/api/scripts/README.rst.in @@ -0,0 +1,22 @@ +# This file is used to generate README.rst + +product: + name: Google Cloud IoT Core + short_name: Cloud IoT Core + url: https://cloud.google.com/iot/ + description: > + `Google Cloud IoT Core`_ is a fully-managed, globally distributed solution + for managing devices and sending / receiving messages from devices. Set the + `GOOGLE_CLOUD_PROJECT` environment variable and call the script with your + topic ID. + +setup: +- auth +- install_deps + +samples: +- name: PubSub helper + file: iam.py + show_help: true + +cloud_client_library: true diff --git a/iot/api/scripts/iam.py b/iot/api/scripts/iam.py new file mode 100644 index 00000000000..79220849040 --- /dev/null +++ b/iot/api/scripts/iam.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python + +# Copyright 2017 Google Inc. All Rights Reserved. +# +# 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. + +"""This application demonstrates how programatically grant access to the +Cloud IoT Core service account on a given PubSub topic. + +For more information, see https://cloud.google.com/iot. +""" + +import argparse + +from google.cloud import pubsub + + +def set_topic_policy(topic_name): + """Sets the IAM policy for a topic for Cloud IoT Core.""" + pubsub_client = pubsub.Client() + topic = pubsub_client.topic(topic_name) + policy = topic.get_iam_policy() + + # Add a group as publishers. + publishers = policy.get('roles/pubsub.publisher', []) + publishers.append(policy.service_account( + 'cloud-iot@system.gserviceaccount.com')) + policy['roles/pubsub.publisher'] = publishers + + # Set the policy + topic.set_iam_policy(policy) + + print('IAM policy for topic {} set.'.format(topic.name)) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter + ) + + subparsers = parser.add_argument(dest='topic_name', + help='The PubSub topic to grant Cloud IoT Core access to') + + args = parser.parse_args() + + set_topic_policy(args.topic_name) diff --git a/iot/api/scripts/requirements.txt b/iot/api/scripts/requirements.txt new file mode 100644 index 00000000000..1412eed3dca --- /dev/null +++ b/iot/api/scripts/requirements.txt @@ -0,0 +1 @@ +google-cloud-pubsub==0.25.0 From 889dcc101573722708fd465cdc504d42a6585c54 Mon Sep 17 00:00:00 2001 From: Gus Class Date: Mon, 22 May 2017 11:23:27 -0700 Subject: [PATCH 2/4] Checkstyle fixes. --- iot/{api => api-client}/README.md | 4 ++-- iot/{api => api-client}/generate_keys.sh | 0 iot/{api => api-client}/manager/README.md | 0 iot/{api => api-client}/manager/pom.xml | 8 ++++++++ .../maven-metadata.xml | 0 .../maven-metadata.xml | 0 .../iot/examples/DeviceRegistryExample.java | 1 + .../DeviceRegistryExampleOptions.java | 4 ++-- .../examples/RetryHttpInitializerWrapper.java | 1 + .../mqtt_example/README.md | 0 iot/{api => api-client}/mqtt_example/pom.xml | 12 ++++++++++++ .../cloud/iot/examples/MqttExample.java | 19 ++++++++++--------- .../iot/examples/MqttExampleOptions.java | 4 ++-- iot/{api => api-client}/scripts/README.rst | 0 iot/{api => api-client}/scripts/README.rst.in | 0 iot/{api => api-client}/scripts/iam.py | 2 +- .../scripts/requirements.txt | 0 17 files changed, 39 insertions(+), 16 deletions(-) rename iot/{api => api-client}/README.md (92%) rename iot/{api => api-client}/generate_keys.sh (100%) rename iot/{api => api-client}/manager/README.md (100%) rename iot/{api => api-client}/manager/pom.xml (91%) rename iot/{api => api-client}/manager/repo/com/google/apis/google-api-services-cloudiot/maven-metadata.xml (100%) rename iot/{api => api-client}/manager/repo/com/google/apis/google-api-services-cloudiot/v1beta1-rev20170418-1.22.0-SNAPSHOT/maven-metadata.xml (100%) rename iot/{api => api-client}/manager/src/main/java/com/google/cloud/iot/examples/DeviceRegistryExample.java (99%) rename iot/{api => api-client}/manager/src/main/java/com/google/cloud/iot/examples/DeviceRegistryExampleOptions.java (96%) rename iot/{api => api-client}/manager/src/main/java/com/google/cloud/iot/examples/RetryHttpInitializerWrapper.java (99%) rename iot/{api => api-client}/mqtt_example/README.md (100%) rename iot/{api => api-client}/mqtt_example/pom.xml (85%) rename iot/{api => api-client}/mqtt_example/src/main/java/com/google/cloud/iot/examples/MqttExample.java (96%) rename iot/{api => api-client}/mqtt_example/src/main/java/com/google/cloud/iot/examples/MqttExampleOptions.java (97%) rename iot/{api => api-client}/scripts/README.rst (100%) rename iot/{api => api-client}/scripts/README.rst.in (100%) rename iot/{api => api-client}/scripts/iam.py (97%) rename iot/{api => api-client}/scripts/requirements.txt (100%) diff --git a/iot/api/README.md b/iot/api-client/README.md similarity index 92% rename from iot/api/README.md rename to iot/api-client/README.md index e4406401d81..d9893de61c0 100644 --- a/iot/api/README.md +++ b/iot/api-client/README.md @@ -1,5 +1,5 @@ -# Cloud IoT Core NodeJS Samples -This folder contains NodeJS samples that demonstrate an overview of the +# Cloud IoT Core Java Samples +This folder contains Java samples that demonstrate an overview of the Google Cloud IoT Core platform. ## Quickstart diff --git a/iot/api/generate_keys.sh b/iot/api-client/generate_keys.sh similarity index 100% rename from iot/api/generate_keys.sh rename to iot/api-client/generate_keys.sh diff --git a/iot/api/manager/README.md b/iot/api-client/manager/README.md similarity index 100% rename from iot/api/manager/README.md rename to iot/api-client/manager/README.md diff --git a/iot/api/manager/pom.xml b/iot/api-client/manager/pom.xml similarity index 91% rename from iot/api/manager/pom.xml rename to iot/api-client/manager/pom.xml index d2dc277b82d..0abef513523 100644 --- a/iot/api/manager/pom.xml +++ b/iot/api-client/manager/pom.xml @@ -23,6 +23,14 @@ cloudiot-manager-demo http://maven.apache.org + + + doc-samples + com.google.cloud + 1.0.0 + ../../../ + + diff --git a/iot/api/manager/repo/com/google/apis/google-api-services-cloudiot/maven-metadata.xml b/iot/api-client/manager/repo/com/google/apis/google-api-services-cloudiot/maven-metadata.xml similarity index 100% rename from iot/api/manager/repo/com/google/apis/google-api-services-cloudiot/maven-metadata.xml rename to iot/api-client/manager/repo/com/google/apis/google-api-services-cloudiot/maven-metadata.xml diff --git a/iot/api/manager/repo/com/google/apis/google-api-services-cloudiot/v1beta1-rev20170418-1.22.0-SNAPSHOT/maven-metadata.xml b/iot/api-client/manager/repo/com/google/apis/google-api-services-cloudiot/v1beta1-rev20170418-1.22.0-SNAPSHOT/maven-metadata.xml similarity index 100% rename from iot/api/manager/repo/com/google/apis/google-api-services-cloudiot/v1beta1-rev20170418-1.22.0-SNAPSHOT/maven-metadata.xml rename to iot/api-client/manager/repo/com/google/apis/google-api-services-cloudiot/v1beta1-rev20170418-1.22.0-SNAPSHOT/maven-metadata.xml diff --git a/iot/api/manager/src/main/java/com/google/cloud/iot/examples/DeviceRegistryExample.java b/iot/api-client/manager/src/main/java/com/google/cloud/iot/examples/DeviceRegistryExample.java similarity index 99% rename from iot/api/manager/src/main/java/com/google/cloud/iot/examples/DeviceRegistryExample.java rename to iot/api-client/manager/src/main/java/com/google/cloud/iot/examples/DeviceRegistryExample.java index 0061db51ebd..d6f53f90a82 100644 --- a/iot/api/manager/src/main/java/com/google/cloud/iot/examples/DeviceRegistryExample.java +++ b/iot/api-client/manager/src/main/java/com/google/cloud/iot/examples/DeviceRegistryExample.java @@ -30,6 +30,7 @@ import com.google.api.services.cloudiot.v1beta1.model.NotificationConfig; import com.google.api.services.cloudiot.v1beta1.model.PublicKeyCredential; import com.google.common.io.Files; + import java.io.File; import java.io.IOException; import java.security.GeneralSecurityException; diff --git a/iot/api/manager/src/main/java/com/google/cloud/iot/examples/DeviceRegistryExampleOptions.java b/iot/api-client/manager/src/main/java/com/google/cloud/iot/examples/DeviceRegistryExampleOptions.java similarity index 96% rename from iot/api/manager/src/main/java/com/google/cloud/iot/examples/DeviceRegistryExampleOptions.java rename to iot/api-client/manager/src/main/java/com/google/cloud/iot/examples/DeviceRegistryExampleOptions.java index 34eb9e9f590..57e13153881 100644 --- a/iot/api/manager/src/main/java/com/google/cloud/iot/examples/DeviceRegistryExampleOptions.java +++ b/iot/api-client/manager/src/main/java/com/google/cloud/iot/examples/DeviceRegistryExampleOptions.java @@ -20,7 +20,7 @@ import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; -/** Command line options for the Device Manager example */ +/** Command line options for the Device Manager example. */ public class DeviceRegistryExampleOptions { String projectId; String pubsubTopic; @@ -29,7 +29,7 @@ public class DeviceRegistryExampleOptions { String cloudRegion = "us-central1"; /** Construct an DeviceRegistryExampleOptions class from command line flags. */ - public static DeviceRegistryExampleOptions fromFlags(String args[]) { + public static DeviceRegistryExampleOptions fromFlags(String[] args) { Options options = new Options(); // Required arguments options.addOption( diff --git a/iot/api/manager/src/main/java/com/google/cloud/iot/examples/RetryHttpInitializerWrapper.java b/iot/api-client/manager/src/main/java/com/google/cloud/iot/examples/RetryHttpInitializerWrapper.java similarity index 99% rename from iot/api/manager/src/main/java/com/google/cloud/iot/examples/RetryHttpInitializerWrapper.java rename to iot/api-client/manager/src/main/java/com/google/cloud/iot/examples/RetryHttpInitializerWrapper.java index 7d499f22d32..71aad88ef33 100644 --- a/iot/api/manager/src/main/java/com/google/cloud/iot/examples/RetryHttpInitializerWrapper.java +++ b/iot/api-client/manager/src/main/java/com/google/cloud/iot/examples/RetryHttpInitializerWrapper.java @@ -23,6 +23,7 @@ import com.google.api.client.util.ExponentialBackOff; import com.google.api.client.util.Sleeper; import com.google.common.base.Preconditions; + import java.io.IOException; import java.util.logging.Logger; diff --git a/iot/api/mqtt_example/README.md b/iot/api-client/mqtt_example/README.md similarity index 100% rename from iot/api/mqtt_example/README.md rename to iot/api-client/mqtt_example/README.md diff --git a/iot/api/mqtt_example/pom.xml b/iot/api-client/mqtt_example/pom.xml similarity index 85% rename from iot/api/mqtt_example/pom.xml rename to iot/api-client/mqtt_example/pom.xml index f98e0953aed..09b95f4966e 100644 --- a/iot/api/mqtt_example/pom.xml +++ b/iot/api-client/mqtt_example/pom.xml @@ -7,16 +7,27 @@ 1.0 cloudiot-mqtt-example http://maven.apache.org + 1.7 1.7 + + + + doc-samples + com.google.cloud + 1.0.0 + ../../../ + + Eclipse Paho Repo https://repo.eclipse.org/content/repositories/paho-releases/ + org.eclipse.paho @@ -39,4 +50,5 @@ 1.3 + diff --git a/iot/api/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 similarity index 96% rename from iot/api/mqtt_example/src/main/java/com/google/cloud/iot/examples/MqttExample.java rename to iot/api-client/mqtt_example/src/main/java/com/google/cloud/iot/examples/MqttExample.java index 69b7df1ca0c..b9496052f2a 100644 --- a/iot/api/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,17 +3,18 @@ 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.PrivateKey; -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. * @@ -46,7 +47,7 @@ private static PrivateKey loadKeyFile(String filename, String algorithm) throws return kf.generatePrivate(spec); } - /** Create a Cloud IoT Core JWT for the given project id, signed with the given private key */ + /** Create a Cloud IoT Core JWT for the given project id, signed with the given private key. */ private static String createJwt(String projectId, String privateKeyFile, String algorithm) throws Exception { DateTime now = new DateTime(); @@ -97,9 +98,9 @@ public static void main(String[] args) throws Exception { // connection to your device. connectOptions.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1_1); - // With Google Cloud IoT Core, the username field is ignored, however it must be set for the Paho - // client library to send the password field. The password field is used to transmit a JWT to - // authorize the device. + // With Google Cloud IoT Core, the username field is ignored, however it must be set for the + // Paho client library to send the password field. The password field is used to transmit a JWT + // to authorize the device. connectOptions.setUserName("unused"); connectOptions.setPassword( createJwt(options.projectId, options.privateKeyFile, options.algorithm).toCharArray()); diff --git a/iot/api/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 similarity index 97% rename from iot/api/mqtt_example/src/main/java/com/google/cloud/iot/examples/MqttExampleOptions.java rename to iot/api-client/mqtt_example/src/main/java/com/google/cloud/iot/examples/MqttExampleOptions.java index d27e64579b4..472ee15bb47 100644 --- a/iot/api/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 @@ -7,7 +7,7 @@ import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; -/** Command line options for the MQTT example */ +/** Command line options for the MQTT example. */ public class MqttExampleOptions { String projectId; String registryId; @@ -20,7 +20,7 @@ public class MqttExampleOptions { short mqttBridgePort = 8883; /** Construct an MqttExampleOptions class from command line flags. */ - public static MqttExampleOptions fromFlags(String args[]) { + public static MqttExampleOptions fromFlags(String[] args) { Options options = new Options(); // Required arguments options.addOption( diff --git a/iot/api/scripts/README.rst b/iot/api-client/scripts/README.rst similarity index 100% rename from iot/api/scripts/README.rst rename to iot/api-client/scripts/README.rst diff --git a/iot/api/scripts/README.rst.in b/iot/api-client/scripts/README.rst.in similarity index 100% rename from iot/api/scripts/README.rst.in rename to iot/api-client/scripts/README.rst.in diff --git a/iot/api/scripts/iam.py b/iot/api-client/scripts/iam.py similarity index 97% rename from iot/api/scripts/iam.py rename to iot/api-client/scripts/iam.py index 79220849040..43cd838b78c 100644 --- a/iot/api/scripts/iam.py +++ b/iot/api-client/scripts/iam.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -# Copyright 2017 Google Inc. All Rights Reserved. +# 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. diff --git a/iot/api/scripts/requirements.txt b/iot/api-client/scripts/requirements.txt similarity index 100% rename from iot/api/scripts/requirements.txt rename to iot/api-client/scripts/requirements.txt From 12c348798121a4e07944b5f4383f24b6c269d9b4 Mon Sep 17 00:00:00 2001 From: Gus Class Date: Mon, 22 May 2017 17:20:56 -0700 Subject: [PATCH 3/4] Fixes issues found in review. --- iot/api-client/README.md | 6 ++--- iot/api-client/manager/README.md | 11 ++++++-- iot/api-client/manager/pom.xml | 11 -------- .../maven-metadata.xml | 11 -------- .../maven-metadata.xml | 25 ------------------- .../iot/examples/DeviceRegistryExample.java | 21 ++++++---------- iot/api-client/mqtt_example/README.md | 6 ++--- .../cloud/iot/examples/MqttExample.java | 4 +-- 8 files changed, 25 insertions(+), 70 deletions(-) delete mode 100644 iot/api-client/manager/repo/com/google/apis/google-api-services-cloudiot/maven-metadata.xml delete mode 100644 iot/api-client/manager/repo/com/google/apis/google-api-services-cloudiot/v1beta1-rev20170418-1.22.0-SNAPSHOT/maven-metadata.xml diff --git a/iot/api-client/README.md b/iot/api-client/README.md index d9893de61c0..2c61fe001a9 100644 --- a/iot/api-client/README.md +++ b/iot/api-client/README.md @@ -11,7 +11,7 @@ Google Cloud IoT Core platform. 3. Add the special account `cloud-iot@system.gserviceaccount.com` to that PubSub topic from the [Cloud Developer Console](https://console.cloud.google.com) -or by using the helper script in the /scripts folder. +or by using the helper script in the [/scripts](./scripts) folder. 4. Create a registry: @@ -20,7 +20,7 @@ or by using the helper script in the /scripts folder. --region=us-central1 \ --pubsub-topic=projects/my-iot-project/topics/device-events -5. Use the `generate_keys.sh` script to generate your signing keys: +5. Use the [`generate_keys.sh`](generate_keys.sh) script to generate your signing keys: ./generate_keys.sh @@ -32,7 +32,7 @@ or by using the helper script in the /scripts folder. --registry=my-registry \ --public-key path=rsa_cert.pem,type=rs256 -7. Connect a sample device using the sample app in the `mqtt_example` folder. +7. Connect a sample device using the sample app in the [`mqtt_example`](./mqtt_example) folder. 8. Learn how to manage devices programatically with the sample app in the `manager` folder. diff --git a/iot/api-client/manager/README.md b/iot/api-client/manager/README.md index 7632c866c10..e457aca8ad4 100644 --- a/iot/api-client/manager/README.md +++ b/iot/api-client/manager/README.md @@ -1,12 +1,19 @@ -# Cloud IoT Core Java Device Management example +r)# Cloud IoT Core Java Device Management example This sample app demonstrates device management for Google Cloud IoT Core. Note that before you can run the sample, you must configure a Google Cloud -PubSub topic for Cloud IoT as described in the parent README. +PubSub topic for Cloud IoT as described in [the parent README](../README.md). ## Setup +Manually install [the provided client library](https://cloud.google.com/iot/resources/java/cloud-iot-core-library.jar) +for Cloud IoT Core to Maven: + + mvn install:install-file -Dfile=cloud-iot-core-library.jar -DgroupId=com.google.apis \ + -DartifactId=google-api-services-cloudiot -Dversion=v1beta1-rev20170418-1.22.0-SNAPSHOT \ + -Dpackaging=jar + Run the following command to install the libraries and build the sample with Maven: diff --git a/iot/api-client/manager/pom.xml b/iot/api-client/manager/pom.xml index 0abef513523..100af9a82e2 100644 --- a/iot/api-client/manager/pom.xml +++ b/iot/api-client/manager/pom.xml @@ -31,17 +31,6 @@ ../../../ - - - - - project.local - project - file:./repo - - - - 1.7 1.7 diff --git a/iot/api-client/manager/repo/com/google/apis/google-api-services-cloudiot/maven-metadata.xml b/iot/api-client/manager/repo/com/google/apis/google-api-services-cloudiot/maven-metadata.xml deleted file mode 100644 index fb8b8c01301..00000000000 --- a/iot/api-client/manager/repo/com/google/apis/google-api-services-cloudiot/maven-metadata.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - com.google.apis - google-api-services-cloudiot - - - v1beta1-rev20170418-1.22.0-SNAPSHOT - - 20170502215244 - - diff --git a/iot/api-client/manager/repo/com/google/apis/google-api-services-cloudiot/v1beta1-rev20170418-1.22.0-SNAPSHOT/maven-metadata.xml b/iot/api-client/manager/repo/com/google/apis/google-api-services-cloudiot/v1beta1-rev20170418-1.22.0-SNAPSHOT/maven-metadata.xml deleted file mode 100644 index 30cc78dd286..00000000000 --- a/iot/api-client/manager/repo/com/google/apis/google-api-services-cloudiot/v1beta1-rev20170418-1.22.0-SNAPSHOT/maven-metadata.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - com.google.apis - google-api-services-cloudiot - v1beta1-rev20170418-1.22.0-SNAPSHOT - - - 20170502.215244 - 1 - - 20170502215244 - - - jar - v1beta1-rev20170418-1.22.0-20170502.215244-1 - 20170502215244 - - - pom - v1beta1-rev20170418-1.22.0-20170502.215244-1 - 20170502215244 - - - - diff --git a/iot/api-client/manager/src/main/java/com/google/cloud/iot/examples/DeviceRegistryExample.java b/iot/api-client/manager/src/main/java/com/google/cloud/iot/examples/DeviceRegistryExample.java index d6f53f90a82..e17766ea578 100644 --- a/iot/api-client/manager/src/main/java/com/google/cloud/iot/examples/DeviceRegistryExample.java +++ b/iot/api-client/manager/src/main/java/com/google/cloud/iot/examples/DeviceRegistryExample.java @@ -46,20 +46,15 @@ * IoT devices and registries, using both RSA and eliptic curve keys for authentication. * *

To start, follow the instructions on the Developer Guide at cloud.google.com/iot to create a - * service_account.json file. Also create a Cloud Pub/Sub topic as discussed in the guide. After - * doing this, you should have service_account.json in your directory. Tell gcloud about this file - * by setting it in an environment variable: - * - *

- * 
- * $ export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service_account.json
- * 
- * 
+ * service_account.json file and Cloud Pub/Sub topic as discussed in the guide. You will then need + * to point to the service_account.json file as described in + * https://developers.google.com/identity/protocols/application-default-credentials#howtheywork * *

Before running the example, we have to create private and public keys, as described in * cloud.google.com/iot. Since we are interacting with the device manager, we will only use the - * public keys. The private keys are used to sign JWTs to authenticate devices. See the MQTT client - * example in the developer guide for an example of this. + * public keys. The private keys are used to sign JWTs to authenticate devices. See the + * MQTT client example + * for more information. * *

Finally, compile and run the example with: * @@ -76,7 +71,7 @@ * */ public class DeviceRegistryExample { - // CloudIot service for administering Cloud IoT devices, registries and projects. + // Service for administering Cloud IoT Core devices, registries and projects. private CloudIot service; // Path to the project and location: "projects/my-project-id/locations/us-central1" private String projectPath; @@ -255,7 +250,7 @@ public void modifyCloudToDeviceConfig(String deviceId, String configData) throws ModifyCloudToDeviceConfigRequest request = new ModifyCloudToDeviceConfigRequest(); DeviceConfigData data = new DeviceConfigData(); data.setBinaryData(DatatypeConverter.printBase64Binary(configData.getBytes(Charsets.UTF_8))); - request.setVersionToUpdate(0L); + request.setVersionToUpdate(0L); // 0L indicates update all versions request.setData(data); DeviceConfig config = service diff --git a/iot/api-client/mqtt_example/README.md b/iot/api-client/mqtt_example/README.md index b8d7239b60f..969813330a9 100644 --- a/iot/api-client/mqtt_example/README.md +++ b/iot/api-client/mqtt_example/README.md @@ -5,7 +5,7 @@ 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. +[parent README](../README.md). ## Setup @@ -27,8 +27,8 @@ The following command summarizes the sample usage: 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 shell script provided in the parent folder, you can -run the sample as: +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.MqttExample" \ 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 b9496052f2a..09528953587 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 @@ -25,8 +25,8 @@ *

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 ompile 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

From 0aa4b9bfd951299a5fb1701f4f1431d387becfe0 Mon Sep 17 00:00:00 2001
From: Gus Class 
Date: Mon, 22 May 2017 17:24:34 -0700
Subject: [PATCH 4/4] Readme typo

---
 iot/api-client/manager/README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/iot/api-client/manager/README.md b/iot/api-client/manager/README.md
index e457aca8ad4..c2036a5277c 100644
--- a/iot/api-client/manager/README.md
+++ b/iot/api-client/manager/README.md
@@ -1,4 +1,4 @@
-r)# Cloud IoT Core Java Device Management example
+# Cloud IoT Core Java Device Management example
 
 This sample app demonstrates device management for Google Cloud IoT Core.