JMS-in-gateway: destination to requestDestination (#3494)

* JMS-in-gateway: destination to requestDestination

Related to https://stackoverflow.com/questions/66174779/what-is-the-dsl-version-of-int-jmsinbound-gateway-request-destination

In Java DSL, the `JmsInboundGatewaySpec` expose a `destination()` option which is not correlated
what we have in XML with a `request-destination` and what we have with a `replyQueue` option

* Deprecate an existing `destination()` option in the `JmsInboundGatewaySpec`
in favor of newly introduced `requestDestination()`
* Clean up a bit `jms.adoc` and add some code block-switch sections to show
Java & Kotlin DSLs along side with existing XML configs

* Fix typo.

Co-authored-by: Gary Russell <grussell@vmware.com>
This commit is contained in:
Artem Bilan
2021-02-16 17:08:24 -05:00
committed by GitHub
parent b1cb9069fe
commit aeb43f3069
3 changed files with 169 additions and 28 deletions

View File

@@ -197,7 +197,8 @@ public class JmsInboundGatewaySpec<S extends JmsInboundGatewaySpec<S>>
* @param <S> the target {@link JmsListenerContainerSpec} implementation type.
* @param <C> the target {@link AbstractMessageListenerContainer} implementation type.
*/
public static class JmsInboundGatewayListenerContainerSpec<S extends JmsListenerContainerSpec<S, C>, C extends AbstractMessageListenerContainer>
public static class JmsInboundGatewayListenerContainerSpec<S extends JmsListenerContainerSpec<S, C>,
C extends AbstractMessageListenerContainer>
extends JmsInboundGatewaySpec<JmsInboundGatewayListenerContainerSpec<S, C>> {
private final S spec;
@@ -212,9 +213,22 @@ public class JmsInboundGatewaySpec<S extends JmsInboundGatewaySpec<S>>
* @param destination the destination
* @return the spec.
* @see JmsListenerContainerSpec#destination(Destination)
* @deprecated since 5.5 in favor of {@link #requestDestination(Destination)}
*/
@Deprecated
public JmsInboundGatewayListenerContainerSpec<S, C> destination(Destination destination) {
this.spec.destination(destination);
return requestDestination(destination);
}
/**
* Specify a request destination for incoming messages.
* @param requestDestination the destination
* @return the spec.
* @see JmsListenerContainerSpec#destination(Destination)
* @since 5.5
*/
public JmsInboundGatewayListenerContainerSpec<S, C> requestDestination(Destination requestDestination) {
this.spec.destination(requestDestination);
return _this();
}
@@ -222,14 +236,32 @@ public class JmsInboundGatewaySpec<S extends JmsInboundGatewaySpec<S>>
* @param destinationName the destinationName
* @return the spec.
* @see JmsListenerContainerSpec#destination(String)
* @deprecated since 5.5 in favor of {@link #requestDestination(String)}
*/
@Deprecated
public JmsInboundGatewayListenerContainerSpec<S, C> destination(String destinationName) {
this.spec.destination(destinationName);
return requestDestination(destinationName);
}
/**
* Specify a request destination for incoming messages.
* @param requestDestinationName the destination name
* @return the spec.
* @see JmsListenerContainerSpec#destination(String)
* @since 5.5
*/
public JmsInboundGatewayListenerContainerSpec<S, C> requestDestination(String requestDestinationName) {
this.spec.destination(requestDestinationName);
return _this();
}
public JmsInboundGatewayListenerContainerSpec<S, C> configureListenerContainer(
Consumer<S> configurer) {
/**
* Specify a {@link Consumer} to accept a {@link JmsListenerContainerSpec} for further configuration.
* @param configurer the {@link Consumer} to accept a {@link JmsListenerContainerSpec}
* for further configuration.
* @return the spec
*/
public JmsInboundGatewayListenerContainerSpec<S, C> configureListenerContainer(Consumer<S> configurer) {
Assert.notNull(configurer, "'configurer' must not be null");
configurer.accept(this.spec);
return _this();

View File

@@ -444,7 +444,7 @@ public class JmsTests extends ActiveMQMultiContextTests {
}
}))
.destination("jmsPipelineTest")
.requestDestination("jmsPipelineTest")
.configureListenerContainer(c ->
c.transactionManager(mock(PlatformTransactionManager.class))))
.filter(payload -> !"junk".equals(payload))

View File

@@ -6,8 +6,8 @@ Spring Integration provides channel adapters for receiving and sending JMS messa
You need to include this dependency into your project:
====
[source, xml, subs="normal", role="primary"]
.Maven
[source, xml, subs="normal"]
----
<dependency>
<groupId>org.springframework.integration</groupId>
@@ -15,9 +15,8 @@ You need to include this dependency into your project:
<version>{project-version}</version>
</dependency>
----
[source, groovy, subs="normal", role="secondary"]
.Gradle
[source, groovy, subs="normal"]
----
compile "org.springframework.integration:spring-integration-jms:{project-version}"
----
@@ -52,7 +51,44 @@ The inbound channel adapter requires a reference to either a single `JmsTemplate
The following example defines an inbound channel adapter with a `Destination` reference:
====
[source,xml]
[source, java, role="primary"]
.Java DSL
----
@Bean
public IntegrationFlow jmsInbound(ConnectionFactory connectionFactory) {
return IntegrationFlows.from(
Jms.inboundAdapter(connectionFactory)
.destination("inQueue"),
e -> e.poller(poller -> poller.fixedRate(30000)))
.handle(m -> System.out.println(m.getPayload()))
.get();
}
----
[source, kotlin, role="secondary"]
.Kotlin DSL
----
@Bean
fun jmsInbound(connectionFactory: ConnectionFactory) =
integrationFlow(
Jms.inboundAdapter(connectionFactory).destination("inQueue"),
{ poller { Pollers.fixedRate(30000) } })
{
handle { m -> println(m.payload) }
}
----
[source, java, role="secondary"]
.Java
----
@Bean
@InboundChannelAdapter(value = "exampleChannel", poller = @Poller(fixedRate = "30000"))
public MessageSource<Object> jmsIn(ConnectionFactory connectionFactory) {
JmsDestinationPollingSource source = new JmsDestinationPollingSource(new JmsTemplate(connectionFactory));
source.setDestinationName("inQueue");
return source;
}
----
[source, xml, role="secondary"]
.XML
----
<int-jms:inbound-channel-adapter id="jmsIn" destination="inQueue" channel="exampleChannel">
<int:poller fixed-rate="30000"/>
@@ -72,19 +108,7 @@ However, if your JMS `ConnectionFactory` has a different bean name, you need to
If `extract-payload` is set to `true` (the default), the received JMS Message is passed through the `MessageConverter`.
When relying on the default `SimpleMessageConverter`, this means that the resulting Spring Integration Message has the JMS message's body as its payload.
A JMS `TextMessage` produces a string-based payload, a JMS `BytesMessage` produces a byte array payload, and the serializable instance of a JMS `ObjectMessage` becomes the Spring Integration message's payload.
If you prefer to have the raw JMS message as the Spring Integration message's payload, set `extract-payload` to `false`, as the following example shows:
====
[source,xml]
----
<int-jms:inbound-channel-adapter id="jmsIn"
destination="inQueue"
channel="exampleChannel"
extract-payload="false"/>
<int:poller fixed-rate="30000"/>
</int-jms:inbound-channel-adapter>
----
====
If you prefer to have the raw JMS message as the Spring Integration message's payload, set the `extractPayload` option to `false`.
Starting with version 5.0.8, a default value of the `receive-timeout` is `-1` (no wait) for the `org.springframework.jms.connection.CachingConnectionFactory` and `cacheConsumers`, otherwise it is 1 second.
The JMS Inbound Channel Adapter crates a `DynamicJmsTemplate` based on the provided `ConnectionFactory` and options.
@@ -119,7 +143,54 @@ The `message-driven-channel-adapter` requires a reference to either an instance
The following example defines a message-driven channel adapter with a `Destination` reference:
====
[source,xml]
[source, java, role="primary"]
.Java DSL
----
@Bean
public IntegrationFlow jmsMessageDrivenRedeliveryFlow() {
return IntegrationFlows
.from(Jms.messageDrivenChannelAdapter(jmsConnectionFactory())
.destination("inQueue"))
.channel("exampleChannel")
.get();
}
----
[source, kotlin, role="secondary"]
.Kotlin DSL
----
@Bean
fun jmsMessageDrivenFlowWithContainer() =
integrationFlow(
Jms.messageDrivenChannelAdapter(jmsConnectionFactory())
.destination("inQueue")) {
channel("exampleChannel")
}
----
[source, java, role="secondary"]
.Java
----
@Bean
public JmsMessageDrivenEndpoint jmsIn() {
JmsMessageDrivenEndpoint endpoint = new JmsMessageDrivenEndpoint(container(), listener());
return endpoint;
}
@Bean
public AbstractMessageListenerContainer container() {
DefaultMessageListenerContainer container = new DefaultMessageListenerContainer();
container.setConnectionFactory(cf());
container.setDestinationName("inQueue");
return container;
}
@Bean
public ChannelPublishingJmsMessageListener listener() {
ChannelPublishingJmsMessageListener listener = new ChannelPublishingJmsMessageListener();
listener.setRequestChannelName("exampleChannel");
return listener;
}
----
[source, xml, role="secondary"]
.XML
----
<int-jms:message-driven-channel-adapter id="jmsIn" destination="inQueue" channel="exampleChannel"/>
----
@@ -203,7 +274,46 @@ As with the inbound channel adapter, the easiest way to configure this adapter i
The following configuration produces an adapter that receives Spring Integration messages from the `exampleChannel`, converts those into JMS messages, and sends them to the JMS destination reference whose bean name is `outQueue`:
====
[source,xml]
[source, java, role="primary"]
.Java DSL
----
@Bean
public IntegrationFlow jmsOutboundFlow() {
return f -> f
.handle(Jms.outboundAdapter(cachingConnectionFactory())
.destinationExpression("headers." + SimpMessageHeaderAccessor.DESTINATION_HEADER)
.configureJmsTemplate(t -> t.id("jmsOutboundFlowTemplate")));
}
----
[source, kotlin, role="secondary"]
.Kotlin DSL
----
@Bean
fun jmsOutboundFlow() =
integrationFlow {
handle(Jms.outboundAdapter(jmsConnectionFactory())
.apply {
destinationExpression("headers." + SimpMessageHeaderAccessor.DESTINATION_HEADER)
deliveryModeFunction<Any> { DeliveryMode.NON_PERSISTENT }
timeToLiveExpression("10000")
configureJmsTemplate { it.explicitQosEnabled(true) }
}
)
}
----
[source, java, role="secondary"]
.Java
----
@Bean
@ServiceActivator(inputChannel = "exampleChannel")
public MessageHandler jmsOut() {
JmsSendingMessageHandler handler = new JmsSendingMessageHandler(new JmsTemplate(connectionFactory));
handler.setDestinationName("outQueue");
return handler;
}
----
[source, xml, role="secondary"]
.XML
----
<int-jms:outbound-channel-adapter id="jmsOut" destination="outQueue" channel="exampleChannel"/>
----
@@ -263,8 +373,7 @@ The default is 'true'.
Similarly, for an inbound-gateway, the 'extract-reply-payload' property applies to the Spring Integration message that is to be converted into a reply JMS Message.
If you want to pass the whole Spring Integration message (as the body of a JMS ObjectMessage), set value this to 'false'.
By default, it is also 'true' that the Spring Integration message payload is converted into a JMS Message (for example, a
`String` payload becomes a JMS TextMessage).
By default, it is also 'true' that the Spring Integration message payload is converted into a JMS Message (for example, a `String` payload becomes a JMS TextMessage).
As with anything else, gateway invocation might result in error.
By default, a producer is not notified of the errors that might have occurred on the consumer side and times out waiting for the reply.
@@ -306,7 +415,7 @@ To revert to the previous behavior, set the `shutdownContainerOnStop` on the `Jm
=== Outbound Gateway
The outbound gateway creates JMS messages from Spring Integration messages and sends them to a 'request-destination'.
It thens handle the JMS reply message either by using a selector to receive from the 'reply-destination' that you configure or, if no 'reply-destination' is provided, by creating JMS `TemporaryQueue` instances.
It then handles the JMS reply message either by using a selector to receive from the 'reply-destination' that you configure or, if no 'reply-destination' is provided, by creating JMS `TemporaryQueue` instances.
[[jms-outbound-gateway-memory-caution]]
[CAUTION]