To add support for MQTT to an existing project, you must first add either the Micronaut MQTT HiveMQ dependency or the Micronaut MQTT Eclipse Paho v3 or v5 (depending on the version of MQTT you require) to your build.
Important
If you are running into the exception org.eclipse.paho.client.mqttv3.MqttException: Timed out waiting for a response from the server, please make sure to run your program on a machine with more than one core. The Eclipse Paho MQTT client will run into connection timeouts if it has only one core available.
4.1 Creating an MQTT Publisher with MqttPublisher annotation
To create an MQTT client that produces messages you can simply define an interface that is annotated with MqttPublisher.
For example the following is a trivial MqttPublisher interface:
At compile time Micronaut will produce an implementation of the above interface. You can retrieve an instance of ProductClient either by looking up the bean from the ApplicationContext or by injecting the bean with @Inject:
All properties on the MqttConnectionOptions are available to be modified, either through configuration or a BeanCreatedEventListener.
The properties that can be converted from the string values in a configuration file can be configured directly.
Note
Without any configuration the defaults in the MqttConnectionOptions will be used.
Tip
It is also possible to disable the integration entirely with mqtt.enabled: false
7.3 Properties
Publishers
It is also supported to supply properties when publishing messages. Any of the org.eclipse.paho.mqttv5.common.packet.MqttProperties can be set dynamically per execution or statically for all executions. Properties can be set using the @MqttProperty annotation.
For method arguments, if the value is not supplied to the annotation, the argument name will be used as the property name. For example, @MqttProperty String userId would result in the property userId being set on the properties object before publishing.
Important
If the annotation or argument name cannot be matched to a property name in the MqttProperties class, a user property will be added instead.
Subscribers
Any properties found in messages received can be bound to subscriber arguments through the @v5,MqttProperty annotation.
Note
Arguments are required by default and an exception will be thrown if they cannot be found or converted to the requested type. Making the argument nullable allows null values to be accepted.
8 SSL Connections
This library supports connecting to MQTT brokers over SSL. Because that feature requires a third party dependency (Bouncy Castle), the functionality exists in a separate module that you must express a dependency on.
After adding the dependency, you must configure the client with the appropriate certificate authority, certificate, key, and password.
Note
The files can be configured with the file: prefix to reference absolute paths on the file system or classpath: to reference files on the classpath.
Once the configuration is in place the client will connect over SSL.
Tip
The server URI must start with ssl:.
9 MQTT Publishers
The example in the quick start presented a trivial definition of an interface that be implemented automatically for you using the @MqttPublisher annotation.
The implementation that powers @MqttPublisher is, however, very flexible and offers a range of options for defining MQTT producers.
9.1 Defining Publisher Methods
All methods that publish messages to MQTT must meet the following conditions:
The method must reside in a class annotated with @MqttPublisher.
The method must provide the topic to publish to, either through an argument, or the topic annotation on the method or class.
Important
If the topic cannot be found, an exception will be thrown. Unless a reactive type or future is returned from the publishing method, the action is blocking.
9.1.1 Publisher Parameters
All options are available to be set for publishing messages, either through static values in annotations on the class or method, or through annotations on the method arguments.
9.1.1.1 Topic
To set the topic to publish to, apply the @Topic annotation to the method or an argument of the method. Apply the annotation to the class or method if the value is static for every execution. Apply the annotation to an argument of the method if the value should be set per execution.
9.1.1.2 Qos
Quality of service can be set for publishing messages either through the @Topic or @Qos annotations. The @Qos annotation can be applied to a method argument to set the Qos per execution.
9.1.1.3 Retained
The retained flagg can be set for publishing messages through the @Retained annotation. The annotation can also be applied to a method argument to set the retained flag per execution.
9.1.1.4 Payload
Most examples up to this point have been using a byte[] as the body type for simplicity. This library supports most standard Java types and JSON serialization (using Jackson) by default. The functionality is extensible and it is possible to add support for additional types and serialization strategies. See the section on Message Serialization/Deserialization for more information.
9.1.2 Publisher Acknowledgements
MQTT supports publisher acknowledgements that behave different based on the qos of the message being sent. The broker will acknowledge the message and then that will cause the publishing method to complete. For publishers that return void, that means the method will block the current thread until the acknowledgement is received. For futures and reactive types, they will complete after the acknowledgement is received.
Since the publisher is cold, the message will not actually be published until the stream is subscribed to.
For example:
10 MQTT Subscribers
The example in the quick start presented a trivial definition of a class that listens for messages using the @MqttSubscriber annotation.
The implementation that powers @MqttSubscriber (defined by the AbstractMqttSubscriberAdvice class) is, however, very flexible and offers a range of options for defining MQTT subscribers.
10.1 Defining @MqttSubscriber Methods
All methods that consume messages from MQTT must meet the following conditions:
The method must reside in a class annotated with @MqttSubscriber.
The method must be annotated with one or more @Topic annotations.
Note
In order for all of the functionality to work as designed in this guide your classes must be compiled with the parameters flag set to true. If your application was created with the Micronaut CLI, then that has already been configured for you.
10.1.1 Subscriber Parameters
In order for the subscriber method to be invoked, all arguments must be satisfied. To allow execution of the method with a null value, the argument must be declared as nullable. If the arguments cannot be satisfied, an exception will be thrown. See the section on consumer exceptions for details on how to handle exceptions thrown during the subscription process.
10.1.1.1 Topic
A @Topic annotation is required for a method to be a subscriber of messages from MQTT. Simply apply the annotation to the method and supply the name of the topic you would like to listen to. Multiple topics can be subscribed to by supplying multiple annotations. Each topic can have its own qos value set in the annotation.
10.1.1.2 Payload
Most examples up to this point have been using a byte[] as the body type for simplicity. This library supports most standard Java types and JSON deserialization (using Jackson) by default. The functionality is extensible and it is possible to add support for additional types and deserialization strategies. See the section on Message Serialization/Deserialization for more information.
10.1.2 Acknowledging Messages
If manual acknowledge is enabled through the mqtt.client.manual-acks configuration property, messages can be acknowledged manually.
For methods that accept an argument of type Acknowledgement, the message will only be acknowledged when the ack method is called. The nack method is not relevant for MQTT and should not be called.
Acknowledgement Type
10.2 Handling Subscriber Exceptions
Exceptions can occur in a number of different ways. Possible problem areas include:
Binding the message to the method arguments
Exceptions thrown from the subscriber methods
Exceptions as a result of message acknowledgement
Exceptions thrown attempting to subscribe to a topic
If the subscriber bean implements MqttSubscriberExceptionHandler, then exceptions will be sent to the method implementation.
If the subscriber bean does not implement MqttSubscriberExceptionHandler, then the exceptions will be routed to the primary exception handler bean. To override the default exception handler, replace the DefaultMqttSubscriberExceptionHandler with your own implementation that is designated as @Primary.
10.3 Subscriber Execution
MQTT allows an ExecutorService to be supplied for new connections. The service is used to execute subscribers. A single connection is used for the entire application and it is configured to use the consumer named executor service. The executor can be configured through application configuration. See ExecutorConfiguration for the full list of options.
If no configuration is supplied, a scheduled thread pool with 2 times the amount of available processors is used.
Note
The executor type must be scheduled.
11 Customizing Parameter Binding
Default Binding Functionality
Argument binding is achieved through an MqttBinderRegistry. The registry is responsible for choosing which @MqttBinder should be responsible for binding an argument. There are two methods, bindFrom and bindTo, that control binding to subscribers and publishers respectively.
The registry supports argument binders that are used based on an annotation applied to the argument or the argument type. All argument binders must implement either AnnotatedMqttBinder or TypedMqttBinder. The exception to that rule is the FallbackMqttBinder which is used when no other binders support a given argument. The binder registry follows a small sequence of steps to attempt to find a binder that supports the argument.
Search the annotation based binders for one that matches any annotation on the argument that is annotated with @Bindable.
Search the type based binders for one that matches or is a subclass of the argument type.
Return the default binder.
The default binder checks if the argument name matches one of the supported properties. If the name does not match, the body of the message is bound to the argument.
The MqttBindingContext is the context used to binding data from and to messages. Each implementation (v3 and v5) has their own implementation. If you know your binder will only be working with a specific implementation, binders can reference the implementation instead of the interface.
Custom Binding
To inject your own argument binding behavior, it is as simple as registering a bean. The existing binder registry will inject it and include it in the normal processing.
Annotation Binding
A custom annotation can be created to bind subscriber arguments. A custom binder can then be created to use that annotation. In this example an annotation is used to bind to the correlation data (only available in v5).
The annotation can now be used on the argument in a subscriber method.
A custom binder can be created to support any argument type. For example the following class could be created to bind values from user properties (V5 only). This functionality could allow the work of retrieving and converting the properties to occur in a single place instead of multiple times in your code.
A type argument binder can then be created to create the ProductInfo instance to bind to and from method arguments.
12 Message Serialization/Deserialization (SerDes)
The serialization and deserialization of message payloads is handled through instances of MqttPayloadSerDes. The ser-des (Serializer/Deserializer) is responsible for both serialization and deserialization of MQTT message payloads into the types defined in your publisher and subscriber methods.
The ser-des are managed by a MqttPayloadSerDesRegistry. All ser-des beans are injected in order into the registry and then searched for when serialization or deserialization is needed. The first ser-des that returns true for supports-java.lang.Class- is returned and used.
By default, standard Java lang types and JSON format (with Jackson) are supported. You can supply your own ser-des by simply registering a bean of type MqttPayloadSerDes. All ser-des implement the Ordered interface, so custom implementations can come before, after, or in between the default implementations.
12.1 Custom SerDes
A custom serializer/deserializer would be necessary to support custom data formats. In the section on Custom Binding an example was demonstrated that allowed binding a ProductInfo type from the properties of the message. If instead that object should represent the payload of the message with a custom data format, you could register your own serializer/deserializer to do so.
In this example a simple data format of the string representation of the fields are concatenated together with a pipe character.
Tip
Because the getOrder method was not overridden, the default order of 0 is used. All default ser-des have a lower precedent than the default order which means this ser-des will be checked before the others.
13 GraalVM support
Micronaut MQTT is compatible with GraalVM. Everything is handled automatically by the library
so users don’t need any special configuration.
The only additional GraalVM configuration you need if you are using mqtt-ssl module is adding the option
--report-unsupported-elements-at-runtime.
Note
See the section on GraalVM in the user guide for more
information.
14 Breaking Changes
This section documents breaking changes for Mqtt:
3.0.0
The annotations for Mqtt have moved into the core module, and therefore have changed package names.
The new package names are:
Mqtt3
Old package name
New package name
io.micronaut.mqtt.v3.annotation.MqttPublisher
io.micronaut.mqtt.annotation.v3.MqttPublisher
Mqtt5
Old package name
New package name
io.micronaut.mqtt.v5.annotation.MqttPublisher
io.micronaut.mqtt.annotation.v5.MqttPublisher
io.micronaut.mqtt.v5.annotation.MqttProperty
io.micronaut.mqtt.annotation.v5.MqttProperty
io.micronaut.mqtt.v5.annotation.MqttProperties
io.micronaut.mqtt.annotation.v5.MqttProperties
15 Repository
You can find the source code of this project in this repository: