use of org.eclipse.hono.client.kafka.HonoTopic in project hono by eclipse.
the class KafkaBasedCommandTest method testFromRecordFailsForMissingCorrelationIdWithResponseRequired.
/**
* Verifies that a valid command cannot be created from a record that has the <em>response-required</em>
* header set to "true" but has no <em>correlation-id</em> header.
*/
@Test
public void testFromRecordFailsForMissingCorrelationIdWithResponseRequired() {
final String topic = new HonoTopic(HonoTopic.Type.COMMAND, Constants.DEFAULT_TENANT).toString();
final String deviceId = "4711";
final String subject = "doThis";
final List<KafkaHeader> headers = new ArrayList<>(getHeaders(deviceId, subject));
headers.add(KafkaRecordHelper.createResponseRequiredHeader(true));
final KafkaConsumerRecord<String, Buffer> commandRecord = getCommandRecord(topic, deviceId, headers);
final KafkaBasedCommand cmd = KafkaBasedCommand.from(commandRecord);
assertFalse(cmd.isValid());
assertThat(cmd.getInvalidCommandReason()).contains("correlation-id");
}
use of org.eclipse.hono.client.kafka.HonoTopic in project hono by eclipse.
the class KafkaBasedCommandTest method testGetDeliveryFailureNotificationProperties.
/**
* Verifies the return value of getDeliveryFailureNotificationProperties().
*/
@Test
public void testGetDeliveryFailureNotificationProperties() {
final String topic = new HonoTopic(HonoTopic.Type.COMMAND, Constants.DEFAULT_TENANT).toString();
final String correlationId = "the-correlation-id";
final String deviceId = "4711";
final String subject = "doThis";
final List<KafkaHeader> headers = new ArrayList<>(getHeaders(deviceId, subject, correlationId));
headers.add(KafkaHeader.header(KafkaBasedCommand.DELIVERY_FAILURE_NOTIFICATION_METADATA_PREFIX, "v1"));
headers.add(KafkaHeader.header(KafkaBasedCommand.DELIVERY_FAILURE_NOTIFICATION_METADATA_PREFIX, "toBeIgnored"));
headers.add(KafkaHeader.header(KafkaBasedCommand.DELIVERY_FAILURE_NOTIFICATION_METADATA_PREFIX + "-title", "v2"));
final KafkaConsumerRecord<String, Buffer> commandRecord = getCommandRecord(topic, deviceId, headers);
final KafkaBasedCommand cmd = KafkaBasedCommand.from(commandRecord);
final Map<String, String> deliveryFailureNotificationProperties = cmd.getDeliveryFailureNotificationProperties();
assertThat(deliveryFailureNotificationProperties.size()).isEqualTo(2);
assertThat(deliveryFailureNotificationProperties.get(KafkaBasedCommand.DELIVERY_FAILURE_NOTIFICATION_METADATA_PREFIX)).isEqualTo("v1");
assertThat(deliveryFailureNotificationProperties.get(KafkaBasedCommand.DELIVERY_FAILURE_NOTIFICATION_METADATA_PREFIX + "-title")).isEqualTo("v2");
}
use of org.eclipse.hono.client.kafka.HonoTopic in project hono by eclipse.
the class CommandAndControlMqttIT 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 = 20)
@AssumeMessagingSystem(type = MessagingType.kafka)
public void testSendCommandViaKafkaFailsForMalformedMessage(final MqttCommandEndpointConfiguration 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 ready = setup.checkpoint(2);
final Future<MessageConsumer> kafkaAsyncErrorResponseConsumer = helper.createDeliveryFailureCommandResponseConsumer(ctx, tenantId, HttpURLConnection.HTTP_BAD_REQUEST, response -> expectedCommandResponses.countDown(), null);
createConsumer(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);
LOGGER.info("received notification [{}]", notification);
if (notification.getTtd() == -1) {
ready.flag();
}
}).compose(consumer -> helper.registry.addDeviceToTenant(tenantId, deviceId, password)).compose(ok -> connectToAdapter(IntegrationTestSupport.getUsername(deviceId, tenantId), password)).compose(conAck -> subscribeToCommands(commandTargetDeviceId, msg -> {
// all commands should get rejected because they fail to pass the validity check
ctx.failNow(new IllegalStateException("should not have received command"));
}, endpointConfig, MqttQoS.AT_MOST_ONCE)).compose(ok -> helper.createGenericKafkaSender().onSuccess(kafkaSenderRef::set).mapEmpty()).compose(ok -> kafkaAsyncErrorResponseConsumer).onComplete(setup.succeeding(v -> ready.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();
LOGGER.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 -> {
}));
LOGGER.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)) {
LOGGER.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 java.lang.IllegalStateException("did not receive command response"));
}
});
}
use of org.eclipse.hono.client.kafka.HonoTopic in project hono by eclipse.
the class KafkaBasedEventSender method sendEvent.
@Override
public Future<Void> sendEvent(final TenantObject tenant, final RegistrationAssertion device, final String contentType, final Buffer payload, final Map<String, Object> properties, final SpanContext context) {
Objects.requireNonNull(tenant);
Objects.requireNonNull(device);
if (log.isTraceEnabled()) {
log.trace("sending event [tenantId: {}, deviceId: {}, contentType: {}, properties: {}]", tenant.getTenantId(), device.getDeviceId(), contentType, properties);
}
final HonoTopic topic = new HonoTopic(HonoTopic.Type.EVENT, tenant.getTenantId());
final Map<String, Object> propsWithDefaults = addDefaults(topic.getType().endpoint, tenant, device, QoS.AT_LEAST_ONCE, contentType, payload, properties);
final String topicName = topic.toString();
final Span currentSpan = startChildSpan("forward Event", topicName, tenant.getTenantId(), device.getDeviceId(), context);
return sendAndWaitForOutcome(topic.toString(), tenant.getTenantId(), device.getDeviceId(), payload, propsWithDefaults, currentSpan).onComplete(ar -> currentSpan.finish());
}
use of org.eclipse.hono.client.kafka.HonoTopic in project hono by eclipse.
the class KafkaBasedCommandConsumerFactoryImpl method createCommandConsumer.
@Override
public Future<Void> createCommandConsumer(final String tenantId, final SpanContext context) {
final String topic = new HonoTopic(HonoTopic.Type.COMMAND, tenantId).toString();
if (kafkaConsumer.isAmongKnownSubscribedTopics(topic)) {
LOG.debug("createCommandConsumer: topic is already subscribed [{}]", topic);
return Future.succeededFuture();
}
LOG.debug("createCommandConsumer: topic not subscribed; check for its existence, triggering auto-creation if enabled [{}]", topic);
final Span span = TracingHelper.buildServerChildSpan(tracer, context, "wait for topic subscription update", CommandConsumerFactory.class.getSimpleName()).start();
TracingHelper.TAG_TENANT_ID.set(span, tenantId);
Tags.MESSAGE_BUS_DESTINATION.set(span, topic);
return kafkaConsumer.ensureTopicIsAmongSubscribedTopicPatternTopics(topic).onComplete(ar -> {
if (ar.failed()) {
TracingHelper.logError(span, ar.cause());
}
span.finish();
});
}
Aggregations