Search in sources :

Example 6 with CommandConsumer

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

the class AbstractHonoResource method doUploadMessage.

/**
 * Forwards a message to the south bound Telemetry or Event API of the messaging infrastructure configured
 * for the tenant that the origin device belongs to.
 * <p>
 * Depending on the outcome of the attempt to upload the message, the CoAP response code is set as
 * described by the <a href="https://www.eclipse.org/hono/docs/user-guide/coap-adapter/">CoAP adapter user guide</a>
 *
 * @param context The request that contains the uploaded message.
 * @param endpoint The type of API endpoint to forward the message to.
 * @return A future indicating the outcome of the operation.
 *         The future will be succeeded if the message has been forwarded successfully.
 *         In this case one of the context's <em>respond</em> methods will have been invoked to send a CoAP response
 *         back to the device.
 *         Otherwise the future will be failed with a {@link org.eclipse.hono.client.ServiceInvocationException}.
 * @throws NullPointerException if any of the parameters are {@code null}.
 */
protected final Future<Void> doUploadMessage(final CoapContext context, final MetricsTags.EndpointType endpoint) {
    Objects.requireNonNull(context);
    Objects.requireNonNull(endpoint);
    final String contentType = context.getContentType();
    final Buffer payload = context.getPayload();
    if (contentType == null) {
        return Future.failedFuture(new ClientErrorException(HttpURLConnection.HTTP_BAD_REQUEST, "request message must contain content-format option"));
    } else if (payload.length() == 0 && !context.isEmptyNotification()) {
        return Future.failedFuture(new ClientErrorException(HttpURLConnection.HTTP_BAD_REQUEST, "request contains no body but is not marked as empty notification"));
    } else {
        final String gatewayId = context.getGatewayId();
        final String tenantId = context.getOriginDevice().getTenantId();
        final String deviceId = context.getOriginDevice().getDeviceId();
        final MetricsTags.QoS qos = context.isConfirmable() ? MetricsTags.QoS.AT_LEAST_ONCE : MetricsTags.QoS.AT_MOST_ONCE;
        final Span currentSpan = TracingHelper.buildChildSpan(getTracer(), context.getTracingContext(), "upload " + endpoint.getCanonicalName(), getAdapter().getTypeName()).withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT).withTag(TracingHelper.TAG_TENANT_ID, tenantId).withTag(TracingHelper.TAG_DEVICE_ID, deviceId).withTag(TracingHelper.TAG_AUTHENTICATED.getKey(), context.isDeviceAuthenticated()).withTag(Constants.HEADER_QOS_LEVEL, qos.asTag().getValue()).start();
        final Promise<Void> responseReady = Promise.promise();
        final Future<RegistrationAssertion> tokenTracker = getAdapter().getRegistrationAssertion(tenantId, deviceId, context.getAuthenticatedDevice(), currentSpan.context());
        final Future<TenantObject> tenantTracker = getAdapter().getTenantClient().get(tenantId, currentSpan.context());
        final Future<TenantObject> tenantValidationTracker = tenantTracker.compose(tenantObject -> CompositeFuture.all(getAdapter().isAdapterEnabled(tenantObject), getAdapter().checkMessageLimit(tenantObject, payload.length(), currentSpan.context())).map(tenantObject));
        // we only need to consider TTD if the device and tenant are enabled and the adapter
        // is enabled for the tenant
        final Future<Integer> ttdTracker = CompositeFuture.all(tenantValidationTracker, tokenTracker).compose(ok -> {
            final Integer ttdParam = context.getTimeUntilDisconnect();
            return getAdapter().getTimeUntilDisconnect(tenantTracker.result(), ttdParam).map(effectiveTtd -> {
                if (effectiveTtd != null) {
                    currentSpan.setTag(MessageHelper.APP_PROPERTY_DEVICE_TTD, effectiveTtd);
                }
                return effectiveTtd;
            });
        });
        final Future<CommandConsumer> commandConsumerTracker = ttdTracker.compose(ttd -> createCommandConsumer(ttd, tenantTracker.result(), deviceId, gatewayId, context, responseReady, currentSpan));
        return commandConsumerTracker.compose(commandConsumer -> {
            final Map<String, Object> props = getAdapter().getDownstreamMessageProperties(context);
            Optional.ofNullable(commandConsumer).map(c -> ttdTracker.result()).ifPresent(ttd -> props.put(MessageHelper.APP_PROPERTY_DEVICE_TTD, ttd));
            customizeDownstreamMessageProperties(props, context);
            if (context.isConfirmable()) {
                context.startAcceptTimer(vertx, tenantTracker.result(), getAdapter().getConfig().getTimeoutToAck());
            }
            final Future<Void> sendResult;
            if (endpoint == EndpointType.EVENT) {
                sendResult = getAdapter().getEventSender(tenantValidationTracker.result()).sendEvent(tenantTracker.result(), tokenTracker.result(), contentType, payload, props, currentSpan.context());
            } else {
                sendResult = getAdapter().getTelemetrySender(tenantValidationTracker.result()).sendTelemetry(tenantTracker.result(), tokenTracker.result(), context.getRequestedQos(), contentType, payload, props, currentSpan.context());
            }
            return CompositeFuture.all(sendResult, responseReady.future()).mapEmpty();
        }).compose(proceed -> {
            // request and the CommandConsumer from the current request has not been closed yet
            return Optional.ofNullable(commandConsumerTracker.result()).map(consumer -> consumer.close(currentSpan.context()).otherwise(thr -> {
                TracingHelper.logError(currentSpan, thr);
                return (Void) null;
            })).orElseGet(Future::succeededFuture);
        }).map(proceed -> {
            final CommandContext commandContext = context.get(CommandContext.KEY_COMMAND_CONTEXT);
            final Response response = new Response(ResponseCode.CHANGED);
            if (commandContext != null) {
                addCommandToResponse(response, commandContext, currentSpan);
                commandContext.accept();
                getAdapter().getMetrics().reportCommand(commandContext.getCommand().isOneWay() ? Direction.ONE_WAY : Direction.REQUEST, tenantId, tenantTracker.result(), ProcessingOutcome.FORWARDED, commandContext.getCommand().getPayloadSize(), getMicrometerSample(commandContext));
            }
            LOG.trace("successfully processed message for device [tenantId: {}, deviceId: {}, endpoint: {}]", tenantId, deviceId, endpoint.getCanonicalName());
            getAdapter().getMetrics().reportTelemetry(endpoint, tenantId, tenantTracker.result(), MetricsTags.ProcessingOutcome.FORWARDED, qos, payload.length(), getTtdStatus(context), context.getTimer());
            context.respond(response);
            currentSpan.finish();
            return (Void) null;
        }).recover(t -> {
            LOG.debug("cannot process message from device [tenantId: {}, deviceId: {}, endpoint: {}]", tenantId, deviceId, endpoint.getCanonicalName(), t);
            final Future<Void> commandConsumerClosedTracker = Optional.ofNullable(commandConsumerTracker.result()).map(consumer -> consumer.close(currentSpan.context()).onFailure(thr -> TracingHelper.logError(currentSpan, thr))).orElseGet(Future::succeededFuture);
            final CommandContext commandContext = context.get(CommandContext.KEY_COMMAND_CONTEXT);
            if (commandContext != null) {
                TracingHelper.logError(commandContext.getTracingSpan(), "command won't be forwarded to device in CoAP response, CoAP request handling failed", t);
                commandContext.release(t);
                currentSpan.log("released command for device");
            }
            getAdapter().getMetrics().reportTelemetry(endpoint, tenantId, tenantTracker.result(), ClientErrorException.class.isInstance(t) ? MetricsTags.ProcessingOutcome.UNPROCESSABLE : MetricsTags.ProcessingOutcome.UNDELIVERABLE, qos, payload.length(), getTtdStatus(context), context.getTimer());
            TracingHelper.logError(currentSpan, t);
            commandConsumerClosedTracker.onComplete(res -> currentSpan.finish());
            return Future.failedFuture(t);
        });
    }
}
Also used : Buffer(io.vertx.core.buffer.Buffer) 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) CommandContext(org.eclipse.hono.client.command.CommandContext) Span(io.opentracing.Span) Response(org.eclipse.californium.core.coap.Response) Promise(io.vertx.core.Promise) ClientErrorException(org.eclipse.hono.client.ClientErrorException) CompositeFuture(io.vertx.core.CompositeFuture) Future(io.vertx.core.Future) TenantObject(org.eclipse.hono.util.TenantObject)

Example 7 with CommandConsumer

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

the class VertxBasedAmqpProtocolAdapterTest method testDeviceConnectionIsClosedOnDeviceOrTenantChangeNotification.

private <T extends AbstractNotification> void testDeviceConnectionIsClosedOnDeviceOrTenantChangeNotification(final T notification) {
    // GIVEN an AMQP adapter
    givenAnAdapter(properties);
    final Promise<Void> startPromise = Promise.promise();
    adapter.doStart(startPromise);
    assertThat(startPromise.future().succeeded()).isTrue();
    @SuppressWarnings("unchecked") final ArgumentCaptor<Handler<io.vertx.core.eventbus.Message<T>>> notificationHandlerCaptor = getEventBusConsumerHandlerArgumentCaptor((NotificationType<T>) notification.getType());
    // with an enabled tenant
    givenAConfiguredTenant(TEST_TENANT_ID, true);
    // WHEN a device connects
    final Device authenticatedDevice = new Device(TEST_TENANT_ID, TEST_DEVICE);
    final Record record = new RecordImpl();
    record.set(AmqpAdapterConstants.KEY_CLIENT_DEVICE, Device.class, authenticatedDevice);
    final ProtonConnection deviceConnection = mock(ProtonConnection.class);
    when(deviceConnection.attachments()).thenReturn(record);
    when(deviceConnection.getRemoteContainer()).thenReturn("deviceContainer");
    adapter.onConnectRequest(deviceConnection);
    final ArgumentCaptor<Handler<AsyncResult<ProtonConnection>>> openHandler = VertxMockSupport.argumentCaptorHandler();
    verify(deviceConnection).openHandler(openHandler.capture());
    openHandler.getValue().handle(Future.succeededFuture(deviceConnection));
    // that wants to receive commands
    final CommandConsumer commandConsumer = mock(CommandConsumer.class);
    when(commandConsumer.close(any())).thenReturn(Future.failedFuture(new ClientErrorException(HttpURLConnection.HTTP_PRECON_FAILED)));
    when(commandConsumerFactory.createCommandConsumer(eq(TEST_TENANT_ID), eq(TEST_DEVICE), VertxMockSupport.anyHandler(), any(), any())).thenReturn(Future.succeededFuture(commandConsumer));
    final String sourceAddress = getCommandEndpoint();
    final ProtonSender sender = getSender(sourceAddress);
    adapter.handleRemoteSenderOpenForCommands(deviceConnection, sender);
    // THEN the connection count is incremented
    verify(metrics).incrementConnections(TEST_TENANT_ID);
    // AND WHEN a notification is sent about the tenant/device having been deleted or disabled
    sendViaEventBusMock(notification, notificationHandlerCaptor.getValue());
    // THEN the device connection is closed
    verify(deviceConnection).close();
    // and the connection count is decremented
    verify(metrics).decrementConnections(TEST_TENANT_ID);
    // and the adapter has closed the command consumer
    verify(commandConsumer).close(any());
}
Also used : Device(org.eclipse.hono.auth.Device) Handler(io.vertx.core.Handler) RecordImpl(org.apache.qpid.proton.engine.impl.RecordImpl) ArgumentMatchers.anyString(org.mockito.ArgumentMatchers.anyString) ProtonConnection(io.vertx.proton.ProtonConnection) ProtonSender(io.vertx.proton.ProtonSender) CommandConsumer(org.eclipse.hono.client.command.CommandConsumer) ClientErrorException(org.eclipse.hono.client.ClientErrorException) Record(org.apache.qpid.proton.engine.Record)

Example 8 with CommandConsumer

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

the class VertxBasedAmqpProtocolAdapter method openCommandSenderLink.

private Future<CommandConsumer> openCommandSenderLink(final ProtonConnection connection, final ProtonSender sender, final ResourceIdentifier address, final Device authenticatedDevice, final Span span, final OptionalInt traceSamplingPriority) {
    return createCommandConsumer(sender, address, authenticatedDevice, span).map(consumer -> {
        final String tenantId = address.getTenantId();
        final String deviceId = address.getResourceId();
        sender.setSource(sender.getRemoteSource());
        sender.setTarget(sender.getRemoteTarget());
        sender.setQoS(ProtonQoS.AT_LEAST_ONCE);
        final Handler<AsyncResult<ProtonSender>> detachHandler = link -> {
            final Span detachHandlerSpan = newSpan("detach device command receiver link", authenticatedDevice, traceSamplingPriority);
            removeCommandSubscription(connection, address.toString());
            onLinkDetach(sender);
            closeCommandConsumer(consumer, address, authenticatedDevice, true, detachHandlerSpan).onComplete(v -> detachHandlerSpan.finish());
        };
        HonoProtonHelper.setCloseHandler(sender, detachHandler);
        HonoProtonHelper.setDetachHandler(sender, detachHandler);
        sender.open();
        // At this point, the remote peer's receiver link is successfully opened and is ready to receive
        // commands. Send "device ready for command" notification downstream.
        log.debug("established link [address: {}] for sending commands to device", address);
        sendConnectedTtdEvent(tenantId, deviceId, authenticatedDevice, span.context());
        registerCommandSubscription(connection, new CommandSubscription(consumer, address));
        return consumer;
    }).recover(t -> Future.failedFuture(new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE, "cannot create command consumer")));
}
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) ProtonSender(io.vertx.proton.ProtonSender) Handler(io.vertx.core.Handler) ServerErrorException(org.eclipse.hono.client.ServerErrorException) Span(io.opentracing.Span)

Example 9 with CommandConsumer

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

the class AbstractVertxBasedMqttProtocolAdapterTest method testAdapterSkipsTtdEventOnCmdConnectionCloseIfRemoveConsumerFails.

/**
 * Verifies that the adapter doesn't send a 'disconnectedTtdEvent' on connection loss
 * when removal of the command consumer mapping entry fails (which would be the case
 * when another command consumer mapping had been registered in the mean time, meaning
 * the device has already reconnected).
 */
@Test
public void testAdapterSkipsTtdEventOnCmdConnectionCloseIfRemoveConsumerFails() {
    // GIVEN a device connected to an adapter
    givenAnAdapter(properties);
    givenAnEventSenderForAnyTenant();
    final MqttEndpoint endpoint = mockEndpoint();
    when(endpoint.isConnected()).thenReturn(true);
    // 10 seconds
    when(endpoint.keepAliveTimeSeconds()).thenReturn(10);
    // WHEN a device subscribes to commands
    final CommandConsumer commandConsumer = mock(CommandConsumer.class);
    when(commandConsumer.close(any())).thenReturn(Future.failedFuture(new ClientErrorException(HttpURLConnection.HTTP_PRECON_FAILED)));
    when(commandConsumerFactory.createCommandConsumer(eq("tenant"), eq("deviceId"), VertxMockSupport.anyHandler(), any(), any())).thenReturn(Future.succeededFuture(commandConsumer));
    final List<MqttTopicSubscription> subscriptions = Collections.singletonList(newMockTopicSubscription(getCommandSubscriptionTopic("tenant", "deviceId"), MqttQoS.AT_MOST_ONCE));
    final MqttSubscribeMessage msg = mock(MqttSubscribeMessage.class);
    when(msg.messageId()).thenReturn(15);
    when(msg.topicSubscriptions()).thenReturn(subscriptions);
    final var mqttDeviceEndpoint = adapter.createMqttDeviceEndpoint(endpoint, null, OptionalInt.empty());
    endpoint.closeHandler(handler -> mqttDeviceEndpoint.onClose());
    mqttDeviceEndpoint.onSubscribe(msg);
    // THEN the adapter creates a command consumer that is checked periodically
    verify(commandConsumerFactory).createCommandConsumer(eq("tenant"), eq("deviceId"), VertxMockSupport.anyHandler(), any(), any());
    // and the adapter registers a hook on the connection to the device
    final ArgumentCaptor<Handler<Void>> closeHookCaptor = VertxMockSupport.argumentCaptorHandler();
    verify(endpoint).closeHandler(closeHookCaptor.capture());
    // which closes the command consumer when the device disconnects
    closeHookCaptor.getValue().handle(null);
    when(endpoint.isConnected()).thenReturn(false);
    verify(commandConsumer).close(any());
    // and since closing the command consumer fails with a precon-failed exception
    // there is only one notification sent during consumer creation,
    assertEmptyNotificationHasBeenSentDownstream("tenant", "deviceId", -1);
    // no 'disconnectedTtdEvent' event with TTD = 0
    assertEmptyNotificationHasNotBeenSentDownstream("tenant", "deviceId", 0);
}
Also used : MqttSubscribeMessage(io.vertx.mqtt.messages.MqttSubscribeMessage) MqttEndpoint(io.vertx.mqtt.MqttEndpoint) MqttTopicSubscription(io.vertx.mqtt.MqttTopicSubscription) CommandConsumer(org.eclipse.hono.client.command.CommandConsumer) ClientErrorException(org.eclipse.hono.client.ClientErrorException) AuthHandler(org.eclipse.hono.adapter.auth.device.AuthHandler) Handler(io.vertx.core.Handler) Test(org.junit.jupiter.api.Test)

Example 10 with CommandConsumer

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

the class LoraProtocolAdapter method registerCommandConsumerIfNeeded.

private void registerCommandConsumerIfNeeded(final LoraProvider provider, final Device gatewayDevice, final SpanContext context) {
    final String tenantId = gatewayDevice.getTenantId();
    final String gatewayId = gatewayDevice.getDeviceId();
    final SubscriptionKey key = new SubscriptionKey(tenantId, gatewayId);
    if (commandSubscriptions.containsKey(key)) {
        return;
    }
    // use FOLLOWS_FROM span since this operation is decoupled from the rest of the request handling
    final Span currentSpan = TracingHelper.buildFollowsFromSpan(tracer, context, "create command consumer").withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT).start();
    TracingHelper.setDeviceTags(currentSpan, tenantId, gatewayId);
    TAG_LORA_PROVIDER.set(currentSpan, provider.getProviderName());
    getRegistrationClient().assertRegistration(tenantId, gatewayId, null, currentSpan.context()).onFailure(thr -> {
        LOG.debug("error asserting gateway registration, no command consumer will be created [tenant: {}, gateway-id: {}]", tenantId, gatewayId);
        TracingHelper.logError(currentSpan, "error asserting gateway registration, no command consumer will be created", thr);
    }).compose(assertion -> {
        if (assertion.getCommandEndpoint() == null) {
            LOG.debug("gateway has no command endpoint defined, skipping command consumer creation [tenant: {}, gateway-id: {}]", tenantId, gatewayId);
            currentSpan.log("gateway has no command endpoint defined, skipping command consumer creation");
            return Future.succeededFuture((Void) null);
        }
        return getCommandConsumerFactory().createCommandConsumer(tenantId, gatewayId, this::handleCommand, null, currentSpan.context()).onFailure(thr -> TracingHelper.logError(currentSpan, thr)).map(commandConsumer -> commandSubscriptions.put(key, Pair.of(commandConsumer, provider))).mapEmpty();
    }).onComplete(ar -> currentSpan.finish());
}
Also used : HttpURLConnection(java.net.HttpURLConnection) LoraProviderMalformedPayloadException(org.eclipse.hono.adapter.lora.providers.LoraProviderMalformedPayloadException) LoggerFactory(org.slf4j.LoggerFactory) Router(io.vertx.ext.web.Router) Tag(io.opentracing.tag.Tag) RoutingContext(io.vertx.ext.web.RoutingContext) Tags(io.opentracing.tag.Tags) Map(java.util.Map) Pair(org.eclipse.hono.util.Pair) Fields(io.opentracing.log.Fields) JsonObject(io.vertx.core.json.JsonObject) TracingHelper(org.eclipse.hono.tracing.TracingHelper) TenantServiceBasedX509Authentication(org.eclipse.hono.adapter.auth.device.TenantServiceBasedX509Authentication) ChainAuthHandler(io.vertx.ext.web.handler.ChainAuthHandler) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) CommandContext(org.eclipse.hono.client.command.CommandContext) MetricsTags(org.eclipse.hono.service.metric.MetricsTags) Message(io.vertx.core.eventbus.Message) EventConstants(org.eclipse.hono.util.EventConstants) StringTag(io.opentracing.tag.StringTag) Future(io.vertx.core.Future) HonoBasicAuthHandler(org.eclipse.hono.adapter.http.HonoBasicAuthHandler) Device(org.eclipse.hono.auth.Device) Objects(java.util.Objects) List(java.util.List) Buffer(io.vertx.core.buffer.Buffer) X509AuthHandler(org.eclipse.hono.adapter.http.X509AuthHandler) CommandConsumer(org.eclipse.hono.client.command.CommandConsumer) Optional(java.util.Optional) Span(io.opentracing.Span) UsernamePasswordCredentials(org.eclipse.hono.adapter.auth.device.UsernamePasswordCredentials) CommandEndpoint(org.eclipse.hono.util.CommandEndpoint) HttpContext(org.eclipse.hono.service.http.HttpContext) Json(io.vertx.core.json.Json) LoraProvider(org.eclipse.hono.adapter.lora.providers.LoraProvider) WebClient(io.vertx.ext.web.client.WebClient) Command(org.eclipse.hono.client.command.Command) ClientErrorException(org.eclipse.hono.client.ClientErrorException) TenantDisabledOrNotRegisteredException(org.eclipse.hono.client.registry.TenantDisabledOrNotRegisteredException) Constants(org.eclipse.hono.util.Constants) ArrayList(java.util.ArrayList) TracingHandler(org.eclipse.hono.service.http.TracingHandler) CompositeFuture(io.vertx.core.CompositeFuture) StatusCodeMapper(org.eclipse.hono.client.StatusCodeMapper) HttpUtils(org.eclipse.hono.service.http.HttpUtils) LinkedList(java.util.LinkedList) UsernamePasswordAuthProvider(org.eclipse.hono.adapter.auth.device.UsernamePasswordAuthProvider) Logger(org.slf4j.Logger) Direction(org.eclipse.hono.service.metric.MetricsTags.Direction) AbstractVertxBasedHttpProtocolAdapter(org.eclipse.hono.adapter.http.AbstractVertxBasedHttpProtocolAdapter) Promise(io.vertx.core.Promise) ServerErrorException(org.eclipse.hono.client.ServerErrorException) Sample(io.micrometer.core.instrument.Timer.Sample) TenantObject(org.eclipse.hono.util.TenantObject) SpanContext(io.opentracing.SpanContext) HttpRequest(io.vertx.ext.web.client.HttpRequest) DeviceCredentialsAuthProvider(org.eclipse.hono.adapter.auth.device.DeviceCredentialsAuthProvider) X509AuthProvider(org.eclipse.hono.adapter.auth.device.X509AuthProvider) HttpMethod(io.vertx.core.http.HttpMethod) HttpProtocolAdapterProperties(org.eclipse.hono.adapter.http.HttpProtocolAdapterProperties) SubjectDnCredentials(org.eclipse.hono.adapter.auth.device.SubjectDnCredentials) Span(io.opentracing.Span)

Aggregations

CommandConsumer (org.eclipse.hono.client.command.CommandConsumer)23 Handler (io.vertx.core.Handler)15 ClientErrorException (org.eclipse.hono.client.ClientErrorException)14 Device (org.eclipse.hono.auth.Device)12 CommandContext (org.eclipse.hono.client.command.CommandContext)11 Future (io.vertx.core.Future)10 HttpURLConnection (java.net.HttpURLConnection)10 ServerErrorException (org.eclipse.hono.client.ServerErrorException)10 Command (org.eclipse.hono.client.command.Command)10 Test (org.junit.jupiter.api.Test)10 Sample (io.micrometer.core.instrument.Timer.Sample)9 Span (io.opentracing.Span)9 SpanContext (io.opentracing.SpanContext)9 Promise (io.vertx.core.Promise)9 Buffer (io.vertx.core.buffer.Buffer)9 List (java.util.List)9 Map (java.util.Map)9 Constants (org.eclipse.hono.util.Constants)9 Tags (io.opentracing.tag.Tags)8 AsyncResult (io.vertx.core.AsyncResult)8