Search in sources :

Example 86 with Future

use of io.vertx.core.Future in project hono by eclipse.

the class GenericSenderLink method sendMessageAndWaitForOutcome.

private Future<ProtonDelivery> sendMessageAndWaitForOutcome(final Message message, final Span currentSpan, final boolean mapUnacceptedOutcomeToErrorResult) {
    Objects.requireNonNull(message);
    Objects.requireNonNull(currentSpan);
    final AtomicReference<ProtonDelivery> deliveryRef = new AtomicReference<>();
    final Promise<ProtonDelivery> result = Promise.promise();
    final String messageId = String.format("%s-%d", getClass().getSimpleName(), MESSAGE_COUNTER.getAndIncrement());
    message.setMessageId(messageId);
    logMessageIdAndSenderInfo(currentSpan, messageId);
    final SendMessageSampler.Sample sample = sampler.start(tenantId);
    final ClientConfigProperties config = connection.getConfig();
    final Long timerId = config.getSendMessageTimeout() > 0 ? connection.getVertx().setTimer(config.getSendMessageTimeout(), id -> {
        if (!result.future().isComplete()) {
            handleSendMessageTimeout(message, config.getSendMessageTimeout(), deliveryRef.get(), sample, result, null);
        }
    }) : null;
    deliveryRef.set(sender.send(message, deliveryUpdated -> {
        if (timerId != null) {
            connection.getVertx().cancelTimer(timerId);
        }
        final DeliveryState remoteState = deliveryUpdated.getRemoteState();
        if (result.future().isComplete()) {
            log.debug("ignoring received delivery update for message [ID: {}, address: {}]: waiting for the update has already timed out", messageId, getMessageAddress(message));
        } else if (deliveryUpdated.remotelySettled()) {
            logUpdatedDeliveryState(currentSpan, message, deliveryUpdated);
            sample.completed(remoteState);
            if (Accepted.class.isInstance(remoteState)) {
                result.complete(deliveryUpdated);
            } else {
                if (mapUnacceptedOutcomeToErrorResult) {
                    result.handle(mapUnacceptedOutcomeToErrorResult(deliveryUpdated));
                } else {
                    result.complete(deliveryUpdated);
                }
            }
        } else {
            logMessageSendingError("peer did not settle message [ID: {}, address: {}, remote state: {}], failing delivery", messageId, getMessageAddress(message), remoteState.getClass().getSimpleName());
            final ServiceInvocationException e = new ServerErrorException(HttpURLConnection.HTTP_INTERNAL_ERROR, "peer did not settle message, failing delivery");
            result.fail(e);
        }
    }));
    log.trace("sent AT_LEAST_ONCE message [ID: {}, address: {}], remaining credit: {}, queued messages: {}", messageId, getMessageAddress(message), sender.getCredit(), sender.getQueued());
    return result.future().onSuccess(delivery -> Tags.HTTP_STATUS.set(currentSpan, HttpURLConnection.HTTP_ACCEPTED)).onFailure(t -> {
        TracingHelper.logError(currentSpan, t);
        Tags.HTTP_STATUS.set(currentSpan, ServiceInvocationException.extractStatusCode(t));
    }).onComplete(r -> currentSpan.finish());
}
Also used : HttpURLConnection(java.net.HttpURLConnection) ProtonDelivery(io.vertx.proton.ProtonDelivery) Rejected(org.apache.qpid.proton.amqp.messaging.Rejected) LoggerFactory(org.slf4j.LoggerFactory) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) HashMap(java.util.HashMap) ClientErrorException(org.eclipse.hono.client.ClientErrorException) ServiceInvocationException(org.eclipse.hono.client.ServiceInvocationException) AtomicReference(java.util.concurrent.atomic.AtomicReference) Supplier(java.util.function.Supplier) Tags(io.opentracing.tag.Tags) NoConsumerException(org.eclipse.hono.client.NoConsumerException) HonoProtonHelper(org.eclipse.hono.util.HonoProtonHelper) MessageUndeliverableException(org.eclipse.hono.client.MessageUndeliverableException) Modified(org.apache.qpid.proton.amqp.messaging.Modified) StatusCodeMapper(org.eclipse.hono.client.StatusCodeMapper) Map(java.util.Map) DeliveryState(org.apache.qpid.proton.amqp.transport.DeliveryState) Message(org.apache.qpid.proton.message.Message) Fields(io.opentracing.log.Fields) TracingHelper(org.eclipse.hono.tracing.TracingHelper) HonoConnection(org.eclipse.hono.client.HonoConnection) ClientConfigProperties(org.eclipse.hono.config.ClientConfigProperties) Logger(org.slf4j.Logger) Promise(io.vertx.core.Promise) AddressHelper(org.eclipse.hono.util.AddressHelper) ServerErrorException(org.eclipse.hono.client.ServerErrorException) ProtonHelper(io.vertx.proton.ProtonHelper) ProtonQoS(io.vertx.proton.ProtonQoS) MessageHelper(org.eclipse.hono.util.MessageHelper) Released(org.apache.qpid.proton.amqp.messaging.Released) Future(io.vertx.core.Future) Objects(java.util.Objects) AtomicLong(java.util.concurrent.atomic.AtomicLong) List(java.util.List) Optional(java.util.Optional) SendMessageTimeoutException(org.eclipse.hono.client.SendMessageTimeoutException) Span(io.opentracing.Span) ProtonSender(io.vertx.proton.ProtonSender) SendMessageSampler(org.eclipse.hono.client.SendMessageSampler) Handler(io.vertx.core.Handler) Collections(java.util.Collections) Accepted(org.apache.qpid.proton.amqp.messaging.Accepted) MessageNotProcessedException(org.eclipse.hono.client.MessageNotProcessedException) ProtonDelivery(io.vertx.proton.ProtonDelivery) AtomicReference(java.util.concurrent.atomic.AtomicReference) DeliveryState(org.apache.qpid.proton.amqp.transport.DeliveryState) AtomicLong(java.util.concurrent.atomic.AtomicLong) ClientConfigProperties(org.eclipse.hono.config.ClientConfigProperties) ServerErrorException(org.eclipse.hono.client.ServerErrorException) ServiceInvocationException(org.eclipse.hono.client.ServiceInvocationException) SendMessageSampler(org.eclipse.hono.client.SendMessageSampler)

Example 87 with Future

use of io.vertx.core.Future in project hono by eclipse.

the class KafkaBasedCommandConsumerFactoryImplIT method testCommandsGetForwardedInIncomingOrder.

/**
 * Verifies that records, published on the tenant-specific Kafka command topic, get received by
 * the consumer created by the factory and get forwarded on the internal command topic in the
 * same order they were published.
 *
 * @param ctx The vert.x test context.
 * @throws InterruptedException if test execution gets interrupted.
 */
@Test
@Timeout(value = 10, timeUnit = TimeUnit.SECONDS)
public void testCommandsGetForwardedInIncomingOrder(final VertxTestContext ctx) throws InterruptedException {
    final String tenantId = "tenant_" + UUID.randomUUID();
    final VertxTestContext setup = new VertxTestContext();
    final int numTestCommands = 10;
    final List<KafkaConsumerRecord<String, Buffer>> receivedRecords = new ArrayList<>();
    final Promise<Void> allRecordsReceivedPromise = Promise.promise();
    final List<String> receivedCommandSubjects = new ArrayList<>();
    final Handler<KafkaConsumerRecord<String, Buffer>> recordHandler = record -> {
        receivedRecords.add(record);
        LOG.trace("received {}", record);
        receivedCommandSubjects.add(KafkaRecordHelper.getSubject(record.headers()).orElse(""));
        if (receivedRecords.size() == numTestCommands) {
            allRecordsReceivedPromise.tryComplete();
        }
    };
    final Deque<Promise<Void>> completionPromisesQueue = new LinkedList<>();
    // don't let getting the target adapter instance finish immediately
    // - let the futures complete in the reverse order
    final Supplier<Future<Void>> targetAdapterInstanceGetterCompletionFutureSupplier = () -> {
        final Promise<Void> resultPromise = Promise.promise();
        completionPromisesQueue.addFirst(resultPromise);
        // complete all promises in reverse order when processing the last command
        if (completionPromisesQueue.size() == numTestCommands) {
            completionPromisesQueue.forEach(Promise::complete);
        }
        return resultPromise.future();
    };
    final Context vertxContext = vertx.getOrCreateContext();
    vertxContext.runOnContext(v0 -> {
        final HonoKafkaConsumer internalConsumer = getInternalCommandConsumer(recordHandler);
        final KafkaBasedCommandConsumerFactoryImpl consumerFactory = getKafkaBasedCommandConsumerFactory(targetAdapterInstanceGetterCompletionFutureSupplier, tenantId);
        CompositeFuture.join(internalConsumer.start(), consumerFactory.start()).compose(f -> createCommandConsumer(tenantId, consumerFactory)).onComplete(setup.succeedingThenComplete());
    });
    assertThat(setup.awaitCompletion(IntegrationTestSupport.getTestSetupTimeout(), TimeUnit.SECONDS)).isTrue();
    if (setup.failed()) {
        ctx.failNow(setup.causeOfFailure());
        return;
    }
    LOG.debug("command consumer started");
    final List<String> sentCommandSubjects = new ArrayList<>();
    IntStream.range(0, numTestCommands).forEach(i -> {
        final String subject = "cmd_" + i;
        sentCommandSubjects.add(subject);
        sendOneWayCommand(tenantId, "myDeviceId", subject);
    });
    final long timerId = vertx.setTimer(8000, tid -> {
        LOG.info("received records:{}{}", System.lineSeparator(), receivedRecords.stream().map(Object::toString).collect(Collectors.joining("," + System.lineSeparator())));
        allRecordsReceivedPromise.tryFail(String.format("only received %d out of %d expected messages after 8s", receivedRecords.size(), numTestCommands));
    });
    allRecordsReceivedPromise.future().onComplete(ctx.succeeding(v -> {
        vertx.cancelTimer(timerId);
        ctx.verify(() -> {
            assertThat(receivedCommandSubjects).isEqualTo(sentCommandSubjects);
        });
        ctx.completeNow();
    }));
}
Also used : BeforeEach(org.junit.jupiter.api.BeforeEach) MessagingKafkaConsumerConfigProperties(org.eclipse.hono.client.kafka.consumer.MessagingKafkaConsumerConfigProperties) LoggerFactory(org.slf4j.LoggerFactory) Context(io.vertx.core.Context) KafkaProducer(io.vertx.kafka.client.producer.KafkaProducer) Timeout(io.vertx.junit5.Timeout) AfterAll(org.junit.jupiter.api.AfterAll) 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) BeforeAll(org.junit.jupiter.api.BeforeAll) Map(java.util.Map) TracingMockSupport(org.eclipse.hono.test.TracingMockSupport) JsonObject(io.vertx.core.json.JsonObject) DeviceConnectionConstants(org.eclipse.hono.util.DeviceConnectionConstants) CachingKafkaProducerFactory(org.eclipse.hono.client.kafka.producer.CachingKafkaProducerFactory) Set(java.util.Set) CommandTargetMapper(org.eclipse.hono.commandrouter.CommandTargetMapper) ConsumerConfig(org.apache.kafka.clients.consumer.ConsumerConfig) UUID(java.util.UUID) TenantClient(org.eclipse.hono.client.registry.TenantClient) KafkaBasedCommandConsumerFactoryImpl(org.eclipse.hono.commandrouter.impl.kafka.KafkaBasedCommandConsumerFactoryImpl) VertxExtension(io.vertx.junit5.VertxExtension) Collectors(java.util.stream.Collectors) Future(io.vertx.core.Future) AsyncHandlingAutoCommitKafkaConsumer(org.eclipse.hono.client.kafka.consumer.AsyncHandlingAutoCommitKafkaConsumer) TestInfo(org.junit.jupiter.api.TestInfo) Test(org.junit.jupiter.api.Test) List(java.util.List) KafkaProducerFactory(org.eclipse.hono.client.kafka.producer.KafkaProducerFactory) Buffer(io.vertx.core.buffer.Buffer) KafkaConsumerRecord(io.vertx.kafka.client.consumer.KafkaConsumerRecord) Span(io.opentracing.Span) Mockito.mock(org.mockito.Mockito.mock) IntStream(java.util.stream.IntStream) VertxTestContext(io.vertx.junit5.VertxTestContext) X500Principal(javax.security.auth.x500.X500Principal) HonoKafkaConsumer(org.eclipse.hono.client.kafka.consumer.HonoKafkaConsumer) Lifecycle(org.eclipse.hono.util.Lifecycle) Deque(java.util.Deque) AtomicReference(java.util.concurrent.atomic.AtomicReference) Supplier(java.util.function.Supplier) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) CompositeFuture(io.vertx.core.CompositeFuture) Timer(io.micrometer.core.instrument.Timer) LinkedList(java.util.LinkedList) Logger(org.slf4j.Logger) Tracer(io.opentracing.Tracer) Promise(io.vertx.core.Promise) Vertx(io.vertx.core.Vertx) Mockito.when(org.mockito.Mockito.when) KafkaRecordHelper(org.eclipse.hono.client.kafka.KafkaRecordHelper) Truth.assertThat(com.google.common.truth.Truth.assertThat) AssumeMessagingSystem(org.eclipse.hono.tests.AssumeMessagingSystem) TenantObject(org.eclipse.hono.util.TenantObject) SpanContext(io.opentracing.SpanContext) TimeUnit(java.util.concurrent.TimeUnit) NoopKafkaClientMetricsSupport(org.eclipse.hono.client.kafka.metrics.NoopKafkaClientMetricsSupport) HonoTopic(org.eclipse.hono.client.kafka.HonoTopic) CommandRouterMetrics(org.eclipse.hono.commandrouter.CommandRouterMetrics) AfterEach(org.junit.jupiter.api.AfterEach) Handler(io.vertx.core.Handler) KafkaHeader(io.vertx.kafka.client.producer.KafkaHeader) KafkaProducerRecord(io.vertx.kafka.client.producer.KafkaProducerRecord) KafkaAdminClient(io.vertx.kafka.admin.KafkaAdminClient) Context(io.vertx.core.Context) VertxTestContext(io.vertx.junit5.VertxTestContext) SpanContext(io.opentracing.SpanContext) KafkaBasedCommandConsumerFactoryImpl(org.eclipse.hono.commandrouter.impl.kafka.KafkaBasedCommandConsumerFactoryImpl) VertxTestContext(io.vertx.junit5.VertxTestContext) ArrayList(java.util.ArrayList) KafkaConsumerRecord(io.vertx.kafka.client.consumer.KafkaConsumerRecord) LinkedList(java.util.LinkedList) Promise(io.vertx.core.Promise) HonoKafkaConsumer(org.eclipse.hono.client.kafka.consumer.HonoKafkaConsumer) Future(io.vertx.core.Future) CompositeFuture(io.vertx.core.CompositeFuture) JsonObject(io.vertx.core.json.JsonObject) TenantObject(org.eclipse.hono.util.TenantObject) Test(org.junit.jupiter.api.Test) Timeout(io.vertx.junit5.Timeout)

Example 88 with Future

use of io.vertx.core.Future in project hono by eclipse.

the class HttpTestBase method testUploadMessagesWithTtdThatReplyWithCommand.

private void testUploadMessagesWithTtdThatReplyWithCommand(final HttpCommandEndpointConfiguration endpointConfig, final Tenant tenant, final VertxTestContext ctx) throws InterruptedException {
    final VertxTestContext setup = new VertxTestContext();
    final MultiMap requestHeaders = MultiMap.caseInsensitiveMultiMap().add(HttpHeaders.CONTENT_TYPE, "text/plain").add(HttpHeaders.AUTHORIZATION, authorization).add(HttpHeaders.ORIGIN, ORIGIN_URI).add(Constants.HEADER_TIME_TILL_DISCONNECT, "5");
    final MultiMap cmdResponseRequestHeaders = MultiMap.caseInsensitiveMultiMap().add(HttpHeaders.CONTENT_TYPE, "text/plain").add(HttpHeaders.AUTHORIZATION, authorization).add(HttpHeaders.ORIGIN, ORIGIN_URI).add(Constants.HEADER_COMMAND_RESPONSE_STATUS, "200");
    helper.registry.addDeviceForTenant(tenantId, tenant, deviceId, PWD).onComplete(setup.succeedingThenComplete());
    assertThat(setup.awaitCompletion(5, TimeUnit.SECONDS)).isTrue();
    if (setup.failed()) {
        ctx.failNow(setup.causeOfFailure());
        return;
    }
    final String commandTargetDeviceId = endpointConfig.isSubscribeAsGateway() ? helper.setupGatewayDeviceBlocking(tenantId, deviceId, 5) : deviceId;
    final String subscribingDeviceId = endpointConfig.isSubscribeAsGatewayForSingleDevice() ? commandTargetDeviceId : deviceId;
    testUploadMessages(ctx, tenantId, msg -> {
        return msg.getTimeUntilDisconnectNotification().map(notification -> {
            logger.trace("received piggy backed message [ttd: {}]: {}", notification.getTtd(), msg);
            ctx.verify(() -> {
                assertThat(notification.getTenantId()).isEqualTo(tenantId);
                assertThat(notification.getDeviceId()).isEqualTo(subscribingDeviceId);
            });
            // now ready to send a command
            final JsonObject inputData = new JsonObject().put(COMMAND_JSON_KEY, (int) (Math.random() * 100));
            return helper.sendCommand(tenantId, commandTargetDeviceId, COMMAND_TO_SEND, "application/json", inputData.toBuffer(), notification.getMillisecondsUntilExpiry()).map(response -> {
                ctx.verify(() -> {
                    assertThat(response.getContentType()).isEqualTo("text/plain");
                    assertThat(response.getDeviceId()).isEqualTo(commandTargetDeviceId);
                    assertThat(response.getTenantId()).isEqualTo(tenantId);
                    assertThat(response.getCreationTime()).isNotNull();
                });
                return (Void) null;
            });
        }).orElseGet(Future::succeededFuture);
    }, count -> {
        final Buffer buffer = Buffer.buffer("hello " + count);
        return sendHttpRequestForGatewayOrDevice(buffer, requestHeaders, endpointConfig, commandTargetDeviceId, true).map(httpResponse -> {
            final String requestId = httpResponse.getHeader(Constants.HEADER_COMMAND_REQUEST_ID);
            ctx.verify(() -> {
                // assert that the response contains a command
                assertWithMessage("response no. %s '%s' header", count, Constants.HEADER_COMMAND).that(httpResponse.getHeader(Constants.HEADER_COMMAND)).isNotNull();
                assertThat(httpResponse.getHeader(Constants.HEADER_COMMAND)).isEqualTo(COMMAND_TO_SEND);
                assertThat(httpResponse.getHeader(HttpHeaders.CONTENT_TYPE.toString())).isEqualTo("application/json");
                assertThat(requestId).isNotNull();
                assertThat(httpResponse.getHeader(HttpHeaders.CONTENT_LENGTH.toString())).isNotEqualTo("0");
            });
            return requestId;
        }).compose(receivedCommandRequestId -> {
            // send a response to the command now
            final String responseUri = endpointConfig.getCommandResponseUri(tenantId, commandTargetDeviceId, receivedCommandRequestId);
            logger.debug("sending response to command [uri: {}]", responseUri);
            final Buffer body = Buffer.buffer("ok");
            final Future<HttpResponse<Buffer>> result;
            if (endpointConfig.isSubscribeAsGateway()) {
                // GW uses PUT when acting on behalf of a device
                result = httpClient.update(responseUri, body, cmdResponseRequestHeaders, ResponsePredicate.status(HttpURLConnection.HTTP_ACCEPTED));
            } else {
                result = httpClient.create(responseUri, body, cmdResponseRequestHeaders, ResponsePredicate.status(HttpURLConnection.HTTP_ACCEPTED));
            }
            return result.recover(thr -> {
                // wrap exception, making clear it occurred when sending the command response, not the preceding telemetry/event message
                final String msg = "Error sending command response: " + thr.getMessage();
                return Future.failedFuture(new RuntimeException(msg, thr));
            });
        });
    });
}
Also used : HttpURLConnection(java.net.HttpURLConnection) KeyPair(java.security.KeyPair) BeforeEach(org.junit.jupiter.api.BeforeEach) Arrays(java.util.Arrays) ResponsePredicate(io.vertx.ext.web.client.predicate.ResponsePredicate) DownstreamMessage(org.eclipse.hono.application.client.DownstreamMessage) LoggerFactory(org.slf4j.LoggerFactory) MultiMap(io.vertx.core.MultiMap) Tenant(org.eclipse.hono.service.management.tenant.Tenant) Timeout(io.vertx.junit5.Timeout) GeneralSecurityException(java.security.GeneralSecurityException) IntegrationTestSupport(org.eclipse.hono.tests.IntegrationTestSupport) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) BeforeAll(org.junit.jupiter.api.BeforeAll) JsonObject(io.vertx.core.json.JsonObject) Tenants(org.eclipse.hono.tests.Tenants) MethodSource(org.junit.jupiter.params.provider.MethodSource) Device(org.eclipse.hono.service.management.device.Device) MessageContext(org.eclipse.hono.application.client.MessageContext) SubscriberRole(org.eclipse.hono.tests.CommandEndpointConfiguration.SubscriberRole) Truth.assertWithMessage(com.google.common.truth.Truth.assertWithMessage) MetricsTags(org.eclipse.hono.service.metric.MetricsTags) Set(java.util.Set) HttpHeaders(io.vertx.core.http.HttpHeaders) DownstreamMessageAssertions(org.eclipse.hono.tests.DownstreamMessageAssertions) UUID(java.util.UUID) 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) Base64(java.util.Base64) Stream(java.util.stream.Stream) Buffer(io.vertx.core.buffer.Buffer) Optional(java.util.Optional) Checkpoint(io.vertx.junit5.Checkpoint) QoS(org.eclipse.hono.util.QoS) VertxTestContext(io.vertx.junit5.VertxTestContext) X500Principal(javax.security.auth.x500.X500Principal) HttpResponse(io.vertx.ext.web.client.HttpResponse) SelfSignedCertificate(io.vertx.core.net.SelfSignedCertificate) Function(java.util.function.Function) Constants(org.eclipse.hono.util.Constants) CompositeFuture(io.vertx.core.CompositeFuture) HttpClientOptions(io.vertx.core.http.HttpClientOptions) PemTrustOptions(io.vertx.core.net.PemTrustOptions) RegistryManagementConstants(org.eclipse.hono.util.RegistryManagementConstants) Logger(org.slf4j.Logger) Promise(io.vertx.core.Promise) Vertx(io.vertx.core.Vertx) Truth.assertThat(com.google.common.truth.Truth.assertThat) TimeUnit(java.util.concurrent.TimeUnit) Adapter(org.eclipse.hono.util.Adapter) AfterEach(org.junit.jupiter.api.AfterEach) ParameterizedTest(org.junit.jupiter.params.ParameterizedTest) CrudHttpClient(org.eclipse.hono.tests.CrudHttpClient) MessageConsumer(org.eclipse.hono.application.client.MessageConsumer) Handler(io.vertx.core.Handler) Collections(java.util.Collections) Buffer(io.vertx.core.buffer.Buffer) MultiMap(io.vertx.core.MultiMap) VertxTestContext(io.vertx.junit5.VertxTestContext) JsonObject(io.vertx.core.json.JsonObject) Future(io.vertx.core.Future) CompositeFuture(io.vertx.core.CompositeFuture) HttpResponse(io.vertx.ext.web.client.HttpResponse)

Example 89 with Future

use of io.vertx.core.Future in project hono by eclipse.

the class JmsBasedCredentialsClient method sendRequest.

/**
 * Sends a request for an operation.
 *
 * @param tenantId The tenant to send the message for.
 * @param operation The name of the operation to invoke or {@code null} if the message
 *                  should not have a subject.
 * @param applicationProperties Application properties to set on the request message or
 *                              {@code null} if no properties should be set.
 * @param payload Payload to include or {@code null} if the message should have no body.
 * @return A future indicating the outcome of the operation.
 */
public Future<CredentialsObject> sendRequest(final String tenantId, final String operation, final Map<String, Object> applicationProperties, final Buffer payload) {
    final Message request;
    try {
        request = createMessage(payload);
        addProperties(request, applicationProperties);
        if (operation != null) {
            request.setJMSType(operation);
        }
        if (applicationProperties != null) {
            for (Map.Entry<String, Object> entry : applicationProperties.entrySet()) {
                if (entry.getValue() instanceof String) {
                    request.setStringProperty(entry.getKey(), (String) entry.getValue());
                } else {
                    request.setObjectProperty(entry.getKey(), entry.getValue());
                }
            }
        }
    } catch (final JMSException e) {
        return Future.failedFuture(new ClientErrorException(HttpURLConnection.HTTP_BAD_REQUEST, e));
    }
    final Future<JmsBasedRequestResponseClient<CredentialsResult<CredentialsObject>>> client = JmsBasedRequestResponseClient.forEndpoint(connection, CredentialsConstants.CREDENTIALS_ENDPOINT, tenantId);
    return client.compose(c -> c.send(request, this::getResult)).compose(credentialsResult -> {
        final Promise<CredentialsObject> result = Promise.promise();
        switch(credentialsResult.getStatus()) {
            case HttpURLConnection.HTTP_OK:
            case HttpURLConnection.HTTP_CREATED:
                result.complete(credentialsResult.getPayload());
                break;
            case HttpURLConnection.HTTP_NOT_FOUND:
                result.fail(new ClientErrorException(credentialsResult.getStatus(), "no such credentials"));
                break;
            default:
                result.fail(StatusCodeMapper.from(credentialsResult));
        }
        return result.future();
    });
}
Also used : HttpURLConnection(java.net.HttpURLConnection) Logger(org.slf4j.Logger) CredentialsResult(org.eclipse.hono.util.CredentialsResult) DecodeException(io.vertx.core.json.DecodeException) Promise(io.vertx.core.Promise) LoggerFactory(org.slf4j.LoggerFactory) ClientErrorException(org.eclipse.hono.client.ClientErrorException) ServiceInvocationException(org.eclipse.hono.client.ServiceInvocationException) JMSException(javax.jms.JMSException) Future(io.vertx.core.Future) CredentialsConstants(org.eclipse.hono.util.CredentialsConstants) SpanContext(io.opentracing.SpanContext) Objects(java.util.Objects) Buffer(io.vertx.core.buffer.Buffer) StatusCodeMapper(org.eclipse.hono.client.StatusCodeMapper) Map(java.util.Map) CredentialsClient(org.eclipse.hono.client.registry.CredentialsClient) JsonObject(io.vertx.core.json.JsonObject) Message(javax.jms.Message) ClientConfigProperties(org.eclipse.hono.config.ClientConfigProperties) CredentialsAction(org.eclipse.hono.util.CredentialsConstants.CredentialsAction) CredentialsObject(org.eclipse.hono.util.CredentialsObject) Message(javax.jms.Message) CredentialsObject(org.eclipse.hono.util.CredentialsObject) ClientErrorException(org.eclipse.hono.client.ClientErrorException) JsonObject(io.vertx.core.json.JsonObject) CredentialsObject(org.eclipse.hono.util.CredentialsObject) JMSException(javax.jms.JMSException) Map(java.util.Map)

Example 90 with Future

use of io.vertx.core.Future in project hono by eclipse.

the class CommandAndControlMqttIT method testSendCommandFailsForCommandNotAcknowledgedByDevice.

/**
 * Verifies that the adapter forwards the <em>released</em> disposition back to the
 * application if the device hasn't sent an acknowledgement for the command message
 * published to the device.
 *
 * @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)
public void testSendCommandFailsForCommandNotAcknowledgedByDevice(final MqttCommandEndpointConfiguration endpointConfig, final VertxTestContext ctx) throws InterruptedException {
    final MqttQoS subscribeQos = MqttQoS.AT_LEAST_ONCE;
    final VertxTestContext setup = new VertxTestContext();
    final Checkpoint ready = setup.checkpoint(2);
    final String commandTargetDeviceId = endpointConfig.isSubscribeAsGateway() ? helper.setupGatewayDeviceBlocking(tenantId, deviceId, 5) : deviceId;
    final int totalNoOfCommandsToSend = 2;
    final CountDownLatch commandsFailed = new CountDownLatch(totalNoOfCommandsToSend);
    final AtomicInteger receivedMessagesCounter = new AtomicInteger(0);
    final AtomicInteger counter = new AtomicInteger();
    final Handler<MqttPublishMessage> commandConsumer = msg -> {
        LOGGER.trace("received command [{}] - no response sent here", msg.topicName());
        final ResourceIdentifier topic = ResourceIdentifier.fromString(msg.topicName());
        ctx.verify(() -> {
            endpointConfig.assertCommandPublishTopicStructure(topic, commandTargetDeviceId, false, "setValue");
        });
        receivedMessagesCounter.incrementAndGet();
    };
    final Function<Buffer, Future<Void>> commandSender = payload -> {
        counter.incrementAndGet();
        return helper.sendCommand(tenantId, commandTargetDeviceId, "setValue", "text/plain", payload, helper.getSendCommandTimeout(counter.get() == 1)).mapEmpty();
    };
    helper.registry.addDeviceToTenant(tenantId, deviceId, password).compose(ok -> connectToAdapter(IntegrationTestSupport.getUsername(deviceId, tenantId), password)).compose(ok -> injectMqttClientPubAckBlocker(new AtomicBoolean(true))).compose(ok -> 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);
        setup.verify(() -> assertThat(notification).isNotNull());
        if (notification.getTtd() == -1) {
            ready.flag();
        }
    })).compose(conAck -> subscribeToCommands(commandTargetDeviceId, commandConsumer, endpointConfig, subscribeQos)).onComplete(setup.succeeding(ok -> 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 AtomicInteger commandsSent = new AtomicInteger(0);
    final AtomicLong lastReceivedTimestamp = new AtomicLong(0);
    final long start = System.currentTimeMillis();
    while (commandsSent.get() < totalNoOfCommandsToSend) {
        final CountDownLatch commandSent = new CountDownLatch(1);
        context.runOnContext(go -> {
            final Buffer msg = Buffer.buffer("value: " + commandsSent.getAndIncrement());
            commandSender.apply(msg).onComplete(sendAttempt -> {
                if (sendAttempt.succeeded()) {
                    LOGGER.debug("sending command {} succeeded unexpectedly", commandsSent.get());
                } else {
                    if (sendAttempt.cause() instanceof ServerErrorException && ((ServerErrorException) sendAttempt.cause()).getErrorCode() == HttpURLConnection.HTTP_UNAVAILABLE && !(sendAttempt.cause() instanceof SendMessageTimeoutException)) {
                        LOGGER.debug("sending command {} failed as expected: {}", commandsSent.get(), sendAttempt.cause().toString());
                        lastReceivedTimestamp.set(System.currentTimeMillis());
                        commandsFailed.countDown();
                        if (commandsFailed.getCount() % 20 == 0) {
                            LOGGER.info("commands failed as expected: {}", totalNoOfCommandsToSend - commandsFailed.getCount());
                        }
                    } else {
                        LOGGER.debug("sending command {} failed with an unexpected error", commandsSent.get(), sendAttempt.cause());
                    }
                }
                commandSent.countDown();
            });
        });
        commandSent.await();
    }
    // have to wait an extra MqttAdapterProperties.DEFAULT_COMMAND_ACK_TIMEOUT (100ms) for each command message
    final long timeToWait = totalNoOfCommandsToSend * 300;
    if (!commandsFailed.await(timeToWait, TimeUnit.MILLISECONDS)) {
        LOGGER.info("Timeout of {} milliseconds reached, stop waiting for commands", timeToWait);
    }
    assertThat(receivedMessagesCounter.get()).isEqualTo(totalNoOfCommandsToSend);
    final long commandsCompleted = totalNoOfCommandsToSend - commandsFailed.getCount();
    LOGGER.info("commands sent: {}, commands failed: {} after {} milliseconds", commandsSent.get(), commandsCompleted, lastReceivedTimestamp.get() - start);
    if (commandsCompleted == commandsSent.get()) {
        ctx.completeNow();
    } else {
        ctx.failNow(new java.lang.IllegalStateException("did not complete all commands sent"));
    }
}
Also used : HttpURLConnection(java.net.HttpURLConnection) BeforeEach(org.junit.jupiter.api.BeforeEach) DownstreamMessage(org.eclipse.hono.application.client.DownstreamMessage) MqttPublishMessage(io.vertx.mqtt.messages.MqttPublishMessage) Timeout(io.vertx.junit5.Timeout) MessagingType(org.eclipse.hono.util.MessagingType) IntegrationTestSupport(org.eclipse.hono.tests.IntegrationTestSupport) ExtendWith(org.junit.jupiter.api.extension.ExtendWith) ChannelPromise(io.netty.channel.ChannelPromise) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) Map(java.util.Map) MqttClientImpl(io.vertx.mqtt.impl.MqttClientImpl) ResourceIdentifier(org.eclipse.hono.util.ResourceIdentifier) Method(java.lang.reflect.Method) MethodSource(org.junit.jupiter.params.provider.MethodSource) ChannelOutboundHandlerAdapter(io.netty.channel.ChannelOutboundHandlerAdapter) MessageContext(org.eclipse.hono.application.client.MessageContext) SubscriberRole(org.eclipse.hono.tests.CommandEndpointConfiguration.SubscriberRole) Truth.assertWithMessage(com.google.common.truth.Truth.assertWithMessage) IllegalStateException(javax.jms.IllegalStateException) MessageHelper(org.eclipse.hono.util.MessageHelper) VertxExtension(io.vertx.junit5.VertxExtension) EventConstants(org.eclipse.hono.util.EventConstants) Future(io.vertx.core.Future) CountDownLatch(java.util.concurrent.CountDownLatch) Stream(java.util.stream.Stream) Buffer(io.vertx.core.buffer.Buffer) Checkpoint(io.vertx.junit5.Checkpoint) NetSocketInternal(io.vertx.core.net.impl.NetSocketInternal) VertxTestContext(io.vertx.junit5.VertxTestContext) GenericKafkaSender(org.eclipse.hono.tests.GenericKafkaSender) MqttQoS(io.netty.handler.codec.mqtt.MqttQoS) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) ClientErrorException(org.eclipse.hono.client.ClientErrorException) AtomicReference(java.util.concurrent.atomic.AtomicReference) Function(java.util.function.Function) TimeUntilDisconnectNotification(org.eclipse.hono.util.TimeUntilDisconnectNotification) ChannelHandlerContext(io.netty.channel.ChannelHandlerContext) Message(org.apache.qpid.proton.message.Message) MqttPubAckMessage(io.netty.handler.codec.mqtt.MqttPubAckMessage) 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) VertxTestContext(io.vertx.junit5.VertxTestContext) CountDownLatch(java.util.concurrent.CountDownLatch) Checkpoint(io.vertx.junit5.Checkpoint) ResourceIdentifier(org.eclipse.hono.util.ResourceIdentifier) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) Checkpoint(io.vertx.junit5.Checkpoint) AtomicLong(java.util.concurrent.atomic.AtomicLong) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) MqttPublishMessage(io.vertx.mqtt.messages.MqttPublishMessage) TimeUntilDisconnectNotification(org.eclipse.hono.util.TimeUntilDisconnectNotification) Future(io.vertx.core.Future) ServerErrorException(org.eclipse.hono.client.ServerErrorException) SendMessageTimeoutException(org.eclipse.hono.client.SendMessageTimeoutException) MqttQoS(io.netty.handler.codec.mqtt.MqttQoS) Timeout(io.vertx.junit5.Timeout) ParameterizedTest(org.junit.jupiter.params.ParameterizedTest) MethodSource(org.junit.jupiter.params.provider.MethodSource)

Aggregations

Future (io.vertx.core.Future)370 HttpURLConnection (java.net.HttpURLConnection)195 Handler (io.vertx.core.Handler)174 List (java.util.List)166 Objects (java.util.Objects)164 JsonObject (io.vertx.core.json.JsonObject)163 Promise (io.vertx.core.Promise)160 Vertx (io.vertx.core.Vertx)157 Buffer (io.vertx.core.buffer.Buffer)149 Optional (java.util.Optional)147 Logger (org.slf4j.Logger)136 LoggerFactory (org.slf4j.LoggerFactory)136 CompositeFuture (io.vertx.core.CompositeFuture)127 ClientErrorException (org.eclipse.hono.client.ClientErrorException)127 Map (java.util.Map)122 Span (io.opentracing.Span)117 AsyncResult (io.vertx.core.AsyncResult)112 TracingHelper (org.eclipse.hono.tracing.TracingHelper)98 Constants (org.eclipse.hono.util.Constants)97 ArrayList (java.util.ArrayList)94