Search in sources :

Example 6 with MessageConsumer

use of org.eclipse.hono.application.client.MessageConsumer in project hono by eclipse.

the class CommandAndControlAmqpIT method testSendCommandFailsForCommandNotAcknowledgedByDevice.

/**
 * Verifies that the adapter forwards the <em>released</em> disposition back to the
 * application if the device hasn't sent a disposition update for the delivery of
 * the command message sent to the device.
 * <p>
 * If Kafka is used, this means a corresponding error command response is published.
 *
 * @param ctx The vert.x test context.
 * @throws InterruptedException if not all commands and responses are exchanged in time.
 */
@Test
@Timeout(timeUnit = TimeUnit.SECONDS, value = 10)
public void testSendCommandFailsForCommandNotAcknowledgedByDevice(final VertxTestContext ctx) throws InterruptedException {
    final AmqpCommandEndpointConfiguration endpointConfig = new AmqpCommandEndpointConfiguration(SubscriberRole.DEVICE);
    final String commandTargetDeviceId = endpointConfig.isSubscribeAsGateway() ? helper.setupGatewayDeviceBlocking(tenantId, deviceId, 5) : deviceId;
    final AtomicInteger receivedMessagesCounter = new AtomicInteger(0);
    final int totalNoOfCommandsToSend = 2;
    // command handler won't send a disposition update
    connectAndSubscribe(ctx, commandTargetDeviceId, endpointConfig, (cmdReceiver, cmdResponseSender) -> createNotSendingDeliveryUpdateCommandConsumer(ctx, cmdReceiver, receivedMessagesCounter), totalNoOfCommandsToSend);
    if (ctx.failed()) {
        return;
    }
    final String replyId = "reply-id";
    final CountDownLatch commandsFailed = new CountDownLatch(totalNoOfCommandsToSend);
    final AtomicInteger commandsSent = new AtomicInteger(0);
    final AtomicLong lastReceivedTimestamp = new AtomicLong();
    final long start = System.currentTimeMillis();
    final long commandTimeout = IntegrationTestSupport.getSendCommandTimeout();
    final Handler<Void> failureNotificationReceivedHandler = v -> {
        lastReceivedTimestamp.set(System.currentTimeMillis());
        commandsFailed.countDown();
    };
    final VertxTestContext setup = new VertxTestContext();
    final Future<MessageConsumer> kafkaAsyncErrorResponseConsumer = IntegrationTestSupport.isUsingKafkaMessaging() ? helper.createDeliveryFailureCommandResponseConsumer(ctx, tenantId, HttpURLConnection.HTTP_UNAVAILABLE, response -> failureNotificationReceivedHandler.handle(null), null) : Future.succeededFuture(null);
    kafkaAsyncErrorResponseConsumer.onComplete(setup.succeedingThenComplete());
    assertWithMessage("setup of command response consumer finished within %s seconds", IntegrationTestSupport.getTestSetupTimeout()).that(setup.awaitCompletion(IntegrationTestSupport.getTestSetupTimeout(), TimeUnit.SECONDS)).isTrue();
    if (setup.failed()) {
        ctx.failNow(setup.causeOfFailure());
        return;
    }
    while (commandsSent.get() < totalNoOfCommandsToSend) {
        final CountDownLatch commandSent = new CountDownLatch(1);
        context.runOnContext(go -> {
            final String correlationId = String.valueOf(commandsSent.getAndIncrement());
            final Buffer msg = Buffer.buffer("value: " + commandsSent.get());
            helper.applicationClient.sendAsyncCommand(tenantId, commandTargetDeviceId, "setValue", "text/plain", msg, correlationId, replyId, null).onComplete(sendAttempt -> {
                if (IntegrationTestSupport.isUsingAmqpMessaging()) {
                    if (sendAttempt.succeeded()) {
                        log.debug("sending command {} succeeded unexpectedly", commandsSent.get());
                    } else {
                        if (sendAttempt.cause() instanceof ServerErrorException && ((ServerErrorException) sendAttempt.cause()).getErrorCode() == HttpURLConnection.HTTP_UNAVAILABLE && !(sendAttempt.cause() instanceof SendMessageTimeoutException)) {
                            log.debug("sending command {} failed as expected: {}", commandsSent.get(), sendAttempt.cause().toString());
                            failureNotificationReceivedHandler.handle(null);
                        } else {
                            log.debug("sending command {} failed with an unexpected error", commandsSent.get(), sendAttempt.cause());
                        }
                    }
                } else if (sendAttempt.failed()) {
                    log.debug("sending command {} via Kafka failed unexpectedly", commandsSent.get(), sendAttempt.cause());
                }
                commandSent.countDown();
            });
        });
        commandSent.await();
    }
    final long timeToWait = 300 + (totalNoOfCommandsToSend * commandTimeout);
    if (!commandsFailed.await(timeToWait, TimeUnit.MILLISECONDS)) {
        log.info("Timeout of {} milliseconds reached, stop waiting for commands", timeToWait);
    }
    assertThat(receivedMessagesCounter.get()).isEqualTo(totalNoOfCommandsToSend);
    final long commandsCompleted = totalNoOfCommandsToSend - commandsFailed.getCount();
    log.info("commands sent: {}, commands failed: {} after {} milliseconds", commandsSent.get(), commandsCompleted, lastReceivedTimestamp.get() - start);
    Optional.ofNullable(kafkaAsyncErrorResponseConsumer.result()).map(MessageConsumer::close).orElseGet(Future::succeededFuture).onComplete(ar -> {
        if (commandsCompleted == commandsSent.get()) {
            ctx.completeNow();
        } else {
            ctx.failNow(new IllegalStateException("did not complete all commands sent"));
        }
    });
}
Also used : HttpURLConnection(java.net.HttpURLConnection) ProtonConnection(io.vertx.proton.ProtonConnection) ProtonReceiver(io.vertx.proton.ProtonReceiver) BeforeEach(org.junit.jupiter.api.BeforeEach) DownstreamMessage(org.eclipse.hono.application.client.DownstreamMessage) BiFunction(java.util.function.BiFunction) Tenant(org.eclipse.hono.service.management.tenant.Tenant) HonoProtonHelper(org.eclipse.hono.util.HonoProtonHelper) Timeout(io.vertx.junit5.Timeout) MessagingType(org.eclipse.hono.util.MessagingType) IntegrationTestSupport(org.eclipse.hono.tests.IntegrationTestSupport) ExtendWith(org.junit.jupiter.api.extension.ExtendWith) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) ProtonMessageHandler(io.vertx.proton.ProtonMessageHandler) Duration(java.time.Duration) Map(java.util.Map) AmqpError(org.apache.qpid.proton.amqp.transport.AmqpError) MethodSource(org.junit.jupiter.params.provider.MethodSource) ResourceLimits(org.eclipse.hono.util.ResourceLimits) MessageContext(org.eclipse.hono.application.client.MessageContext) SubscriberRole(org.eclipse.hono.tests.CommandEndpointConfiguration.SubscriberRole) Truth.assertWithMessage(com.google.common.truth.Truth.assertWithMessage) DownstreamMessageAssertions(org.eclipse.hono.tests.DownstreamMessageAssertions) ProtonQoS(io.vertx.proton.ProtonQoS) MessageHelper(org.eclipse.hono.util.MessageHelper) VertxExtension(io.vertx.junit5.VertxExtension) EventConstants(org.eclipse.hono.util.EventConstants) Future(io.vertx.core.Future) Test(org.junit.jupiter.api.Test) CountDownLatch(java.util.concurrent.CountDownLatch) ErrorCondition(org.apache.qpid.proton.amqp.transport.ErrorCondition) Stream(java.util.stream.Stream) Buffer(io.vertx.core.buffer.Buffer) Optional(java.util.Optional) ProtonSender(io.vertx.proton.ProtonSender) Checkpoint(io.vertx.junit5.Checkpoint) Accepted(org.apache.qpid.proton.amqp.messaging.Accepted) VertxTestContext(io.vertx.junit5.VertxTestContext) GenericKafkaSender(org.eclipse.hono.tests.GenericKafkaSender) Rejected(org.apache.qpid.proton.amqp.messaging.Rejected) ClientErrorException(org.eclipse.hono.client.ClientErrorException) AtomicReference(java.util.concurrent.atomic.AtomicReference) Function(java.util.function.Function) Supplier(java.util.function.Supplier) Constants(org.eclipse.hono.util.Constants) TimeUntilDisconnectNotification(org.eclipse.hono.util.TimeUntilDisconnectNotification) Message(org.apache.qpid.proton.message.Message) Promise(io.vertx.core.Promise) ServerErrorException(org.eclipse.hono.client.ServerErrorException) ProtonHelper(io.vertx.proton.ProtonHelper) KafkaRecordHelper(org.eclipse.hono.client.kafka.KafkaRecordHelper) Truth.assertThat(com.google.common.truth.Truth.assertThat) AssumeMessagingSystem(org.eclipse.hono.tests.AssumeMessagingSystem) TimeUnit(java.util.concurrent.TimeUnit) AtomicLong(java.util.concurrent.atomic.AtomicLong) HonoTopic(org.eclipse.hono.client.kafka.HonoTopic) ParameterizedTest(org.junit.jupiter.params.ParameterizedTest) MessageConsumer(org.eclipse.hono.application.client.MessageConsumer) SendMessageTimeoutException(org.eclipse.hono.client.SendMessageTimeoutException) NoopSpan(io.opentracing.noop.NoopSpan) GenericSenderLink(org.eclipse.hono.client.amqp.GenericSenderLink) Handler(io.vertx.core.Handler) Buffer(io.vertx.core.buffer.Buffer) MessageConsumer(org.eclipse.hono.application.client.MessageConsumer) VertxTestContext(io.vertx.junit5.VertxTestContext) CountDownLatch(java.util.concurrent.CountDownLatch) Checkpoint(io.vertx.junit5.Checkpoint) AtomicLong(java.util.concurrent.atomic.AtomicLong) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) ServerErrorException(org.eclipse.hono.client.ServerErrorException) SendMessageTimeoutException(org.eclipse.hono.client.SendMessageTimeoutException) Test(org.junit.jupiter.api.Test) ParameterizedTest(org.junit.jupiter.params.ParameterizedTest) Timeout(io.vertx.junit5.Timeout)

Example 7 with MessageConsumer

use of org.eclipse.hono.application.client.MessageConsumer in project hono by eclipse.

the class HonoExampleApplicationBase method consumeData.

/**
 * Start the application client and set the message handling method to treat data that is received.
 */
protected void consumeData() {
    final CompletableFuture<ApplicationClient<? extends MessageContext>> startup = new CompletableFuture<>();
    if (client instanceof AmqpApplicationClient) {
        final AmqpApplicationClient ac = (AmqpApplicationClient) client;
        ac.addDisconnectListener(c -> LOG.info("lost connection to Hono, trying to reconnect ..."));
        ac.addReconnectListener(c -> LOG.info("reconnected to Hono"));
    }
    client.start().compose(v -> CompositeFuture.all(createEventConsumer(), createTelemetryConsumer())).onSuccess(ok -> startup.complete(client)).onFailure(startup::completeExceptionally);
    try {
        startup.join();
        LOG.info("Consumer ready for telemetry and event messages");
        System.in.read();
    } catch (final CompletionException e) {
        LOG.error("{} consumer failed to start [{}:{}]", USE_KAFKA ? "Kafka" : "AMQP", HonoExampleConstants.HONO_MESSAGING_HOST, port, e.getCause());
    } catch (final IOException e) {
    // nothing we can do
    }
    final CompletableFuture<ApplicationClient<? extends MessageContext>> shutDown = new CompletableFuture<>();
    @SuppressWarnings("rawtypes") final List<Future> closeFutures = new ArrayList<>();
    Optional.ofNullable(eventConsumer).map(MessageConsumer::close).ifPresent(closeFutures::add);
    Optional.ofNullable(telemetryConsumer).map(MessageConsumer::close).ifPresent(closeFutures::add);
    Optional.ofNullable(client).map(Lifecycle::stop).ifPresent(closeFutures::add);
    CompositeFuture.join(closeFutures).compose(ok -> vertx.close()).recover(t -> vertx.close()).onComplete(ar -> shutDown.complete(client));
    // wait for clients to be closed
    shutDown.join();
    LOG.info("Consumer has been shut down");
}
Also used : ApplicationClient(org.eclipse.hono.application.client.ApplicationClient) MessagingKafkaConsumerConfigProperties(org.eclipse.hono.client.kafka.consumer.MessagingKafkaConsumerConfigProperties) DownstreamMessage(org.eclipse.hono.application.client.DownstreamMessage) MessagingKafkaProducerConfigProperties(org.eclipse.hono.client.kafka.producer.MessagingKafkaProducerConfigProperties) Lifecycle(org.eclipse.hono.util.Lifecycle) LoggerFactory(org.slf4j.LoggerFactory) HashMap(java.util.HashMap) Random(java.util.Random) CompletableFuture(java.util.concurrent.CompletableFuture) ServiceInvocationException(org.eclipse.hono.client.ServiceInvocationException) TimeUntilDisconnectNotification(org.eclipse.hono.util.TimeUntilDisconnectNotification) ArrayList(java.util.ArrayList) CompositeFuture(io.vertx.core.CompositeFuture) ProtonBasedApplicationClient(org.eclipse.hono.application.client.amqp.ProtonBasedApplicationClient) Map(java.util.Map) JsonObject(io.vertx.core.json.JsonObject) HonoConnection(org.eclipse.hono.client.HonoConnection) ClientConfigProperties(org.eclipse.hono.config.ClientConfigProperties) Logger(org.slf4j.Logger) MessageContext(org.eclipse.hono.application.client.MessageContext) KafkaApplicationClientImpl(org.eclipse.hono.application.client.kafka.impl.KafkaApplicationClientImpl) CachingKafkaProducerFactory(org.eclipse.hono.client.kafka.producer.CachingKafkaProducerFactory) Vertx(io.vertx.core.Vertx) IOException(java.io.IOException) CompletionException(java.util.concurrent.CompletionException) Future(io.vertx.core.Future) List(java.util.List) KafkaProducerFactory(org.eclipse.hono.client.kafka.producer.KafkaProducerFactory) Buffer(io.vertx.core.buffer.Buffer) AmqpApplicationClient(org.eclipse.hono.application.client.amqp.AmqpApplicationClient) Optional(java.util.Optional) MessageConsumer(org.eclipse.hono.application.client.MessageConsumer) CommonKafkaClientConfigProperties(org.eclipse.hono.client.kafka.CommonKafkaClientConfigProperties) Handler(io.vertx.core.Handler) ApplicationClient(org.eclipse.hono.application.client.ApplicationClient) ProtonBasedApplicationClient(org.eclipse.hono.application.client.amqp.ProtonBasedApplicationClient) AmqpApplicationClient(org.eclipse.hono.application.client.amqp.AmqpApplicationClient) ArrayList(java.util.ArrayList) IOException(java.io.IOException) AmqpApplicationClient(org.eclipse.hono.application.client.amqp.AmqpApplicationClient) CompletableFuture(java.util.concurrent.CompletableFuture) CompletionException(java.util.concurrent.CompletionException) CompletableFuture(java.util.concurrent.CompletableFuture) CompositeFuture(io.vertx.core.CompositeFuture) Future(io.vertx.core.Future) MessageContext(org.eclipse.hono.application.client.MessageContext)

Example 8 with MessageConsumer

use of org.eclipse.hono.application.client.MessageConsumer in project hono by eclipse.

the class CoapTestBase method testUploadMessages.

/**
 * Uploads messages to the CoAP endpoint.
 *
 * @param ctx The test context to run on.
 * @param tenantId The tenant that the device belongs to.
 * @param warmUp A sender of messages used to warm up the adapter before running the test itself or {@code null} if
 *            no warm up should be performed.
 * @param messageConsumer Consumer that is invoked when a message was received.
 * @param requestSender The test device that will publish the data.
 * @param numberOfMessages The number of messages that are uploaded.
 * @param expectedQos The expected QoS level, may be {@code null} leading to expecting the default for event or telemetry.
 * @throws InterruptedException if the test is interrupted before it has finished.
 */
protected void testUploadMessages(final VertxTestContext ctx, final String tenantId, final Supplier<Future<Void>> warmUp, final Consumer<DownstreamMessage<? extends MessageContext>> messageConsumer, final Function<Integer, Future<OptionSet>> requestSender, final int numberOfMessages, final QoS expectedQos) throws InterruptedException {
    final CountDownLatch received = new CountDownLatch(numberOfMessages);
    final VertxTestContext setup = new VertxTestContext();
    createConsumer(tenantId, msg -> {
        ctx.verify(() -> {
            logger.trace("received {}", msg);
            DownstreamMessageAssertions.assertTelemetryMessageProperties(msg, tenantId);
            assertThat(msg.getQos()).isEqualTo(getExpectedQoS(expectedQos));
            assertAdditionalMessageProperties(msg);
            if (messageConsumer != null) {
                messageConsumer.accept(msg);
            }
        });
        received.countDown();
        if (received.getCount() % 20 == 0) {
            logger.info("messages received: {}", numberOfMessages - received.getCount());
        }
    }).compose(ok -> Optional.ofNullable(warmUp).map(w -> w.get()).orElseGet(() -> Future.succeededFuture())).onComplete(setup.succeedingThenComplete());
    ctx.verify(() -> assertThat(setup.awaitCompletion(5, TimeUnit.SECONDS)).isTrue());
    final long start = System.currentTimeMillis();
    final AtomicInteger messageCount = new AtomicInteger(0);
    while (messageCount.get() < numberOfMessages && !ctx.failed()) {
        final CountDownLatch sending = new CountDownLatch(1);
        requestSender.apply(messageCount.getAndIncrement()).compose(this::assertCoapResponse).onComplete(attempt -> {
            if (attempt.succeeded()) {
                logger.debug("sent message {}", messageCount.get());
            } else {
                logger.info("failed to send message {}: {}", messageCount.get(), attempt.cause().getMessage());
                ctx.failNow(attempt.cause());
            }
            sending.countDown();
        });
        if (messageCount.get() % 20 == 0) {
            logger.info("messages sent: {}", messageCount.get());
        }
        sending.await();
    }
    if (ctx.failed()) {
        return;
    }
    final long timeToWait = Math.max(TEST_TIMEOUT_MILLIS - 1000, Math.round(numberOfMessages * 20));
    if (received.await(timeToWait, TimeUnit.MILLISECONDS)) {
        logger.info("sent {} and received {} messages after {} milliseconds", messageCount, numberOfMessages - received.getCount(), System.currentTimeMillis() - start);
        ctx.completeNow();
    } else {
        logger.info("sent {} and received {} messages after {} milliseconds", messageCount, numberOfMessages - received.getCount(), System.currentTimeMillis() - start);
        ctx.failNow(new AssertionError("did not receive all messages sent"));
    }
}
Also used : HttpURLConnection(java.net.HttpURLConnection) X509Certificate(java.security.cert.X509Certificate) ResponseCode(org.eclipse.californium.core.coap.CoAP.ResponseCode) KeyPair(java.security.KeyPair) BeforeEach(org.junit.jupiter.api.BeforeEach) Arrays(java.util.Arrays) DownstreamMessage(org.eclipse.hono.application.client.DownstreamMessage) AdvancedPskStore(org.eclipse.californium.scandium.dtls.pskstore.AdvancedPskStore) URISyntaxException(java.net.URISyntaxException) LoggerFactory(org.slf4j.LoggerFactory) CertificateType(org.eclipse.californium.scandium.dtls.CertificateType) Tenant(org.eclipse.hono.service.management.tenant.Tenant) Timeout(io.vertx.junit5.Timeout) CoapResponse(org.eclipse.californium.core.CoapResponse) NetworkConfig(org.eclipse.californium.core.network.config.NetworkConfig) GeneralSecurityException(java.security.GeneralSecurityException) DTLSConnector(org.eclipse.californium.scandium.DTLSConnector) IntegrationTestSupport(org.eclipse.hono.tests.IntegrationTestSupport) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) Utils(org.eclipse.californium.core.Utils) JsonObject(io.vertx.core.json.JsonObject) URI(java.net.URI) Tenants(org.eclipse.hono.tests.Tenants) MethodSource(org.junit.jupiter.params.provider.MethodSource) Device(org.eclipse.hono.service.management.device.Device) DtlsConnectorConfig(org.eclipse.californium.scandium.config.DtlsConnectorConfig) MessageContext(org.eclipse.hono.application.client.MessageContext) SubscriberRole(org.eclipse.hono.tests.CommandEndpointConfiguration.SubscriberRole) Truth.assertWithMessage(com.google.common.truth.Truth.assertWithMessage) DownstreamMessageAssertions(org.eclipse.hono.tests.DownstreamMessageAssertions) InetSocketAddress(java.net.InetSocketAddress) Future(io.vertx.core.Future) StandardCharsets(java.nio.charset.StandardCharsets) TestInfo(org.junit.jupiter.api.TestInfo) Test(org.junit.jupiter.api.Test) CountDownLatch(java.util.concurrent.CountDownLatch) List(java.util.List) Stream(java.util.stream.Stream) Request(org.eclipse.californium.core.coap.Request) Buffer(io.vertx.core.buffer.Buffer) Optional(java.util.Optional) QoS(org.eclipse.hono.util.QoS) VertxTestContext(io.vertx.junit5.VertxTestContext) CoapClient(org.eclipse.californium.core.CoapClient) X500Principal(javax.security.auth.x500.X500Principal) KeyLoader(org.eclipse.hono.config.KeyLoader) ServiceInvocationException(org.eclipse.hono.client.ServiceInvocationException) Function(java.util.function.Function) Supplier(java.util.function.Supplier) Constants(org.eclipse.hono.util.Constants) CoapEndpoint(org.eclipse.californium.core.network.CoapEndpoint) MediaTypeRegistry(org.eclipse.californium.core.coap.MediaTypeRegistry) StaticNewAdvancedCertificateVerifier(org.eclipse.californium.scandium.dtls.x509.StaticNewAdvancedCertificateVerifier) AsyncResult(io.vertx.core.AsyncResult) CommandConstants(org.eclipse.hono.util.CommandConstants) RegistryManagementConstants(org.eclipse.hono.util.RegistryManagementConstants) Type(org.eclipse.californium.core.coap.CoAP.Type) Logger(org.slf4j.Logger) AdvancedSinglePskStore(org.eclipse.californium.scandium.dtls.pskstore.AdvancedSinglePskStore) Promise(io.vertx.core.Promise) Vertx(io.vertx.core.Vertx) Truth.assertThat(com.google.common.truth.Truth.assertThat) Inet4Address(java.net.Inet4Address) UnknownHostException(java.net.UnknownHostException) TimeUnit(java.util.concurrent.TimeUnit) Consumer(java.util.function.Consumer) Code(org.eclipse.californium.core.coap.CoAP.Code) Adapter(org.eclipse.hono.util.Adapter) AfterEach(org.junit.jupiter.api.AfterEach) ParameterizedTest(org.junit.jupiter.params.ParameterizedTest) CoapHandler(org.eclipse.californium.core.CoapHandler) MessageConsumer(org.eclipse.hono.application.client.MessageConsumer) OptionSet(org.eclipse.californium.core.coap.OptionSet) Handler(io.vertx.core.Handler) Collections(java.util.Collections) VertxTestContext(io.vertx.junit5.VertxTestContext) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) CountDownLatch(java.util.concurrent.CountDownLatch)

Example 9 with MessageConsumer

use of org.eclipse.hono.application.client.MessageConsumer in project hono by eclipse.

the class EventMqttIT method testMessagesExpire.

/**
 * Verifies that an event from a device for which a default TTL has been
 * specified cannot be consumed after the TTL has expired.
 *
 * @param ctx The vert.x test context.
 * @throws InterruptedException if test execution gets interrupted.
 */
@Test
public void testMessagesExpire(final VertxTestContext ctx) throws InterruptedException {
    // GIVEN a tenant for which all messages have a TTL of 500ms
    final String tenantId = helper.getRandomTenantId();
    final String deviceId = helper.getRandomDeviceId(tenantId);
    final Tenant tenant = new Tenant();
    // seconds
    tenant.setDefaults(Map.of(MessageHelper.SYS_HEADER_PROPERTY_TTL, 3));
    final VertxTestContext setup = new VertxTestContext();
    helper.registry.addDeviceForTenant(tenantId, tenant, deviceId, "secret").onComplete(setup.succeedingThenComplete());
    assertThat(setup.awaitCompletion(IntegrationTestSupport.getTestSetupTimeout(), TimeUnit.SECONDS)).isTrue();
    if (setup.failed()) {
        ctx.failNow(setup.causeOfFailure());
        return;
    }
    // WHEN a device that belongs to the tenant publishes an event
    final AtomicInteger receivedMessageCount = new AtomicInteger(0);
    connectToAdapter(IntegrationTestSupport.getUsername(deviceId, tenantId), "secret").compose(connAck -> send(tenantId, deviceId, Buffer.buffer("hello"), false, null, (sendAttempt, result) -> {
        if (sendAttempt.succeeded()) {
            LOGGER.info("successfully sent event [tenant-id: {}, device-id: {}]", tenantId, deviceId);
            result.complete();
        } else {
            result.fail(sendAttempt.cause());
        }
    })).compose(ok -> {
        final Promise<MessageConsumer> consumerCreated = Promise.promise();
        vertx.setTimer(4000, tid -> {
            LOGGER.info("opening event consumer for tenant [{}]", tenantId);
            // THEN no messages can be consumed after the TTL has expired
            createConsumer(tenantId, msg -> receivedMessageCount.incrementAndGet()).onComplete(consumerCreated);
        });
        return consumerCreated.future();
    }).compose(c -> {
        final Promise<Void> done = Promise.promise();
        vertx.setTimer(1000, tid -> {
            if (receivedMessageCount.get() > 0) {
                done.fail(new IllegalStateException("should not have received any events after TTL has expired"));
            } else {
                done.complete();
            }
        });
        return done.future();
    }).onComplete(ctx.succeedingThenComplete());
}
Also used : VertxTestContext(io.vertx.junit5.VertxTestContext) MqttQoS(io.netty.handler.codec.mqtt.MqttQoS) DownstreamMessage(org.eclipse.hono.application.client.DownstreamMessage) MessageContext(org.eclipse.hono.application.client.MessageContext) Promise(io.vertx.core.Promise) DownstreamMessageAssertions(org.eclipse.hono.tests.DownstreamMessageAssertions) UUID(java.util.UUID) Truth.assertThat(com.google.common.truth.Truth.assertThat) MessageHelper(org.eclipse.hono.util.MessageHelper) VertxExtension(io.vertx.junit5.VertxExtension) EventConstants(org.eclipse.hono.util.EventConstants) Future(io.vertx.core.Future) Tenant(org.eclipse.hono.service.management.tenant.Tenant) TimeUnit(java.util.concurrent.TimeUnit) Test(org.junit.jupiter.api.Test) IntegrationTestSupport(org.eclipse.hono.tests.IntegrationTestSupport) ExtendWith(org.junit.jupiter.api.extension.ExtendWith) Buffer(io.vertx.core.buffer.Buffer) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) Map(java.util.Map) BiConsumer(java.util.function.BiConsumer) MessageConsumer(org.eclipse.hono.application.client.MessageConsumer) AsyncResult(io.vertx.core.AsyncResult) Handler(io.vertx.core.Handler) Promise(io.vertx.core.Promise) MessageConsumer(org.eclipse.hono.application.client.MessageConsumer) Tenant(org.eclipse.hono.service.management.tenant.Tenant) VertxTestContext(io.vertx.junit5.VertxTestContext) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) Test(org.junit.jupiter.api.Test)

Example 10 with MessageConsumer

use of org.eclipse.hono.application.client.MessageConsumer in project hono by eclipse.

the class MqttPublishTestBase method doTestUploadMessages.

/**
 * Uploads a number of messages and verifies that they are either received via the northbound consumer or that
 * corresponding error messages are published to the client on the error topic.
 *
 * @param ctx The test context.
 * @param tenantId The tenant identifier.
 * @param connection The MQTT connection future.
 * @param sender The message sender. The Future result is the correlation/message id of the sent message.
 * @param consumerSupplier The message consumer. The result may be succeeded with a {@code null} value in case
 *                         error message handling for a non-existing consumer shall get tested.
 * @param errorMsgHandler The handler to invoke with received error messages or {@code null} if no error messages
 *            are expected. The future result is the error message correlation id.
 * @param errorTopic The errorTopic to subscribe to. Will be ignored of errorMsgHandler is {@code null}.
 * @throws InterruptedException if the test fails.
 */
protected void doTestUploadMessages(final VertxTestContext ctx, final String tenantId, final Future<MqttConnAckMessage> connection, final Function<Buffer, Future<String>> sender, final Function<Handler<DownstreamMessage<? extends MessageContext>>, Future<MessageConsumer>> consumerSupplier, final Function<MqttPublishMessage, Future<String>> errorMsgHandler, final String errorTopic) throws InterruptedException {
    final boolean errorMessagesExpected = errorMsgHandler != null;
    final CountDownLatch received = new CountDownLatch(MESSAGES_TO_SEND);
    final AtomicInteger messageCount = new AtomicInteger(0);
    final AtomicLong lastReceivedTimestamp = new AtomicLong(0);
    // <correlation id of the sent telemetry/event message, errorMessageReceived promise>
    final Map<String, Promise<Void>> pendingErrorMessages = new HashMap<>();
    final AtomicBoolean consumerIsSet = new AtomicBoolean();
    final VertxTestContext setup = new VertxTestContext();
    connection.compose(ok -> consumerSupplier.apply(msg -> {
        if (errorMessagesExpected) {
            ctx.failNow(new IllegalStateException("consumer received message although sending was supposed to fail"));
            return;
        }
        LOGGER.trace("received {}", msg);
        ctx.verify(() -> {
            DownstreamMessageAssertions.assertTelemetryMessageProperties(msg, tenantId);
            assertThat(msg.getQos().ordinal()).isEqualTo(getQos().ordinal());
            assertAdditionalMessageProperties(msg);
        });
        received.countDown();
        lastReceivedTimestamp.set(System.currentTimeMillis());
        if (received.getCount() % 50 == 0) {
            LOGGER.info("messages received: {}", MESSAGES_TO_SEND - received.getCount());
        }
    })).compose(msgConsumer -> {
        consumerIsSet.set(msgConsumer != null);
        if (errorMsgHandler == null) {
            return Future.succeededFuture();
        }
        mqttClient.publishHandler(msg -> {
            LOGGER.trace("received error message [topic: {}]", msg.topicName());
            errorMsgHandler.apply(msg).onSuccess(correlationId -> {
                // correlate the error message with the corresponding publish operation and complete the publish operation promise here
                pendingErrorMessages.compute(correlationId, (key, oldValue) -> {
                    final Promise<Void> promise = Optional.ofNullable(oldValue).orElseGet(Promise::promise);
                    promise.tryComplete();
                    // remove mapping if oldValue is set
                    return oldValue != null ? null : promise;
                });
            }).onFailure(ctx::failNow);
        });
        return subscribeToErrorTopic(errorTopic);
    }).onComplete(setup.succeedingThenComplete());
    assertThat(setup.awaitCompletion(5, TimeUnit.SECONDS)).isTrue();
    if (setup.failed()) {
        ctx.failNow(setup.causeOfFailure());
        return;
    }
    customizeConnectedClient();
    final long start = System.currentTimeMillis();
    while (messageCount.get() < MESSAGES_TO_SEND) {
        final CountDownLatch messageHandlingCompleted = new CountDownLatch(errorMessagesExpected ? 2 : 1);
        context.runOnContext(go -> {
            final Buffer msg = Buffer.buffer("hello " + messageCount.getAndIncrement());
            sender.apply(msg).onComplete(sendAttempt -> {
                if (sendAttempt.failed()) {
                    LOGGER.error("error sending message {}", messageCount.get(), sendAttempt.cause());
                }
                if (messageCount.get() % 50 == 0) {
                    LOGGER.info("messages sent: " + messageCount.get());
                }
                messageHandlingCompleted.countDown();
                if (errorMessagesExpected) {
                    if (sendAttempt.failed()) {
                        messageHandlingCompleted.countDown();
                    } else {
                        // wait til error message has been received
                        final String correlationId = sendAttempt.result();
                        final long timerId = vertx.setTimer(1000, tid -> {
                            Optional.ofNullable(pendingErrorMessages.remove(correlationId)).ifPresent(promise -> promise.tryFail(new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE, "timeout waiting for error response")));
                        });
                        final Handler<AsyncResult<Void>> errorMessageReceivedOrTimeoutHandler = ar -> {
                            vertx.cancelTimer(timerId);
                            if (ar.succeeded()) {
                                received.countDown();
                                lastReceivedTimestamp.set(System.currentTimeMillis());
                                if (received.getCount() % 50 == 0) {
                                    LOGGER.info("error messages received: {}", MESSAGES_TO_SEND - received.getCount());
                                }
                            } else {
                                LOGGER.warn("failed to handle error message with correlation id [{}]", correlationId, ar.cause());
                            }
                            messageHandlingCompleted.countDown();
                        };
                        pendingErrorMessages.compute(correlationId, (key, oldValue) -> {
                            final Promise<Void> promise = Optional.ofNullable(oldValue).orElseGet(Promise::promise);
                            promise.future().onComplete(errorMessageReceivedOrTimeoutHandler);
                            // remove mapping if oldValue is set
                            return oldValue != null ? null : promise;
                        });
                    }
                }
            });
        });
        messageHandlingCompleted.await();
    }
    // in case no consumer is set, waiting time needs to be longer (adapter will wait for credit when creating the first downstream sender)
    final long timeToWait = getTimeToWait() + (!consumerIsSet.get() ? 2000 : 0);
    if (!received.await(timeToWait, TimeUnit.MILLISECONDS)) {
        LOGGER.info("Timeout of {} milliseconds reached, stop waiting to receive messages.", timeToWait);
    }
    if (lastReceivedTimestamp.get() == 0L) {
        // no message has been received at all
        lastReceivedTimestamp.set(System.currentTimeMillis());
    }
    final long messagesReceived = MESSAGES_TO_SEND - received.getCount();
    LOGGER.info("sent {} and received {} messages in {} milliseconds", messageCount.get(), messagesReceived, lastReceivedTimestamp.get() - start);
    assertMessageReceivedRatio(messagesReceived, messageCount.get(), ctx);
}
Also used : HttpURLConnection(java.net.HttpURLConnection) VertxTestContext(io.vertx.junit5.VertxTestContext) MqttQoS(io.netty.handler.codec.mqtt.MqttQoS) DownstreamMessage(org.eclipse.hono.application.client.DownstreamMessage) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) MqttPublishMessage(io.vertx.mqtt.messages.MqttPublishMessage) HashMap(java.util.HashMap) SelfSignedCertificate(io.vertx.core.net.SelfSignedCertificate) ServiceInvocationException(org.eclipse.hono.client.ServiceInvocationException) AtomicReference(java.util.concurrent.atomic.AtomicReference) Function(java.util.function.Function) ServiceConfigProperties(org.eclipse.hono.config.ServiceConfigProperties) Tenant(org.eclipse.hono.service.management.tenant.Tenant) CompositeFuture(io.vertx.core.CompositeFuture) IntegrationTestSupport(org.eclipse.hono.tests.IntegrationTestSupport) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) Map(java.util.Map) JsonObject(io.vertx.core.json.JsonObject) AsyncResult(io.vertx.core.AsyncResult) Tenants(org.eclipse.hono.tests.Tenants) RegistryManagementConstants(org.eclipse.hono.util.RegistryManagementConstants) MqttConnAckMessage(io.vertx.mqtt.messages.MqttConnAckMessage) Device(org.eclipse.hono.service.management.device.Device) QueryStringEncoder(io.netty.handler.codec.http.QueryStringEncoder) MessageContext(org.eclipse.hono.application.client.MessageContext) Promise(io.vertx.core.Promise) Set(java.util.Set) ServerErrorException(org.eclipse.hono.client.ServerErrorException) DownstreamMessageAssertions(org.eclipse.hono.tests.DownstreamMessageAssertions) UUID(java.util.UUID) Truth.assertThat(com.google.common.truth.Truth.assertThat) MessageHelper(org.eclipse.hono.util.MessageHelper) EventConstants(org.eclipse.hono.util.EventConstants) Future(io.vertx.core.Future) TimeUnit(java.util.concurrent.TimeUnit) Test(org.junit.jupiter.api.Test) CountDownLatch(java.util.concurrent.CountDownLatch) AtomicLong(java.util.concurrent.atomic.AtomicLong) List(java.util.List) Buffer(io.vertx.core.buffer.Buffer) Optional(java.util.Optional) MessageConsumer(org.eclipse.hono.application.client.MessageConsumer) Handler(io.vertx.core.Handler) Buffer(io.vertx.core.buffer.Buffer) HashMap(java.util.HashMap) VertxTestContext(io.vertx.junit5.VertxTestContext) CountDownLatch(java.util.concurrent.CountDownLatch) Promise(io.vertx.core.Promise) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) AtomicLong(java.util.concurrent.atomic.AtomicLong) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) ServerErrorException(org.eclipse.hono.client.ServerErrorException) AsyncResult(io.vertx.core.AsyncResult)

Aggregations

Buffer (io.vertx.core.buffer.Buffer)12 MessageConsumer (org.eclipse.hono.application.client.MessageConsumer)12 Future (io.vertx.core.Future)11 Handler (io.vertx.core.Handler)11 DownstreamMessage (org.eclipse.hono.application.client.DownstreamMessage)11 VertxTestContext (io.vertx.junit5.VertxTestContext)10 AtomicInteger (java.util.concurrent.atomic.AtomicInteger)10 MessageContext (org.eclipse.hono.application.client.MessageContext)10 Truth.assertThat (com.google.common.truth.Truth.assertThat)9 Promise (io.vertx.core.Promise)9 Optional (java.util.Optional)9 CountDownLatch (java.util.concurrent.CountDownLatch)9 TimeUnit (java.util.concurrent.TimeUnit)9 HttpURLConnection (java.net.HttpURLConnection)8 Map (java.util.Map)8 Function (java.util.function.Function)8 Truth.assertWithMessage (com.google.common.truth.Truth.assertWithMessage)7 Checkpoint (io.vertx.junit5.Checkpoint)7 Timeout (io.vertx.junit5.Timeout)7 AtomicLong (java.util.concurrent.atomic.AtomicLong)7