use of org.eclipse.hono.application.client.MessageContext 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");
}
use of org.eclipse.hono.application.client.MessageContext in project hono by eclipse.
the class HttpTestBase method testUploadMessages.
/**
* Uploads messages to the HTTP endpoint.
*
* @param ctx The test context to run on.
* @param tenantId The tenant that the device belongs to.
* @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 Function<DownstreamMessage<? extends MessageContext>, Future<Void>> messageConsumer, final Function<Integer, Future<HttpResponse<Buffer>>> requestSender, final int numberOfMessages, final QoS expectedQos) throws InterruptedException {
final VertxTestContext messageSending = new VertxTestContext();
final Checkpoint messageSent = messageSending.checkpoint(numberOfMessages);
final Checkpoint messageReceived = messageSending.laxCheckpoint(numberOfMessages);
final AtomicInteger receivedMessageCount = new AtomicInteger(0);
final VertxTestContext setup = new VertxTestContext();
createConsumer(tenantId, msg -> {
logger.trace("received {}", msg);
ctx.verify(() -> {
DownstreamMessageAssertions.assertTelemetryMessageProperties(msg, tenantId);
assertThat(msg.getQos()).isEqualTo(getExpectedQoS(expectedQos));
assertAdditionalMessageProperties(msg);
});
Optional.ofNullable(messageConsumer).map(consumer -> consumer.apply(msg)).orElseGet(() -> Future.succeededFuture()).onComplete(attempt -> {
if (attempt.succeeded()) {
receivedMessageCount.incrementAndGet();
messageReceived.flag();
} else {
logger.error("failed to process message from device", attempt.cause());
messageSending.failNow(attempt.cause());
}
});
if (receivedMessageCount.get() % 20 == 0) {
logger.info("messages received: {}", receivedMessageCount.get());
}
}).onComplete(setup.succeedingThenComplete());
assertThat(setup.awaitCompletion(5, TimeUnit.SECONDS)).isTrue();
if (setup.failed()) {
ctx.failNow(setup.causeOfFailure());
return;
}
final long start = System.currentTimeMillis();
int messageCount = 0;
while (messageCount < numberOfMessages && !messageSending.failed()) {
messageCount++;
final int currentMessage = messageCount;
final CountDownLatch sending = new CountDownLatch(1);
requestSender.apply(currentMessage).compose(this::assertHttpResponse).onComplete(attempt -> {
try {
if (attempt.succeeded()) {
logger.debug("sent message {}", currentMessage);
messageSent.flag();
} else {
logger.info("failed to send message {}: {}", currentMessage, attempt.cause().getMessage());
messageSending.failNow(attempt.cause());
}
} finally {
sending.countDown();
}
});
sending.await();
if (currentMessage % 20 == 0) {
logger.info("messages sent: " + currentMessage);
}
}
final long timeToWait = Math.max(TEST_TIMEOUT_MILLIS - 50 - (System.currentTimeMillis() - testStartTimeMillis), 1);
assertThat(messageSending.awaitCompletion(timeToWait, TimeUnit.MILLISECONDS)).isTrue();
if (messageSending.failed()) {
logger.error("test execution failed", messageSending.causeOfFailure());
ctx.failNow(messageSending.causeOfFailure());
} else {
logger.info("successfully sent {} and received {} messages after {} milliseconds", messageCount, receivedMessageCount.get(), System.currentTimeMillis() - start);
ctx.completeNow();
}
}
use of org.eclipse.hono.application.client.MessageContext 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"));
}
}
use of org.eclipse.hono.application.client.MessageContext 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);
}
Aggregations