Listen to connection errors in Mqtt3AsyncClient

I want to get any information on connection errors with Mqtt3AsyncClient.

I try to make it like this:

mqttClient.connect().whenComplete((connAck, throwable) -> {
          Log.d(TAG,"whenComplete");
          if (connAck.getReturnCode().isError()) {
              Log.w(TAG, "MQTT connect failed with code "+connAck.getReturnCode());
          }
      });

But whenComplete seems to only be called on success and never by connection error. What is the right way to listen to connection issues?

Hello @aloz77,

the connect will complete exceptionally if the CONNECT meachnism fails. There are two options to react to this:

  • add a disconnect listener when creating the client
 final Mqtt3AsyncClient client = Mqtt3Client.builder()
                .serverAddress(nodeAddresses.getNodeAddress()
                        .getInetSocketAddress())
                .identifier("test")
                .addDisconnectedListener(context -> {
                    final Throwable cause = context.getCause();
                    if (cause instanceof Mqtt3ConnAckException) {
                        logger.info("disconnect listener: Connect failed because of Negative CONNACK with code {} ", ((Mqtt3ConnAckException) cause).getMqttMessage().getReturnCode());
                    } else {
                        logger.info("disconnect listener: MQTT connect failed because of {}", cause.getMessage());
                    }
                })
                .buildAsync();
  • or as you already did you can check connection success in the when complete method of the connect
 client.connect().whenComplete((connAck, throwable) -> {
            if (connAck != null) {
                logger.info("whenComplete: MQTT connect success with code {}", connAck.getReturnCode());
            } else {
                if (throwable instanceof Mqtt3ConnAckException) {
                    logger.info("whenComplete: Connect failed because of Negative CONNACK with code {} ", ((Mqtt3ConnAckException) throwable).getMqttMessage().getReturnCode());
                } else {
                    logger.info("whenComplete: MQTT connect failed because of {}", throwable.getMessage());
                }
            }
        });

Some notes:

  • the whenComplete only works if a problem occurred in the given action (in the example during connection establishing)
  • the disconnect listener is more general, this listener will always be called when the connection to the broker is broken (even if client initiated the disconnect via a DISCONNECT packet)
  • in both examples you saw a check if the exception is a Mqtt3ConnackException, this exception is thrown by the client internally when a Connack with an error code was received

Hope this helped, cheers
Michael from the HiveMQ team

Thank you, this topic definitely needs some documentation. Or may be need a new onFailureListener to make things easier to implement?

From my tests with addDisconnectedListener approach throwable is only Mqtt3ConnAckException if MQTT authentication failed.

In other cases throwable is not Mqtt3ConnAckException. How can I clearly distinguish between intentional disconnects caused by disconnect() call and different connection failures? Only by parsing cause.getMessage() string? The cause.getMessage() string are like this:

Client sent DISCONNECT
java.io.IOException: Software caused connection abort
java.net.UnknownHostException: Unable to resolve host “farmer.cloudmqtt.com”: No address associated with hostname

You could create an issue: GitHub - hivemq/hivemq-mqtt-client: HiveMQ MQTT Client is an MQTT 5.0 and MQTT 3.1.1 compatible and feature-rich high-performance Java client library with different API flavours and backpressure support

This is correct, the Mqtt3ConnAckException is thrown when the client receives a negative ConnAck (a ConnAck with a failure return code indication connection is not allowed)

You can do the same as with the Mtt3ConnAckException and check if the throwable from the context is an Mqtt3DisconnectException:

else if (cause instanceof Mqtt3DisconnectException) {
   logger.info("disconnect listener: Client closed connection with a DISCONNECT");
}

Greetings,
Michael

1 Like

Thank you, by filtering out Mqtt3DisconnectException makes it some easier

The disconnect listener is called if

  • a connection failed
  • a connection is closed or
  • a connection is disconnected.

You can check context.getSource() which returns either CLIENT, SERVER or USER (https://javadoc.io/doc/com.hivemq/hivemq-mqtt-client/latest/index.html)

  • If you call disconnect() then the source is USER
  • If the server actively closes the connection (with or without sending a DISCONNECT packet first) the the source is SERVER
  • If the client detects that the connection is not active anymore (connect timeout, keepalive timeout, IO error, …) then the source is CLIENT
1 Like