Search in sources :

Example 1 with DisconnectListener

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

the class HonoConnectionImplTest method testOnRemoteCloseTriggeredReconnectChecksReconnectAttempts.

/**
 * Verifies that when the client tries to re-connect to a server instance if the
 * connection is closed by the peer, the configured number of reconnect attempts is taken
 * into account, skipping the reconnect if that number is zero.
 *
 * @param ctx The test context.
 */
@Test
public void testOnRemoteCloseTriggeredReconnectChecksReconnectAttempts(final VertxTestContext ctx) {
    // GIVEN a client that is connected to a server but should do no automatic reconnect
    props.setReconnectAttempts(0);
    honoConnection = new HonoConnectionImpl(vertx, connectionFactory, props);
    final Promise<HonoConnection> connected = Promise.promise();
    @SuppressWarnings("unchecked") final DisconnectListener<HonoConnection> disconnectListener = mock(DisconnectListener.class);
    honoConnection.addDisconnectListener(disconnectListener);
    final AtomicInteger reconnectListenerInvocations = new AtomicInteger();
    honoConnection.addReconnectListener(con -> reconnectListenerInvocations.incrementAndGet());
    honoConnection.connect(new ProtonClientOptions().setReconnectAttempts(0)).onComplete(connected);
    connected.future().onComplete(ctx.succeeding(c -> {
        // WHEN the peer closes the connection
        connectionFactory.getCloseHandler().handle(Future.failedFuture("shutting down for maintenance"));
        ctx.verify(() -> {
            // THEN the client invokes the registered disconnect handler
            verify(disconnectListener).onDisconnect(honoConnection);
            // and the original connection has been closed locally
            verify(con).close();
            verify(con).disconnectHandler(null);
            // and no further connect invocation has been done
            assertThat(connectionFactory.getConnectInvocations()).isEqualTo(1);
            assertThat(reconnectListenerInvocations.get()).isEqualTo(0);
        });
        ctx.completeNow();
    }));
}
Also used : HttpURLConnection(java.net.HttpURLConnection) ProtonConnection(io.vertx.proton.ProtonConnection) ProtonReceiver(io.vertx.proton.ProtonReceiver) BeforeEach(org.junit.jupiter.api.BeforeEach) ArgumentMatchers.eq(org.mockito.ArgumentMatchers.eq) Context(io.vertx.core.Context) Timeout(io.vertx.junit5.Timeout) ConnectionFactory(org.eclipse.hono.connection.ConnectionFactory) SaslSystemException(io.vertx.proton.sasl.SaslSystemException) ExtendWith(org.junit.jupiter.api.extension.ExtendWith) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) ProtonMessageHandler(io.vertx.proton.ProtonMessageHandler) Mockito.doAnswer(org.mockito.Mockito.doAnswer) DisconnectListener(org.eclipse.hono.client.DisconnectListener) AmqpError(org.apache.qpid.proton.amqp.transport.AmqpError) ClientConfigProperties(org.eclipse.hono.config.ClientConfigProperties) Predicate(java.util.function.Predicate) ProtonQoS(io.vertx.proton.ProtonQoS) VertxExtension(io.vertx.junit5.VertxExtension) Future(io.vertx.core.Future) AdditionalAnswers(org.mockito.AdditionalAnswers) Test(org.junit.jupiter.api.Test) ErrorCondition(org.apache.qpid.proton.amqp.transport.ErrorCondition) VertxMockSupport(org.eclipse.hono.test.VertxMockSupport) ProtonSender(io.vertx.proton.ProtonSender) Mockito.mock(org.mockito.Mockito.mock) ArgumentMatchers.any(org.mockito.ArgumentMatchers.any) VertxTestContext(io.vertx.junit5.VertxTestContext) ArgumentMatchers.anyLong(org.mockito.ArgumentMatchers.anyLong) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) Target(org.apache.qpid.proton.amqp.messaging.Target) ClientErrorException(org.eclipse.hono.client.ClientErrorException) ServiceInvocationException(org.eclipse.hono.client.ServiceInvocationException) AtomicReference(java.util.concurrent.atomic.AtomicReference) Supplier(java.util.function.Supplier) CompositeFuture(io.vertx.core.CompositeFuture) ProtonSession(io.vertx.proton.ProtonSession) TelemetryConstants(org.eclipse.hono.util.TelemetryConstants) ArgumentCaptor(org.mockito.ArgumentCaptor) ProtonClientOptions(io.vertx.proton.ProtonClientOptions) Symbol(org.apache.qpid.proton.amqp.Symbol) BiConsumer(java.util.function.BiConsumer) UnsignedLong(org.apache.qpid.proton.amqp.UnsignedLong) AsyncResult(io.vertx.core.AsyncResult) HonoConnection(org.eclipse.hono.client.HonoConnection) Promise(io.vertx.core.Promise) Vertx(io.vertx.core.Vertx) ServerErrorException(org.eclipse.hono.client.ServerErrorException) Mockito.times(org.mockito.Mockito.times) Mockito.when(org.mockito.Mockito.when) Truth.assertThat(com.google.common.truth.Truth.assertThat) Mockito.verify(org.mockito.Mockito.verify) TimeUnit(java.util.concurrent.TimeUnit) Mockito.never(org.mockito.Mockito.never) Source(org.apache.qpid.proton.amqp.transport.Source) Handler(io.vertx.core.Handler) ArgumentMatchers.anyString(org.mockito.ArgumentMatchers.anyString) HonoConnection(org.eclipse.hono.client.HonoConnection) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) ProtonClientOptions(io.vertx.proton.ProtonClientOptions) Test(org.junit.jupiter.api.Test)

Example 2 with DisconnectListener

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

the class HonoConnectionImplTest method testRemoteSessionCloseTriggersReconnection.

/**
 * Verifies that the client tries to reconnect to the peer if the peer
 * closes the connection's session.
 *
 * @param ctx The test context.
 */
@Test
public void testRemoteSessionCloseTriggersReconnection(final VertxTestContext ctx) {
    // GIVEN a client that is connected to a server
    final Promise<HonoConnection> connected = Promise.promise();
    @SuppressWarnings("unchecked") final DisconnectListener<HonoConnection> disconnectListener = mock(DisconnectListener.class);
    honoConnection.addDisconnectListener(disconnectListener);
    final AtomicInteger reconnectListenerInvocations = new AtomicInteger();
    honoConnection.addReconnectListener(con -> reconnectListenerInvocations.incrementAndGet());
    props.setServerRole("service-provider");
    honoConnection.connect(new ProtonClientOptions()).onComplete(connected);
    connectionFactory.setExpectedSucceedingConnectionAttempts(1);
    connected.future().onComplete(ctx.succeeding(c -> {
        ctx.verify(() -> {
            // WHEN the peer closes the session
            final ArgumentCaptor<Handler<AsyncResult<ProtonSession>>> sessionCloseHandler = VertxMockSupport.argumentCaptorHandler();
            verify(session).closeHandler(sessionCloseHandler.capture());
            sessionCloseHandler.getValue().handle(Future.succeededFuture(session));
            // THEN the client invokes the registered disconnect handler
            verify(disconnectListener).onDisconnect(honoConnection);
            // and the original connection has been closed locally
            verify(con).close();
            verify(con).disconnectHandler(null);
            // and the connection is re-established
            assertThat(connectionFactory.await()).isTrue();
            assertThat(reconnectListenerInvocations.get()).isEqualTo(1);
        });
        ctx.completeNow();
    }));
}
Also used : HttpURLConnection(java.net.HttpURLConnection) ProtonConnection(io.vertx.proton.ProtonConnection) ProtonReceiver(io.vertx.proton.ProtonReceiver) BeforeEach(org.junit.jupiter.api.BeforeEach) ArgumentMatchers.eq(org.mockito.ArgumentMatchers.eq) Context(io.vertx.core.Context) Timeout(io.vertx.junit5.Timeout) ConnectionFactory(org.eclipse.hono.connection.ConnectionFactory) SaslSystemException(io.vertx.proton.sasl.SaslSystemException) ExtendWith(org.junit.jupiter.api.extension.ExtendWith) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) ProtonMessageHandler(io.vertx.proton.ProtonMessageHandler) Mockito.doAnswer(org.mockito.Mockito.doAnswer) DisconnectListener(org.eclipse.hono.client.DisconnectListener) AmqpError(org.apache.qpid.proton.amqp.transport.AmqpError) ClientConfigProperties(org.eclipse.hono.config.ClientConfigProperties) Predicate(java.util.function.Predicate) ProtonQoS(io.vertx.proton.ProtonQoS) VertxExtension(io.vertx.junit5.VertxExtension) Future(io.vertx.core.Future) AdditionalAnswers(org.mockito.AdditionalAnswers) Test(org.junit.jupiter.api.Test) ErrorCondition(org.apache.qpid.proton.amqp.transport.ErrorCondition) VertxMockSupport(org.eclipse.hono.test.VertxMockSupport) ProtonSender(io.vertx.proton.ProtonSender) Mockito.mock(org.mockito.Mockito.mock) ArgumentMatchers.any(org.mockito.ArgumentMatchers.any) VertxTestContext(io.vertx.junit5.VertxTestContext) ArgumentMatchers.anyLong(org.mockito.ArgumentMatchers.anyLong) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) Target(org.apache.qpid.proton.amqp.messaging.Target) ClientErrorException(org.eclipse.hono.client.ClientErrorException) ServiceInvocationException(org.eclipse.hono.client.ServiceInvocationException) AtomicReference(java.util.concurrent.atomic.AtomicReference) Supplier(java.util.function.Supplier) CompositeFuture(io.vertx.core.CompositeFuture) ProtonSession(io.vertx.proton.ProtonSession) TelemetryConstants(org.eclipse.hono.util.TelemetryConstants) ArgumentCaptor(org.mockito.ArgumentCaptor) ProtonClientOptions(io.vertx.proton.ProtonClientOptions) Symbol(org.apache.qpid.proton.amqp.Symbol) BiConsumer(java.util.function.BiConsumer) UnsignedLong(org.apache.qpid.proton.amqp.UnsignedLong) AsyncResult(io.vertx.core.AsyncResult) HonoConnection(org.eclipse.hono.client.HonoConnection) Promise(io.vertx.core.Promise) Vertx(io.vertx.core.Vertx) ServerErrorException(org.eclipse.hono.client.ServerErrorException) Mockito.times(org.mockito.Mockito.times) Mockito.when(org.mockito.Mockito.when) Truth.assertThat(com.google.common.truth.Truth.assertThat) Mockito.verify(org.mockito.Mockito.verify) TimeUnit(java.util.concurrent.TimeUnit) Mockito.never(org.mockito.Mockito.never) Source(org.apache.qpid.proton.amqp.transport.Source) Handler(io.vertx.core.Handler) ArgumentMatchers.anyString(org.mockito.ArgumentMatchers.anyString) HonoConnection(org.eclipse.hono.client.HonoConnection) ArgumentCaptor(org.mockito.ArgumentCaptor) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) ProtonClientOptions(io.vertx.proton.ProtonClientOptions) AsyncResult(io.vertx.core.AsyncResult) Test(org.junit.jupiter.api.Test)

Example 3 with DisconnectListener

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

the class HonoConnectionImpl method createReceiver.

@Override
public Future<ProtonReceiver> createReceiver(final String sourceAddress, final ProtonQoS qos, final ProtonMessageHandler messageHandler, final int preFetchSize, final boolean autoAccept, final Handler<String> remoteCloseHook) {
    Objects.requireNonNull(sourceAddress);
    Objects.requireNonNull(qos);
    Objects.requireNonNull(messageHandler);
    if (preFetchSize < 0) {
        throw new IllegalArgumentException("pre-fetch size must be >= 0");
    }
    return executeOnContext(result -> {
        checkConnected().compose(v -> {
            final Promise<ProtonReceiver> receiverPromise = Promise.promise();
            final ProtonReceiver receiver = session.createReceiver(sourceAddress);
            if (clientConfigProperties.getMaxMessageSize() > ClientConfigProperties.MAX_MESSAGE_SIZE_UNLIMITED) {
                receiver.setMaxMessageSize(new UnsignedLong(clientConfigProperties.getMaxMessageSize()));
            }
            receiver.setAutoAccept(autoAccept);
            receiver.setQoS(qos);
            receiver.setPrefetch(preFetchSize);
            receiver.handler((delivery, message) -> {
                HonoProtonHelper.onReceivedMessageDeliveryUpdatedFromRemote(delivery, d -> log.debug("got unexpected disposition update for received message [remote state: {}]", delivery.getRemoteState()));
                try {
                    messageHandler.handle(delivery, message);
                    if (log.isTraceEnabled()) {
                        final int remainingCredits = receiver.getCredit() - receiver.getQueued();
                        log.trace("handling message [remotely settled: {}, queued messages: {}, remaining credit: {}]", delivery.remotelySettled(), receiver.getQueued(), remainingCredits);
                    }
                } catch (final Exception ex) {
                    log.warn("error handling message", ex);
                    ProtonHelper.released(delivery, true);
                }
            });
            final DisconnectListener<HonoConnection> disconnectBeforeOpenListener = (con) -> {
                log.debug("opening receiver [{}] failed: got disconnected", sourceAddress);
                receiverPromise.tryFail(new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE, "not connected"));
            };
            oneTimeDisconnectListeners.add(disconnectBeforeOpenListener);
            receiver.openHandler(recvOpen -> {
                oneTimeDisconnectListeners.remove(disconnectBeforeOpenListener);
                // the result future may have already been completed here in case of a link establishment timeout
                if (receiverPromise.future().isComplete()) {
                    log.debug("ignoring server response for opening receiver [{}]: receiver creation already timed out", sourceAddress);
                } else if (recvOpen.failed()) {
                    // this means that we have received the peer's attach
                    // and the subsequent detach frame in one TCP read
                    final ErrorCondition error = receiver.getRemoteCondition();
                    if (error == null) {
                        log.debug("opening receiver [{}] failed", sourceAddress, recvOpen.cause());
                        receiverPromise.tryFail(new ClientErrorException(HttpURLConnection.HTTP_NOT_FOUND, "cannot open receiver", recvOpen.cause()));
                    } else {
                        log.debug("opening receiver [{}] failed: {} - {}", sourceAddress, error.getCondition(), error.getDescription());
                        receiverPromise.tryFail(StatusCodeMapper.fromAttachError(error));
                    }
                } else if (HonoProtonHelper.isLinkEstablished(receiver)) {
                    log.debug("receiver open [source: {}]", sourceAddress);
                    receiverPromise.tryComplete(recvOpen.result());
                } else {
                    // this means that the peer did not create a local terminus for the link
                    // and will send a detach frame for closing the link very shortly
                    // see AMQP 1.0 spec section 2.6.3
                    log.debug("peer did not create terminus for source [{}] and will detach the link", sourceAddress);
                    receiverPromise.tryFail(new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE));
                }
            });
            HonoProtonHelper.setDetachHandler(receiver, remoteDetached -> onRemoteDetach(receiver, connection.getRemoteContainer(), false, remoteCloseHook));
            HonoProtonHelper.setCloseHandler(receiver, remoteClosed -> onRemoteDetach(receiver, connection.getRemoteContainer(), true, remoteCloseHook));
            receiver.open();
            vertx.setTimer(clientConfigProperties.getLinkEstablishmentTimeout(), tid -> {
                final boolean notOpenedAndNotDisconnectedYet = oneTimeDisconnectListeners.remove(disconnectBeforeOpenListener);
                if (notOpenedAndNotDisconnectedYet) {
                    onLinkEstablishmentTimeout(receiver, clientConfigProperties, receiverPromise);
                }
            });
            return receiverPromise.future();
        }).onComplete(result);
    });
}
Also used : HttpURLConnection(java.net.HttpURLConnection) ProtonConnection(io.vertx.proton.ProtonConnection) ProtonReceiver(io.vertx.proton.ProtonReceiver) Arrays(java.util.Arrays) LoggerFactory(org.slf4j.LoggerFactory) Context(io.vertx.core.Context) HonoProtonHelper(org.eclipse.hono.util.HonoProtonHelper) ConnectionFactory(org.eclipse.hono.connection.ConnectionFactory) SaslSystemException(io.vertx.proton.sasl.SaslSystemException) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) ProtonMessageHandler(io.vertx.proton.ProtonMessageHandler) DisconnectListener(org.eclipse.hono.client.DisconnectListener) ClientConfigProperties(org.eclipse.hono.config.ClientConfigProperties) MechanismMismatchException(io.vertx.proton.sasl.MechanismMismatchException) ProtonQoS(io.vertx.proton.ProtonQoS) UUID(java.util.UUID) Future(io.vertx.core.Future) Objects(java.util.Objects) CountDownLatch(java.util.concurrent.CountDownLatch) ErrorCondition(org.apache.qpid.proton.amqp.transport.ErrorCondition) ReconnectListener(org.eclipse.hono.client.ReconnectListener) List(java.util.List) SSLException(javax.net.ssl.SSLException) Optional(java.util.Optional) ProtonSender(io.vertx.proton.ProtonSender) AuthenticationException(javax.security.sasl.AuthenticationException) ProtonLink(io.vertx.proton.ProtonLink) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) ClientErrorException(org.eclipse.hono.client.ClientErrorException) ServiceInvocationException(org.eclipse.hono.client.ServiceInvocationException) AtomicReference(java.util.concurrent.atomic.AtomicReference) Constants(org.eclipse.hono.util.Constants) ArrayList(java.util.ArrayList) ProtonSession(io.vertx.proton.ProtonSession) ProtonClientOptions(io.vertx.proton.ProtonClientOptions) Symbol(org.apache.qpid.proton.amqp.Symbol) StatusCodeMapper(org.eclipse.hono.client.StatusCodeMapper) ThreadLocalRandom(java.util.concurrent.ThreadLocalRandom) UnsignedLong(org.apache.qpid.proton.amqp.UnsignedLong) AsyncResult(io.vertx.core.AsyncResult) HonoConnection(org.eclipse.hono.client.HonoConnection) Logger(org.slf4j.Logger) VertxInternal(io.vertx.core.impl.VertxInternal) Iterator(java.util.Iterator) Tracer(io.opentracing.Tracer) NoopTracerFactory(io.opentracing.noop.NoopTracerFactory) Promise(io.vertx.core.Promise) Vertx(io.vertx.core.Vertx) ServerErrorException(org.eclipse.hono.client.ServerErrorException) ProtonHelper(io.vertx.proton.ProtonHelper) TimeUnit(java.util.concurrent.TimeUnit) Handler(io.vertx.core.Handler) Collections(java.util.Collections) ProtonReceiver(io.vertx.proton.ProtonReceiver) Promise(io.vertx.core.Promise) DisconnectListener(org.eclipse.hono.client.DisconnectListener) UnsignedLong(org.apache.qpid.proton.amqp.UnsignedLong) ErrorCondition(org.apache.qpid.proton.amqp.transport.ErrorCondition) ClientErrorException(org.eclipse.hono.client.ClientErrorException) ServerErrorException(org.eclipse.hono.client.ServerErrorException) SaslSystemException(io.vertx.proton.sasl.SaslSystemException) MechanismMismatchException(io.vertx.proton.sasl.MechanismMismatchException) SSLException(javax.net.ssl.SSLException) AuthenticationException(javax.security.sasl.AuthenticationException) ClientErrorException(org.eclipse.hono.client.ClientErrorException) ServiceInvocationException(org.eclipse.hono.client.ServiceInvocationException) ServerErrorException(org.eclipse.hono.client.ServerErrorException)

Example 4 with DisconnectListener

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

the class HonoConnectionImpl method createSender.

/**
 * Creates a sender link.
 *
 * @param targetAddress The target address of the link. If the address is {@code null}, the
 *                      sender link will be established to the 'anonymous relay' and each
 *                      message must specify its destination address.
 * @param qos The quality of service to use for the link.
 * @param closeHook The handler to invoke when the link is closed by the peer (may be {@code null}).
 * @return A future for the created link. The future will be completed once the link is open.
 *         The future will fail with a {@link ServiceInvocationException} if the link cannot be opened.
 * @throws NullPointerException if qos is {@code null}.
 */
@Override
public final Future<ProtonSender> createSender(final String targetAddress, final ProtonQoS qos, final Handler<String> closeHook) {
    Objects.requireNonNull(qos);
    return executeOnContext(result -> {
        checkConnected().compose(v -> {
            if (targetAddress == null && !supportsCapability(Constants.CAP_ANONYMOUS_RELAY)) {
                // before a client can use anonymous terminus
                return Future.failedFuture(new ServerErrorException(HttpURLConnection.HTTP_NOT_IMPLEMENTED, "server does not support anonymous terminus"));
            }
            final Promise<ProtonSender> senderPromise = Promise.promise();
            final ProtonSender sender = session.createSender(targetAddress);
            sender.setQoS(qos);
            sender.setAutoSettle(true);
            final DisconnectListener<HonoConnection> disconnectBeforeOpenListener = (con) -> {
                log.debug("opening sender [{}] failed: got disconnected", targetAddress);
                senderPromise.tryFail(new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE, "not connected"));
            };
            oneTimeDisconnectListeners.add(disconnectBeforeOpenListener);
            sender.openHandler(senderOpen -> {
                oneTimeDisconnectListeners.remove(disconnectBeforeOpenListener);
                // the result future may have already been completed here in case of a link establishment timeout
                if (senderPromise.future().isComplete()) {
                    log.debug("ignoring server response for opening sender [{}]: sender creation already timed out", targetAddress);
                } else if (senderOpen.failed()) {
                    // this means that we have received the peer's attach
                    // and the subsequent detach frame in one TCP read
                    final ErrorCondition error = sender.getRemoteCondition();
                    if (error == null) {
                        log.debug("opening sender [{}] failed", targetAddress, senderOpen.cause());
                        senderPromise.tryFail(new ClientErrorException(HttpURLConnection.HTTP_NOT_FOUND, "cannot open sender", senderOpen.cause()));
                    } else {
                        log.debug("opening sender [{}] failed: {} - {}", targetAddress, error.getCondition(), error.getDescription());
                        senderPromise.tryFail(StatusCodeMapper.fromAttachError(error));
                    }
                } else if (HonoProtonHelper.isLinkEstablished(sender)) {
                    log.debug("sender open [target: {}, sendQueueFull: {}, remote max-message-size: {}]", targetAddress, sender.sendQueueFull(), sender.getRemoteMaxMessageSize());
                    final long remoteMaxMessageSize = Optional.ofNullable(sender.getRemoteMaxMessageSize()).map(UnsignedLong::longValue).orElse(0L);
                    if (remoteMaxMessageSize > 0 && remoteMaxMessageSize < clientConfigProperties.getMinMaxMessageSize()) {
                        // peer won't accept our (biggest) messages
                        sender.close();
                        final String msg = String.format("peer does not support minimum max-message-size [required: %d, supported: %d", clientConfigProperties.getMinMaxMessageSize(), remoteMaxMessageSize);
                        log.debug(msg);
                        senderPromise.tryFail(new ClientErrorException(HttpURLConnection.HTTP_PRECON_FAILED, msg));
                    } else if (sender.getCredit() <= 0) {
                        // wait on credits a little time, if not already given
                        final long waitOnCreditsTimerId = vertx.setTimer(clientConfigProperties.getFlowLatency(), timerID -> {
                            log.debug("sender [target: {}] has {} credits after grace period of {}ms", targetAddress, sender.getCredit(), clientConfigProperties.getFlowLatency());
                            sender.sendQueueDrainHandler(null);
                            senderPromise.tryComplete(sender);
                        });
                        sender.sendQueueDrainHandler(replenishedSender -> {
                            log.debug("sender [target: {}] has received {} initial credits", targetAddress, replenishedSender.getCredit());
                            if (vertx.cancelTimer(waitOnCreditsTimerId)) {
                                result.tryComplete(replenishedSender);
                                replenishedSender.sendQueueDrainHandler(null);
                            }
                        // otherwise the timer has already completed the future and cleaned up
                        // sendQueueDrainHandler
                        });
                    } else {
                        senderPromise.tryComplete(sender);
                    }
                } else {
                    // this means that the peer did not create a local terminus for the link
                    // and will send a detach frame for closing the link very shortly
                    // see AMQP 1.0 spec section 2.6.3
                    log.debug("peer did not create terminus for target [{}] and will detach the link", targetAddress);
                    senderPromise.tryFail(new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE));
                }
            });
            HonoProtonHelper.setDetachHandler(sender, remoteDetached -> onRemoteDetach(sender, connection.getRemoteContainer(), false, closeHook));
            HonoProtonHelper.setCloseHandler(sender, remoteClosed -> onRemoteDetach(sender, connection.getRemoteContainer(), true, closeHook));
            sender.open();
            vertx.setTimer(clientConfigProperties.getLinkEstablishmentTimeout(), tid -> {
                final boolean notOpenedAndNotDisconnectedYet = oneTimeDisconnectListeners.remove(disconnectBeforeOpenListener);
                if (notOpenedAndNotDisconnectedYet) {
                    onLinkEstablishmentTimeout(sender, clientConfigProperties, senderPromise);
                }
            });
            return senderPromise.future();
        }).onComplete(result);
    });
}
Also used : HttpURLConnection(java.net.HttpURLConnection) ProtonConnection(io.vertx.proton.ProtonConnection) ProtonReceiver(io.vertx.proton.ProtonReceiver) Arrays(java.util.Arrays) LoggerFactory(org.slf4j.LoggerFactory) Context(io.vertx.core.Context) HonoProtonHelper(org.eclipse.hono.util.HonoProtonHelper) ConnectionFactory(org.eclipse.hono.connection.ConnectionFactory) SaslSystemException(io.vertx.proton.sasl.SaslSystemException) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) ProtonMessageHandler(io.vertx.proton.ProtonMessageHandler) DisconnectListener(org.eclipse.hono.client.DisconnectListener) ClientConfigProperties(org.eclipse.hono.config.ClientConfigProperties) MechanismMismatchException(io.vertx.proton.sasl.MechanismMismatchException) ProtonQoS(io.vertx.proton.ProtonQoS) UUID(java.util.UUID) Future(io.vertx.core.Future) Objects(java.util.Objects) CountDownLatch(java.util.concurrent.CountDownLatch) ErrorCondition(org.apache.qpid.proton.amqp.transport.ErrorCondition) ReconnectListener(org.eclipse.hono.client.ReconnectListener) List(java.util.List) SSLException(javax.net.ssl.SSLException) Optional(java.util.Optional) ProtonSender(io.vertx.proton.ProtonSender) AuthenticationException(javax.security.sasl.AuthenticationException) ProtonLink(io.vertx.proton.ProtonLink) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) ClientErrorException(org.eclipse.hono.client.ClientErrorException) ServiceInvocationException(org.eclipse.hono.client.ServiceInvocationException) AtomicReference(java.util.concurrent.atomic.AtomicReference) Constants(org.eclipse.hono.util.Constants) ArrayList(java.util.ArrayList) ProtonSession(io.vertx.proton.ProtonSession) ProtonClientOptions(io.vertx.proton.ProtonClientOptions) Symbol(org.apache.qpid.proton.amqp.Symbol) StatusCodeMapper(org.eclipse.hono.client.StatusCodeMapper) ThreadLocalRandom(java.util.concurrent.ThreadLocalRandom) UnsignedLong(org.apache.qpid.proton.amqp.UnsignedLong) AsyncResult(io.vertx.core.AsyncResult) HonoConnection(org.eclipse.hono.client.HonoConnection) Logger(org.slf4j.Logger) VertxInternal(io.vertx.core.impl.VertxInternal) Iterator(java.util.Iterator) Tracer(io.opentracing.Tracer) NoopTracerFactory(io.opentracing.noop.NoopTracerFactory) Promise(io.vertx.core.Promise) Vertx(io.vertx.core.Vertx) ServerErrorException(org.eclipse.hono.client.ServerErrorException) ProtonHelper(io.vertx.proton.ProtonHelper) TimeUnit(java.util.concurrent.TimeUnit) Handler(io.vertx.core.Handler) Collections(java.util.Collections) Promise(io.vertx.core.Promise) ProtonSender(io.vertx.proton.ProtonSender) DisconnectListener(org.eclipse.hono.client.DisconnectListener) UnsignedLong(org.apache.qpid.proton.amqp.UnsignedLong) ErrorCondition(org.apache.qpid.proton.amqp.transport.ErrorCondition) ClientErrorException(org.eclipse.hono.client.ClientErrorException) ServerErrorException(org.eclipse.hono.client.ServerErrorException)

Example 5 with DisconnectListener

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

the class HonoConnectionImplTest method testOnRemoteCloseTriggersReconnection.

/**
 * Verifies that the client tries to re-connect to a server instance if the
 * connection is closed by the peer.
 *
 * @param ctx The test context.
 */
@Test
public void testOnRemoteCloseTriggersReconnection(final VertxTestContext ctx) {
    // GIVEN a client that is connected to a server
    final Promise<HonoConnection> connected = Promise.promise();
    @SuppressWarnings("unchecked") final DisconnectListener<HonoConnection> disconnectListener = mock(DisconnectListener.class);
    honoConnection.addDisconnectListener(disconnectListener);
    final AtomicInteger reconnectListenerInvocations = new AtomicInteger();
    honoConnection.addReconnectListener(con -> reconnectListenerInvocations.incrementAndGet());
    honoConnection.connect(new ProtonClientOptions()).onComplete(connected);
    connectionFactory.setExpectedSucceedingConnectionAttempts(1);
    connected.future().onComplete(ctx.succeeding(c -> {
        // WHEN the peer closes the connection
        connectionFactory.getCloseHandler().handle(Future.failedFuture("shutting down for maintenance"));
        ctx.verify(() -> {
            // THEN the client invokes the registered disconnect handler
            verify(disconnectListener).onDisconnect(honoConnection);
            // and the original connection has been closed locally
            verify(con).close();
            verify(con).disconnectHandler(null);
            // and the connection is re-established
            assertThat(connectionFactory.await()).isTrue();
            assertThat(reconnectListenerInvocations.get()).isEqualTo(1);
        });
        ctx.completeNow();
    }));
}
Also used : HttpURLConnection(java.net.HttpURLConnection) ProtonConnection(io.vertx.proton.ProtonConnection) ProtonReceiver(io.vertx.proton.ProtonReceiver) BeforeEach(org.junit.jupiter.api.BeforeEach) ArgumentMatchers.eq(org.mockito.ArgumentMatchers.eq) Context(io.vertx.core.Context) Timeout(io.vertx.junit5.Timeout) ConnectionFactory(org.eclipse.hono.connection.ConnectionFactory) SaslSystemException(io.vertx.proton.sasl.SaslSystemException) ExtendWith(org.junit.jupiter.api.extension.ExtendWith) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) ProtonMessageHandler(io.vertx.proton.ProtonMessageHandler) Mockito.doAnswer(org.mockito.Mockito.doAnswer) DisconnectListener(org.eclipse.hono.client.DisconnectListener) AmqpError(org.apache.qpid.proton.amqp.transport.AmqpError) ClientConfigProperties(org.eclipse.hono.config.ClientConfigProperties) Predicate(java.util.function.Predicate) ProtonQoS(io.vertx.proton.ProtonQoS) VertxExtension(io.vertx.junit5.VertxExtension) Future(io.vertx.core.Future) AdditionalAnswers(org.mockito.AdditionalAnswers) Test(org.junit.jupiter.api.Test) ErrorCondition(org.apache.qpid.proton.amqp.transport.ErrorCondition) VertxMockSupport(org.eclipse.hono.test.VertxMockSupport) ProtonSender(io.vertx.proton.ProtonSender) Mockito.mock(org.mockito.Mockito.mock) ArgumentMatchers.any(org.mockito.ArgumentMatchers.any) VertxTestContext(io.vertx.junit5.VertxTestContext) ArgumentMatchers.anyLong(org.mockito.ArgumentMatchers.anyLong) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) Target(org.apache.qpid.proton.amqp.messaging.Target) ClientErrorException(org.eclipse.hono.client.ClientErrorException) ServiceInvocationException(org.eclipse.hono.client.ServiceInvocationException) AtomicReference(java.util.concurrent.atomic.AtomicReference) Supplier(java.util.function.Supplier) CompositeFuture(io.vertx.core.CompositeFuture) ProtonSession(io.vertx.proton.ProtonSession) TelemetryConstants(org.eclipse.hono.util.TelemetryConstants) ArgumentCaptor(org.mockito.ArgumentCaptor) ProtonClientOptions(io.vertx.proton.ProtonClientOptions) Symbol(org.apache.qpid.proton.amqp.Symbol) BiConsumer(java.util.function.BiConsumer) UnsignedLong(org.apache.qpid.proton.amqp.UnsignedLong) AsyncResult(io.vertx.core.AsyncResult) HonoConnection(org.eclipse.hono.client.HonoConnection) Promise(io.vertx.core.Promise) Vertx(io.vertx.core.Vertx) ServerErrorException(org.eclipse.hono.client.ServerErrorException) Mockito.times(org.mockito.Mockito.times) Mockito.when(org.mockito.Mockito.when) Truth.assertThat(com.google.common.truth.Truth.assertThat) Mockito.verify(org.mockito.Mockito.verify) TimeUnit(java.util.concurrent.TimeUnit) Mockito.never(org.mockito.Mockito.never) Source(org.apache.qpid.proton.amqp.transport.Source) Handler(io.vertx.core.Handler) ArgumentMatchers.anyString(org.mockito.ArgumentMatchers.anyString) HonoConnection(org.eclipse.hono.client.HonoConnection) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) ProtonClientOptions(io.vertx.proton.ProtonClientOptions) Test(org.junit.jupiter.api.Test)

Aggregations

AsyncResult (io.vertx.core.AsyncResult)5 Context (io.vertx.core.Context)5 Future (io.vertx.core.Future)5 Handler (io.vertx.core.Handler)5 Promise (io.vertx.core.Promise)5 Vertx (io.vertx.core.Vertx)5 ProtonClientOptions (io.vertx.proton.ProtonClientOptions)5 ProtonConnection (io.vertx.proton.ProtonConnection)5 ProtonMessageHandler (io.vertx.proton.ProtonMessageHandler)5 ProtonQoS (io.vertx.proton.ProtonQoS)5 ProtonReceiver (io.vertx.proton.ProtonReceiver)5 ProtonSender (io.vertx.proton.ProtonSender)5 ProtonSession (io.vertx.proton.ProtonSession)5 SaslSystemException (io.vertx.proton.sasl.SaslSystemException)5 HttpURLConnection (java.net.HttpURLConnection)5 TimeUnit (java.util.concurrent.TimeUnit)5 AtomicBoolean (java.util.concurrent.atomic.AtomicBoolean)5 AtomicInteger (java.util.concurrent.atomic.AtomicInteger)5 AtomicReference (java.util.concurrent.atomic.AtomicReference)5 Symbol (org.apache.qpid.proton.amqp.Symbol)5