use of org.eclipse.hono.client.command.Command in project hono by eclipse.
the class LoraProtocolAdapter method handleTenantTimeout.
private void handleTenantTimeout(final Message<String> msg) {
final String tenantId = msg.body();
log.debug("check command subscriptions on timeout of tenant [{}]", tenantId);
final Span span = TracingHelper.buildSpan(tracer, null, "check command subscriptions on tenant timeout", getClass().getSimpleName()).withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT).start();
TracingHelper.setDeviceTags(span, tenantId, null);
// check if tenant still exists
getTenantConfiguration(tenantId, span.context()).recover(thr -> {
if (thr instanceof TenantDisabledOrNotRegisteredException) {
log.debug("tenant [{}] disabled or removed, removing corresponding command consumers", tenantId);
span.log("tenant disabled or removed, corresponding command consumers will be closed");
@SuppressWarnings("rawtypes") final List<Future> consumerCloseFutures = new LinkedList<>();
for (final var iter = commandSubscriptions.entrySet().iterator(); iter.hasNext(); ) {
final var entry = iter.next();
if (entry.getKey().getTenant().equals(tenantId)) {
final CommandConsumer commandConsumer = entry.getValue().one();
consumerCloseFutures.add(commandConsumer.close(span.context()));
iter.remove();
}
}
return CompositeFuture.join(consumerCloseFutures).mapEmpty();
} else {
return Future.failedFuture(thr);
}
}).onFailure(thr -> TracingHelper.logError(span, thr)).onComplete(ar -> span.finish());
}
use of org.eclipse.hono.client.command.Command in project hono by eclipse.
the class LoraProtocolAdapterTest method handleCommandForLNS.
/**
* Verifies that an uplink message triggers a command subscription.
*/
@SuppressWarnings("unchecked")
@Test
public void handleCommandForLNS() {
givenATelemetrySenderForAnyTenant();
final LoraProvider providerMock = getLoraProviderMock();
final HttpServerRequest request = mock(HttpServerRequest.class);
final HttpContext httpContext = newHttpContext();
when(httpContext.request()).thenReturn(request);
final CommandEndpoint commandEndpoint = new CommandEndpoint();
commandEndpoint.setHeaders(Map.of("my-header", "my-header-value"));
commandEndpoint.setUri("https://my-server.com/commands/{{deviceId}}/send");
setGatewayDeviceCommandEndpoint(commandEndpoint);
final CommandConsumer commandConsumer = mock(CommandConsumer.class);
when(commandConsumer.close(any())).thenReturn(Future.succeededFuture());
when(commandConsumerFactory.createCommandConsumer(any(), any(), any(), any(), any())).thenReturn(Future.succeededFuture(commandConsumer));
adapter.handleProviderRoute(httpContext, providerMock);
final ArgumentCaptor<Handler<CommandContext>> handlerArgumentCaptor = VertxMockSupport.argumentCaptorHandler();
verify(commandConsumerFactory).createCommandConsumer(eq(TEST_TENANT_ID), eq(TEST_GATEWAY_ID), handlerArgumentCaptor.capture(), isNull(), any());
final Handler<CommandContext> commandHandler = handlerArgumentCaptor.getValue();
final Command command = mock(Command.class);
when(command.getTenant()).thenReturn(TEST_TENANT_ID);
when(command.getDeviceId()).thenReturn(TEST_DEVICE_ID);
when(command.getGatewayId()).thenReturn(TEST_GATEWAY_ID);
when(command.getPayload()).thenReturn(Buffer.buffer("bumlux"));
when(command.isValid()).thenReturn(true);
final CommandContext commandContext = mock(CommandContext.class);
when(commandContext.getCommand()).thenReturn(command);
when(commandContext.getTracingSpan()).thenReturn(processMessageSpan);
final JsonObject json = new JsonObject().put("my-payload", "bumlux");
final LoraCommand loraCommand = new LoraCommand(json, "https://my-server.com/commands/deviceId/send");
when(providerMock.getCommand(any(), any(), any(), any())).thenReturn(loraCommand);
when(providerMock.getDefaultHeaders()).thenReturn(Map.of("my-provider-header", "my-provider-header-value"));
final HttpRequest<Buffer> httpClientRequest = mock(HttpRequest.class, withSettings().defaultAnswer(RETURNS_SELF));
final HttpResponse<Buffer> httpResponse = mock(HttpResponse.class);
when(httpResponse.statusCode()).thenReturn(HttpURLConnection.HTTP_NO_CONTENT);
when(httpClientRequest.sendJson(any(JsonObject.class))).thenReturn(Future.succeededFuture(httpResponse));
when(webClient.postAbs(anyString())).thenReturn(httpClientRequest);
commandHandler.handle(commandContext);
verify(webClient, times(1)).postAbs("https://my-server.com/commands/deviceId/send");
verify(httpClientRequest, times(1)).putHeader("my-header", "my-header-value");
verify(httpClientRequest, times(1)).putHeader("my-provider-header", "my-provider-header-value");
verify(httpClientRequest, times(1)).sendJson(json);
}
use of org.eclipse.hono.client.command.Command in project hono by eclipse.
the class CommandSubscriptionTest method testSubscriptionSucceedsForAuthenticatedDevice.
/**
* Verifies that an authenticated device can successfully subscribe for commands
* targeted at itself using all variants of topic names.
*
* @param endpointName The endpoint name used in the topic.
* @param reqPartName The request part name used in the topic.
* @param qos The requested QoS.
*/
@ParameterizedTest
@MethodSource("endpointAndReqNamesWithQoS")
public void testSubscriptionSucceedsForAuthenticatedDevice(final String endpointName, final String reqPartName, final MqttQoS qos) {
final Command command = mock(Command.class);
when(command.isTargetedAtGateway()).thenReturn(false);
when(command.getTenant()).thenReturn(device.getTenantId());
when(command.getGatewayOrDeviceId()).thenReturn(device.getDeviceId());
when(command.getRequestId()).thenReturn("requestId");
when(command.getName()).thenReturn("doSomething");
// WHEN subscribing to commands using explicit topic
MqttTopicSubscription mqttTopicSubscription = new MqttTopicSubscriptionImpl(String.format("%s/tenant/device/%s/#", endpointName, reqPartName), qos);
CommandSubscription subscription = CommandSubscription.fromTopic(mqttTopicSubscription, device);
assertThat(subscription).isNotNull();
assertThat(subscription.getTenant()).isEqualTo("tenant");
assertThat(subscription.getDeviceId()).isEqualTo("device");
assertThat(subscription.getEndpoint()).isEqualTo(endpointName);
assertThat(subscription.getRequestPart()).isEqualTo(reqPartName);
assertThat(subscription.getQos()).isEqualTo(qos);
// THEN the command topic does include both the tenant and device ID
assertThat(subscription.getCommandPublishTopic(command)).isEqualTo(String.format("%s/%s/%s/%s/requestId/doSomething", endpointName, device.getTenantId(), device.getDeviceId(), reqPartName));
// WHEN subscribing to commands including tenant only
mqttTopicSubscription = new MqttTopicSubscriptionImpl(String.format("%s/tenant//%s/#", endpointName, reqPartName), qos);
subscription = CommandSubscription.fromTopic(mqttTopicSubscription, device);
assertThat(subscription).isNotNull();
assertThat(subscription.getTenant()).isEqualTo("tenant");
assertThat(subscription.getDeviceId()).isEqualTo("device");
assertThat(subscription.getEndpoint()).isEqualTo(endpointName);
assertThat(subscription.getRequestPart()).isEqualTo(reqPartName);
assertThat(subscription.getQos()).isEqualTo(qos);
// THEN the command topic does include the tenant as well
assertThat(subscription.getCommandPublishTopic(command)).isEqualTo(String.format("%s/%s//%s/requestId/doSomething", endpointName, device.getTenantId(), reqPartName));
// WHEN subscribing to commands including device ID only
mqttTopicSubscription = new MqttTopicSubscriptionImpl(String.format("%s//device/%s/#", endpointName, reqPartName), qos);
subscription = CommandSubscription.fromTopic(mqttTopicSubscription, device);
assertThat(subscription).isNotNull();
assertThat(subscription.getTenant()).isEqualTo("tenant");
assertThat(subscription.getDeviceId()).isEqualTo("device");
assertThat(subscription.getEndpoint()).isEqualTo(endpointName);
assertThat(subscription.getRequestPart()).isEqualTo(reqPartName);
assertThat(subscription.getQos()).isEqualTo(qos);
// THEN the command topic does include the device ID as well
assertThat(subscription.getCommandPublishTopic(command)).isEqualTo(String.format("%s//%s/%s/requestId/doSomething", endpointName, device.getDeviceId(), reqPartName));
// WHEN subscribing to commands using implicit topic
mqttTopicSubscription = new MqttTopicSubscriptionImpl(String.format("%s///%s/#", endpointName, reqPartName), qos);
subscription = CommandSubscription.fromTopic(mqttTopicSubscription, device);
assertThat(subscription).isNotNull();
assertThat(subscription.getTenant()).isEqualTo("tenant");
assertThat(subscription.getDeviceId()).isEqualTo("device");
assertThat(subscription.getEndpoint()).isEqualTo(endpointName);
assertThat(subscription.getRequestPart()).isEqualTo(reqPartName);
assertThat(subscription.getQos()).isEqualTo(qos);
// THEN the command topic does not include tenant nor device ID
assertThat(subscription.getCommandPublishTopic(command)).isEqualTo(String.format("%s///%s/requestId/doSomething", endpointName, reqPartName));
// using a tenant other than the tenant that the device belongs to should fail
mqttTopicSubscription = new MqttTopicSubscriptionImpl(String.format("%s/otherTenant/device/%s/#", endpointName, reqPartName), qos);
assertThat(CommandSubscription.fromTopic(mqttTopicSubscription, device)).isNull();
}
use of org.eclipse.hono.client.command.Command in project hono by eclipse.
the class CommandSubscriptionTest method testSubscriptionSucceedsForAuthenticatedGateway.
/**
* Verifies that an authenticated gateway can successfully subscribe for commands
* targeted at one of devices that it is authorized to act on behalf of.
*
* @param endpointName The endpoint name used in the topic.
* @param reqPartName The request part name used in the topic.
* @param qos The requested QoS.
*/
@ParameterizedTest
@MethodSource("endpointAndReqNamesWithQoS")
public void testSubscriptionSucceedsForAuthenticatedGateway(final String endpointName, final String reqPartName, final MqttQoS qos) {
final String gatewayManagedDeviceId = "gatewayManagedDevice";
final Command command = mock(Command.class);
when(command.isTargetedAtGateway()).thenReturn(true);
when(command.getTenant()).thenReturn(gw.getTenantId());
when(command.getGatewayId()).thenReturn(gw.getDeviceId());
when(command.getGatewayOrDeviceId()).thenReturn(gw.getDeviceId());
when(command.getDeviceId()).thenReturn(gatewayManagedDeviceId);
when(command.getRequestId()).thenReturn("requestId");
when(command.getName()).thenReturn("doSomething");
// WHEN subscribing to commands for a specific device omitting tenant
MqttTopicSubscription mqttTopicSubscription = new MqttTopicSubscriptionImpl(String.format("%s//%s/%s/#", endpointName, gatewayManagedDeviceId, reqPartName), qos);
CommandSubscription subscription = CommandSubscription.fromTopic(mqttTopicSubscription, gw);
assertThat(subscription).isNotNull();
assertThat(subscription.getTenant()).isEqualTo(gw.getTenantId());
assertThat(subscription.getDeviceId()).isEqualTo(gatewayManagedDeviceId);
assertThat(subscription.getAuthenticatedDeviceId()).isEqualTo(gw.getDeviceId());
assertThat(subscription.getQos()).isEqualTo(qos);
assertThat(subscription.isGatewaySubscriptionForSpecificDevice()).isEqualTo(true);
// THEN the command topic does not include the tenant either
assertThat(subscription.getCommandPublishTopic(command)).isEqualTo(String.format("%s//%s/%s/requestId/doSomething", endpointName, gatewayManagedDeviceId, reqPartName));
// WHEN subscribing to commands for a specific device including the tenant
mqttTopicSubscription = new MqttTopicSubscriptionImpl(String.format("%s/%s/%s/%s/#", endpointName, device.getTenantId(), gatewayManagedDeviceId, reqPartName), qos);
subscription = CommandSubscription.fromTopic(mqttTopicSubscription, gw);
assertThat(subscription).isNotNull();
assertThat(subscription.getTenant()).isEqualTo(gw.getTenantId());
assertThat(subscription.getDeviceId()).isEqualTo(gatewayManagedDeviceId);
assertThat(subscription.getAuthenticatedDeviceId()).isEqualTo(gw.getDeviceId());
assertThat(subscription.getQos()).isEqualTo(qos);
assertThat(subscription.isGatewaySubscriptionForSpecificDevice()).isEqualTo(true);
// THEN the command topic does include the tenant as well
assertThat(subscription.getCommandPublishTopic(command)).isEqualTo(String.format("%s/%s/%s/%s/requestId/doSomething", endpointName, gw.getTenantId(), gatewayManagedDeviceId, reqPartName));
// WHEN subscribing to commands for all devices omitting tenant
mqttTopicSubscription = new MqttTopicSubscriptionImpl(String.format("%s//+/%s/#", endpointName, reqPartName), qos);
subscription = CommandSubscription.fromTopic(mqttTopicSubscription, gw);
assertThat(subscription).isNotNull();
assertThat(subscription.getTenant()).isEqualTo(gw.getTenantId());
assertThat(subscription.getDeviceId()).isEqualTo(gw.getDeviceId());
assertThat(subscription.getAuthenticatedDeviceId()).isEqualTo(gw.getDeviceId());
assertThat(subscription.getQos()).isEqualTo(qos);
assertThat(subscription.isGatewaySubscriptionForSpecificDevice()).isEqualTo(false);
// THEN the command topic does not include the tenant either
assertThat(subscription.getCommandPublishTopic(command)).isEqualTo(String.format("%s//%s/%s/requestId/doSomething", endpointName, gatewayManagedDeviceId, reqPartName));
// WHEN subscribing to commands for all devices including the tenant
mqttTopicSubscription = new MqttTopicSubscriptionImpl(String.format("%s/%s/+/%s/#", endpointName, device.getTenantId(), reqPartName), qos);
subscription = CommandSubscription.fromTopic(mqttTopicSubscription, gw);
assertThat(subscription).isNotNull();
assertThat(subscription.getTenant()).isEqualTo(gw.getTenantId());
assertThat(subscription.getDeviceId()).isEqualTo(gw.getDeviceId());
assertThat(subscription.getAuthenticatedDeviceId()).isEqualTo(gw.getDeviceId());
assertThat(subscription.getQos()).isEqualTo(qos);
assertThat(subscription.isGatewaySubscriptionForSpecificDevice()).isEqualTo(false);
// THEN the command topic does include the tenant as well
assertThat(subscription.getCommandPublishTopic(command)).isEqualTo(String.format("%s/%s/%s/%s/requestId/doSomething", endpointName, gw.getTenantId(), gatewayManagedDeviceId, reqPartName));
// using a tenant other than the tenant that the gateway belongs to should fail
mqttTopicSubscription = new MqttTopicSubscriptionImpl(String.format("%s/otherTenant/+/%s/#", endpointName, reqPartName), qos);
assertThat(CommandSubscription.fromTopic(mqttTopicSubscription, gw)).isNull();
}
use of org.eclipse.hono.client.command.Command in project hono by eclipse.
the class AbstractMappingAndDelegatingCommandHandler method mapAndDelegateIncomingCommand.
/**
* Delegates an incoming command to the protocol adapter instance that the target
* device is connected to.
* <p>
* Determines the target gateway (if applicable) and protocol adapter instance for an incoming command
* and delegates the command to the resulting protocol adapter instance.
*
* @param commandContext The context of the command to send.
* @param timer The timer indicating the amount of time used for processing the command message.
* @return A future indicating the outcome of the operation.
* @throws NullPointerException if any of the parameters are {@code null}.
*/
protected final Future<Void> mapAndDelegateIncomingCommand(final CommandContext commandContext, final Timer.Sample timer) {
Objects.requireNonNull(commandContext);
Objects.requireNonNull(timer);
final Command command = commandContext.getCommand();
// determine last used gateway device id
if (log.isTraceEnabled()) {
log.trace("determine command target gateway/adapter for [{}]", command);
}
final Future<TenantObject> tenantObjectFuture = tenantClient.get(command.getTenant(), commandContext.getTracingContext());
return tenantObjectFuture.compose(tenantObject -> {
TenantTraceSamplingHelper.applyTraceSamplingPriority(tenantObject, null, commandContext.getTracingSpan());
commandContext.put(CommandContext.KEY_TENANT_CONFIG, tenantObject);
// check whether the handler messaging type is equal to the messaging type of the tenant (if set)
final MessagingType tenantMessagingType = Optional.ofNullable(tenantObject.getProperty(TenantConstants.FIELD_EXT, JsonObject.class)).map(ext -> ext.getString(TenantConstants.FIELD_EXT_MESSAGING_TYPE)).map(MessagingType::valueOf).orElse(null);
if (tenantMessagingType != null && getMessagingType() != tenantMessagingType) {
log.info("command received via {} but tenant is configured to use {} [{}]", getMessagingType(), tenantMessagingType, commandContext.getCommand());
commandContext.getTracingSpan().log(String.format("command received via %s but tenant is configured to use %s", getMessagingType(), tenantMessagingType));
}
return commandTargetMapper.getTargetGatewayAndAdapterInstance(command.getTenant(), command.getDeviceId(), commandContext.getTracingContext());
}).recover(cause -> {
final Throwable error;
if (tenantObjectFuture.failed() && ServiceInvocationException.extractStatusCode(cause) == HttpURLConnection.HTTP_NOT_FOUND) {
error = new TenantDisabledOrNotRegisteredException(command.getTenant(), HttpURLConnection.HTTP_NOT_FOUND);
} else if (cause instanceof DeviceDisabledOrNotRegisteredException) {
error = cause;
} else if (ServiceInvocationException.extractStatusCode(cause) == HttpURLConnection.HTTP_NOT_FOUND) {
log.debug("no target adapter instance found for command with device id " + command.getDeviceId(), cause);
error = new NoConsumerException("no target adapter instance found");
} else {
log.debug("error getting target gateway and adapter instance for command with device id " + command.getDeviceId(), cause);
error = new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE, "error getting target gateway and adapter instance", cause);
}
if (error instanceof ClientErrorException) {
commandContext.reject(error);
} else {
commandContext.release(error);
}
reportCommandProcessingError(command, tenantObjectFuture.result(), error, timer);
return Future.failedFuture(cause);
}).compose(result -> {
final String targetAdapterInstanceId = result.getString(DeviceConnectionConstants.FIELD_ADAPTER_INSTANCE_ID);
final String targetDeviceId = result.getString(DeviceConnectionConstants.FIELD_PAYLOAD_DEVICE_ID);
final String targetGatewayId = targetDeviceId.equals(command.getDeviceId()) ? null : targetDeviceId;
if (Objects.isNull(targetGatewayId)) {
log.trace("determined target adapter instance [{}] for [{}] (command not mapped to gateway)", targetAdapterInstanceId, command);
} else {
command.setGatewayId(targetGatewayId);
log.trace("determined target gateway [{}] and adapter instance [{}] for [{}]", targetGatewayId, targetAdapterInstanceId, command);
commandContext.getTracingSpan().log("determined target gateway [" + targetGatewayId + "]");
}
return sendCommand(commandContext, targetAdapterInstanceId, tenantObjectFuture.result(), timer);
});
}
Aggregations