Skip to content

Commit 6b711b1

Browse files
Modified Java code to allow passing Null as valid input for desired and reported properties (#216)
* Modified Java code to allow passing Null as valid input for desired and reported properties * Renamed variableIsNullValid to variableIsNullable for better readability. Added setting IsNullable to parsing from payload * Removed legacy leftovers from a previous test attempt at passing Null
1 parent 2e88250 commit 6b711b1

30 files changed

+184
-8
lines changed

samples/Shadow/src/main/java/shadow/ShadowSample.java

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,23 @@ static void onShadowDeltaUpdated(ShadowDeltaUpdatedEvent response) {
159159
}
160160

161161
static void onUpdateShadowAccepted(UpdateShadowResponse response) {
162-
String value = response.state.reported.get(SHADOW_PROPERTY).toString();
163-
System.out.println("Shadow updated, value is " + value);
162+
if (response.state.reported != null) {
163+
if (response.state.reported.containsKey(SHADOW_PROPERTY)) {
164+
String value = response.state.reported.get(SHADOW_PROPERTY).toString();
165+
System.out.println("Shadow updated, value is " + value);
166+
}
167+
else {
168+
System.out.println("Shadow updated, value is Null");
169+
}
170+
}
171+
else {
172+
if (response.state.reportedIsNullable == true) {
173+
System.out.println("Shadow updated, reported and desired is null");
174+
}
175+
else {
176+
System.out.println("Shadow update, data cleared");
177+
}
178+
}
164179
gotResponse.complete(null);
165180
}
166181

@@ -170,11 +185,13 @@ static void onUpdateShadowRejected(ErrorResponse response) {
170185
}
171186

172187
static CompletableFuture<Void> changeShadowValue(String value) {
173-
if (localValue.equals(value)) {
174-
System.out.println("Local value is already " + value);
175-
CompletableFuture<Void> result = new CompletableFuture<>();
176-
result.complete(null);
177-
return result;
188+
if (localValue != null) {
189+
if (localValue.equals(value)) {
190+
System.out.println("Local value is already " + value);
191+
CompletableFuture<Void> result = new CompletableFuture<>();
192+
result.complete(null);
193+
return result;
194+
}
178195
}
179196

180197
System.out.println("Changed local value to " + value);
@@ -186,13 +203,36 @@ static CompletableFuture<Void> changeShadowValue(String value) {
186203
UpdateShadowRequest request = new UpdateShadowRequest();
187204
request.thingName = thingName;
188205
request.state = new ShadowState();
206+
189207
request.state.reported = new HashMap<String, Object>() {{
190208
put(SHADOW_PROPERTY, value);
191209
}};
192210
request.state.desired = new HashMap<String, Object>() {{
193211
put(SHADOW_PROPERTY, value);
194212
}};
195213

214+
if (value.compareToIgnoreCase("clear_shadow") == 0) {
215+
request.state.desiredIsNullable = true;
216+
request.state.reportedIsNullable = true;
217+
request.state.desired = null;
218+
request.state.reported = null;
219+
}
220+
else if (value.compareToIgnoreCase("null") == 0) {
221+
// A bit of a hack - we have to set reportedNullIsValid OR desiredNullIsValid
222+
// so the JSON formatter will allow null , otherwise null will always be
223+
// be converted to "null"
224+
// As long as we're passing a Hashmap that is NOT assigned to null, it will not
225+
// clear the data - so we pass an empty HashMap to avoid clearing data we want to keep
226+
request.state.desiredIsNullable = true;
227+
request.state.reportedIsNullable = false;
228+
229+
// We will only clear desired, so we need to pass an empty HashMap for reported
230+
request.state.reported = new HashMap<String, Object>() {{}};
231+
request.state.desired = new HashMap<String, Object>() {{
232+
put(SHADOW_PROPERTY, null);
233+
}};
234+
}
235+
196236
// Publish the request
197237
return shadow.PublishUpdateShadow(request, QualityOfService.AT_LEAST_ONCE).thenRun(() -> {
198238
System.out.println("Update request published");

sdk/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
<dependency>
4343
<groupId>software.amazon.awssdk.crt</groupId>
4444
<artifactId>aws-crt</artifactId>
45-
<version>0.15.9</version>
45+
<version>0.15.17</version>
4646
</dependency>
4747
<dependency>
4848
<groupId>org.slf4j</groupId>
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package software.amazon.awssdk.iot;
2+
3+
import software.amazon.awssdk.iot.iotshadow.model.ShadowState;
4+
5+
import java.io.IOException;
6+
import java.util.HashMap;
7+
8+
import com.google.gson.Gson;
9+
import com.google.gson.TypeAdapter;
10+
import com.google.gson.TypeAdapterFactory;
11+
import com.google.gson.reflect.TypeToken;
12+
import com.google.gson.stream.JsonReader;
13+
import com.google.gson.stream.JsonWriter;
14+
15+
public class ShadowStateFactory implements TypeAdapterFactory {
16+
17+
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
18+
19+
Class<T> rawType = (Class<T>)type.getRawType();
20+
if (rawType != ShadowState.class) {
21+
return null;
22+
}
23+
24+
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
25+
26+
return new TypeAdapter<T>() {
27+
public void write(JsonWriter out, T shadowValue) throws IOException {
28+
// Are null values allowed?
29+
ShadowState shadow = (ShadowState)shadowValue;
30+
if (shadow.desiredIsNullable == true || shadow.reportedIsNullable == true) {
31+
out.setSerializeNulls(true);
32+
}
33+
// If a property is null but null is not valid for it, then just send an empty HashMap
34+
if (shadow.desired == null && shadow.desiredIsNullable == false) {
35+
shadow.desired = new HashMap<String, Object>();
36+
}
37+
if (shadow.reported == null && shadow.reportedIsNullable == false) {
38+
shadow.reported = new HashMap<String, Object>();
39+
}
40+
delegate.write(out, shadowValue);
41+
if (shadow.desiredIsNullable == true || shadow.reportedIsNullable == true) {
42+
out.setSerializeNulls(false);
43+
}
44+
}
45+
public T read(JsonReader in) throws IOException {
46+
T returnType = delegate.read(in);
47+
48+
// Set <Name>IsNullable if the field we get is null
49+
ShadowState shadow = (ShadowState)returnType;
50+
if (shadow.desired == null) {
51+
shadow.desiredIsNullable = true;
52+
}
53+
if (shadow.reported == null) {
54+
shadow.reportedIsNullable = true;
55+
}
56+
returnType = (T)shadow;
57+
58+
return returnType;
59+
}
60+
};
61+
}
62+
}

sdk/src/main/java/software/amazon/awssdk/iot/iotshadow/IotShadowClient.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@
4545
import software.amazon.awssdk.iot.Timestamp;
4646
import software.amazon.awssdk.iot.EnumSerializer;
4747

48+
import software.amazon.awssdk.iot.ShadowStateFactory;
49+
4850
import com.google.gson.Gson;
4951
import com.google.gson.GsonBuilder;
5052

@@ -77,6 +79,8 @@ private Gson getGson() {
7779
}
7880

7981
private void addTypeAdapters(GsonBuilder gson) {
82+
ShadowStateFactory shadowStateFactory = new ShadowStateFactory();
83+
gson.registerTypeAdapterFactory(shadowStateFactory);
8084
}
8185

8286
/**

sdk/src/main/java/software/amazon/awssdk/iot/iotshadow/model/DeleteNamedShadowRequest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,19 @@ public class DeleteNamedShadowRequest {
2020
*/
2121
public String clientToken;
2222

23+
2324
/**
2425
* Name of the shadow to delete.
2526
*
2627
*/
2728
public String shadowName;
2829

30+
2931
/**
3032
* AWS IoT thing to delete a named shadow from.
3133
*
3234
*/
3335
public String thingName;
3436

37+
3538
}

sdk/src/main/java/software/amazon/awssdk/iot/iotshadow/model/DeleteNamedShadowSubscriptionRequest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@ public class DeleteNamedShadowSubscriptionRequest {
2020
*/
2121
public String thingName;
2222

23+
2324
/**
2425
* Name of the shadow to subscribe to DeleteNamedShadow operations for.
2526
*
2627
*/
2728
public String shadowName;
2829

30+
2931
}

sdk/src/main/java/software/amazon/awssdk/iot/iotshadow/model/DeleteShadowRequest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@ public class DeleteShadowRequest {
2020
*/
2121
public String thingName;
2222

23+
2324
/**
2425
* Optional. A client token used to correlate requests and responses. Enter an arbitrary value here and it is reflected in the response.
2526
*
2627
*/
2728
public String clientToken;
2829

30+
2931
}

sdk/src/main/java/software/amazon/awssdk/iot/iotshadow/model/DeleteShadowResponse.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,19 @@ public class DeleteShadowResponse {
2121
*/
2222
public Integer version;
2323

24+
2425
/**
2526
* A client token used to correlate requests and responses.
2627
*
2728
*/
2829
public String clientToken;
2930

31+
3032
/**
3133
* The time the response was generated by AWS IoT.
3234
*
3335
*/
3436
public Timestamp timestamp;
3537

38+
3639
}

sdk/src/main/java/software/amazon/awssdk/iot/iotshadow/model/DeleteShadowSubscriptionRequest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@ public class DeleteShadowSubscriptionRequest {
2020
*/
2121
public String thingName;
2222

23+
2324
}

sdk/src/main/java/software/amazon/awssdk/iot/iotshadow/model/ErrorResponse.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,22 +21,26 @@ public class ErrorResponse {
2121
*/
2222
public Timestamp timestamp;
2323

24+
2425
/**
2526
* A text message that provides additional information.
2627
*
2728
*/
2829
public String message;
2930

31+
3032
/**
3133
* Opaque request-response correlation data. Present only if a client token was used in the request.
3234
*
3335
*/
3436
public String clientToken;
3537

38+
3639
/**
3740
* An HTTP response code that indicates the type of error.
3841
*
3942
*/
4043
public Integer code;
4144

45+
4246
}

0 commit comments

Comments
 (0)