Skip to content

Commit a1a3ded

Browse files
gguussjabubake
authored andcommitted
Final additions in private beta (IoT) (#861)
* Final additions in private beta
1 parent d553cd1 commit a1a3ded

File tree

14 files changed

+735
-134
lines changed

14 files changed

+735
-134
lines changed

iot/api-client/http_example/README.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff 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+
```

iot/api-client/http_example/pom.xml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff 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>
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff 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+
}

0 commit comments

Comments
 (0)