Skip to content

Commit

Permalink
Modified Java code to allow passing Null as valid input for desired a…
Browse files Browse the repository at this point in the history
…nd 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
  • Loading branch information
TwistedTwigleg authored Feb 17, 2022
1 parent 2e88250 commit 6b711b1
Show file tree
Hide file tree
Showing 30 changed files with 184 additions and 8 deletions.
54 changes: 47 additions & 7 deletions samples/Shadow/src/main/java/shadow/ShadowSample.java
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,23 @@ static void onShadowDeltaUpdated(ShadowDeltaUpdatedEvent response) {
}

static void onUpdateShadowAccepted(UpdateShadowResponse response) {
String value = response.state.reported.get(SHADOW_PROPERTY).toString();
System.out.println("Shadow updated, value is " + value);
if (response.state.reported != null) {
if (response.state.reported.containsKey(SHADOW_PROPERTY)) {
String value = response.state.reported.get(SHADOW_PROPERTY).toString();
System.out.println("Shadow updated, value is " + value);
}
else {
System.out.println("Shadow updated, value is Null");
}
}
else {
if (response.state.reportedIsNullable == true) {
System.out.println("Shadow updated, reported and desired is null");
}
else {
System.out.println("Shadow update, data cleared");
}
}
gotResponse.complete(null);
}

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

static CompletableFuture<Void> changeShadowValue(String value) {
if (localValue.equals(value)) {
System.out.println("Local value is already " + value);
CompletableFuture<Void> result = new CompletableFuture<>();
result.complete(null);
return result;
if (localValue != null) {
if (localValue.equals(value)) {
System.out.println("Local value is already " + value);
CompletableFuture<Void> result = new CompletableFuture<>();
result.complete(null);
return result;
}
}

System.out.println("Changed local value to " + value);
Expand All @@ -186,13 +203,36 @@ static CompletableFuture<Void> changeShadowValue(String value) {
UpdateShadowRequest request = new UpdateShadowRequest();
request.thingName = thingName;
request.state = new ShadowState();

request.state.reported = new HashMap<String, Object>() {{
put(SHADOW_PROPERTY, value);
}};
request.state.desired = new HashMap<String, Object>() {{
put(SHADOW_PROPERTY, value);
}};

if (value.compareToIgnoreCase("clear_shadow") == 0) {
request.state.desiredIsNullable = true;
request.state.reportedIsNullable = true;
request.state.desired = null;
request.state.reported = null;
}
else if (value.compareToIgnoreCase("null") == 0) {
// A bit of a hack - we have to set reportedNullIsValid OR desiredNullIsValid
// so the JSON formatter will allow null , otherwise null will always be
// be converted to "null"
// As long as we're passing a Hashmap that is NOT assigned to null, it will not
// clear the data - so we pass an empty HashMap to avoid clearing data we want to keep
request.state.desiredIsNullable = true;
request.state.reportedIsNullable = false;

// We will only clear desired, so we need to pass an empty HashMap for reported
request.state.reported = new HashMap<String, Object>() {{}};
request.state.desired = new HashMap<String, Object>() {{
put(SHADOW_PROPERTY, null);
}};
}

// Publish the request
return shadow.PublishUpdateShadow(request, QualityOfService.AT_LEAST_ONCE).thenRun(() -> {
System.out.println("Update request published");
Expand Down
2 changes: 1 addition & 1 deletion sdk/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
<dependency>
<groupId>software.amazon.awssdk.crt</groupId>
<artifactId>aws-crt</artifactId>
<version>0.15.9</version>
<version>0.15.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package software.amazon.awssdk.iot;

import software.amazon.awssdk.iot.iotshadow.model.ShadowState;

import java.io.IOException;
import java.util.HashMap;

import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;

public class ShadowStateFactory implements TypeAdapterFactory {

public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {

Class<T> rawType = (Class<T>)type.getRawType();
if (rawType != ShadowState.class) {
return null;
}

final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);

return new TypeAdapter<T>() {
public void write(JsonWriter out, T shadowValue) throws IOException {
// Are null values allowed?
ShadowState shadow = (ShadowState)shadowValue;
if (shadow.desiredIsNullable == true || shadow.reportedIsNullable == true) {
out.setSerializeNulls(true);
}
// If a property is null but null is not valid for it, then just send an empty HashMap
if (shadow.desired == null && shadow.desiredIsNullable == false) {
shadow.desired = new HashMap<String, Object>();
}
if (shadow.reported == null && shadow.reportedIsNullable == false) {
shadow.reported = new HashMap<String, Object>();
}
delegate.write(out, shadowValue);
if (shadow.desiredIsNullable == true || shadow.reportedIsNullable == true) {
out.setSerializeNulls(false);
}
}
public T read(JsonReader in) throws IOException {
T returnType = delegate.read(in);

// Set <Name>IsNullable if the field we get is null
ShadowState shadow = (ShadowState)returnType;
if (shadow.desired == null) {
shadow.desiredIsNullable = true;
}
if (shadow.reported == null) {
shadow.reportedIsNullable = true;
}
returnType = (T)shadow;

return returnType;
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
import software.amazon.awssdk.iot.Timestamp;
import software.amazon.awssdk.iot.EnumSerializer;

import software.amazon.awssdk.iot.ShadowStateFactory;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

Expand Down Expand Up @@ -77,6 +79,8 @@ private Gson getGson() {
}

private void addTypeAdapters(GsonBuilder gson) {
ShadowStateFactory shadowStateFactory = new ShadowStateFactory();
gson.registerTypeAdapterFactory(shadowStateFactory);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,19 @@ public class DeleteNamedShadowRequest {
*/
public String clientToken;


/**
* Name of the shadow to delete.
*
*/
public String shadowName;


/**
* AWS IoT thing to delete a named shadow from.
*
*/
public String thingName;


}
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ public class DeleteNamedShadowSubscriptionRequest {
*/
public String thingName;


/**
* Name of the shadow to subscribe to DeleteNamedShadow operations for.
*
*/
public String shadowName;


}
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ public class DeleteShadowRequest {
*/
public String thingName;


/**
* Optional. A client token used to correlate requests and responses. Enter an arbitrary value here and it is reflected in the response.
*
*/
public String clientToken;


}
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,19 @@ public class DeleteShadowResponse {
*/
public Integer version;


/**
* A client token used to correlate requests and responses.
*
*/
public String clientToken;


/**
* The time the response was generated by AWS IoT.
*
*/
public Timestamp timestamp;


}
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ public class DeleteShadowSubscriptionRequest {
*/
public String thingName;


}
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,26 @@ public class ErrorResponse {
*/
public Timestamp timestamp;


/**
* A text message that provides additional information.
*
*/
public String message;


/**
* Opaque request-response correlation data. Present only if a client token was used in the request.
*
*/
public String clientToken;


/**
* An HTTP response code that indicates the type of error.
*
*/
public Integer code;


}
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,19 @@ public class GetNamedShadowRequest {
*/
public String clientToken;


/**
* Name of the shadow to get.
*
*/
public String shadowName;


/**
* AWS IoT thing to get the named shadow for.
*
*/
public String thingName;


}
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ public class GetNamedShadowSubscriptionRequest {
*/
public String thingName;


/**
* Name of the shadow to subscribe to GetNamedShadow responses for.
*
*/
public String shadowName;


}
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ public class GetShadowRequest {
*/
public String thingName;


/**
* Optional. A client token used to correlate requests and responses. Enter an arbitrary value here and it is reflected in the response.
*
*/
public String clientToken;


}
Original file line number Diff line number Diff line change
Expand Up @@ -23,28 +23,33 @@ public class GetShadowResponse {
*/
public Integer version;


/**
* An opaque token used to correlate requests and responses.
*
*/
public String clientToken;


/**
* The (classic) shadow state of the AWS IoT thing.
*
*/
public ShadowStateWithDelta state;


/**
* Contains the timestamps for each attribute in the desired and reported sections of the state.
*
*/
public ShadowMetadata metadata;


/**
* The time the response was generated by AWS IoT.
*
*/
public Timestamp timestamp;


}
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ public class GetShadowSubscriptionRequest {
*/
public String thingName;


}
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ public class NamedShadowDeltaUpdatedSubscriptionRequest {
*/
public String thingName;


/**
* Name of the shadow to get ShadowDelta events for.
*
*/
public String shadowName;


}
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ public class NamedShadowUpdatedSubscriptionRequest {
*/
public String shadowName;


/**
* Name of the AWS IoT thing to get NamedShadowUpdated events for.
*
*/
public String thingName;


}
Loading

0 comments on commit 6b711b1

Please sign in to comment.