Search in sources :

Example 1 with KafkaBasedCommandContext

use of org.eclipse.hono.client.command.kafka.KafkaBasedCommandContext in project hono by eclipse.

the class KafkaCommandProcessingQueue method remove.

/**
 * Removes the command represented by the given command context.
 * <p>
 * To be used for commands for which processing resulted in an error
 * and {@link #applySendCommandAction(KafkaBasedCommandContext, Supplier)}
 * will not be invoked.
 *
 * @return {@code true} if the command was removed.
 * @param commandContext The context containing the command to remove.
 */
public boolean remove(final KafkaBasedCommandContext commandContext) {
    Objects.requireNonNull(commandContext);
    final KafkaConsumerRecord<String, Buffer> record = commandContext.getCommand().getRecord();
    final TopicPartition topicPartition = new TopicPartition(record.topic(), record.partition());
    return Optional.ofNullable(commandQueues.get(topicPartition)).map(commandQueue -> commandQueue.remove(commandContext)).orElse(false);
}
Also used : Buffer(io.vertx.core.buffer.Buffer) TopicPartition(org.apache.kafka.common.TopicPartition) Logger(org.slf4j.Logger) Predicate(java.util.function.Predicate) CommandToBeReprocessedException(org.eclipse.hono.client.command.CommandToBeReprocessedException) Collection(java.util.Collection) Promise(io.vertx.core.Promise) LoggerFactory(org.slf4j.LoggerFactory) ServerErrorException(org.eclipse.hono.client.ServerErrorException) HashMap(java.util.HashMap) Deque(java.util.Deque) Supplier(java.util.function.Supplier) Context(io.vertx.core.Context) Future(io.vertx.core.Future) KafkaBasedCommandContext(org.eclipse.hono.client.command.kafka.KafkaBasedCommandContext) Objects(java.util.Objects) CommandAlreadyProcessedException(org.eclipse.hono.client.command.CommandAlreadyProcessedException) Buffer(io.vertx.core.buffer.Buffer) KafkaConsumerRecord(io.vertx.kafka.client.consumer.KafkaConsumerRecord) Map(java.util.Map) Pair(org.eclipse.hono.util.Pair) Optional(java.util.Optional) TracingHelper(org.eclipse.hono.tracing.TracingHelper) LinkedList(java.util.LinkedList) TopicPartition(org.apache.kafka.common.TopicPartition)

Example 2 with KafkaBasedCommandContext

use of org.eclipse.hono.client.command.kafka.KafkaBasedCommandContext in project hono by eclipse.

the class KafkaBasedMappingAndDelegatingCommandHandler method mapAndDelegateIncomingCommandMessage.

/**
 * Delegates an incoming command to the protocol adapter instance that the target
 * device is connected to.
 * <p>
 * Determines the target gateway (if applicable) and protocol adapter instance for an incoming command
 * and delegates the command to the resulting protocol adapter instance.
 *
 * @param consumerRecord The consumer record corresponding to the command.
 * @return A future indicating the outcome of the operation.
 * @throws NullPointerException if any of the parameters is {@code null}.
 */
public Future<Void> mapAndDelegateIncomingCommandMessage(final KafkaConsumerRecord<String, Buffer> consumerRecord) {
    Objects.requireNonNull(consumerRecord);
    final Timer.Sample timer = getMetrics().startTimer();
    final KafkaBasedCommand command;
    try {
        command = KafkaBasedCommand.from(consumerRecord);
    } catch (final IllegalArgumentException exception) {
        log.debug("command record is invalid", exception);
        return Future.failedFuture("command record is invalid");
    }
    final SpanContext spanContext = KafkaTracingHelper.extractSpanContext(tracer, consumerRecord);
    final Span currentSpan = createSpan(command.getTenant(), command.getDeviceId(), spanContext);
    KafkaTracingHelper.setRecordTags(currentSpan, consumerRecord);
    final KafkaBasedCommandContext commandContext = new KafkaBasedCommandContext(command, kafkaBasedCommandResponseSender, currentSpan);
    command.logToSpan(currentSpan);
    if (!command.isValid()) {
        log.debug("received invalid command record [{}]", command);
        return tenantClient.get(command.getTenant(), currentSpan.context()).compose(tenantConfig -> {
            commandContext.put(CommandContext.KEY_TENANT_CONFIG, tenantConfig);
            return Future.failedFuture("command is invalid");
        }).onComplete(ar -> {
            commandContext.reject("malformed command message");
            reportInvalidCommand(commandContext, timer);
        }).mapEmpty();
    }
    log.trace("received valid command record [{}]", command);
    commandQueue.add(commandContext);
    final Promise<Void> resultPromise = Promise.promise();
    final long timerId = vertx.setTimer(PROCESSING_TIMEOUT.toMillis(), tid -> {
        if (commandQueue.remove(commandContext) || !commandContext.isCompleted()) {
            log.info("command processing timed out after {}s [{}]", PROCESSING_TIMEOUT.toSeconds(), commandContext.getCommand());
            TracingHelper.logError(commandContext.getTracingSpan(), String.format("command processing timed out after %ds", PROCESSING_TIMEOUT.toSeconds()));
            final ServerErrorException error = new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE, "command processing timed out");
            commandContext.release(error);
            resultPromise.tryFail(error);
        }
    });
    mapAndDelegateIncomingCommand(commandContext, timer).onComplete(ar -> {
        vertx.cancelTimer(timerId);
        if (ar.failed()) {
            commandQueue.remove(commandContext);
        }
        Futures.tryHandleResult(resultPromise, ar);
    });
    return resultPromise.future();
}
Also used : HttpURLConnection(java.net.HttpURLConnection) KafkaBasedCommandResponseSender(org.eclipse.hono.client.command.kafka.KafkaBasedCommandResponseSender) AbstractMappingAndDelegatingCommandHandler(org.eclipse.hono.commandrouter.impl.AbstractMappingAndDelegatingCommandHandler) MessagingType(org.eclipse.hono.util.MessagingType) Timer(io.micrometer.core.instrument.Timer) KafkaBasedInternalCommandSender(org.eclipse.hono.client.command.kafka.KafkaBasedInternalCommandSender) Duration(java.time.Duration) TracingHelper(org.eclipse.hono.tracing.TracingHelper) KafkaTracingHelper(org.eclipse.hono.client.kafka.tracing.KafkaTracingHelper) Futures(org.eclipse.hono.util.Futures) Tracer(io.opentracing.Tracer) KafkaBasedCommand(org.eclipse.hono.client.command.kafka.KafkaBasedCommand) Promise(io.vertx.core.Promise) CommandContext(org.eclipse.hono.client.command.CommandContext) Vertx(io.vertx.core.Vertx) ServerErrorException(org.eclipse.hono.client.ServerErrorException) CommandTargetMapper(org.eclipse.hono.commandrouter.CommandTargetMapper) TenantClient(org.eclipse.hono.client.registry.TenantClient) Future(io.vertx.core.Future) KafkaBasedCommandContext(org.eclipse.hono.client.command.kafka.KafkaBasedCommandContext) TenantObject(org.eclipse.hono.util.TenantObject) SpanContext(io.opentracing.SpanContext) Objects(java.util.Objects) List(java.util.List) CommandRouterMetrics(org.eclipse.hono.commandrouter.CommandRouterMetrics) Buffer(io.vertx.core.buffer.Buffer) KafkaConsumerRecord(io.vertx.kafka.client.consumer.KafkaConsumerRecord) Span(io.opentracing.Span) SpanContext(io.opentracing.SpanContext) Timer(io.micrometer.core.instrument.Timer) KafkaBasedCommand(org.eclipse.hono.client.command.kafka.KafkaBasedCommand) ServerErrorException(org.eclipse.hono.client.ServerErrorException) KafkaBasedCommandContext(org.eclipse.hono.client.command.kafka.KafkaBasedCommandContext) Span(io.opentracing.Span)

Example 3 with KafkaBasedCommandContext

use of org.eclipse.hono.client.command.kafka.KafkaBasedCommandContext in project hono by eclipse.

the class KafkaBasedMappingAndDelegatingCommandHandlerTest method testCommandDelegationForValidCommand.

/**
 * Verifies the behavior of the
 * {@link KafkaBasedMappingAndDelegatingCommandHandler#mapAndDelegateIncomingCommandMessage(KafkaConsumerRecord)}
 * method in a scenario with a valid command record.
 */
@Test
public void testCommandDelegationForValidCommand() {
    // GIVEN a valid command record
    final KafkaConsumerRecord<String, Buffer> commandRecord = getCommandRecord(tenantId, deviceId, "cmd-subject", 0, 0);
    // WHEN mapping and delegating the command
    cmdHandler.mapAndDelegateIncomingCommandMessage(commandRecord);
    final ArgumentCaptor<KafkaBasedCommandContext> commandContextArgumentCaptor = ArgumentCaptor.forClass(KafkaBasedCommandContext.class);
    // THEN the message is properly delegated
    verify(internalCommandSender, times(1)).sendCommand(commandContextArgumentCaptor.capture(), eq(adapterInstanceId));
    final KafkaBasedCommandContext commandContext = commandContextArgumentCaptor.getValue();
    assertNotNull(commandContext);
    assertTrue(commandContext.getCommand().isValid());
    assertEquals(tenantId, commandContext.getCommand().getTenant());
    assertEquals(deviceId, commandContext.getCommand().getDeviceId());
    assertEquals("cmd-subject", commandContext.getCommand().getName());
}
Also used : Buffer(io.vertx.core.buffer.Buffer) ArgumentMatchers.anyString(org.mockito.ArgumentMatchers.anyString) KafkaBasedCommandContext(org.eclipse.hono.client.command.kafka.KafkaBasedCommandContext) Test(org.junit.jupiter.api.Test)

Example 4 with KafkaBasedCommandContext

use of org.eclipse.hono.client.command.kafka.KafkaBasedCommandContext in project hono by eclipse.

the class KafkaBasedMappingAndDelegatingCommandHandlerTest method testCommandDelegationTimesOutWhileCommandGetsSent.

/**
 * Verifies the behaviour of the
 * {@link KafkaBasedMappingAndDelegatingCommandHandler#mapAndDelegateIncomingCommandMessage(KafkaConsumerRecord)}
 * method in a scenario where mapping/delegation of a valid command record times out while the command gets sent.
 */
@Test
public void testCommandDelegationTimesOutWhileCommandGetsSent() {
    final AtomicReference<Handler<Long>> timerHandlerRef = new AtomicReference<>();
    when(vertx.setTimer(anyLong(), VertxMockSupport.anyHandler())).thenAnswer(invocation -> {
        final Handler<Long> handler = invocation.getArgument(1);
        timerHandlerRef.set(handler);
        return 1L;
    });
    // GIVEN a valid command record
    final KafkaConsumerRecord<String, Buffer> commandRecord = getCommandRecord(tenantId, deviceId, "cmd-subject", 0, 0);
    final Promise<Void> sendCommandPromise = Promise.promise();
    when(internalCommandSender.sendCommand(any(CommandContext.class), anyString())).thenReturn(sendCommandPromise.future());
    // WHEN mapping and delegating the command (with no timeout triggered yet)
    final Future<Void> resultFuture = cmdHandler.mapAndDelegateIncomingCommandMessage(commandRecord);
    final ArgumentCaptor<KafkaBasedCommandContext> commandContextArgumentCaptor = ArgumentCaptor.forClass(KafkaBasedCommandContext.class);
    // THEN the message is properly delegated
    verify(internalCommandSender, times(1)).sendCommand(commandContextArgumentCaptor.capture(), eq(adapterInstanceId));
    final KafkaBasedCommandContext commandContext = commandContextArgumentCaptor.getValue();
    assertNotNull(commandContext);
    assertTrue(commandContext.getCommand().isValid());
    assertEquals(tenantId, commandContext.getCommand().getTenant());
    assertEquals(deviceId, commandContext.getCommand().getDeviceId());
    assertEquals("cmd-subject", commandContext.getCommand().getName());
    assertThat(resultFuture.isComplete()).isFalse();
    // WHEN the timeout is triggered and then sending the command succeeds
    timerHandlerRef.get().handle(1L);
    sendCommandPromise.complete();
    // THEN the result future is failed because the timeout came first
    assertThat(resultFuture.failed()).isTrue();
}
Also used : Buffer(io.vertx.core.buffer.Buffer) CommandContext(org.eclipse.hono.client.command.CommandContext) KafkaBasedCommandContext(org.eclipse.hono.client.command.kafka.KafkaBasedCommandContext) Handler(io.vertx.core.Handler) AtomicReference(java.util.concurrent.atomic.AtomicReference) ArgumentMatchers.anyString(org.mockito.ArgumentMatchers.anyString) ArgumentMatchers.anyLong(org.mockito.ArgumentMatchers.anyLong) KafkaBasedCommandContext(org.eclipse.hono.client.command.kafka.KafkaBasedCommandContext) Test(org.junit.jupiter.api.Test)

Example 5 with KafkaBasedCommandContext

use of org.eclipse.hono.client.command.kafka.KafkaBasedCommandContext in project hono by eclipse.

the class KafkaCommandProcessingQueueTest method testSendActionIsInvokedInOrder.

/**
 * Verifies that the sendAction is invoked on commands in the queue in the expected order.
 *
 * @param vertx The vert.x instance to use.
 * @param ctx The vert.x test context.
 */
@Test
@SuppressWarnings("rawtypes")
void testSendActionIsInvokedInOrder(final Vertx vertx, final VertxTestContext ctx) {
    final Context vertxContext = vertx.getOrCreateContext();
    vertxContext.runOnContext(v -> {
        final KafkaCommandProcessingQueue kafkaCommandProcessingQueue = new KafkaCommandProcessingQueue(vertxContext);
        // GIVEN a number of test commands
        final LinkedList<KafkaBasedCommandContext> commandContexts = IntStream.range(0, 5).mapToObj(this::getTestCommandContext).collect(Collectors.toCollection(LinkedList::new));
        // ... added to the queue in order
        commandContexts.forEach(kafkaCommandProcessingQueue::add);
        // WHEN applying the sendAction on these commands in the reverse order
        final LinkedList<KafkaBasedCommandContext> sendActionInvoked = new LinkedList<>();
        final LinkedList<KafkaBasedCommandContext> applySendActionSucceeded = new LinkedList<>();
        final List<Future> resultFutures = new LinkedList<>();
        commandContexts.descendingIterator().forEachRemaining(context -> {
            resultFutures.add(kafkaCommandProcessingQueue.applySendCommandAction(context, () -> {
                sendActionInvoked.add(context);
                return Future.succeededFuture();
            }).onSuccess(v2 -> applySendActionSucceeded.add(context)));
        });
        CompositeFuture.all(resultFutures).onComplete(ctx.succeeding(ar -> {
            ctx.verify(() -> {
                // THEN the commands got sent in the original order
                assertThat(sendActionInvoked).isEqualTo(commandContexts);
                assertThat(applySendActionSucceeded).isEqualTo(commandContexts);
            });
            ctx.completeNow();
        }));
    });
}
Also used : VertxTestContext(io.vertx.junit5.VertxTestContext) Context(io.vertx.core.Context) KafkaBasedCommandContext(org.eclipse.hono.client.command.kafka.KafkaBasedCommandContext) IntStream(java.util.stream.IntStream) VertxTestContext(io.vertx.junit5.VertxTestContext) CommandResponseSender(org.eclipse.hono.client.command.CommandResponseSender) Constants(org.eclipse.hono.util.Constants) Context(io.vertx.core.Context) Timeout(io.vertx.junit5.Timeout) ArrayList(java.util.ArrayList) CompositeFuture(io.vertx.core.CompositeFuture) ExtendWith(org.junit.jupiter.api.extension.ExtendWith) LinkedList(java.util.LinkedList) TopicPartition(org.apache.kafka.common.TopicPartition) KafkaBasedCommand(org.eclipse.hono.client.command.kafka.KafkaBasedCommand) CommandToBeReprocessedException(org.eclipse.hono.client.command.CommandToBeReprocessedException) Vertx(io.vertx.core.Vertx) Set(java.util.Set) Mockito.when(org.mockito.Mockito.when) KafkaRecordHelper(org.eclipse.hono.client.kafka.KafkaRecordHelper) Truth.assertThat(com.google.common.truth.Truth.assertThat) VertxExtension(io.vertx.junit5.VertxExtension) Collectors(java.util.stream.Collectors) Future(io.vertx.core.Future) KafkaBasedCommandContext(org.eclipse.hono.client.command.kafka.KafkaBasedCommandContext) TimeUnit(java.util.concurrent.TimeUnit) Test(org.junit.jupiter.api.Test) HonoTopic(org.eclipse.hono.client.kafka.HonoTopic) List(java.util.List) Buffer(io.vertx.core.buffer.Buffer) KafkaConsumerRecord(io.vertx.kafka.client.consumer.KafkaConsumerRecord) Span(io.opentracing.Span) KafkaHeader(io.vertx.kafka.client.producer.KafkaHeader) Mockito.mock(org.mockito.Mockito.mock) CompositeFuture(io.vertx.core.CompositeFuture) Future(io.vertx.core.Future) KafkaBasedCommandContext(org.eclipse.hono.client.command.kafka.KafkaBasedCommandContext) LinkedList(java.util.LinkedList) Test(org.junit.jupiter.api.Test)

Aggregations

Buffer (io.vertx.core.buffer.Buffer)6 KafkaBasedCommandContext (org.eclipse.hono.client.command.kafka.KafkaBasedCommandContext)6 Span (io.opentracing.Span)3 Future (io.vertx.core.Future)3 KafkaConsumerRecord (io.vertx.kafka.client.consumer.KafkaConsumerRecord)3 KafkaBasedCommand (org.eclipse.hono.client.command.kafka.KafkaBasedCommand)3 Test (org.junit.jupiter.api.Test)3 Context (io.vertx.core.Context)2 Promise (io.vertx.core.Promise)2 Vertx (io.vertx.core.Vertx)2 KafkaHeader (io.vertx.kafka.client.producer.KafkaHeader)2 ArrayList (java.util.ArrayList)2 LinkedList (java.util.LinkedList)2 List (java.util.List)2 Objects (java.util.Objects)2 TopicPartition (org.apache.kafka.common.TopicPartition)2 ServerErrorException (org.eclipse.hono.client.ServerErrorException)2 CommandContext (org.eclipse.hono.client.command.CommandContext)2 CommandResponseSender (org.eclipse.hono.client.command.CommandResponseSender)2 CommandToBeReprocessedException (org.eclipse.hono.client.command.CommandToBeReprocessedException)2