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..a60c66ba065
--- /dev/null
+++ b/iot/api-client/http_example/pom.xml
@@ -0,0 +1,62 @@
+
+
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; +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()); + + 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/README.md b/iot/api-client/manager/README.md
index 1c80a53f695..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
@@ -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..8c060ff5f91 100644
--- a/iot/api-client/manager/pom.xml
+++ b/iot/api-client/manager/pom.xml
@@ -40,18 +40,23 @@
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;
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:
*
* 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;
@@ -18,6 +32,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 +108,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 +140,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());
* $ mvn compile
@@ -86,12 +99,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);
@@ -121,27 +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);
-
- // 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);
+ 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 472ee15bb47..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.
+ *
+ *