use of org.eclipse.hono.client.kafka.HonoTopic in project hono by eclipse.
the class KafkaBasedCommandSender method sendCommand.
private Future<Void> sendCommand(final String tenantId, final String deviceId, final String command, final String contentType, final Buffer data, final String correlationId, final Map<String, Object> properties, final boolean responseRequired, final String spanOperationName, final SpanContext context) {
final HonoTopic topic = new HonoTopic(HonoTopic.Type.COMMAND, tenantId);
final Map<String, Object> headerProperties = getHeaderProperties(deviceId, command, contentType, correlationId, responseRequired, properties);
final String topicName = topic.toString();
final Span currentSpan = startChildSpan(spanOperationName, topicName, tenantId, deviceId, context);
return sendAndWaitForOutcome(topicName, tenantId, deviceId, data, headerProperties, currentSpan).onComplete(ar -> currentSpan.finish());
}
use of org.eclipse.hono.client.kafka.HonoTopic in project hono by eclipse.
the class KafkaApplicationClientImpl method createKafkaBasedDownstreamMessageConsumer.
private Future<MessageConsumer> createKafkaBasedDownstreamMessageConsumer(final String tenantId, final HonoTopic.Type type, final Handler<DownstreamMessage<KafkaMessageContext>> messageHandler) {
Objects.requireNonNull(tenantId);
Objects.requireNonNull(type);
Objects.requireNonNull(messageHandler);
final String topic = new HonoTopic(type, tenantId).toString();
final Handler<KafkaConsumerRecord<String, Buffer>> recordHandler = record -> {
messageHandler.handle(new KafkaDownstreamMessage(record));
};
final HonoKafkaConsumer consumer = new HonoKafkaConsumer(vertx, Set.of(topic), recordHandler, consumerConfig.getConsumerConfig(type.toString()));
consumer.setPollTimeout(Duration.ofMillis(consumerConfig.getPollTimeout()));
Optional.ofNullable(kafkaConsumerSupplier).ifPresent(consumer::setKafkaConsumerSupplier);
return consumer.start().map(v -> (MessageConsumer) new MessageConsumer() {
@Override
public Future<Void> close() {
return consumer.stop();
}
}).onSuccess(consumersToCloseOnStop::add);
}
use of org.eclipse.hono.client.kafka.HonoTopic in project hono by eclipse.
the class KafkaBasedInternalCommandConsumerTest method getCommandRecord.
private KafkaConsumerRecord<String, Buffer> getCommandRecord(final String key, final List<KafkaHeader> headers) {
final String adapterInstanceId = "the-adapter-instance-id";
final String topic = new HonoTopic(HonoTopic.Type.COMMAND_INTERNAL, adapterInstanceId).toString();
return KafkaClientUnitTestHelper.newMockConsumerRecord(topic, key, headers);
}
use of org.eclipse.hono.client.kafka.HonoTopic in project hono by eclipse.
the class CommandAndControlAmqpIT method testSendCommandViaKafkaFailsForMalformedMessage.
/**
* Verifies that the adapter rejects malformed command messages sent by applications.
* <p>
* This test is applicable only if the messaging network type is Kafka.
*
* @param endpointConfig The endpoints to use for sending/receiving commands.
* @param ctx The vert.x test context.
* @throws InterruptedException if not all commands and responses are exchanged in time.
*/
@ParameterizedTest(name = IntegrationTestSupport.PARAMETERIZED_TEST_NAME_PATTERN)
@MethodSource("allCombinations")
@Timeout(timeUnit = TimeUnit.SECONDS, value = 10)
@AssumeMessagingSystem(type = MessagingType.kafka)
public void testSendCommandViaKafkaFailsForMalformedMessage(final AmqpCommandEndpointConfiguration endpointConfig, final VertxTestContext ctx) throws InterruptedException {
final String commandTargetDeviceId = endpointConfig.isSubscribeAsGateway() ? helper.setupGatewayDeviceBlocking(tenantId, deviceId, 5) : deviceId;
final AtomicReference<GenericKafkaSender> kafkaSenderRef = new AtomicReference<>();
final CountDownLatch expectedCommandResponses = new CountDownLatch(1);
final VertxTestContext setup = new VertxTestContext();
final Checkpoint setupDone = setup.checkpoint();
final Checkpoint ttdReceivedPrecondition = setup.checkpoint();
final Future<MessageConsumer> kafkaAsyncErrorResponseConsumer = helper.createDeliveryFailureCommandResponseConsumer(ctx, tenantId, HttpURLConnection.HTTP_BAD_REQUEST, response -> {
ctx.verify(() -> {
DownstreamMessageAssertions.assertMessageContainsTimeToLive(response, TTL_COMMAND_RESPONSE);
});
expectedCommandResponses.countDown();
}, null);
connectToAdapter(tenantId, deviceId, password, () -> createEventConsumer(tenantId, msg -> {
// expect empty notification with TTD -1
setup.verify(() -> assertThat(msg.getContentType()).isEqualTo(EventConstants.CONTENT_TYPE_EMPTY_NOTIFICATION));
final TimeUntilDisconnectNotification notification = msg.getTimeUntilDisconnectNotification().orElse(null);
log.debug("received notification [{}]", notification);
setup.verify(() -> assertThat(notification).isNotNull());
if (notification.getTtd() == -1) {
ttdReceivedPrecondition.flag();
}
})).compose(con -> subscribeToCommands(endpointConfig, tenantId, commandTargetDeviceId).onSuccess(recv -> recv.handler((delivery, msg) -> ctx.failNow(new IllegalStateException("should not have received command"))))).compose(ok -> helper.createGenericKafkaSender().onSuccess(kafkaSenderRef::set).mapEmpty()).compose(v -> kafkaAsyncErrorResponseConsumer).onComplete(setup.succeeding(v -> setupDone.flag()));
assertWithMessage("setup of adapter finished within %s seconds", IntegrationTestSupport.getTestSetupTimeout()).that(setup.awaitCompletion(IntegrationTestSupport.getTestSetupTimeout(), TimeUnit.SECONDS)).isTrue();
if (setup.failed()) {
ctx.failNow(setup.causeOfFailure());
return;
}
final String commandTopic = new HonoTopic(HonoTopic.Type.COMMAND, tenantId).toString();
log.debug("sending command message lacking subject and correlation ID - no failure response expected here");
final Map<String, Object> properties1 = Map.of(MessageHelper.APP_PROPERTY_DEVICE_ID, deviceId, MessageHelper.SYS_PROPERTY_CONTENT_TYPE, MessageHelper.CONTENT_TYPE_OCTET_STREAM, KafkaRecordHelper.HEADER_RESPONSE_REQUIRED, true);
kafkaSenderRef.get().sendAndWaitForOutcome(commandTopic, tenantId, deviceId, Buffer.buffer(), properties1).onComplete(ctx.succeeding(ok -> {
}));
log.debug("sending command message lacking subject");
final String correlationId = "1";
final Map<String, Object> properties2 = Map.of(MessageHelper.SYS_PROPERTY_CORRELATION_ID, correlationId, MessageHelper.APP_PROPERTY_DEVICE_ID, deviceId, MessageHelper.SYS_PROPERTY_CONTENT_TYPE, MessageHelper.CONTENT_TYPE_OCTET_STREAM, KafkaRecordHelper.HEADER_RESPONSE_REQUIRED, true);
kafkaSenderRef.get().sendAndWaitForOutcome(commandTopic, tenantId, deviceId, Buffer.buffer(), properties2).onComplete(ctx.succeeding(ok -> {
}));
final long timeToWait = 2500;
if (!expectedCommandResponses.await(timeToWait, TimeUnit.MILLISECONDS)) {
log.info("Timeout of {} milliseconds reached, stop waiting for command response", timeToWait);
}
kafkaAsyncErrorResponseConsumer.result().close().onComplete(ar -> {
if (expectedCommandResponses.getCount() == 0) {
ctx.completeNow();
} else {
ctx.failNow(new IllegalStateException("did not receive command response"));
}
});
}
use of org.eclipse.hono.client.kafka.HonoTopic in project hono by eclipse.
the class KafkaBasedCommandTest method testFromRecordFailsForRecordWithoutDeviceId.
/**
* Verifies that a command cannot be created from a record that doesn't contain a <em>device_id</em> header.
*/
@Test
public void testFromRecordFailsForRecordWithoutDeviceId() {
final String topic = new HonoTopic(HonoTopic.Type.COMMAND, Constants.DEFAULT_TENANT).toString();
final String deviceId = "4711";
final String subject = "doThis";
final List<KafkaHeader> headers = List.of(KafkaRecordHelper.createSubjectHeader(subject));
final KafkaConsumerRecord<String, Buffer> commandRecord = getCommandRecord(topic, deviceId, headers);
assertThrows(IllegalArgumentException.class, () -> {
KafkaBasedCommand.from(commandRecord);
});
}
Aggregations