Search in sources :

Example 26 with Command

use of org.eclipse.hono.client.command.Command 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 Command

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

the class VertxBasedAmqpProtocolAdapter method handleRemoteSenderOpenForCommands.

/**
 * This method is invoked when a device wants to open a link for receiving commands.
 * <p>
 * The link will be closed immediately if
 * <ul>
 *  <li>the device does not specify a source address for its receive link or</li>
 *  <li>the source address cannot be parsed or does not point to the command endpoint or</li>
 *  <li>the AMQP adapter is disabled for the tenant that the device belongs to.</li>
 * </ul>
 *
 * @param connection The AMQP connection to the device.
 * @param sender The link to use for sending commands to the device.
 */
protected void handleRemoteSenderOpenForCommands(final ProtonConnection connection, final ProtonSender sender) {
    final Device authenticatedDevice = getAuthenticatedDevice(connection);
    final OptionalInt traceSamplingPriority = getTraceSamplingPriority(connection);
    final Span span = newSpan("attach device command receiver link", authenticatedDevice, traceSamplingPriority);
    getResourceIdentifier(sender.getRemoteSource()).compose(address -> validateAddress(address, authenticatedDevice)).compose(validAddress -> {
        // validAddress ALWAYS contains the tenant and device ID
        if (CommandConstants.isCommandEndpoint(validAddress.getEndpoint())) {
            return openCommandSenderLink(connection, sender, validAddress, authenticatedDevice, span, traceSamplingPriority);
        } else {
            return Future.failedFuture(new ClientErrorException(HttpURLConnection.HTTP_NOT_FOUND, "no such node"));
        }
    }).map(consumer -> {
        span.log("link established");
        return consumer;
    }).recover(t -> {
        if (t instanceof ServiceInvocationException) {
            closeLinkWithError(sender, t, span);
        } else {
            closeLinkWithError(sender, new ClientErrorException(HttpURLConnection.HTTP_BAD_REQUEST, "Invalid source address"), span);
        }
        return Future.failedFuture(t);
    }).onComplete(s -> {
        span.finish();
    });
}
Also used : HttpURLConnection(java.net.HttpURLConnection) ProtonConnection(io.vertx.proton.ProtonConnection) ProtonReceiver(io.vertx.proton.ProtonReceiver) LifecycleChange(org.eclipse.hono.notification.deviceregistry.LifecycleChange) DeviceChangeNotification(org.eclipse.hono.notification.deviceregistry.DeviceChangeNotification) Tags(io.opentracing.tag.Tags) ProtonServer(io.vertx.proton.ProtonServer) HonoProtonHelper(org.eclipse.hono.util.HonoProtonHelper) ProcessingOutcome(org.eclipse.hono.service.metric.MetricsTags.ProcessingOutcome) EndpointType(org.eclipse.hono.service.metric.MetricsTags.EndpointType) Modified(org.apache.qpid.proton.amqp.messaging.Modified) DeviceCredentials(org.eclipse.hono.adapter.auth.device.DeviceCredentials) Map(java.util.Map) DeliveryState(org.apache.qpid.proton.amqp.transport.DeliveryState) AuthorizationException(org.eclipse.hono.adapter.AuthorizationException) ResourceIdentifier(org.eclipse.hono.util.ResourceIdentifier) Fields(io.opentracing.log.Fields) AmqpError(org.apache.qpid.proton.amqp.transport.AmqpError) TracingHelper(org.eclipse.hono.tracing.TracingHelper) ProtonSaslAuthenticatorFactory(io.vertx.proton.sasl.ProtonSaslAuthenticatorFactory) AllDevicesOfTenantDeletedNotification(org.eclipse.hono.notification.deviceregistry.AllDevicesOfTenantDeletedNotification) TenantServiceBasedX509Authentication(org.eclipse.hono.adapter.auth.device.TenantServiceBasedX509Authentication) Predicate(java.util.function.Predicate) Collection(java.util.Collection) CommandContext(org.eclipse.hono.client.command.CommandContext) RegistrationAssertion(org.eclipse.hono.util.RegistrationAssertion) ProtonQoS(io.vertx.proton.ProtonQoS) MessageHelper(org.eclipse.hono.util.MessageHelper) Collectors(java.util.stream.Collectors) Future(io.vertx.core.Future) Device(org.eclipse.hono.auth.Device) Objects(java.util.Objects) ErrorCondition(org.apache.qpid.proton.amqp.transport.ErrorCondition) List(java.util.List) QoS(org.eclipse.hono.service.metric.MetricsTags.QoS) TenantTraceSamplingHelper(org.eclipse.hono.tracing.TenantTraceSamplingHelper) CommandConsumer(org.eclipse.hono.client.command.CommandConsumer) Optional(java.util.Optional) Span(io.opentracing.Span) ProtonSender(io.vertx.proton.ProtonSender) NotificationEventBusSupport(org.eclipse.hono.notification.NotificationEventBusSupport) ProtonLink(io.vertx.proton.ProtonLink) Accepted(org.apache.qpid.proton.amqp.messaging.Accepted) ProtonServerOptions(io.vertx.proton.ProtonServerOptions) Rejected(org.apache.qpid.proton.amqp.messaging.Rejected) ConnectionLimitManager(org.eclipse.hono.adapter.limiting.ConnectionLimitManager) Command(org.eclipse.hono.client.command.Command) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) HashMap(java.util.HashMap) ClientErrorException(org.eclipse.hono.client.ClientErrorException) AdapterDisabledException(org.eclipse.hono.adapter.AdapterDisabledException) ServiceInvocationException(org.eclipse.hono.client.ServiceInvocationException) OptionalInt(java.util.OptionalInt) AtomicReference(java.util.concurrent.atomic.AtomicReference) Function(java.util.function.Function) Commands(org.eclipse.hono.client.command.Commands) Constants(org.eclipse.hono.util.Constants) CompositeFuture(io.vertx.core.CompositeFuture) ProtonSession(io.vertx.proton.ProtonSession) Symbol(org.apache.qpid.proton.amqp.Symbol) AdapterConnectionsExceededException(org.eclipse.hono.adapter.AdapterConnectionsExceededException) Target(org.apache.qpid.proton.amqp.transport.Target) UnsignedLong(org.apache.qpid.proton.amqp.UnsignedLong) Message(org.apache.qpid.proton.message.Message) HttpUtils(org.eclipse.hono.service.http.HttpUtils) AsyncResult(io.vertx.core.AsyncResult) CommandConstants(org.eclipse.hono.util.CommandConstants) TenantChangeNotification(org.eclipse.hono.notification.deviceregistry.TenantChangeNotification) Strings(org.eclipse.hono.util.Strings) UsernamePasswordAuthProvider(org.eclipse.hono.adapter.auth.device.UsernamePasswordAuthProvider) 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) ProtonHelper(io.vertx.proton.ProtonHelper) Sample(io.micrometer.core.instrument.Timer.Sample) Released(org.apache.qpid.proton.amqp.messaging.Released) CommandResponse(org.eclipse.hono.client.command.CommandResponse) TenantObject(org.eclipse.hono.util.TenantObject) SpanContext(io.opentracing.SpanContext) Source(org.apache.qpid.proton.amqp.transport.Source) ConnectionAttemptOutcome(org.eclipse.hono.service.metric.MetricsTags.ConnectionAttemptOutcome) MemoryBasedConnectionLimitStrategy(org.eclipse.hono.adapter.limiting.MemoryBasedConnectionLimitStrategy) X509AuthProvider(org.eclipse.hono.adapter.auth.device.X509AuthProvider) Handler(io.vertx.core.Handler) Collections(java.util.Collections) DefaultConnectionLimitManager(org.eclipse.hono.adapter.limiting.DefaultConnectionLimitManager) Device(org.eclipse.hono.auth.Device) ClientErrorException(org.eclipse.hono.client.ClientErrorException) OptionalInt(java.util.OptionalInt) ServiceInvocationException(org.eclipse.hono.client.ServiceInvocationException) Span(io.opentracing.Span)

Example 28 with Command

use of org.eclipse.hono.client.command.Command 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 29 with Command

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

the class AbstractVertxBasedHttpProtocolAdapter method setNonEmptyResponsePayload.

/**
 * Response to a request with a non-empty command response.
 * <p>
 * The default implementation sets the command headers and the status to {@link HttpURLConnection#HTTP_OK}.
 *
 * @param response The response to update.
 * @param commandContext The command context, will not be {@code null}.
 * @param currentSpan The current tracing span.
 */
protected void setNonEmptyResponsePayload(final HttpServerResponse response, final CommandContext commandContext, final Span currentSpan) {
    final Command command = commandContext.getCommand();
    response.putHeader(Constants.HEADER_COMMAND, command.getName());
    currentSpan.setTag(Constants.HEADER_COMMAND, command.getName());
    log.debug("adding command [name: {}, request-id: {}] to response for device [tenant-id: {}, device-id: {}]", command.getName(), command.getRequestId(), command.getTenant(), command.getGatewayOrDeviceId());
    if (!command.isOneWay()) {
        response.putHeader(Constants.HEADER_COMMAND_REQUEST_ID, command.getRequestId());
        currentSpan.setTag(Constants.HEADER_COMMAND_REQUEST_ID, command.getRequestId());
    }
    if (command.isTargetedAtGateway()) {
        response.putHeader(Constants.HEADER_COMMAND_TARGET_DEVICE, command.getDeviceId());
        currentSpan.setTag(Constants.HEADER_COMMAND_TARGET_DEVICE, command.getDeviceId());
    }
    response.setStatusCode(HttpURLConnection.HTTP_OK);
    HttpUtils.setResponseBody(response, command.getPayload(), command.getContentType());
}
Also used : Command(org.eclipse.hono.client.command.Command)

Example 30 with Command

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

the class LoraProtocolAdapter method sendCommandToGateway.

private Future<Void> sendCommandToGateway(final CommandContext commandContext, final LoraProvider loraProvider, final CommandEndpoint commandEndpoint) {
    if (commandEndpoint == null) {
        return Future.failedFuture("gateway has no command endpoint defined");
    } else if (!commandEndpoint.isUriValid()) {
        return Future.failedFuture(String.format("gateway has command endpoint with invalid uri [%s]", commandEndpoint.getUri()));
    }
    final Command command = commandContext.getCommand();
    final Promise<Void> sendPromise = Promise.promise();
    final Buffer payload = Optional.ofNullable(command.getPayload()).orElseGet(Buffer::buffer);
    final String subject = command.getName();
    final LoraCommand loraCommand = loraProvider.getCommand(commandEndpoint, command.getDeviceId(), payload, subject);
    commandContext.getTracingSpan().log(String.format("sending loraCommand to LNS [%s]", loraCommand.getUri()));
    LOG.debug("sending loraCommand to LNS [{}]", loraCommand.getUri());
    if (LOG.isTraceEnabled()) {
        LOG.trace("command payload:{}{}", System.lineSeparator(), loraCommand.getPayload().encodePrettily());
    }
    final HttpRequest<Buffer> request = webClient.postAbs(loraCommand.getUri());
    commandEndpoint.getHeaders().forEach(request::putHeader);
    loraProvider.getDefaultHeaders().forEach(request::putHeader);
    request.sendJson(loraCommand.getPayload()).onFailure(sendPromise::tryFail).onSuccess(httpClientResponse -> {
        Tags.HTTP_STATUS.set(commandContext.getTracingSpan(), httpClientResponse.statusCode());
        if (StatusCodeMapper.isSuccessful(httpClientResponse.statusCode())) {
            sendPromise.tryComplete();
        } else {
            sendPromise.tryFail(httpClientResponse.statusMessage());
        }
    });
    return sendPromise.future();
}
Also used : Buffer(io.vertx.core.buffer.Buffer) Command(org.eclipse.hono.client.command.Command)

Aggregations

Command (org.eclipse.hono.client.command.Command)34 Span (io.opentracing.Span)23 Future (io.vertx.core.Future)23 HttpURLConnection (java.net.HttpURLConnection)22 ServerErrorException (org.eclipse.hono.client.ServerErrorException)22 CommandContext (org.eclipse.hono.client.command.CommandContext)22 Map (java.util.Map)19 Objects (java.util.Objects)19 Optional (java.util.Optional)19 Device (org.eclipse.hono.auth.Device)19 Constants (org.eclipse.hono.util.Constants)19 Buffer (io.vertx.core.buffer.Buffer)18 TracingHelper (org.eclipse.hono.tracing.TracingHelper)18 SpanContext (io.opentracing.SpanContext)17 Tags (io.opentracing.tag.Tags)17 Handler (io.vertx.core.Handler)17 ClientErrorException (org.eclipse.hono.client.ClientErrorException)17 MessageHelper (org.eclipse.hono.util.MessageHelper)17 TenantObject (org.eclipse.hono.util.TenantObject)17 List (java.util.List)16