Search in sources :

Example 1 with CommandHandlerWrapper

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

the class ProtonBasedInternalCommandConsumer method handleCommandMessage.

void handleCommandMessage(final ProtonDelivery delivery, final Message msg) {
    final ProtonBasedCommand command;
    try {
        command = ProtonBasedCommand.fromRoutedCommandMessage(msg);
    } catch (final IllegalArgumentException e) {
        log.debug("address of command message is invalid: {}", msg.getAddress());
        final Rejected rejected = new Rejected();
        rejected.setError(new ErrorCondition(Constants.AMQP_BAD_REQUEST, "invalid command target address"));
        delivery.disposition(rejected, true);
        return;
    }
    final CommandHandlerWrapper commandHandler = commandHandlers.getCommandHandler(command.getTenant(), command.getGatewayOrDeviceId());
    if (commandHandler != null && commandHandler.getGatewayId() != null) {
        // Gateway information set in command handler means a gateway has subscribed for commands for a specific device.
        // This information isn't getting set in the message (by the Command Router) and therefore has to be adopted manually here.
        command.setGatewayId(commandHandler.getGatewayId());
    }
    final SpanContext spanContext = TracingHelper.extractSpanContext(tracer, msg);
    final SpanContext followsFromSpanContext = commandHandler != null ? commandHandler.getConsumerCreationSpanContext() : null;
    final Span currentSpan = CommandContext.createSpan(tracer, command, spanContext, followsFromSpanContext, getClass().getSimpleName());
    currentSpan.setTag(MessageHelper.APP_PROPERTY_ADAPTER_INSTANCE_ID, adapterInstanceId);
    final CommandContext commandContext = new ProtonBasedCommandContext(command, delivery, currentSpan);
    if (commandHandler != null) {
        log.trace("using [{}] for received command [{}]", commandHandler, command);
        // command.isValid() check not done here - it is to be done in the command handler
        commandHandler.handleCommand(commandContext);
    } else {
        log.info("no command handler found for command [{}]", command);
        commandContext.release(new NoConsumerException("no command handler found for command"));
    }
}
Also used : SpanContext(io.opentracing.SpanContext) CommandContext(org.eclipse.hono.client.command.CommandContext) CommandHandlerWrapper(org.eclipse.hono.client.command.CommandHandlerWrapper) ErrorCondition(org.apache.qpid.proton.amqp.transport.ErrorCondition) NoConsumerException(org.eclipse.hono.client.NoConsumerException) Rejected(org.apache.qpid.proton.amqp.messaging.Rejected) Span(io.opentracing.Span)

Example 2 with CommandHandlerWrapper

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

the class KafkaBasedInternalCommandConsumer method handleCommandMessage.

void handleCommandMessage(final KafkaConsumerRecord<String, Buffer> record) {
    // get partition/offset of the command record - related to the tenant-based topic the command was originally received in
    final Integer commandPartition = KafkaRecordHelper.getOriginalPartitionHeader(record.headers()).orElse(null);
    final Long commandOffset = KafkaRecordHelper.getOriginalOffsetHeader(record.headers()).orElse(null);
    if (commandPartition == null || commandOffset == null) {
        LOG.warn("command record is invalid - missing required original partition/offset headers");
        return;
    }
    final KafkaBasedCommand command;
    try {
        command = KafkaBasedCommand.fromRoutedCommandRecord(record);
    } catch (final IllegalArgumentException e) {
        LOG.warn("command record is invalid [tenant-id: {}, device-id: {}]", KafkaRecordHelper.getTenantId(record.headers()).orElse(null), KafkaRecordHelper.getDeviceId(record.headers()).orElse(null), e);
        return;
    }
    // check whether command has already been received and handled;
    // partition index and offset here are related to the *tenant-based* topic the command was originally received in
    // therefore they are stored in a map with the tenant as key
    final Map<Integer, Long> lastHandledPartitionOffsets = lastHandledPartitionOffsetsPerTenant.computeIfAbsent(command.getTenant(), k -> new HashMap<>());
    final Long lastHandledOffset = lastHandledPartitionOffsets.get(commandPartition);
    if (lastHandledOffset != null && commandOffset <= lastHandledOffset) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("ignoring command - record partition offset {} <= last handled offset {} [{}]", commandOffset, lastHandledOffset, command);
        }
    } else {
        lastHandledPartitionOffsets.put(commandPartition, commandOffset);
        final CommandHandlerWrapper commandHandler = commandHandlers.getCommandHandler(command.getTenant(), command.getGatewayOrDeviceId());
        if (commandHandler != null && commandHandler.getGatewayId() != null) {
            // Gateway information set in command handler means a gateway has subscribed for commands for a specific device.
            // This information isn't getting set in the record (by the Command Router) and therefore has to be adopted manually here.
            command.setGatewayId(commandHandler.getGatewayId());
        }
        final SpanContext spanContext = KafkaTracingHelper.extractSpanContext(tracer, record);
        final SpanContext followsFromSpanContext = commandHandler != null ? commandHandler.getConsumerCreationSpanContext() : null;
        final Span currentSpan = CommandContext.createSpan(tracer, command, spanContext, followsFromSpanContext, getClass().getSimpleName());
        currentSpan.setTag(MessageHelper.APP_PROPERTY_ADAPTER_INSTANCE_ID, adapterInstanceId);
        KafkaTracingHelper.TAG_OFFSET.set(currentSpan, record.offset());
        final var commandContext = new KafkaBasedCommandContext(command, commandResponseSender, currentSpan);
        tenantClient.get(command.getTenant(), spanContext).onFailure(t -> {
            if (ServiceInvocationException.extractStatusCode(t) == HttpURLConnection.HTTP_NOT_FOUND) {
                commandContext.reject(new TenantDisabledOrNotRegisteredException(command.getTenant(), HttpURLConnection.HTTP_NOT_FOUND));
            } else {
                commandContext.release(new ServerErrorException(command.getTenant(), HttpURLConnection.HTTP_UNAVAILABLE, "error retrieving tenant configuration", t));
            }
        }).onSuccess(tenantConfig -> {
            commandContext.put(CommandContext.KEY_TENANT_CONFIG, tenantConfig);
            if (commandHandler != null) {
                LOG.trace("using [{}] for received command [{}]", commandHandler, command);
                // command.isValid() check not done here - it is to be done in the command handler
                commandHandler.handleCommand(commandContext);
            } else {
                LOG.info("no command handler found for command [{}]", command);
                commandContext.release(new NoConsumerException("no command handler found for command"));
            }
        });
    }
}
Also used : HttpURLConnection(java.net.HttpURLConnection) MessagingKafkaConsumerConfigProperties(org.eclipse.hono.client.kafka.consumer.MessagingKafkaConsumerConfigProperties) LoggerFactory(org.slf4j.LoggerFactory) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) HashMap(java.util.HashMap) CommandHandlerWrapper(org.eclipse.hono.client.command.CommandHandlerWrapper) TenantDisabledOrNotRegisteredException(org.eclipse.hono.client.registry.TenantDisabledOrNotRegisteredException) ServiceInvocationException(org.eclipse.hono.client.ServiceInvocationException) Supplier(java.util.function.Supplier) CommandResponseSender(org.eclipse.hono.client.command.CommandResponseSender) KafkaClientFactory(org.eclipse.hono.client.kafka.KafkaClientFactory) Context(io.vertx.core.Context) NoConsumerException(org.eclipse.hono.client.NoConsumerException) CompositeFuture(io.vertx.core.CompositeFuture) Status(io.vertx.ext.healthchecks.Status) HealthCheckHandler(io.vertx.ext.healthchecks.HealthCheckHandler) KafkaClientMetricsSupport(org.eclipse.hono.client.kafka.metrics.KafkaClientMetricsSupport) Map(java.util.Map) KafkaAdminClientConfigProperties(org.eclipse.hono.client.kafka.KafkaAdminClientConfigProperties) Admin(org.apache.kafka.clients.admin.Admin) CommandHandlers(org.eclipse.hono.client.command.CommandHandlers) KafkaTracingHelper(org.eclipse.hono.client.kafka.tracing.KafkaTracingHelper) CommonClientConfigs(org.apache.kafka.clients.CommonClientConfigs) Logger(org.slf4j.Logger) Tracer(io.opentracing.Tracer) Promise(io.vertx.core.Promise) NewTopic(org.apache.kafka.clients.admin.NewTopic) CommandContext(org.eclipse.hono.client.command.CommandContext) Vertx(io.vertx.core.Vertx) Set(java.util.Set) ServerErrorException(org.eclipse.hono.client.ServerErrorException) ConsumerConfig(org.apache.kafka.clients.consumer.ConsumerConfig) KafkaRecordHelper(org.eclipse.hono.client.kafka.KafkaRecordHelper) TenantClient(org.eclipse.hono.client.registry.TenantClient) MessageHelper(org.eclipse.hono.util.MessageHelper) Future(io.vertx.core.Future) InternalCommandConsumer(org.eclipse.hono.client.command.InternalCommandConsumer) SpanContext(io.opentracing.SpanContext) TopicPartition(io.vertx.kafka.client.common.TopicPartition) Objects(java.util.Objects) HonoTopic(org.eclipse.hono.client.kafka.HonoTopic) List(java.util.List) TopicExistsException(org.apache.kafka.common.errors.TopicExistsException) Buffer(io.vertx.core.buffer.Buffer) KafkaConsumerRecord(io.vertx.kafka.client.consumer.KafkaConsumerRecord) Optional(java.util.Optional) Span(io.opentracing.Span) KafkaConsumer(io.vertx.kafka.client.consumer.KafkaConsumer) SpanContext(io.opentracing.SpanContext) NoConsumerException(org.eclipse.hono.client.NoConsumerException) TenantDisabledOrNotRegisteredException(org.eclipse.hono.client.registry.TenantDisabledOrNotRegisteredException) Span(io.opentracing.Span) CommandHandlerWrapper(org.eclipse.hono.client.command.CommandHandlerWrapper) ServerErrorException(org.eclipse.hono.client.ServerErrorException)

Aggregations

Span (io.opentracing.Span)2 SpanContext (io.opentracing.SpanContext)2 NoConsumerException (org.eclipse.hono.client.NoConsumerException)2 CommandContext (org.eclipse.hono.client.command.CommandContext)2 CommandHandlerWrapper (org.eclipse.hono.client.command.CommandHandlerWrapper)2 Tracer (io.opentracing.Tracer)1 CompositeFuture (io.vertx.core.CompositeFuture)1 Context (io.vertx.core.Context)1 Future (io.vertx.core.Future)1 Promise (io.vertx.core.Promise)1 Vertx (io.vertx.core.Vertx)1 Buffer (io.vertx.core.buffer.Buffer)1 HealthCheckHandler (io.vertx.ext.healthchecks.HealthCheckHandler)1 Status (io.vertx.ext.healthchecks.Status)1 TopicPartition (io.vertx.kafka.client.common.TopicPartition)1 KafkaConsumer (io.vertx.kafka.client.consumer.KafkaConsumer)1 KafkaConsumerRecord (io.vertx.kafka.client.consumer.KafkaConsumerRecord)1 HttpURLConnection (java.net.HttpURLConnection)1 HashMap (java.util.HashMap)1 List (java.util.List)1