Search in sources :

Example 1 with SendMessageTimeoutException

use of org.eclipse.hono.client.SendMessageTimeoutException 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)

Example 2 with SendMessageTimeoutException

use of org.eclipse.hono.client.SendMessageTimeoutException 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 3 with SendMessageTimeoutException

use of org.eclipse.hono.client.SendMessageTimeoutException in project hono by eclipse.

the class GenericSenderLink method handleSendMessageTimeout.

/**
 * Handles a timeout waiting for a delivery update after sending a message.
 *
 * @param message The message for which the timeout occurred.
 * @param sendMessageTimeout The timeout value.
 * @param delivery The message delivery to release (may be {@code null}).
 * @param sample The sample to log a timeout on (may be {@code null}).
 * @param resultPromise The promise to fail with an exception (may be {@code null}).
 * @param spanToLogToAndFinish The span to log the error to and finish (may be {@code null})
 * @throws NullPointerException if message is {@code null}.
 */
protected void handleSendMessageTimeout(final Message message, final long sendMessageTimeout, final ProtonDelivery delivery, final SendMessageSampler.Sample sample, final Promise<ProtonDelivery> resultPromise, final Span spanToLogToAndFinish) {
    Objects.requireNonNull(message);
    final String linkOrConnectionClosedInfo = HonoProtonHelper.isLinkOpenAndConnected(sender) ? "" : " (link or connection already closed)";
    final String exceptionMsg = "waiting for delivery update timed out after " + sendMessageTimeout + "ms";
    final ServerErrorException exception = HonoProtonHelper.isLinkOpenAndConnected(sender) ? new SendMessageTimeoutException(exceptionMsg) : new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE, exceptionMsg + linkOrConnectionClosedInfo);
    logMessageSendingError("waiting for delivery update timed out for message [ID: {}, address: {}] after {}ms{}", message.getMessageId(), getMessageAddress(message), sendMessageTimeout, linkOrConnectionClosedInfo);
    // (it would be enough to just settle the delivery without an outcome but that cannot be done with proton-j as of now)
    if (delivery != null) {
        ProtonHelper.released(delivery, true);
    }
    if (spanToLogToAndFinish != null) {
        TracingHelper.logError(spanToLogToAndFinish, exception.getMessage());
        Tags.HTTP_STATUS.set(spanToLogToAndFinish, HttpURLConnection.HTTP_UNAVAILABLE);
        spanToLogToAndFinish.finish();
    }
    if (resultPromise != null) {
        resultPromise.fail(exception);
    }
    if (sample != null) {
        sample.timeout();
    }
}
Also used : ServerErrorException(org.eclipse.hono.client.ServerErrorException) SendMessageTimeoutException(org.eclipse.hono.client.SendMessageTimeoutException)

Aggregations

SendMessageTimeoutException (org.eclipse.hono.client.SendMessageTimeoutException)3 ServerErrorException (org.eclipse.hono.client.ServerErrorException)3 Truth.assertThat (com.google.common.truth.Truth.assertThat)2 Truth.assertWithMessage (com.google.common.truth.Truth.assertWithMessage)2 NoopSpan (io.opentracing.noop.NoopSpan)2 Future (io.vertx.core.Future)2 Handler (io.vertx.core.Handler)2 Promise (io.vertx.core.Promise)2 Buffer (io.vertx.core.buffer.Buffer)2 Checkpoint (io.vertx.junit5.Checkpoint)2 Timeout (io.vertx.junit5.Timeout)2 VertxExtension (io.vertx.junit5.VertxExtension)2 VertxTestContext (io.vertx.junit5.VertxTestContext)2 ProtonHelper (io.vertx.proton.ProtonHelper)2 HttpURLConnection (java.net.HttpURLConnection)2 Map (java.util.Map)2 CountDownLatch (java.util.concurrent.CountDownLatch)2 TimeUnit (java.util.concurrent.TimeUnit)2 AtomicInteger (java.util.concurrent.atomic.AtomicInteger)2 AtomicLong (java.util.concurrent.atomic.AtomicLong)2