| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent d553cd1 commit a1a3ded
14 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,60 @@ | |||
| 1 | + # Cloud IoT Core Java HTTP example | ||
| 2 | + | ||
| 3 | + This sample app publishes data to Cloud Pub/Sub using the HTTP bridge provided | ||
| 4 | + as part of Google Cloud IoT Core. | ||
| 5 | + | ||
| 6 | + Note that before you can run the sample, you must configure a Google Cloud | ||
| 7 | + PubSub topic for Cloud IoT Core and register a device as described in the | ||
| 8 | + [parent README](../README.md). | ||
| 9 | + | ||
| 10 | + ## Setup | ||
| 11 | + | ||
| 12 | + Run the following command to install the dependencies using Maven: | ||
| 13 | + | ||
| 14 | + mvn clean compile | ||
| 15 | + | ||
| 16 | + ## Running the sample | ||
| 17 | + | ||
| 18 | + The following command summarizes the sample usage: | ||
| 19 | + | ||
| 20 | + ``` | ||
| 21 | + mvn exec:java \ | ||
| 22 | + -Dexec.mainClass="com.google.cloud.iot.examples.HttpExample" \ | ||
| 23 | + -Dexec.args="-project_id=my-iot-project \ | ||
| 24 | + -registry_id=my-registry \ | ||
| 25 | + -device_id=my-device \ | ||
| 26 | + -private_key_file=rsa_private_pkcs8 \ | ||
| 27 | + -algorithm=RS256" | ||
| 28 | + ``` | ||
| 29 | + | ||
| 30 | + For example, if your project ID is `blue-jet-123`, your service account | ||
| 31 | + credentials are stored in your home folder in creds.json and you have generated | ||
| 32 | + your credentials using the [`generate_keys.sh`](../generate_keys.sh) script | ||
| 33 | + provided in the parent folder, you can run the sample as: | ||
| 34 | + | ||
| 35 | + ``` | ||
| 36 | + mvn exec:java \ | ||
| 37 | + -Dexec.mainClass="com.google.cloud.iot.examples.HttpExample" \ | ||
| 38 | + -Dexec.args="-project_id=blue-jet-123 \ | ||
| 39 | + -registry_id=my-registry \ | ||
| 40 | + -device_id=my-java-device \ | ||
| 41 | + -private_key_file=../rsa_private_pkcs8 \ | ||
| 42 | + -algorithm=RS256" | ||
| 43 | + ``` | ||
| 44 | + | ||
| 45 | + ## Reading the messages written by the sample client | ||
| 46 | + | ||
| 47 | + 1. Create a subscription to your topic. | ||
| 48 | + | ||
| 49 | + ``` | ||
| 50 | + gcloud beta pubsub subscriptions create \ | ||
| 51 | + projects/your-project-id/subscriptions/my-subscription \ | ||
| 52 | + --topic device-events | ||
| 53 | + ``` | ||
| 54 | + | ||
| 55 | + 2. Read messages published to the topic | ||
| 56 | + | ||
| 57 | + ``` | ||
| 58 | + gcloud beta pubsub subscriptions pull --auto-ack \ | ||
| 59 | + projects/my-iot-project/subscriptions/my-subscription | ||
| 60 | + ``` | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,62 @@ | |||
| 1 | + <!-- | ||
| 2 | + Copyright 2017 Google Inc. | ||
| 3 | + | ||
| 4 | + Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + you may not use this file except in compliance with the License. | ||
| 6 | + You may obtain a copy of the License at | ||
| 7 | + | ||
| 8 | + http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + | ||
| 10 | + Unless required by applicable law or agreed to in writing, software | ||
| 11 | + distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + See the License for the specific language governing permissions and | ||
| 14 | + limitations under the License. | ||
| 15 | + --> | ||
| 16 | + <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| 17 | + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> | ||
| 18 | + <modelVersion>4.0.0</modelVersion> | ||
| 19 | + <groupId>com.google.cloud.iot.examples</groupId> | ||
| 20 | + <artifactId>cloudiot-http-example</artifactId> | ||
| 21 | + <packaging>jar</packaging> | ||
| 22 | + <version>1.0</version> | ||
| 23 | + <name>cloudiot-http-example</name> | ||
| 24 | + <url>http://maven.apache.org</url> | ||
| 25 | + | ||
| 26 | + <properties> | ||
| 27 | + <maven.compiler.source>1.7</maven.compiler.source> | ||
| 28 | + <maven.compiler.target>1.7</maven.compiler.target> | ||
| 29 | + </properties> | ||
| 30 | + | ||
| 31 | + <!-- Parent defines config for testing & linting. --> | ||
| 32 | + <parent> | ||
| 33 | + <artifactId>doc-samples</artifactId> | ||
| 34 | + <groupId>com.google.cloud</groupId> | ||
| 35 | + <version>1.0.0</version> | ||
| 36 | + <relativePath>../../../</relativePath> | ||
| 37 | + </parent> | ||
| 38 | + | ||
| 39 | + <dependencies> | ||
| 40 | + <dependency> | ||
| 41 | + <groupId>io.jsonwebtoken</groupId> | ||
| 42 | + <artifactId>jjwt</artifactId> | ||
| 43 | + <version>0.7.0</version> | ||
| 44 | + </dependency> | ||
| 45 | + <dependency> | ||
| 46 | + <groupId>joda-time</groupId> | ||
| 47 | + <artifactId>joda-time</artifactId> | ||
| 48 | + <version>2.1</version> | ||
| 49 | + </dependency> | ||
| 50 | + <dependency> | ||
| 51 | + <groupId>commons-cli</groupId> | ||
| 52 | + <artifactId>commons-cli</artifactId> | ||
| 53 | + <version>1.3</version> | ||
| 54 | + </dependency> | ||
| 55 | + <dependency> | ||
| 56 | + <groupId>org.json</groupId> | ||
| 57 | + <artifactId>json</artifactId> | ||
| 58 | + <version>20090211</version> | ||
| 59 | + </dependency> | ||
| 60 | + </dependencies> | ||
| 61 | + | ||
| 62 | + </project> | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,158 @@ | |||
| 1 | + /** | ||
| 2 | + * Copyright 2017, Google, Inc. | ||
| 3 | + * | ||
| 4 | + * <p>Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file | ||
| 5 | + * except in compliance with the License. You may obtain a copy of the License at | ||
| 6 | + * | ||
| 7 | + * <p>http://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + * | ||
| 9 | + * <p>Unless required by applicable law or agreed to in writing, software distributed under the | ||
| 10 | + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either | ||
| 11 | + * express or implied. See the License for the specific language governing permissions and | ||
| 12 | + * limitations under the License. | ||
| 13 | + */ | ||
| 14 | + | ||
| 15 | + package com.google.cloud.iot.examples; | ||
| 16 | + | ||
| 17 | + import io.jsonwebtoken.JwtBuilder; | ||
| 18 | + import io.jsonwebtoken.Jwts; | ||
| 19 | + import io.jsonwebtoken.SignatureAlgorithm; | ||
| 20 | + import java.net.HttpURLConnection; | ||
| 21 | + import java.net.URL; | ||
| 22 | + import java.nio.file.Files; | ||
| 23 | + import java.nio.file.Paths; | ||
| 24 | + import java.security.KeyFactory; | ||
| 25 | + import java.security.spec.PKCS8EncodedKeySpec; | ||
| 26 | + import java.util.Base64; | ||
| 27 | + import org.joda.time.DateTime; | ||
| 28 | + import org.json.JSONObject; | ||
| 29 | + | ||
| 30 | + /** | ||
| 31 | + * Java sample of connecting to Google Cloud IoT Core vice via HTTP, using JWT. | ||
| 32 | + * | ||
| 33 | + * <p>This example connects to Google Cloud IoT Core via HTTP Bridge, using a JWT for device | ||
| 34 | + * authentication. After connecting, by default the device publishes 100 messages at a rate of one | ||
| 35 | + * per second, and then exits. You can change The behavior to set state instead of events by using | ||
| 36 | + * flag -message_type to 'state'. | ||
| 37 | + * | ||
| 38 | + * <p>To run this example, follow the instructions in the README located in the sample's parent | ||
| 39 | + * folder. | ||
| 40 | + */ | ||
| 41 | + public class HttpExample { | ||
| 42 | + /** Create a Cloud IoT Core JWT for the given project id, signed with the given private key. */ | ||
| 43 | + private static String createJwtRsa(String projectId, String privateKeyFile) throws Exception { | ||
| 44 | + DateTime now = new DateTime(); | ||
| 45 | + // Create a JWT to authenticate this device. The device will be disconnected after the token | ||
| 46 | + // expires, and will have to reconnect with a new token. The audience field should always be set | ||
| 47 | + // to the GCP project id. | ||
| 48 | + JwtBuilder jwtBuilder = | ||
| 49 | + Jwts.builder() | ||
| 50 | + .setIssuedAt(now.toDate()) | ||
| 51 | + .setExpiration(now.plusMinutes(20).toDate()) | ||
| 52 | + .setAudience(projectId); | ||
| 53 | + | ||
| 54 | + byte[] keyBytes = Files.readAllBytes(Paths.get(privateKeyFile)); | ||
| 55 | + PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes); | ||
| 56 | + KeyFactory kf = KeyFactory.getInstance("RSA"); | ||
| 57 | + | ||
| 58 | + return jwtBuilder.signWith(SignatureAlgorithm.RS256, kf.generatePrivate(spec)).compact(); | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + private static String createJwtEs(String projectId, String privateKeyFile) throws Exception { | ||
| 62 | + DateTime now = new DateTime(); | ||
| 63 | + // Create a JWT to authenticate this device. The device will be disconnected after the token | ||
| 64 | + // expires, and will have to reconnect with a new token. The audience field should always be set | ||
| 65 | + // to the GCP project id. | ||
| 66 | + JwtBuilder jwtBuilder = | ||
| 67 | + Jwts.builder() | ||
| 68 | + .setIssuedAt(now.toDate()) | ||
| 69 | + .setExpiration(now.plusMinutes(20).toDate()) | ||
| 70 | + .setAudience(projectId); | ||
| 71 | + | ||
| 72 | + byte[] keyBytes = Files.readAllBytes(Paths.get(privateKeyFile)); | ||
| 73 | + PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes); | ||
| 74 | + KeyFactory kf = KeyFactory.getInstance("ES256"); | ||
| 75 | + | ||
| 76 | + return jwtBuilder.signWith(SignatureAlgorithm.ES256, kf.generatePrivate(spec)).compact(); | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + public static void main(String[] args) throws Exception { | ||
| 80 | + HttpExampleOptions options = HttpExampleOptions.fromFlags(args); | ||
| 81 | + if (options == null) { | ||
| 82 | + // Could not parse the flags. | ||
| 83 | + System.exit(1); | ||
| 84 | + } | ||
| 85 | + | ||
| 86 | + // Build the resource path of the device that is going to be authenticated. | ||
| 87 | + String devicePath = | ||
| 88 | + String.format( | ||
| 89 | + "projects/%s/locations/%s/registries/%s/devices/%s", | ||
| 90 | + options.projectId, options.cloudRegion, options.registryId, options.deviceId); | ||
| 91 | + | ||
| 92 | + // This describes the operation that is going to be perform with the device. | ||
| 93 | + String urlSuffix = options.messageType.equals("event") ? "publishEvent" : "setState"; | ||
| 94 | + | ||
| 95 | + String urlPath = | ||
| 96 | + String.format( | ||
| 97 | + "%s/%s/%s:%s", options.httpBridgeAddress, options.apiVersion, devicePath, urlSuffix); | ||
| 98 | + URL url = new URL(urlPath); | ||
| 99 | + System.out.format("Using URL: '%s'\n", urlPath); | ||
| 100 | + | ||
| 101 | + // Create the corresponding JWT depending on the selected algorithm. | ||
| 102 | + String token; | ||
| 103 | + if (options.algorithm.equals("RS256")) { | ||
| 104 | + token = createJwtRsa(options.projectId, options.privateKeyFile); | ||
| 105 | + } else if (options.algorithm.equals("ES256")) { | ||
| 106 | + token = createJwtEs(options.projectId, options.privateKeyFile); | ||
| 107 | + } else { | ||
| 108 | + throw new IllegalArgumentException( | ||
| 109 | + "Invalid algorithm " + options.algorithm + ". Should be one of 'RS256' or 'ES256'."); | ||
| 110 | + } | ||
| 111 | + | ||
| 112 | + // Data sent through the wire has to be base64 encoded. | ||
| 113 | + Base64.Encoder encoder = Base64.getEncoder(); | ||
| 114 | + | ||
| 115 | + // Publish numMessages messages to the HTTP bridge. | ||
| 116 | + for (int i = 1; i <= options.numMessages; ++i) { | ||
| 117 | + String payload = String.format("%s/%s-payload-%d", options.registryId, options.deviceId, i); | ||
| 118 | + System.out.format( | ||
| 119 | + "Publishing %s message %d/%d: '%s'\n", | ||
| 120 | + options.messageType, i, options.numMessages, payload); | ||
| 121 | + String encPayload = encoder.encodeToString(payload.getBytes("UTF-8")); | ||
| 122 | + | ||
| 123 | + HttpURLConnection httpCon = (HttpURLConnection) url.openConnection(); | ||
| 124 | + httpCon.setDoOutput(true); | ||
| 125 | + httpCon.setRequestMethod("POST"); | ||
| 126 | + | ||
| 127 | + // Adding headers. | ||
| 128 | + httpCon.setRequestProperty("Authorization", String.format("Bearer %s", token)); | ||
| 129 | + httpCon.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); | ||
| 130 | + | ||
| 131 | + // Adding the post data. The structure of the data send depends on whether it is event or a | ||
| 132 | + // state message. | ||
| 133 | + JSONObject data = new JSONObject(); | ||
| 134 | + if (options.messageType.equals("event")) { | ||
| 135 | + data.put("binary_data", encPayload); | ||
| 136 | + } else { | ||
| 137 | + JSONObject state = new JSONObject(); | ||
| 138 | + state.put("binary_data", encPayload); | ||
| 139 | + data.put("state", state); | ||
| 140 | + } | ||
| 141 | + httpCon.getOutputStream().write(data.toString().getBytes("UTF-8")); | ||
| 142 | + httpCon.getOutputStream().close(); | ||
| 143 | + | ||
| 144 | + // This will perform the connection as well. | ||
| 145 | + System.out.println(httpCon.getResponseCode()); | ||
| 146 | + System.out.println(httpCon.getResponseMessage()); | ||
| 147 | + | ||
| 148 | + if (options.messageType.equals("event")) { | ||
| 149 | + // Frequently send event payloads (every second) | ||
| 150 | + Thread.sleep(1000); | ||
| 151 | + } else { | ||
| 152 | + // Update state with low frequency (once every 5 seconds) | ||
| 153 | + Thread.sleep(5000); | ||
| 154 | + } | ||
| 155 | + } | ||
| 156 | + System.out.println("Finished loop successfully. Goodbye!"); | ||
| 157 | + } | ||
| 158 | + } | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments