Search in sources :

Example 26 with CommandContext

use of org.eclipse.hono.client.command.CommandContext in project hono by eclipse.

the class AbstractHonoResource method createCommandConsumer.

/**
 * Creates a consumer for command messages to be sent to a device.
 *
 * @param ttdSecs The number of seconds the device waits for a command.
 * @param tenantObject The tenant configuration object.
 * @param deviceId The identifier of the device.
 * @param gatewayId The identifier of the gateway that is acting on behalf of the device or {@code null} otherwise.
 * @param context The device's currently executing CoAP request context.
 * @param responseReady A future to complete once one of the following conditions are met:
 *            <ul>
 *            <li>the request did not include a <em>hono-ttd</em> query-parameter or</li>
 *            <li>a command has been received and the response ready future has not yet been completed or</li>
 *            <li>the ttd has expired</li>
 *            </ul>
 * @param uploadMessageSpan The OpenTracing Span used for tracking the processing of the request.
 * @return A future indicating the outcome of the operation.
 *         <p>
 *         The future will be completed with the created message consumer or {@code null}, if the response can be
 *         sent back to the device without waiting for a command.
 *         <p>
 *         The future will be failed with a {@code ServiceInvocationException} if the message consumer could not be
 *         created.
 * @throws NullPointerException if any of the parameters other than TTD or gatewayId is {@code null}.
 */
protected final Future<CommandConsumer> createCommandConsumer(final Integer ttdSecs, final TenantObject tenantObject, final String deviceId, final String gatewayId, final CoapContext context, final Handler<AsyncResult<Void>> responseReady, final Span uploadMessageSpan) {
    Objects.requireNonNull(tenantObject);
    Objects.requireNonNull(deviceId);
    Objects.requireNonNull(context);
    Objects.requireNonNull(responseReady);
    Objects.requireNonNull(uploadMessageSpan);
    if (ttdSecs == null || ttdSecs <= 0) {
        // no need to wait for a command
        responseReady.handle(Future.succeededFuture());
        return Future.succeededFuture();
    }
    final AtomicBoolean requestProcessed = new AtomicBoolean(false);
    uploadMessageSpan.setTag(MessageHelper.APP_PROPERTY_DEVICE_TTD, ttdSecs);
    final Span waitForCommandSpan = TracingHelper.buildChildSpan(getTracer(), uploadMessageSpan.context(), "create consumer and wait for command", getAdapter().getTypeName()).withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT).withTag(TracingHelper.TAG_TENANT_ID, tenantObject.getTenantId()).withTag(TracingHelper.TAG_DEVICE_ID, deviceId).start();
    final Handler<CommandContext> commandHandler = commandContext -> {
        final Span processCommandSpan = TracingHelper.buildFollowsFromSpan(getTracer(), waitForCommandSpan.context(), "process received command").withTag(Tags.COMPONENT.getKey(), getAdapter().getTypeName()).withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT).withTag(TracingHelper.TAG_TENANT_ID, tenantObject.getTenantId()).withTag(TracingHelper.TAG_DEVICE_ID, deviceId).addReference(References.FOLLOWS_FROM, commandContext.getTracingContext()).start();
        Tags.COMPONENT.set(commandContext.getTracingSpan(), getAdapter().getTypeName());
        commandContext.logCommandToSpan(processCommandSpan);
        final Command command = commandContext.getCommand();
        final Sample commandSample = getAdapter().getMetrics().startTimer();
        if (isCommandValid(command, processCommandSpan)) {
            if (requestProcessed.compareAndSet(false, true)) {
                waitForCommandSpan.finish();
                getAdapter().checkMessageLimit(tenantObject, command.getPayloadSize(), processCommandSpan.context()).onComplete(result -> {
                    if (result.succeeded()) {
                        addMicrometerSample(commandContext, commandSample);
                        // put command context to routing context and notify
                        context.put(CommandContext.KEY_COMMAND_CONTEXT, commandContext);
                    } else {
                        commandContext.reject(result.cause());
                        TracingHelper.logError(processCommandSpan, "rejected command for device", result.cause());
                        getAdapter().getMetrics().reportCommand(command.isOneWay() ? Direction.ONE_WAY : Direction.REQUEST, tenantObject.getTenantId(), tenantObject, ProcessingOutcome.from(result.cause()), command.getPayloadSize(), commandSample);
                    }
                    cancelCommandReceptionTimer(context);
                    setTtdStatus(context, TtdStatus.COMMAND);
                    responseReady.handle(Future.succeededFuture());
                    processCommandSpan.finish();
                });
            } else {
                final String errorMsg = "waiting time for command has elapsed or another command has already been processed";
                LOG.debug("{} [tenantId: {}, deviceId: {}]", errorMsg, tenantObject.getTenantId(), deviceId);
                getAdapter().getMetrics().reportCommand(command.isOneWay() ? Direction.ONE_WAY : Direction.REQUEST, tenantObject.getTenantId(), tenantObject, ProcessingOutcome.UNDELIVERABLE, command.getPayloadSize(), commandSample);
                commandContext.release(new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE, errorMsg));
                TracingHelper.logError(processCommandSpan, errorMsg);
                processCommandSpan.finish();
            }
        } else {
            getAdapter().getMetrics().reportCommand(command.isOneWay() ? Direction.ONE_WAY : Direction.REQUEST, tenantObject.getTenantId(), tenantObject, ProcessingOutcome.UNPROCESSABLE, command.getPayloadSize(), commandSample);
            LOG.debug("command message is invalid: {}", command);
            commandContext.reject("malformed command message");
            TracingHelper.logError(processCommandSpan, "malformed command message");
            processCommandSpan.finish();
        }
    };
    final Future<CommandConsumer> commandConsumerFuture;
    if (gatewayId != null) {
        // gateway scenario
        commandConsumerFuture = getAdapter().getCommandConsumerFactory().createCommandConsumer(tenantObject.getTenantId(), deviceId, gatewayId, commandHandler, Duration.ofSeconds(ttdSecs), waitForCommandSpan.context());
    } else {
        commandConsumerFuture = getAdapter().getCommandConsumerFactory().createCommandConsumer(tenantObject.getTenantId(), deviceId, commandHandler, Duration.ofSeconds(ttdSecs), waitForCommandSpan.context());
    }
    return commandConsumerFuture.onFailure(thr -> {
        TracingHelper.logError(waitForCommandSpan, thr);
        waitForCommandSpan.finish();
    }).map(consumer -> {
        if (!requestProcessed.get()) {
            // if the request was not responded already, add a timer for triggering an empty response
            addCommandReceptionTimer(context, requestProcessed, responseReady, ttdSecs, waitForCommandSpan);
            context.startAcceptTimer(vertx, tenantObject, getAdapter().getConfig().getTimeoutToAck());
        }
        // for unregistering the command consumer (which is something the parent request span doesn't wait for)
        return new CommandConsumer() {

            @Override
            public Future<Void> close(final SpanContext ignored) {
                final Span closeConsumerSpan = TracingHelper.buildFollowsFromSpan(getTracer(), waitForCommandSpan.context(), "close consumer").withTag(Tags.COMPONENT.getKey(), getAdapter().getTypeName()).withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT).withTag(TracingHelper.TAG_TENANT_ID, tenantObject.getTenantId()).withTag(TracingHelper.TAG_DEVICE_ID, deviceId).start();
                return consumer.close(closeConsumerSpan.context()).onFailure(thr -> TracingHelper.logError(closeConsumerSpan, thr)).onComplete(ar -> closeConsumerSpan.finish());
            }
        };
    });
}
Also used : HttpURLConnection(java.net.HttpURLConnection) ResponseCode(org.eclipse.californium.core.coap.CoAP.ResponseCode) CoapExchange(org.eclipse.californium.core.server.resources.CoapExchange) Response(org.eclipse.californium.core.coap.Response) Command(org.eclipse.hono.client.command.Command) LoggerFactory(org.slf4j.LoggerFactory) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) ClientErrorException(org.eclipse.hono.client.ClientErrorException) Constants(org.eclipse.hono.util.Constants) Tags(io.opentracing.tag.Tags) ProcessingOutcome(org.eclipse.hono.service.metric.MetricsTags.ProcessingOutcome) CompositeFuture(io.vertx.core.CompositeFuture) EndpointType(org.eclipse.hono.service.metric.MetricsTags.EndpointType) TtdStatus(org.eclipse.hono.service.metric.MetricsTags.TtdStatus) References(io.opentracing.References) Duration(java.time.Duration) Map(java.util.Map) MediaTypeRegistry(org.eclipse.californium.core.coap.MediaTypeRegistry) ResourceIdentifier(org.eclipse.hono.util.ResourceIdentifier) TracingHelper(org.eclipse.hono.tracing.TracingHelper) AsyncResult(io.vertx.core.AsyncResult) CommandConstants(org.eclipse.hono.util.CommandConstants) Logger(org.slf4j.Logger) Tracer(io.opentracing.Tracer) Direction(org.eclipse.hono.service.metric.MetricsTags.Direction) Promise(io.vertx.core.Promise) CommandContext(org.eclipse.hono.client.command.CommandContext) MetricsTags(org.eclipse.hono.service.metric.MetricsTags) Vertx(io.vertx.core.Vertx) ServerErrorException(org.eclipse.hono.client.ServerErrorException) RegistrationAssertion(org.eclipse.hono.util.RegistrationAssertion) MessageHelper(org.eclipse.hono.util.MessageHelper) Sample(io.micrometer.core.instrument.Timer.Sample) Future(io.vertx.core.Future) Device(org.eclipse.hono.auth.Device) TenantObject(org.eclipse.hono.util.TenantObject) SpanContext(io.opentracing.SpanContext) Objects(java.util.Objects) List(java.util.List) Principal(java.security.Principal) Buffer(io.vertx.core.buffer.Buffer) CommandConsumer(org.eclipse.hono.client.command.CommandConsumer) Optional(java.util.Optional) Span(io.opentracing.Span) OptionSet(org.eclipse.californium.core.coap.OptionSet) Handler(io.vertx.core.Handler) SpanContext(io.opentracing.SpanContext) CommandContext(org.eclipse.hono.client.command.CommandContext) Sample(io.micrometer.core.instrument.Timer.Sample) Span(io.opentracing.Span) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) Command(org.eclipse.hono.client.command.Command) CommandConsumer(org.eclipse.hono.client.command.CommandConsumer) ServerErrorException(org.eclipse.hono.client.ServerErrorException)

Example 27 with CommandContext

use of org.eclipse.hono.client.command.CommandContext in project hono by eclipse.

the class VertxBasedAmqpProtocolAdapterTest method testOneWayCommandOutcome.

private void testOneWayCommandOutcome(final ProtonDelivery deviceDisposition, final Consumer<CommandContext> outcomeAssertion, final ProcessingOutcome expectedProcessingOutcome) {
    // GIVEN an AMQP adapter
    givenAnAdapter(properties);
    // to which a device is connected
    final ProtonSender deviceLink = mock(ProtonSender.class);
    when(deviceLink.getQoS()).thenReturn(ProtonQoS.AT_LEAST_ONCE);
    // that has subscribed to commands
    final TenantObject tenantObject = givenAConfiguredTenant(TEST_TENANT_ID, true);
    // WHEN an application sends a one-way command to the device
    final Buffer payload = Buffer.buffer("payload");
    final CommandContext context = givenAOneWayCommandContext(TEST_TENANT_ID, TEST_DEVICE, "commandToExecute", "text/plain", payload);
    adapter.onCommandReceived(tenantObject, deviceLink, context);
    // and the device settles it
    final ArgumentCaptor<Handler<ProtonDelivery>> deliveryUpdateHandler = VertxMockSupport.argumentCaptorHandler();
    verify(deviceLink).send(any(Message.class), deliveryUpdateHandler.capture());
    deliveryUpdateHandler.getValue().handle(deviceDisposition);
    // THEN the command is handled according to the device's disposition update
    outcomeAssertion.accept(context);
    // and the command has been reported according to the outcome
    verify(metrics).reportCommand(eq(Direction.ONE_WAY), eq(TEST_TENANT_ID), eq(tenantObject), eq(expectedProcessingOutcome), eq(payload.length()), any());
}
Also used : Buffer(io.vertx.core.buffer.Buffer) ProtonSender(io.vertx.proton.ProtonSender) TenantObject(org.eclipse.hono.util.TenantObject) CommandContext(org.eclipse.hono.client.command.CommandContext) Message(org.apache.qpid.proton.message.Message) Handler(io.vertx.core.Handler)

Example 28 with CommandContext

use of org.eclipse.hono.client.command.CommandContext in project hono by eclipse.

the class VertxBasedAmqpProtocolAdapterTest method testLinkForSendingCommandsCloseAfterTimeout.

/**
 * Verifies that the adapter closes the link for sending commands to a device when no
 * delivery update is received after a certain amount of time.
 */
@Test
public void testLinkForSendingCommandsCloseAfterTimeout() {
    // GIVEN an AMQP adapter
    givenAnAdapter(properties);
    // to which a device is connected
    final ProtonSender deviceLink = mock(ProtonSender.class);
    when(deviceLink.getQoS()).thenReturn(ProtonQoS.AT_LEAST_ONCE);
    // that has subscribed to commands
    final TenantObject tenantObject = givenAConfiguredTenant(TEST_TENANT_ID, true);
    // WHEN an application sends a one-way command to the device
    final Buffer payload = Buffer.buffer("payload");
    final CommandContext context = givenAOneWayCommandContext(TEST_TENANT_ID, TEST_DEVICE, "commandToExecute", "text/plain", payload);
    // AND no delivery update is received from the device after sometime
    doAnswer(invocation -> {
        final Handler<Long> task = invocation.getArgument(1);
        task.handle(1L);
        return 1L;
    }).when(vertx).setTimer(anyLong(), VertxMockSupport.anyHandler());
    adapter.onCommandReceived(tenantObject, deviceLink, context);
    // THEN the adapter releases the command
    verify(context).release(any(Throwable.class));
}
Also used : Buffer(io.vertx.core.buffer.Buffer) ProtonSender(io.vertx.proton.ProtonSender) TenantObject(org.eclipse.hono.util.TenantObject) CommandContext(org.eclipse.hono.client.command.CommandContext) ArgumentMatchers.anyLong(org.mockito.ArgumentMatchers.anyLong) Test(org.junit.jupiter.api.Test)

Example 29 with CommandContext

use of org.eclipse.hono.client.command.CommandContext in project hono by eclipse.

the class AbstractVertxBasedHttpProtocolAdapter method createCommandConsumer.

/**
 * Creates a consumer for command messages to be sent to a device.
 *
 * @param ttdSecs The number of seconds the device waits for a command.
 * @param tenantObject The tenant configuration object.
 * @param deviceId The identifier of the device.
 * @param gatewayId The identifier of the gateway that is acting on behalf of the device
 *                  or {@code null} otherwise.
 * @param ctx The device's currently executing HTTP request.
 * @param responseReady A future to complete once one of the following conditions are met:
 *              <ul>
 *              <li>the request did not include a <em>hono-ttd</em> parameter or</li>
 *              <li>a command has been received and the response ready future has not yet been
 *              completed or</li>
 *              <li>the ttd has expired</li>
 *              </ul>
 * @param uploadMessageSpan The OpenTracing Span used for tracking the processing
 *                       of the request.
 * @return A future indicating the outcome of the operation.
 *         <p>
 *         The future will be completed with the created message consumer or {@code null}, if
 *         the response can be sent back to the device without waiting for a command.
 *         <p>
 *         The future will be failed with a {@code ServiceInvocationException} if the
 *         message consumer could not be created.
 * @throws NullPointerException if any of the parameters other than TTD or gatewayId is {@code null}.
 */
protected final Future<CommandConsumer> createCommandConsumer(final Integer ttdSecs, final TenantObject tenantObject, final String deviceId, final String gatewayId, final RoutingContext ctx, final Handler<AsyncResult<Void>> responseReady, final Span uploadMessageSpan) {
    Objects.requireNonNull(tenantObject);
    Objects.requireNonNull(deviceId);
    Objects.requireNonNull(ctx);
    Objects.requireNonNull(responseReady);
    Objects.requireNonNull(uploadMessageSpan);
    if (ttdSecs == null || ttdSecs <= 0) {
        // no need to wait for a command
        responseReady.handle(Future.succeededFuture());
        return Future.succeededFuture();
    }
    final AtomicBoolean requestProcessed = new AtomicBoolean(false);
    uploadMessageSpan.setTag(MessageHelper.APP_PROPERTY_DEVICE_TTD, ttdSecs);
    final Span waitForCommandSpan = TracingHelper.buildChildSpan(tracer, uploadMessageSpan.context(), "create consumer and wait for command", getTypeName()).withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT).withTag(TracingHelper.TAG_TENANT_ID, tenantObject.getTenantId()).withTag(TracingHelper.TAG_DEVICE_ID, deviceId).start();
    final Handler<CommandContext> commandHandler = commandContext -> {
        final Span processCommandSpan = TracingHelper.buildFollowsFromSpan(tracer, waitForCommandSpan.context(), "process received command").withTag(Tags.COMPONENT.getKey(), getTypeName()).withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT).withTag(TracingHelper.TAG_TENANT_ID, tenantObject.getTenantId()).withTag(TracingHelper.TAG_DEVICE_ID, deviceId).addReference(References.FOLLOWS_FROM, commandContext.getTracingContext()).start();
        Tags.COMPONENT.set(commandContext.getTracingSpan(), getTypeName());
        commandContext.logCommandToSpan(processCommandSpan);
        final Command command = commandContext.getCommand();
        final Sample commandSample = getMetrics().startTimer();
        if (isCommandValid(command, processCommandSpan)) {
            if (requestProcessed.compareAndSet(false, true)) {
                waitForCommandSpan.finish();
                checkMessageLimit(tenantObject, command.getPayloadSize(), processCommandSpan.context()).onComplete(result -> {
                    if (result.succeeded()) {
                        addMicrometerSample(commandContext, commandSample);
                        // put command context to routing context and notify
                        ctx.put(CommandContext.KEY_COMMAND_CONTEXT, commandContext);
                    } else {
                        commandContext.reject(result.cause());
                        TracingHelper.logError(processCommandSpan, "rejected command for device", result.cause());
                        metrics.reportCommand(command.isOneWay() ? Direction.ONE_WAY : Direction.REQUEST, tenantObject.getTenantId(), tenantObject, ProcessingOutcome.from(result.cause()), command.getPayloadSize(), commandSample);
                    }
                    cancelCommandReceptionTimer(ctx);
                    setTtdStatus(ctx, TtdStatus.COMMAND);
                    responseReady.handle(Future.succeededFuture());
                    processCommandSpan.finish();
                });
            } else {
                final String errorMsg = "waiting time for command has elapsed or another command has already been processed";
                log.debug("{} [tenantId: {}, deviceId: {}]", errorMsg, tenantObject.getTenantId(), deviceId);
                getMetrics().reportCommand(command.isOneWay() ? Direction.ONE_WAY : Direction.REQUEST, tenantObject.getTenantId(), tenantObject, ProcessingOutcome.UNDELIVERABLE, command.getPayloadSize(), commandSample);
                commandContext.release(new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE, errorMsg));
                TracingHelper.logError(processCommandSpan, errorMsg);
                processCommandSpan.finish();
            }
        } else {
            getMetrics().reportCommand(command.isOneWay() ? Direction.ONE_WAY : Direction.REQUEST, tenantObject.getTenantId(), tenantObject, ProcessingOutcome.UNPROCESSABLE, command.getPayloadSize(), commandSample);
            log.debug("command message is invalid: {}", command);
            commandContext.reject("malformed command message");
            TracingHelper.logError(processCommandSpan, "malformed command message");
            processCommandSpan.finish();
        }
    };
    final Future<CommandConsumer> commandConsumerFuture;
    if (gatewayId != null) {
        // gateway scenario
        commandConsumerFuture = getCommandConsumerFactory().createCommandConsumer(tenantObject.getTenantId(), deviceId, gatewayId, commandHandler, Duration.ofSeconds(ttdSecs), waitForCommandSpan.context());
    } else {
        commandConsumerFuture = getCommandConsumerFactory().createCommandConsumer(tenantObject.getTenantId(), deviceId, commandHandler, Duration.ofSeconds(ttdSecs), waitForCommandSpan.context());
    }
    return commandConsumerFuture.onFailure(thr -> {
        TracingHelper.logError(waitForCommandSpan, thr);
        waitForCommandSpan.finish();
    }).map(consumer -> {
        if (!requestProcessed.get()) {
            // if the request was not responded already, add a timer for triggering an empty response
            addCommandReceptionTimer(ctx, requestProcessed, responseReady, ttdSecs, waitForCommandSpan);
        }
        // for unregistering the command consumer (which is something the parent request span doesn't wait for)
        return new CommandConsumer() {

            @Override
            public Future<Void> close(final SpanContext ignored) {
                final Span closeConsumerSpan = TracingHelper.buildFollowsFromSpan(tracer, waitForCommandSpan.context(), "close consumer").withTag(Tags.COMPONENT.getKey(), getTypeName()).withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT).withTag(TracingHelper.TAG_TENANT_ID, tenantObject.getTenantId()).withTag(TracingHelper.TAG_DEVICE_ID, deviceId).start();
                return consumer.close(closeConsumerSpan.context()).onFailure(thr -> TracingHelper.logError(closeConsumerSpan, thr)).onComplete(ar -> closeConsumerSpan.finish());
            }
        };
    });
}
Also used : HttpURLConnection(java.net.HttpURLConnection) HttpServer(io.vertx.core.http.HttpServer) Router(io.vertx.ext.web.Router) RoutingContext(io.vertx.ext.web.RoutingContext) BodyHandler(io.vertx.ext.web.handler.BodyHandler) Tags(io.opentracing.tag.Tags) ProcessingOutcome(org.eclipse.hono.service.metric.MetricsTags.ProcessingOutcome) EndpointType(org.eclipse.hono.service.metric.MetricsTags.EndpointType) TtdStatus(org.eclipse.hono.service.metric.MetricsTags.TtdStatus) DeviceCredentials(org.eclipse.hono.adapter.auth.device.DeviceCredentials) References(io.opentracing.References) Duration(java.time.Duration) Map(java.util.Map) WebSpanDecorator(org.eclipse.hono.service.http.WebSpanDecorator) TracingHelper(org.eclipse.hono.tracing.TracingHelper) CommandContext(org.eclipse.hono.client.command.CommandContext) MetricsTags(org.eclipse.hono.service.metric.MetricsTags) RegistrationAssertion(org.eclipse.hono.util.RegistrationAssertion) MessageHelper(org.eclipse.hono.util.MessageHelper) Future(io.vertx.core.Future) Device(org.eclipse.hono.auth.Device) Objects(java.util.Objects) List(java.util.List) ComponentMetaDataDecorator(org.eclipse.hono.service.http.ComponentMetaDataDecorator) TenantTraceSamplingHelper(org.eclipse.hono.tracing.TenantTraceSamplingHelper) Buffer(io.vertx.core.buffer.Buffer) CommandConsumer(org.eclipse.hono.client.command.CommandConsumer) HttpServerResponse(io.vertx.core.http.HttpServerResponse) DefaultFailureHandler(org.eclipse.hono.service.http.DefaultFailureHandler) Optional(java.util.Optional) Span(io.opentracing.Span) HttpContext(org.eclipse.hono.service.http.HttpContext) Command(org.eclipse.hono.client.command.Command) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) HashMap(java.util.HashMap) ClientErrorException(org.eclipse.hono.client.ClientErrorException) Constants(org.eclipse.hono.util.Constants) ArrayList(java.util.ArrayList) TracingHandler(org.eclipse.hono.service.http.TracingHandler) CompositeFuture(io.vertx.core.CompositeFuture) HttpUtils(org.eclipse.hono.service.http.HttpUtils) AsyncResult(io.vertx.core.AsyncResult) Strings(org.eclipse.hono.util.Strings) Route(io.vertx.ext.web.Route) CredentialsApiAuthProvider(org.eclipse.hono.adapter.auth.device.CredentialsApiAuthProvider) AbstractProtocolAdapterBase(org.eclipse.hono.adapter.AbstractProtocolAdapterBase) Direction(org.eclipse.hono.service.metric.MetricsTags.Direction) Promise(io.vertx.core.Promise) ServerErrorException(org.eclipse.hono.client.ServerErrorException) Sample(io.micrometer.core.instrument.Timer.Sample) CommandResponse(org.eclipse.hono.client.command.CommandResponse) TenantObject(org.eclipse.hono.util.TenantObject) SpanContext(io.opentracing.SpanContext) HttpServerOptions(io.vertx.core.http.HttpServerOptions) NoopSpan(io.opentracing.noop.NoopSpan) Handler(io.vertx.core.Handler) SpanContext(io.opentracing.SpanContext) CommandContext(org.eclipse.hono.client.command.CommandContext) Sample(io.micrometer.core.instrument.Timer.Sample) Span(io.opentracing.Span) NoopSpan(io.opentracing.noop.NoopSpan) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) Command(org.eclipse.hono.client.command.Command) CommandConsumer(org.eclipse.hono.client.command.CommandConsumer) ServerErrorException(org.eclipse.hono.client.ServerErrorException)

Example 30 with CommandContext

use of org.eclipse.hono.client.command.CommandContext in project hono by eclipse.

the class VertxBasedHttpProtocolAdapterTest method testPostTelemetryWithTtdSucceedsWithCommandInResponse.

/**
 * Verifies that the adapter includes a command for the device in the response to
 * a POST request which contains a time-til-disconnect.
 *
 * @param ctx The vert.x test context.
 */
@Test
public void testPostTelemetryWithTtdSucceedsWithCommandInResponse(final VertxTestContext ctx) {
    // GIVEN an device for which a command is pending
    givenATelemetrySenderForAnyTenant();
    mockSuccessfulAuthentication("DEFAULT_TENANT", "device_1");
    final CommandContext commandContext = givenARequestResponseCommandContext("DEFAULT_TENANT", "device_1", "doThis", "reply-to-id", null, null, MessagingType.amqp);
    final CommandConsumer commandConsumer = mock(CommandConsumer.class);
    when(commandConsumer.close(any())).thenReturn(Future.succeededFuture());
    when(commandConsumerFactory.createCommandConsumer(eq("DEFAULT_TENANT"), eq("device_1"), VertxMockSupport.anyHandler(), any(), any())).thenAnswer(invocation -> {
        final Handler<CommandContext> consumer = invocation.getArgument(2);
        consumer.handle(commandContext);
        return Future.succeededFuture(commandConsumer);
    });
    // WHEN the device posts a telemetry message including a TTD
    httpClient.post("/telemetry").addQueryParam("hono-ttd", "3").putHeader(HttpHeaders.CONTENT_TYPE.toString(), HttpUtils.CONTENT_TYPE_JSON).basicAuthentication("testuser@DEFAULT_TENANT", "password123").putHeader(HttpHeaders.ORIGIN.toString(), ORIGIN_HEADER_VALUE).expect(ResponsePredicate.SC_OK).expect(this::assertCorsHeaders).expect(response -> {
        if (!"doThis".equals(response.getHeader(Constants.HEADER_COMMAND))) {
            return ResponsePredicateResult.failure("response does not contain expected hono-command header");
        }
        if (response.getHeader(Constants.HEADER_COMMAND_REQUEST_ID) == null) {
            return ResponsePredicateResult.failure("response does not contain hono-cmd-req-id header");
        }
        return ResponsePredicateResult.success();
    }).sendJsonObject(new JsonObject(), ctx.succeeding(r -> {
        ctx.verify(() -> {
            verify(commandConsumerFactory).createCommandConsumer(eq("DEFAULT_TENANT"), eq("device_1"), VertxMockSupport.anyHandler(), any(), any());
            // and the command consumer has been closed again
            verify(commandConsumer).close(any());
            verify(commandContext).accept();
        });
        ctx.completeNow();
    }));
}
Also used : HttpURLConnection(java.net.HttpURLConnection) WebClientOptions(io.vertx.ext.web.client.WebClientOptions) BeforeEach(org.junit.jupiter.api.BeforeEach) ResponsePredicate(io.vertx.ext.web.client.predicate.ResponsePredicate) ArgumentMatchers.eq(org.mockito.ArgumentMatchers.eq) LoggerFactory(org.slf4j.LoggerFactory) MultiMap(io.vertx.core.MultiMap) Timeout(io.vertx.junit5.Timeout) AfterAll(org.junit.jupiter.api.AfterAll) MessagingType(org.eclipse.hono.util.MessagingType) TestInstance(org.junit.jupiter.api.TestInstance) ExtendWith(org.junit.jupiter.api.extension.ExtendWith) BeforeAll(org.junit.jupiter.api.BeforeAll) Mockito.doAnswer(org.mockito.Mockito.doAnswer) JsonObject(io.vertx.core.json.JsonObject) JsonHelper(org.eclipse.hono.util.JsonHelper) CommandContext(org.eclipse.hono.client.command.CommandContext) HttpHeaders(io.vertx.core.http.HttpHeaders) Lifecycle(org.junit.jupiter.api.TestInstance.Lifecycle) VertxExtension(io.vertx.junit5.VertxExtension) Future(io.vertx.core.Future) TestInfo(org.junit.jupiter.api.TestInfo) Test(org.junit.jupiter.api.Test) User(io.vertx.ext.auth.User) CommandConsumer(org.eclipse.hono.client.command.CommandConsumer) VertxMockSupport(org.eclipse.hono.test.VertxMockSupport) UsernamePasswordCredentials(org.eclipse.hono.adapter.auth.device.UsernamePasswordCredentials) HttpAdapterMetrics(org.eclipse.hono.adapter.http.HttpAdapterMetrics) QoS(org.eclipse.hono.util.QoS) Mockito.mock(org.mockito.Mockito.mock) ArgumentMatchers.any(org.mockito.ArgumentMatchers.any) VertxTestContext(io.vertx.junit5.VertxTestContext) HttpResponse(io.vertx.ext.web.client.HttpResponse) ProtonDelivery(io.vertx.proton.ProtonDelivery) WebClient(io.vertx.ext.web.client.WebClient) ResponsePredicateResult(io.vertx.ext.web.client.predicate.ResponsePredicateResult) ClientErrorException(org.eclipse.hono.client.ClientErrorException) Constants(org.eclipse.hono.util.Constants) DeviceUser(org.eclipse.hono.service.auth.DeviceUser) ProtocolAdapterTestSupport(org.eclipse.hono.adapter.test.ProtocolAdapterTestSupport) Timer(io.micrometer.core.instrument.Timer) HttpUtils(org.eclipse.hono.service.http.HttpUtils) AsyncResult(io.vertx.core.AsyncResult) CommandConstants(org.eclipse.hono.util.CommandConstants) Logger(org.slf4j.Logger) Promise(io.vertx.core.Promise) Vertx(io.vertx.core.Vertx) ServerErrorException(org.eclipse.hono.client.ServerErrorException) Mockito.when(org.mockito.Mockito.when) CredentialsConstants(org.eclipse.hono.util.CredentialsConstants) Mockito.verify(org.mockito.Mockito.verify) TimeUnit(java.util.concurrent.TimeUnit) DeviceCredentialsAuthProvider(org.eclipse.hono.adapter.auth.device.DeviceCredentialsAuthProvider) HttpProtocolAdapterProperties(org.eclipse.hono.adapter.http.HttpProtocolAdapterProperties) Handler(io.vertx.core.Handler) ArgumentMatchers.anyString(org.mockito.ArgumentMatchers.anyString) CommandContext(org.eclipse.hono.client.command.CommandContext) CommandConsumer(org.eclipse.hono.client.command.CommandConsumer) JsonObject(io.vertx.core.json.JsonObject) Test(org.junit.jupiter.api.Test)

Aggregations

CommandContext (org.eclipse.hono.client.command.CommandContext)34 Buffer (io.vertx.core.buffer.Buffer)22 Future (io.vertx.core.Future)19 HttpURLConnection (java.net.HttpURLConnection)18 TenantObject (org.eclipse.hono.util.TenantObject)17 Test (org.junit.jupiter.api.Test)17 Span (io.opentracing.Span)15 Handler (io.vertx.core.Handler)15 ServerErrorException (org.eclipse.hono.client.ServerErrorException)15 Promise (io.vertx.core.Promise)14 Optional (java.util.Optional)14 ClientErrorException (org.eclipse.hono.client.ClientErrorException)14 Command (org.eclipse.hono.client.command.Command)14 List (java.util.List)13 Objects (java.util.Objects)13 SpanContext (io.opentracing.SpanContext)12 ArgumentMatchers.anyString (org.mockito.ArgumentMatchers.anyString)12 TracingHelper (org.eclipse.hono.tracing.TracingHelper)11 Tracer (io.opentracing.Tracer)10 CompositeFuture (io.vertx.core.CompositeFuture)10