Search in sources :

Example 91 with SpanContext

use of io.opentracing.SpanContext 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 92 with SpanContext

use of io.opentracing.SpanContext in project hono by eclipse.

the class EventSenderTest method testSendWithTracing.

/**
 * Verifies that {@link TraceableEventSender} uses the given SpanContext.
 *
 * @param ctx The test context to use for running asynchronous tests.
 */
@Test
public void testSendWithTracing(final VertxTestContext ctx) {
    // GIVEN a EventSender instance
    final TraceableEventSender eventSender = ((TraceableEventSender) createEventSender());
    // WHEN sending a message using the API...
    final SpanContext spanContext = mock(SpanContext.class);
    final Future<ProtonDelivery> deliveryFuture = eventSender.send(DEVICE_ID, PAYLOAD, CONTENT_TYPE, APPLICATION_PROPERTIES, spanContext);
    // ...AND WHEN the disposition is updated by the peer
    updateDisposition();
    deliveryFuture.onComplete(ctx.succeeding(delivery -> {
        // THEN the given SpanContext is used
        ctx.verify(() -> {
            verify(spanBuilder).addReference(any(), eq(spanContext));
            assertMessageConformsAmqpAdapterSpec(ADDRESS);
        });
        ctx.completeNow();
    }));
}
Also used : ArgumentMatchers.any(org.mockito.ArgumentMatchers.any) VertxTestContext(io.vertx.junit5.VertxTestContext) ProtonDelivery(io.vertx.proton.ProtonDelivery) ArgumentMatchers.eq(org.mockito.ArgumentMatchers.eq) AmqpAdapterClientEventSenderImpl(org.eclipse.hono.client.device.amqp.impl.AmqpAdapterClientEventSenderImpl) Truth.assertThat(com.google.common.truth.Truth.assertThat) VertxExtension(io.vertx.junit5.VertxExtension) EventConstants(org.eclipse.hono.util.EventConstants) Future(io.vertx.core.Future) Mockito.verify(org.mockito.Mockito.verify) SpanContext(io.opentracing.SpanContext) Test(org.junit.jupiter.api.Test) ExtendWith(org.junit.jupiter.api.extension.ExtendWith) Mockito.mock(org.mockito.Mockito.mock) SpanContext(io.opentracing.SpanContext) ProtonDelivery(io.vertx.proton.ProtonDelivery) Test(org.junit.jupiter.api.Test)

Example 93 with SpanContext

use of io.opentracing.SpanContext in project hono by eclipse.

the class CommandResponderTest method testSendCommandResponseWithTracing.

/**
 * Verifies that {@link TraceableCommandResponder} uses the given SpanContext.
 *
 * @param ctx The test context to use for running asynchronous tests.
 */
@Test
public void testSendCommandResponseWithTracing(final VertxTestContext ctx) {
    // GIVEN a TraceableCommandResponder instance
    final TraceableCommandResponder commandResponder = ((TraceableCommandResponder) createCommandResponder());
    // WHEN sending a message using the API...
    final SpanContext spanContext = mock(SpanContext.class);
    final Future<ProtonDelivery> deliveryFuture = commandResponder.sendCommandResponse(DEVICE_ID, ADDRESS, CORRELATION_ID, STATUS, PAYLOAD, CONTENT_TYPE, APPLICATION_PROPERTIES, spanContext);
    // ...AND WHEN the disposition is updated by the peer
    updateDisposition();
    deliveryFuture.onComplete(ctx.succeeding(delivery -> {
        // THEN the given SpanContext is used
        ctx.verify(() -> {
            verify(spanBuilder).addReference(any(), eq(spanContext));
            assertMessageConformsAmqpAdapterSpec();
        });
        ctx.completeNow();
    }));
}
Also used : ArgumentMatchers.any(org.mockito.ArgumentMatchers.any) VertxTestContext(io.vertx.junit5.VertxTestContext) ProtonDelivery(io.vertx.proton.ProtonDelivery) ArgumentMatchers.eq(org.mockito.ArgumentMatchers.eq) Truth.assertThat(com.google.common.truth.Truth.assertThat) VertxExtension(io.vertx.junit5.VertxExtension) Future(io.vertx.core.Future) Mockito.verify(org.mockito.Mockito.verify) SpanContext(io.opentracing.SpanContext) Test(org.junit.jupiter.api.Test) ExtendWith(org.junit.jupiter.api.extension.ExtendWith) AmqpAdapterClientCommandResponseSender(org.eclipse.hono.client.device.amqp.impl.AmqpAdapterClientCommandResponseSender) Message(org.apache.qpid.proton.message.Message) CommandConstants(org.eclipse.hono.util.CommandConstants) Mockito.mock(org.mockito.Mockito.mock) SpanContext(io.opentracing.SpanContext) ProtonDelivery(io.vertx.proton.ProtonDelivery) Test(org.junit.jupiter.api.Test)

Example 94 with SpanContext

use of io.opentracing.SpanContext in project hono by eclipse.

the class TelemetrySenderTest method testSendWithTracing.

/**
 * Verifies that the message created by {@link TelemetrySender#send(String, byte[], String, java.util.Map)} conforms
 * to the expectations of the AMQP adapter.
 */
@Test
public void testSendWithTracing() {
    // GIVEN a TraceableTelemetrySender instance
    final TraceableTelemetrySender telemetrySender = ((TraceableTelemetrySender) createTelemetrySender());
    // WHEN sending a message using the API
    final SpanContext spanContext = mock(SpanContext.class);
    telemetrySender.send(DEVICE_ID, PAYLOAD, CONTENT_TYPE, APPLICATION_PROPERTIES, spanContext);
    // THEN the given SpanContext is used
    verify(spanBuilder).addReference(any(), eq(spanContext));
    assertMessageConformsAmqpAdapterSpec(ADDRESS);
}
Also used : SpanContext(io.opentracing.SpanContext) Test(org.junit.jupiter.api.Test)

Example 95 with SpanContext

use of io.opentracing.SpanContext in project hono by eclipse.

the class TableManagementStore method getDeviceCount.

/**
 * Gets the number of devices that are registered for a tenant.
 *
 * @param tenantId The tenant to count devices for.
 * @param spanContext The span to contribute to.
 * @return A future tracking the outcome of the operation.
 * @throws NullPointerException if tenant is {@code null}.
 */
public Future<Integer> getDeviceCount(final String tenantId, final SpanContext spanContext) {
    Objects.requireNonNull(tenantId);
    final Span span = TracingHelper.buildChildSpan(this.tracer, spanContext, "get device count", getClass().getSimpleName()).withTag(TracingHelper.TAG_TENANT_ID, tenantId).start();
    final var expanded = this.countDevicesOfTenantStatement.expand(params -> {
        params.put("tenant_id", tenantId);
    });
    log.debug("count - statement: {}", expanded);
    return expanded.trace(this.tracer, span.context()).query(this.client).map(r -> {
        final var entries = r.getRows(true);
        switch(entries.size()) {
            case 1:
                final Integer count = entries.get(0).getInteger("DEVICECOUNT");
                log.debug("found {} devices registered for tenant [tenant-id: {}]", count, tenantId);
                return count;
            default:
                throw new IllegalStateException("Could not count devices of tenant");
        }
    }).onComplete(x -> span.finish());
}
Also used : SQL(org.eclipse.hono.service.base.jdbc.store.SQL) Json(io.vertx.core.json.Json) JdbcBasedDeviceDto(org.eclipse.hono.service.base.jdbc.store.model.JdbcBasedDeviceDto) LoggerFactory(org.slf4j.LoggerFactory) Supplier(java.util.function.Supplier) Tenant(org.eclipse.hono.service.management.tenant.Tenant) Statement(org.eclipse.hono.service.base.jdbc.store.Statement) HashSet(java.util.HashSet) CompositeFuture(io.vertx.core.CompositeFuture) Versioned(org.eclipse.hono.deviceregistry.util.Versioned) Map(java.util.Map) Fields(io.opentracing.log.Fields) JsonObject(io.vertx.core.json.JsonObject) TracingHelper(org.eclipse.hono.tracing.TracingHelper) Device(org.eclipse.hono.service.management.device.Device) Logger(org.slf4j.Logger) Tracer(io.opentracing.Tracer) Timestamp(java.sql.Timestamp) Promise(io.vertx.core.Promise) Set(java.util.Set) DeviceKey(org.eclipse.hono.deviceregistry.service.device.DeviceKey) OptimisticLockingException(org.eclipse.hono.service.base.jdbc.store.OptimisticLockingException) UUID(java.util.UUID) Collectors(java.util.stream.Collectors) Future(io.vertx.core.Future) SpanContext(io.opentracing.SpanContext) Objects(java.util.Objects) List(java.util.List) CommonCredential(org.eclipse.hono.service.management.credentials.CommonCredential) JDBCClient(io.vertx.ext.jdbc.JDBCClient) CredentialsDto(org.eclipse.hono.service.management.credentials.CredentialsDto) UpdateResult(io.vertx.ext.sql.UpdateResult) EntityNotFoundException(org.eclipse.hono.service.base.jdbc.store.EntityNotFoundException) ResultSet(io.vertx.ext.sql.ResultSet) SQLConnection(io.vertx.ext.sql.SQLConnection) Optional(java.util.Optional) Span(io.opentracing.Span) DeviceRegistryUtils(org.eclipse.hono.deviceregistry.util.DeviceRegistryUtils) Collections(java.util.Collections) StatementConfiguration(org.eclipse.hono.service.base.jdbc.store.StatementConfiguration) Span(io.opentracing.Span)

Aggregations

SpanContext (io.opentracing.SpanContext)118 Span (io.opentracing.Span)81 Future (io.vertx.core.Future)70 Tracer (io.opentracing.Tracer)60 Objects (java.util.Objects)57 HttpURLConnection (java.net.HttpURLConnection)56 JsonObject (io.vertx.core.json.JsonObject)55 TracingHelper (org.eclipse.hono.tracing.TracingHelper)55 Logger (org.slf4j.Logger)54 LoggerFactory (org.slf4j.LoggerFactory)54 List (java.util.List)51 Promise (io.vertx.core.Promise)45 Optional (java.util.Optional)45 Map (java.util.Map)40 ClientErrorException (org.eclipse.hono.client.ClientErrorException)39 MessageHelper (org.eclipse.hono.util.MessageHelper)33 UUID (java.util.UUID)31 Collectors (java.util.stream.Collectors)31 HashMap (java.util.HashMap)25 Vertx (io.vertx.core.Vertx)24