INT-4138: MQTT: Outbound Adapter Improvements

JIRA:https://jira.spring.io/browse/INT-4138

Expressions for topic, qos, retained.

Also change inbound mapping to `RECEIVED_...` headers.

Fix some minor asciidoc problems in (s)ftp.

Rework Qos/Retained Expressions/Defaults

Encapsulate the logic entirely in the converter.

Polishing - PR Comments
This commit is contained in:
Gary Russell
2016-11-14 16:04:17 -05:00
committed by Artem Bilan
parent d42357ba02
commit 3035bc716d
17 changed files with 471 additions and 127 deletions

View File

@@ -802,7 +802,7 @@ Typically, you would use the `#remoteDirectory` variable in the `local-directory
Starting with _version 5.0_, the `FtpSimplePatternFileListFilter` and `FtpRegexPatternFileListFilter` can be configured to always pass directories by setting the `alwaysAcceptDirectorties` to `true`.
This allows recursion for a simple pattern; examples follow:
[code, xml]
[source, xml]
----
<bean id="startDotTxtFilter" class="org.springframework.integration.ftp.filters.FtpSimplePatternFileListFilter">
<constructor-arg value="*.txt" />

View File

@@ -20,17 +20,17 @@ A minimal configuration might be:
[source,xml]
----
<bean id="clientFactory"
class="org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory">
<property name="userName" value="${mqtt.username}"/>
<property name="password" value="${mqtt.password}"/>
class="org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory">
<property name="userName" value="${mqtt.username}"/>
<property name="password" value="${mqtt.password}"/>
</bean>
<int-mqtt:message-driven-channel-adapter id="mqttInbound"
client-id="${mqtt.default.client.id}.src"
url="${mqtt.url}"
topics="sometopic"
client-factory="clientFactory"
channel="output"/>
client-id="${mqtt.default.client.id}.src"
url="${mqtt.url}"
topics="sometopic"
client-factory="clientFactory"
channel="output"/>
----
Attributes:
@@ -38,16 +38,16 @@ Attributes:
[source,xml]
----
<int-mqtt:message-driven-channel-adapter id="oneTopicAdapter"
client-id="foo" <1>
url="tcp://localhost:1883" <2>
topics="bar,baz" <3>
qos="1,2" <4>
converter="myConverter" <5>
client-factory="clientFactory" <6>
send-timeout="123" <7>
error-channel="errors" <8>
recovery-interval="10000" <9>
channel="out" />
client-id="foo" <1>
url="tcp://localhost:1883" <2>
topics="bar,baz" <3>
qos="1,2" <4>
converter="myConverter" <5>
client-factory="clientFactory" <6>
send-timeout="123" <7>
error-channel="errors" <8>
recovery-interval="10000" <9>
channel="out" />
----
<1> The client id.
@@ -109,6 +109,11 @@ The latter (the default) will unsubscribe only if the `cleanSession` property is
To revert to the pre-4.2.3 behavior, use `UNSUBSCRIBE_ALWAYS`.
====
[IMPORTANT]
====
Starting with _version 5.0_, the `topic`, `qos` and `retained` properties are mapped to `.RECEIVED_...` headers (`MqttHeaders.RECEIVED_TOPIC`, `MqttHeaders.RECEIVED_QOS`, and `MqttHeaders.RECEIVED_RETAINED`), to avoid inadvertent propagation to an outbound message which (by default) uses the `MqttHeaders.TOPIC`, `MqttHeaders.QOS`, and `MqttHeaders.RETAINED` headers.
====
==== Adding/Removing Topics at Runtime
Starting with _version 4.1_, it is possible to programmatically change the topics to which the adapter is subscribed.
@@ -130,39 +135,39 @@ The following Spring Boot application provides an example of configuring the inb
public class MqttJavaApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(MqttJavaApplication.class)
.web(false)
.run(args);
new SpringApplicationBuilder(MqttJavaApplication.class)
.web(false)
.run(args);
}
@Bean
public MessageChannel mqttInputChannel() {
return new DirectChannel();
return new DirectChannel();
}
@Bean
public MessageProducer inbound() {
MqttPahoMessageDrivenChannelAdapter adapter =
new MqttPahoMessageDrivenChannelAdapter("tcp://localhost:1883", "testClient",
"topic1", "topic2");
adapter.setCompletionTimeout(5000);
adapter.setConverter(new DefaultPahoMessageConverter());
adapter.setQos(1);
adapter.setOutputChannel(mqttInputChannel());
return adapter;
MqttPahoMessageDrivenChannelAdapter adapter =
new MqttPahoMessageDrivenChannelAdapter("tcp://localhost:1883", "testClient",
"topic1", "topic2");
adapter.setCompletionTimeout(5000);
adapter.setConverter(new DefaultPahoMessageConverter());
adapter.setQos(1);
adapter.setOutputChannel(mqttInputChannel());
return adapter;
}
@Bean
@ServiceActivator(inputChannel = "mqttInputChannel")
public MessageHandler handler() {
return new MessageHandler() {
return new MessageHandler() {
@Override
public void handleMessage(Message<?> message) throws MessagingException {
System.out.println(message.getPayload());
}
@Override
public void handleMessage(Message<?> message) throws MessagingException {
System.out.println(message.getPayload());
}
};
};
}
}
@@ -182,16 +187,19 @@ Attributes:
[source,xml]
----
<int-mqtt:outbound-channel-adapter id="withConverter"
client-id="foo" <1>
url="tcp://localhost:1883" <2>
converter="myConverter" <3>
client-factory="clientFactory" <4>
default-qos="1" <5>
default-retained="true" <6>
default-topic="bar" <7>
async="false" <8>
async-events="false" <9>
channel="target" />
client-id="foo" <1>
url="tcp://localhost:1883" <2>
converter="myConverter" <3>
client-factory="clientFactory" <4>
default-qos="1" <5>
qos-expression="" <6>
default-retained="true" <7>
retained-expression="" <8>
default-topic="bar" <9>
topic-expression="" <10>
async="false" <11>
async-events="false" <12>
channel="target" />
----
<1> The client id.
@@ -210,22 +218,25 @@ The default `DefaultPahoMessageConverter` recognizes the following headers: +
<4> The client factory.
<5> The default quality of service (used if no `mqtt_qos` header is found).
Not allowed if a custom `converter` is supplied.
<5> The default quality of service (used if no `mqtt_qos` header is found or the `qos-expression` returns `null`.
Not used if a custom `converter` is supplied.
<6> An expression to evaluate to determine the qos; default `headers[mqtt_qos]`.
<6> The default value of the retained flag (used if no `mqtt_retained` header is found).
Not allowed if a custom `converter` is supplied.
<7> The default value of the retained flag (used if no `mqtt_retained` header is found).
Not used if a custom `converter` is supplied.
<8> An expression to evaluate to determine the retained boolean; default `headers[mqtt_retained]`.
<7> The default topic to which the message will be sent (used if no `mqtt_topic` header is found).
<9> The default topic to which the message will be sent (used if no `mqtt_topic` header is found).
<10> An expression to evaluate to determine the destination topic; default `headers['topic']`.
<8> When `true`, the caller will not block waiting for delivery confirmation when a message is sent.
<11> When `true`, the caller will not block waiting for delivery confirmation when a message is sent.
Default:false (the send blocks until delivery is confirmed).
<9> When `async` and `async-events` are both `true`, an `MqttMessageSentEvent` is emitted, containing the message, the topic, the `messageId` generated by the client library, the `clientId` and the `clientInstance` (incremented each time the client is connected).
<12> When `async` and `async-events` are both `true`, an `MqttMessageSentEvent` is emitted, containing the message, the topic, the `messageId` generated by the client library, the `clientId` and the `clientInstance` (incremented each time the client is connected).
When the delivery is confirmed by the client library, an `MqttMessageDeliveredEvent` is emitted, containing the the `messageId`, `clientId` and the `clientInstance`, enabling delivery to be correlated with the send.
These events can be received by any `ApplicationListener`, or by an event inbound channel adapter.
Note that it is possible that the `MqttMessageDeliveredEvent` might be received before the `MqttMessageSentEvent`.
@@ -245,9 +256,9 @@ public class MqttJavaApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context =
new SpringApplicationBuilder(MqttJavaApplication.class)
.web(false)
.run(args);
new SpringApplicationBuilder(MqttJavaApplication.class)
.web(false)
.run(args);
MyGateway gateway = context.getBean(MyGateway.class);
gateway.sendToMqtt("foo");
}

View File

@@ -891,7 +891,7 @@ Typically, you would use the `#remoteDirectory` variable in the `local-directory
Starting with _version 5.0_, the `SftpSimplePatternFileListFilter` and `SftpRegexPatternFileListFilter` can be configured to always pass directories by setting the `alwaysAcceptDirectorties` to `true`.
This allows recursion for a simple pattern; examples follow:
[code, xml]
[source, xml]
----
<bean id="startDotTxtFilter" class="org.springframework.integration.sftp.filters.SftpSimplePatternFileListFilter">
<constructor-arg value="*.txt" />

View File

@@ -92,3 +92,11 @@ See <<http-header-mapping>> for more information.
Aggregators now use a `SimpleSequenceSizeReleaseStrategy` by default, which is more efficient, especially with large groups.
Empty groups are now scheduled for removal after `empty-group-min-timeout`.
See <<aggregator>> for more information.
==== MQTT Changes
Inbound messages are now mapped with headers `RECEIVED_TOPIC`, `RECEIVED_QOS` and `RECEIVED_RETAINED` to avoid inadvertent propagation to outbound messages when an application is relaying messages.
The outbound channel adapter now supports expressions for the topic, qos and retained properties; the defaults remain the same.
See <<mqtt>> for more information.