use of software.amazon.awssdk.iot.iotshadow.model.GetNamedShadowSubscriptionRequest in project aws-greengrass-nucleus by aws-greengrass.
the class ShadowDeploymentE2ETest method GIVEN_device_deployment_WHEN_shadow_update_messages_gets_delivered_out_of_order_THEN_shadow_updated_with_latest_deployment_status.
@Test
void GIVEN_device_deployment_WHEN_shadow_update_messages_gets_delivered_out_of_order_THEN_shadow_updated_with_latest_deployment_status() throws Exception {
CreateDeploymentRequest createDeploymentRequest = CreateDeploymentRequest.builder().targetArn(thingInfo.getThingArn()).components(Utils.immutableMap("CustomerApp", ComponentDeploymentSpecification.builder().componentVersion("1.0.0").build(), "SomeService", ComponentDeploymentSpecification.builder().componentVersion("1.0.0").build())).build();
draftAndCreateDeployment(createDeploymentRequest);
assertThat(kernel.getMain()::getState, eventuallyEval(is(State.FINISHED)));
IotShadowClient shadowClient = new IotShadowClient(new WrapperMqttClientConnection(kernel.getContext().get(MqttClient.class)));
UpdateNamedShadowSubscriptionRequest req = new UpdateNamedShadowSubscriptionRequest();
req.shadowName = DEPLOYMENT_SHADOW_NAME;
req.thingName = thingInfo.getThingName();
CountDownLatch reportSucceededCdl = new CountDownLatch(1);
CountDownLatch deviceSyncedStateToSucceededCdl = new CountDownLatch(1);
AtomicReference<HashMap<String, Object>> reportedSection = new AtomicReference<>();
AtomicReference<Integer> shadowVersionWhenDeviceFirstReportedSuccess = new AtomicReference<>();
AtomicReference<Integer> shadowVersionWhenDeviceReportedInProgress = new AtomicReference<>();
shadowClient.SubscribeToUpdateNamedShadowAccepted(req, QualityOfService.AT_LEAST_ONCE, (response) -> {
try {
logger.info("Got shadow update: {}", new ObjectMapper().writeValueAsString(response));
} catch (JsonProcessingException e) {
// ignore
}
if (response.state.reported == null) {
return;
}
String reportedStatus = (String) response.state.reported.get(STATUS_KEY);
if (JobStatus.IN_PROGRESS.toString().equals(reportedStatus)) {
reportedSection.set(response.state.reported);
shadowVersionWhenDeviceReportedInProgress.set(response.version);
} else if (JobStatus.SUCCEEDED.toString().equals(reportedStatus)) {
// state to SUCCESS second time the shadow version
if (reportSucceededCdl.getCount() == 0 && response.version > shadowVersionWhenDeviceFirstReportedSuccess.get()) {
deviceSyncedStateToSucceededCdl.countDown();
}
shadowVersionWhenDeviceFirstReportedSuccess.set(response.version);
reportSucceededCdl.countDown();
}
});
// waiting for the device to report success
assertTrue(reportSucceededCdl.await(60, TimeUnit.SECONDS));
// Updating the shadow with deployment status IN_PROGRESS to simulate out-of-order update of shadow
ShadowState shadowState = new ShadowState();
shadowState.reported = reportedSection.get();
UpdateNamedShadowRequest updateNamedShadowRequest = new UpdateNamedShadowRequest();
updateNamedShadowRequest.shadowName = DEPLOYMENT_SHADOW_NAME;
updateNamedShadowRequest.thingName = thingInfo.getThingName();
updateNamedShadowRequest.state = shadowState;
shadowClient.PublishUpdateNamedShadow(updateNamedShadowRequest, QualityOfService.AT_LEAST_ONCE).get(30, TimeUnit.SECONDS);
// verify that the device updates shadow state to SUCCEEDED
assertTrue(deviceSyncedStateToSucceededCdl.await(60, TimeUnit.SECONDS));
// Updating the shadow with a lower version number to trigger a message to /update/rejected event
shadowState = new ShadowState();
shadowState.reported = reportedSection.get();
updateNamedShadowRequest = new UpdateNamedShadowRequest();
updateNamedShadowRequest.shadowName = DEPLOYMENT_SHADOW_NAME;
updateNamedShadowRequest.thingName = thingInfo.getThingName();
updateNamedShadowRequest.state = shadowState;
updateNamedShadowRequest.version = shadowVersionWhenDeviceReportedInProgress.get();
shadowClient.PublishUpdateNamedShadow(updateNamedShadowRequest, QualityOfService.AT_LEAST_ONCE).get(30, TimeUnit.SECONDS);
CountDownLatch deviceRetrievedShadowCdl = new CountDownLatch(1);
GetNamedShadowSubscriptionRequest getNamedShadowSubscriptionRequest = new GetNamedShadowSubscriptionRequest();
getNamedShadowSubscriptionRequest.shadowName = DEPLOYMENT_SHADOW_NAME;
getNamedShadowSubscriptionRequest.thingName = thingInfo.getThingName();
shadowClient.SubscribeToGetNamedShadowAccepted(getNamedShadowSubscriptionRequest, QualityOfService.AT_MOST_ONCE, getShadowResponse -> {
deviceRetrievedShadowCdl.countDown();
}).get(30, TimeUnit.SECONDS);
// verify that the device retrieved the shadow when an update operation was rejected.
assertTrue(deviceRetrievedShadowCdl.await(60, TimeUnit.SECONDS));
}
use of software.amazon.awssdk.iot.iotshadow.model.GetNamedShadowSubscriptionRequest in project aws-iot-device-sdk-java-v2 by aws.
the class IotShadowClient method SubscribeToGetNamedShadowAccepted.
/**
* Subscribes to the accepted topic for the GetNamedShadow operation.
*
* Once subscribed, `handler` is invoked each time a message matching
* the `topic` is received. It is possible for such messages to arrive before
* the SUBACK is received.
*
* AWS documentation: https://docs.aws.amazon.com/iot/latest/developerguide/device-shadow-mqtt.html#get-accepted-pub-sub-topic
*
* @param request Subscription request configuration
* @param qos Maximum requested QoS that server may use when sending messages to the client.
* The server may grant a lower QoS in the SUBACK
* @param handler callback function to invoke with messages received on the subscription topic
* @param exceptionHandler callback function to invoke if an exception occurred deserializing a message
*
* @return a future containing the MQTT packet id used to perform the subscribe operation
*/
public CompletableFuture<Integer> SubscribeToGetNamedShadowAccepted(GetNamedShadowSubscriptionRequest request, QualityOfService qos, Consumer<GetShadowResponse> handler, Consumer<Exception> exceptionHandler) {
String topic = "$aws/things/{thingName}/shadow/name/{shadowName}/get/accepted";
if (request.thingName == null) {
CompletableFuture<Integer> result = new CompletableFuture<Integer>();
result.completeExceptionally(new MqttException("GetNamedShadowSubscriptionRequest must have a non-null thingName"));
return result;
}
topic = topic.replace("{thingName}", request.thingName);
if (request.shadowName == null) {
CompletableFuture<Integer> result = new CompletableFuture<Integer>();
result.completeExceptionally(new MqttException("GetNamedShadowSubscriptionRequest must have a non-null shadowName"));
return result;
}
topic = topic.replace("{shadowName}", request.shadowName);
Consumer<MqttMessage> messageHandler = (message) -> {
try {
String payload = new String(message.getPayload(), StandardCharsets.UTF_8);
GetShadowResponse response = gson.fromJson(payload, GetShadowResponse.class);
handler.accept(response);
} catch (Exception e) {
if (exceptionHandler != null) {
exceptionHandler.accept(e);
}
}
};
return connection.subscribe(topic, qos, messageHandler);
}
use of software.amazon.awssdk.iot.iotshadow.model.GetNamedShadowSubscriptionRequest in project aws-iot-device-sdk-java-v2 by aws.
the class IotShadowClient method SubscribeToGetNamedShadowRejected.
/**
* Subscribes to the rejected topic for the GetNamedShadow operation.
*
* Once subscribed, `handler` is invoked each time a message matching
* the `topic` is received. It is possible for such messages to arrive before
* the SUBACK is received.
*
* AWS documentation: https://docs.aws.amazon.com/iot/latest/developerguide/device-shadow-mqtt.html#get-rejected-pub-sub-topic
*
* @param request Subscription request configuration
* @param qos Maximum requested QoS that server may use when sending messages to the client.
* The server may grant a lower QoS in the SUBACK
* @param handler callback function to invoke with messages received on the subscription topic
* @param exceptionHandler callback function to invoke if an exception occurred deserializing a message
*
* @return a future containing the MQTT packet id used to perform the subscribe operation
*/
public CompletableFuture<Integer> SubscribeToGetNamedShadowRejected(GetNamedShadowSubscriptionRequest request, QualityOfService qos, Consumer<ErrorResponse> handler, Consumer<Exception> exceptionHandler) {
String topic = "$aws/things/{thingName}/shadow/name/{shadowName}/get/rejected";
if (request.thingName == null) {
CompletableFuture<Integer> result = new CompletableFuture<Integer>();
result.completeExceptionally(new MqttException("GetNamedShadowSubscriptionRequest must have a non-null thingName"));
return result;
}
topic = topic.replace("{thingName}", request.thingName);
if (request.shadowName == null) {
CompletableFuture<Integer> result = new CompletableFuture<Integer>();
result.completeExceptionally(new MqttException("GetNamedShadowSubscriptionRequest must have a non-null shadowName"));
return result;
}
topic = topic.replace("{shadowName}", request.shadowName);
Consumer<MqttMessage> messageHandler = (message) -> {
try {
String payload = new String(message.getPayload(), StandardCharsets.UTF_8);
ErrorResponse response = gson.fromJson(payload, ErrorResponse.class);
handler.accept(response);
} catch (Exception e) {
if (exceptionHandler != null) {
exceptionHandler.accept(e);
}
}
};
return connection.subscribe(topic, qos, messageHandler);
}
use of software.amazon.awssdk.iot.iotshadow.model.GetNamedShadowSubscriptionRequest in project aws-greengrass-nucleus by aws-greengrass.
the class ShadowDeploymentListener method subscribeToShadowTopics.
/*
Subscribe to "$aws/things/{thingName}/shadow/update/accepted" topic to get notified when shadow is updated
Subscribe to "$aws/things/{thingName}/shadow/update/rejected" topic to get notified when an update is rejected
Subscribe to "$aws/things/{thingName}/shadow/get/accepted" topic to retrieve shadow by publishing to get topic
*/
private void subscribeToShadowTopics() {
logger.atDebug().log(SUBSCRIBING_TO_SHADOW_TOPICS_MESSAGE);
while (true) {
try {
UpdateNamedShadowSubscriptionRequest updateNamedShadowSubscriptionRequest = new UpdateNamedShadowSubscriptionRequest();
updateNamedShadowSubscriptionRequest.shadowName = DEPLOYMENT_SHADOW_NAME;
updateNamedShadowSubscriptionRequest.thingName = thingName;
iotShadowClient.SubscribeToUpdateNamedShadowAccepted(updateNamedShadowSubscriptionRequest, QualityOfService.AT_LEAST_ONCE, updateShadowResponse -> shadowUpdated(updateShadowResponse.state.desired, updateShadowResponse.state.reported, updateShadowResponse.version), (e) -> logger.atError().log("Error processing updateShadowResponse", e)).get(TIMEOUT_FOR_SUBSCRIBING_TO_TOPICS_SECONDS, TimeUnit.SECONDS);
logger.debug("Subscribed to update named shadow accepted topic");
iotShadowClient.SubscribeToUpdateNamedShadowRejected(updateNamedShadowSubscriptionRequest, QualityOfService.AT_LEAST_ONCE, updateShadowRejected -> handleNamedShadowRejectedEvent(), (e) -> logger.atError().log("Error processing named shadow update rejected response", e)).get(TIMEOUT_FOR_SUBSCRIBING_TO_TOPICS_SECONDS, TimeUnit.SECONDS);
logger.debug("Subscribed to update named shadow rejected topic");
GetNamedShadowSubscriptionRequest getNamedShadowSubscriptionRequest = new GetNamedShadowSubscriptionRequest();
getNamedShadowSubscriptionRequest.shadowName = DEPLOYMENT_SHADOW_NAME;
getNamedShadowSubscriptionRequest.thingName = thingName;
iotShadowClient.SubscribeToGetNamedShadowAccepted(getNamedShadowSubscriptionRequest, QualityOfService.AT_LEAST_ONCE, getShadowResponse -> shadowUpdated(getShadowResponse.state.desired, getShadowResponse.state.reported, getShadowResponse.version), (e) -> logger.atError().log("Error processing getShadowResponse", e)).get(TIMEOUT_FOR_SUBSCRIBING_TO_TOPICS_SECONDS, TimeUnit.SECONDS);
logger.debug("Subscribed to get named shadow topic");
return;
} catch (ExecutionException e) {
Throwable cause = e.getCause();
if (cause instanceof MqttException || cause instanceof TimeoutException) {
logger.atWarn().setCause(cause).log("Caught exception while subscribing to shadow topics, " + "will retry shortly");
} else if (cause instanceof InterruptedException) {
logger.atWarn().log("Interrupted while subscribing to shadow topics");
return;
} else {
logger.atError().setCause(e).log("Caught exception while subscribing to shadow topics, will retry shortly");
}
} catch (TimeoutException e) {
logger.atWarn().setCause(e).log("Subscribe to shadow topics timed out, will retry shortly");
} catch (InterruptedException e) {
// Since this method can run as runnable cannot throw exception so handling exceptions here
logger.atWarn().log("Interrupted while subscribing to shadow topics");
return;
}
try {
// Wait for sometime and then try to subscribe again
Thread.sleep(WAIT_TIME_TO_SUBSCRIBE_AGAIN_IN_MS + JITTER.nextInt(10_000));
} catch (InterruptedException interruptedException) {
logger.atWarn().log("Interrupted while subscribing to device shadow topics");
return;
}
}
}
Aggregations