This document is for developers extending or integrating the Red5 MQTT plugin. It focuses on internal architecture, extension points, and configuration behavior as implemented in this module.
For the proposed MQTT 5.0 implementation plan and class-level mapping, see mqtt/MQTT5_DESIGN.md.
The plugin embeds a Moquette-derived broker implementation (source is included under org.eclipse.moquette.*). Networking is handled by Apache MINA, and broker processing is serialized through a Disruptor ring buffer.
High-level flow:
- Spring creates one or more
MQTTTransportbeans (listen sockets). - Spring creates
MQTTBroker, which wires auth + persistence intoSimpleMessaging. MQTTBrokerinjectsSimpleMessaginginto everyMQTTTransporthandler.- MINA accepts MQTT connections, decodes protocol frames, and hands messages to
MQTTHandler. MQTTHandlerforwards protocol messages toSimpleMessaging, which drivesProtocolProcessorvia the Disruptor ring buffer.
Architecture sketch:
MQTT Client
|
v
MQTTTransport (MINA acceptor + codec)
|
v
MQTTHandler (session -> MinaChannel)
|
v
SimpleMessaging --(Disruptor)--> ProtocolProcessor
| |
| v
+--> SubscriptionsStore Persistence (MapDB)
MQTT protocol version is determined from the CONNECT packet:
- MQTT 3.1.1 uses Protocol Level
4. - MQTT 5.0 uses Protocol Version
5.
Server behavior should be:
- Accept both versions when configured to do so.
- Reject unsupported versions early with the correct return code/reason code, then disconnect.
- Store the negotiated protocol version on the session and branch behavior for encoding/decoding, reason codes, and properties.
-
org.red5.server.mqtt.MQTTBroker- Spring entry point.
- Builds
MapDBPersistentStoreand anIAuthenticator, then callsSimpleMessaging.init(). - Propagates the
SimpleMessaginginstance into allMQTTTransporthandlers.
-
org.red5.server.mqtt.net.MQTTTransport- Owns MINA
NioSocketAcceptor, binds ports/addresses, and installs the MQTT codec. - Extension points: custom
IoFilters, buffer sizes, I/O threads, secure config.
- Owns MINA
-
org.red5.server.mqtt.net.MQTTHandler- Translates MINA sessions into
MinaChanneland forwards messages toIMessaging. - Extension points: protocol logging, session lifecycle hooks, metrics.
- Translates MINA sessions into
-
org.eclipse.moquette.spi.impl.SimpleMessaging- Core broker logic entry point.
- Manages
SubscriptionsStore,ProtocolProcessor, persistence, and ring buffer.
-
org.eclipse.moquette.spi.impl.ProtocolProcessor- Protocol state machine; handles CONNECT/SUBSCRIBE/PUBLISH, etc.
Persistence is handled by MapDBPersistentStore:
- In-memory store when
dbStorePathis empty. - File-backed store when
dbStorePathpoints to a file. - Retained messages, QoS in-flight state, and subscriptions are persisted.
Configuration:
MQTTBroker.dbStorePath(defaults to${user.home}/mqtt_store.mapdb)MQTTBroker.maximumQoS(defaults to2) — CONNACK Maximum QoS (MQTT 5.0)MQTTBroker.retainAvailable(defaults totrue) — CONNACK Retain Available (MQTT 5.0)MQTTBroker.wildcardSubscriptionAvailable(defaults totrue) — CONNACK Wildcard Subscription Available (MQTT 5.0)MQTTBroker.subscriptionIdentifierAvailable(defaults totrue) — CONNACK Subscription Identifier Available (MQTT 5.0)MQTTBroker.sharedSubscriptionAvailable(defaults tofalse) — CONNACK Shared Subscription Available (MQTT 5.0)MQTTBroker.serverKeepAlive(defaults to-1, omitted when <= 0) — CONNACK Server Keep Alive (MQTT 5.0)
Notes:
MapDBPersistentStore.close()commits and closes the DB; broker shutdown calls this.- Values are serialized using MapDB's Java serializer.
Authentication is provided by IAuthenticator:
- If
passwdFileNameis empty,AcceptAllAuthenticatoris used. - If set,
FileAuthenticatorreads a username/password file from${user.home}/${passwdFileName}. - File format is
username:passwordper line;#starts a comment.
Extension ideas:
- Implement
IAuthenticatorfor LDAP, OAuth, or database-backed auth. - Replace the authenticator in
MQTTBrokerbeforeSimpleMessaging.init().
Enhanced auth (MQTT 5.0):
- Implement
IEnhancedAuthenticatorto participate in the AUTH exchange. ProtocolProcessorwill callonConnectwhen CONNECT includes an auth method.- If
onConnectreturns an AUTH with reason codeContinue Authentication, the broker sends AUTH and waits for the next AUTH from the client. - Subsequent AUTH packets are routed to
onAuth. MQTTBrokercan be configured with a customIAuthenticatorbean (includingIEnhancedAuthenticator) via theauthenticatorproperty.
Subscriptions are stored in SubscriptionsStore:
- Supports MQTT wildcards
+and#. - Matching logic lives in
SubscriptionsStore.matchTopicsandTreeNode.matches.
Extension ideas:
- Add ACL checks in
SubscriptionsStore(or inProtocolProcessor) before storing a subscription. - Persist additional metadata per subscription (needs schema changes in MapDB store).
- MINA manages the network I/O threads.
- Broker logic is serialized through a Disruptor ring buffer in
SimpleMessaging/ProtocolProcessor. - The Disruptor thread uses the default Java
ThreadFactory.
Implication:
- Protocol handling is single-threaded by design; avoid blocking operations inside
ProtocolProcessor.
Minimal broker + transport:
<bean id="mqttTransport" class="org.red5.server.mqtt.net.MQTTTransport">
<property name="port" value="1883"/>
</bean>
<bean id="mqttBroker" class="org.red5.server.mqtt.MQTTBroker" depends-on="mqttTransport">
<property name="dbStorePath" value="/opt/red5/mqtt_store.mapdb"/>
<property name="passwdFileName" value=""/>
<property name="maximumQoS" value="2"/>
<property name="retainAvailable" value="true"/>
<property name="wildcardSubscriptionAvailable" value="true"/>
<property name="subscriptionIdentifierAvailable" value="true"/>
<property name="sharedSubscriptionAvailable" value="false"/>
<property name="serverKeepAlive" value="-1"/>
</bean>Secure transport:
<bean id="mqttTransportSecure" class="org.red5.server.mqtt.net.MQTTTransport">
<property name="secureConfig">
<bean id="mqttSecureConfig" class="org.red5.server.mqtt.SecureMQTTConfiguration">
<property name="keystoreType" value="JKS"/>
<property name="keystoreFile" value="conf/keystore"/>
<property name="keystorePassword" value="password"/>
<property name="truststoreFile" value="conf/truststore"/>
<property name="truststorePassword" value="password"/>
</bean>
</property>
<property name="addresses">
<list>
<value>0.0.0.0:8883</value>
</list>
</property>
</bean>Unit tests live under mqtt/src/test/java:
SubscriptionsStoreTestcovers topic wildcard matching.MapDBPersistentStoreTestexercises retained messages and session/QoS storage.
Note: Maven may be configured to skip tests in this environment. Use mvn -DskipTests=false test if needed.
- MQTT 5.0 coverage is partial (reason strings are limited).
- Password authentication is plaintext (no hashing).
- No ACL layer is included; implementers should add authorization checks if required.