INT-3288 JMS 'session-transacted' Attribute
JIRA: https://jira.springsource.org/browse/INT-3288 Previously, if you wanted to have a JMS outbound-channel-adapter participate in an upstream JMS transaction (or use transactions at all), you had to inject a `JmsTemplate` with `sessionTransacted` set to `true`. The `session-transacted` attribute has been added to the adapter, allowing transactions to be used with the default `JmsTemplate`, avoiding the need to inject a separate template. Also, the inbound (polled) channel adapter incorrectly allowed the `acknowledge` attribute to be set to `transacted` which is invalid and, again, you had to inject an appropriately configured JmsTemplate for transactions. The inbound-channel-adapter now supports `session-transacted` as well, and `acknowledge` can no longer be set to `transacted`. There are no changes to the `message-driven-channel-adapter`, you enable transactions with it by setting `acknowledge="transacted"` as before. INT-3288 Doc Polishing; Integration Tests
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.integration.jms.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
@@ -23,7 +25,6 @@ import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
|
||||
import org.springframework.integration.jms.DynamicJmsTemplate;
|
||||
import org.springframework.jms.core.JmsTemplate;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* Utility methods and constants for JMS adapter parsers.
|
||||
@@ -64,7 +65,7 @@ abstract class JmsAdapterParserUtils {
|
||||
private static final String[] JMS_TEMPLATE_ATTRIBUTES = {
|
||||
"connection-factory", "message-converter", "destination-resolver", "pub-sub-domain",
|
||||
"time-to-live", "priority", "delivery-persistent", "explicit-qos-enabled", "acknowledge",
|
||||
"receive-timeout"
|
||||
"receive-timeout", "session-transacted"
|
||||
};
|
||||
|
||||
|
||||
@@ -138,8 +139,14 @@ abstract class JmsAdapterParserUtils {
|
||||
}
|
||||
Integer acknowledgeMode = parseAcknowledgeMode(element, parserContext);
|
||||
if (acknowledgeMode != null) {
|
||||
if (acknowledgeMode == SESSION_TRANSACTED) {
|
||||
parserContext.getReaderContext().error(
|
||||
"'transacted' is not a valid 'acknowledge-mode' here, use 'session-transacted'" +
|
||||
" to enable transactions", element);
|
||||
}
|
||||
builder.addPropertyValue("sessionAcknowledgeMode", acknowledgeMode);
|
||||
}
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "session-transacted");
|
||||
|
||||
return builder.getBeanDefinition();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,6 +23,7 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.config.xml.AbstractPollingInboundChannelAdapterParser;
|
||||
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
|
||||
import org.springframework.integration.jms.JmsDestinationPollingSource;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -32,18 +33,19 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public class JmsInboundChannelAdapterParser extends AbstractPollingInboundChannelAdapterParser {
|
||||
|
||||
@Override
|
||||
protected boolean shouldGenerateId() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldGenerateIdAsFallback() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BeanMetadataElement parseSource(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
"org.springframework.integration.jms.JmsDestinationPollingSource");
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(JmsDestinationPollingSource.class);
|
||||
String componentName = this.resolveId(element, builder.getBeanDefinition(), parserContext);
|
||||
if (StringUtils.hasText(componentName)) {
|
||||
builder.addPropertyValue("componentName", componentName);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -24,19 +24,20 @@ import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.config.ExpressionFactoryBean;
|
||||
import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser;
|
||||
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
|
||||
import org.springframework.integration.jms.JmsSendingMessageHandler;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Parser for the <outbound-channel-adapter/> element of the jms namespace.
|
||||
*
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Gary Russell
|
||||
*/
|
||||
public class JmsOutboundChannelAdapterParser extends AbstractOutboundChannelAdapterParser {
|
||||
|
||||
@Override
|
||||
protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
"org.springframework.integration.jms.JmsSendingMessageHandler");
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(JmsSendingMessageHandler.class);
|
||||
String jmsTemplate = element.getAttribute(JmsAdapterParserUtils.JMS_TEMPLATE_ATTRIBUTE);
|
||||
String destination = element.getAttribute(JmsAdapterParserUtils.DESTINATION_ATTRIBUTE);
|
||||
String destinationName = element.getAttribute(JmsAdapterParserUtils.DESTINATION_NAME_ATTRIBUTE);
|
||||
@@ -53,7 +54,7 @@ public class JmsOutboundChannelAdapterParser extends AbstractOutboundChannelAdap
|
||||
else {
|
||||
builder.addConstructorArgValue(JmsAdapterParserUtils.parseJmsTemplateBeanDefinition(element, parserContext));
|
||||
}
|
||||
|
||||
|
||||
if (hasDestinationRef || hasDestinationName || hasDestinationExpression) {
|
||||
if (!(hasDestinationRef ^ hasDestinationName ^ hasDestinationExpression)) {
|
||||
parserContext.getReaderContext().error("The 'destination', 'destination-name', and " +
|
||||
|
||||
@@ -548,6 +548,14 @@
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="session-transacted" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Setting this attribute to true enables transactions. Cannot be specified if
|
||||
a jms-template is specified. Default false.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="jms-template" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
@@ -1178,6 +1186,16 @@
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="session-transacted" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Setting this attribute to true enables transactions for the message send. If there is already
|
||||
a JMS transaction in process, perhaps from a message-driven-channel-adapter upstream, the
|
||||
same transaction is used; otherwise a new transaction is started. Cannot be specified if
|
||||
a jms-template is specified. Default false.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="order" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
@@ -1245,7 +1263,9 @@
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The native JMS acknowledge mode: "auto", "client", "dups-ok" or "transacted".
|
||||
The latter effectively activates a locally transacted Session.
|
||||
The latter effectively activates a locally transacted Session. 'transacted'
|
||||
is not allowed on the inbound-channel-adapter; use 'session-transacted' instead.
|
||||
acknowlege="transacted" is used on the message-driven-channel-adapter.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,10 +15,13 @@
|
||||
*/
|
||||
package org.springframework.integration.jms;
|
||||
|
||||
import javax.jms.ConnectionFactory;
|
||||
|
||||
import org.apache.activemq.ActiveMQConnectionFactory;
|
||||
import org.apache.activemq.transport.vm.VMTransport;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
|
||||
import org.springframework.jms.connection.CachingConnectionFactory;
|
||||
|
||||
/**
|
||||
@@ -31,8 +34,10 @@ import org.springframework.jms.connection.CachingConnectionFactory;
|
||||
*/
|
||||
public abstract class ActiveMQMultiContextTests {
|
||||
|
||||
private static final CachingConnectionFactory connectionFactory = new CachingConnectionFactory(
|
||||
new ActiveMQConnectionFactory("vm://localhost?broker.persistent=false"));
|
||||
protected static final ConnectionFactory amqFactory = new ActiveMQConnectionFactory("vm://localhost?broker.persistent=false");
|
||||
|
||||
protected static final CachingConnectionFactory connectionFactory = new CachingConnectionFactory(
|
||||
amqFactory);
|
||||
|
||||
@BeforeClass
|
||||
public static void startUp() throws Exception {
|
||||
@@ -43,4 +48,5 @@ public abstract class ActiveMQMultiContextTests {
|
||||
public static void shutDown() {
|
||||
connectionFactory.resetConnection();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:int-jms="http://www.springframework.org/schema/integration/jms"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/integration/jms http://www.springframework.org/schema/integration/jms/spring-integration-jms.xsd">
|
||||
|
||||
<int-jms:inbound-channel-adapter channel="out" session-transacted="true"
|
||||
connection-factory="connectionFactory" destination-name="foo">
|
||||
<int:poller fixed-delay="500"/>
|
||||
</int-jms:inbound-channel-adapter>
|
||||
|
||||
<int:channel id="out">
|
||||
<int:queue/>
|
||||
</int:channel>
|
||||
|
||||
</beans>
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.integration.jms;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import javax.jms.ConnectionFactory;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.ImportResource;
|
||||
import org.springframework.integration.jms.JmsInboundChannelAdapterTests.CFConfig;
|
||||
import org.springframework.jms.core.JmsTemplate;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
* @since 4.0
|
||||
*
|
||||
*/
|
||||
@ContextConfiguration(classes=CFConfig.class)
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@DirtiesContext
|
||||
public class JmsInboundChannelAdapterTests extends ActiveMQMultiContextTests {
|
||||
|
||||
@Autowired
|
||||
private PollableChannel out;
|
||||
|
||||
@Test
|
||||
public void testTransactionalReceive() {
|
||||
JmsTemplate template = new JmsTemplate(connectionFactory);
|
||||
template.convertAndSend("foo", "bar");
|
||||
assertNotNull(out.receive(2000));
|
||||
/*
|
||||
* INT-3288 - previously acknowledge="transacted"
|
||||
* Caused by: javax.jms.JMSException: acknowledgeMode SESSION_TRANSACTED cannot be used for an non-transacted Session
|
||||
*/
|
||||
assertNull(out.receive(1000));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ImportResource("org/springframework/integration/jms/JmsInboundChannelAdapterTests-context.xml")
|
||||
public static class CFConfig {
|
||||
|
||||
@Bean
|
||||
public ConnectionFactory connectionFactory() {
|
||||
return amqFactory;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:int-jms="http://www.springframework.org/schema/integration/jms"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/integration/jms http://www.springframework.org/schema/integration/jms/spring-integration-jms.xsd">
|
||||
|
||||
<int-jms:message-driven-channel-adapter channel="toOut"
|
||||
connection-factory="connectionFactory"
|
||||
acknowledge="transacted"
|
||||
destination-name="foo"/>
|
||||
|
||||
<int:publish-subscribe-channel id="toOut" />
|
||||
|
||||
<int-jms:outbound-channel-adapter channel="toOut" order="1"
|
||||
connection-factory="connectionFactory"
|
||||
destination-name="bar"
|
||||
session-transacted="true" />
|
||||
|
||||
<int:service-activator input-channel="toOut" order="2" ref="aborter"/>
|
||||
|
||||
<bean id="aborter" class="org.springframework.integration.jms.JmsOutboundChannelAdapterTests$Aborter" />
|
||||
|
||||
</beans>
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.integration.jms;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import javax.jms.ConnectionFactory;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.ImportResource;
|
||||
import org.springframework.integration.jms.JmsOutboundChannelAdapterTests.CFConfig;
|
||||
import org.springframework.jms.core.JmsTemplate;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
* @since 4.0
|
||||
*
|
||||
*/
|
||||
@ContextConfiguration(classes=CFConfig.class)
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@DirtiesContext
|
||||
public class JmsOutboundChannelAdapterTests extends ActiveMQMultiContextTests {
|
||||
|
||||
@Autowired
|
||||
private PollableChannel out;
|
||||
|
||||
@Autowired
|
||||
private Aborter aborter;
|
||||
|
||||
@Autowired
|
||||
private JmsMessageDrivenEndpoint endpoint;
|
||||
|
||||
@Test
|
||||
public void testTransactionalSend() {
|
||||
JmsTemplate template = new JmsTemplate(connectionFactory);
|
||||
template.convertAndSend("foo", "Hello, world!");
|
||||
template.setReceiveTimeout(1000);
|
||||
assertNotNull(template.receive("bar"));
|
||||
|
||||
this.aborter.abort = true;
|
||||
template.convertAndSend("foo", "Hello, world!");
|
||||
assertNull(template.receive("bar"));
|
||||
endpoint.stop();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ImportResource("org/springframework/integration/jms/JmsOutboundChannelAdapterTests-context.xml")
|
||||
public static class CFConfig {
|
||||
|
||||
@Bean
|
||||
public ConnectionFactory connectionFactory() {
|
||||
return connectionFactory;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Aborter {
|
||||
|
||||
private volatile boolean abort;
|
||||
|
||||
public void foo() {
|
||||
if (abort) {
|
||||
throw new RuntimeException("intentional");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,7 +17,9 @@
|
||||
package org.springframework.integration.jms.config;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
@@ -27,21 +29,22 @@ import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
import org.springframework.integration.history.MessageHistory;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.jms.core.JmsTemplate;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
* @author Gary Russell
|
||||
*/
|
||||
public class JmsInboundChannelAdapterParserTests {
|
||||
|
||||
long timeoutOnReceive = 3000;
|
||||
|
||||
|
||||
@Test
|
||||
public void adapterWithJmsTemplate() {
|
||||
public void adapterWithJmsTemplate() {
|
||||
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
|
||||
"jmsInboundWithJmsTemplate.xml", this.getClass());
|
||||
PollableChannel output = (PollableChannel) context.getBean("output");
|
||||
@@ -54,16 +57,18 @@ public class JmsInboundChannelAdapterParserTests {
|
||||
assertNotNull("message should not be null", message);
|
||||
assertEquals("polling-test", message.getPayload());
|
||||
context.stop();
|
||||
context.close();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void adapterWithoutJmsTemplateAndAcknowlegeMode() {
|
||||
public void adapterWithoutJmsTemplateAndAcknowlegeMode() {
|
||||
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
|
||||
"jmsInboundWithJmsTemplate.xml", this.getClass());
|
||||
JmsTemplate jmsTemplate =
|
||||
JmsTemplate jmsTemplate =
|
||||
TestUtils.getPropertyValue(context.getBean("inboundAdapterWithoutJmsTemplate"), "source.jmsTemplate", JmsTemplate.class);
|
||||
assertEquals(0, jmsTemplate.getSessionAcknowledgeMode());
|
||||
assertTrue(jmsTemplate.isSessionTransacted());
|
||||
context.stop();
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -74,7 +79,10 @@ public class JmsInboundChannelAdapterParserTests {
|
||||
Message<?> message = output.receive(timeoutOnReceive);
|
||||
assertNotNull("message should not be null", message);
|
||||
assertEquals("polling-test", message.getPayload());
|
||||
assertFalse(TestUtils.getPropertyValue(context.getBean("adapter"), "source.jmsTemplate", JmsTemplate.class)
|
||||
.isSessionTransacted());
|
||||
context.stop();
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -86,11 +94,12 @@ public class JmsInboundChannelAdapterParserTests {
|
||||
assertNotNull("message should not be null", message);
|
||||
assertEquals("polling-test", message.getPayload());
|
||||
context.stop();
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test(expected = BeanDefinitionStoreException.class)
|
||||
public void adapterWithConnectionFactoryOnly() {
|
||||
new ClassPathXmlApplicationContext("jmsInboundWithConnectionFactoryOnly.xml", this.getClass());
|
||||
new ClassPathXmlApplicationContext("jmsInboundWithConnectionFactoryOnly.xml", this.getClass()).close();
|
||||
}
|
||||
|
||||
@Test(expected = BeanCreationException.class)
|
||||
@@ -114,11 +123,12 @@ public class JmsInboundChannelAdapterParserTests {
|
||||
assertNotNull("message should not be null", message);
|
||||
assertEquals("polling-test", message.getPayload());
|
||||
context.stop();
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test(expected=BeanCreationException.class)
|
||||
public void adapterWithDestinationNameOnly() {
|
||||
new ClassPathXmlApplicationContext("jmsInboundWithDestinationNameOnly.xml", this.getClass());
|
||||
new ClassPathXmlApplicationContext("jmsInboundWithDestinationNameOnly.xml", this.getClass()).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -130,6 +140,7 @@ public class JmsInboundChannelAdapterParserTests {
|
||||
assertNotNull("message should not be null", message);
|
||||
assertEquals("polling-test", message.getPayload());
|
||||
context.stop();
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -143,6 +154,7 @@ public class JmsInboundChannelAdapterParserTests {
|
||||
assertEquals("foo", message.getHeaders().get("testProperty"));
|
||||
assertEquals(new Integer(123), message.getHeaders().get("testAttribute"));
|
||||
context.stop();
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -154,15 +166,17 @@ public class JmsInboundChannelAdapterParserTests {
|
||||
assertNotNull("message should not be null", message);
|
||||
assertEquals("test [with selector: TestProperty = 'foo']", message.getPayload());
|
||||
context.stop();
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pollingAdapterWithReceiveTimeout() {
|
||||
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
|
||||
"jmsInboundWithReceiveTimeout.xml", this.getClass());
|
||||
JmsTemplate jmsTemplate =
|
||||
JmsTemplate jmsTemplate =
|
||||
TestUtils.getPropertyValue(context.getBean("adapter"), "source.jmsTemplate", JmsTemplate.class);
|
||||
assertEquals(99, jmsTemplate.getReceiveTimeout());
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -174,6 +188,7 @@ public class JmsInboundChannelAdapterParserTests {
|
||||
assertNotNull("message should not be null", message);
|
||||
assertEquals("converted-test", message.getPayload());
|
||||
context.stop();
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -185,6 +200,7 @@ public class JmsInboundChannelAdapterParserTests {
|
||||
assertNotNull("message should not be null", message);
|
||||
assertEquals("converted-test", message.getPayload());
|
||||
context.stop();
|
||||
context.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,24 +17,26 @@
|
||||
package org.springframework.integration.jms.config;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import javax.jms.DeliveryMode;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.integration.endpoint.EventDrivenConsumer;
|
||||
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
|
||||
import org.springframework.integration.jms.JmsHeaderMapper;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.jms.core.JmsTemplate;
|
||||
import org.springframework.jms.support.converter.MessageConverter;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
@@ -52,6 +54,8 @@ public class JmsOutboundChannelAdapterParserTests {
|
||||
DirectFieldAccessor accessor = new DirectFieldAccessor(
|
||||
new DirectFieldAccessor(endpoint).getPropertyValue("handler"));
|
||||
assertNotNull(accessor.getPropertyValue("jmsTemplate"));
|
||||
assertTrue(TestUtils.getPropertyValue(endpoint, "handler.jmsTemplate.sessionTransacted", Boolean.class));
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -62,6 +66,7 @@ public class JmsOutboundChannelAdapterParserTests {
|
||||
MessageHandler handler = TestUtils.getPropertyValue(endpoint, "handler", MessageHandler.class);
|
||||
handler.handleMessage(new GenericMessage<String>("foo"));
|
||||
assertEquals(1, adviceCalled);
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -72,6 +77,8 @@ public class JmsOutboundChannelAdapterParserTests {
|
||||
DirectFieldAccessor accessor = new DirectFieldAccessor(
|
||||
new DirectFieldAccessor(endpoint).getPropertyValue("handler"));
|
||||
assertNotNull(accessor.getPropertyValue("jmsTemplate"));
|
||||
assertFalse(TestUtils.getPropertyValue(endpoint, "handler.jmsTemplate.sessionTransacted", Boolean.class));
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -82,6 +89,7 @@ public class JmsOutboundChannelAdapterParserTests {
|
||||
DirectFieldAccessor accessor = new DirectFieldAccessor(
|
||||
new DirectFieldAccessor(endpoint).getPropertyValue("handler"));
|
||||
assertNotNull(accessor.getPropertyValue("jmsTemplate"));
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -93,6 +101,7 @@ public class JmsOutboundChannelAdapterParserTests {
|
||||
new DirectFieldAccessor(endpoint).getPropertyValue("handler"));
|
||||
Object order = accessor.getPropertyValue("order");
|
||||
assertEquals(123, order);
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -105,6 +114,7 @@ public class JmsOutboundChannelAdapterParserTests {
|
||||
JmsHeaderMapper headerMapper = (JmsHeaderMapper) accessor.getPropertyValue("headerMapper");
|
||||
assertNotNull(headerMapper);
|
||||
assertEquals(TestJmsHeaderMapper.class, headerMapper.getClass());
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -116,6 +126,7 @@ public class JmsOutboundChannelAdapterParserTests {
|
||||
JmsTemplate jmsTemplate = (JmsTemplate) handlerAccessor.getPropertyValue("jmsTemplate");
|
||||
assertNotNull(jmsTemplate);
|
||||
assertEquals(context.getBean("template"), jmsTemplate);
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -130,6 +141,7 @@ public class JmsOutboundChannelAdapterParserTests {
|
||||
assertTrue(jmsTemplate.isExplicitQosEnabled());
|
||||
assertEquals(7, jmsTemplate.getPriority());
|
||||
assertEquals(12345, jmsTemplate.getTimeToLive());
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -143,6 +155,7 @@ public class JmsOutboundChannelAdapterParserTests {
|
||||
MessageConverter messageConverter = jmsTemlate.getMessageConverter();
|
||||
assertNotNull(messageConverter);
|
||||
assertEquals(TestMessageConverter.class, messageConverter.getClass());
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test(expected = BeanDefinitionStoreException.class)
|
||||
@@ -168,6 +181,7 @@ public class JmsOutboundChannelAdapterParserTests {
|
||||
assertEquals(12345L, accessor.getPropertyValue("timeToLive"));
|
||||
assertEquals(7, accessor.getPropertyValue("priority"));
|
||||
assertEquals(DeliveryMode.NON_PERSISTENT, accessor.getPropertyValue("deliveryMode"));
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -179,6 +193,7 @@ public class JmsOutboundChannelAdapterParserTests {
|
||||
new DirectFieldAccessor(new DirectFieldAccessor(endpoint).getPropertyValue("handler"))
|
||||
.getPropertyValue("jmsTemplate"));
|
||||
assertEquals(false, accessor.getPropertyValue("explicitQosEnabled"));
|
||||
context.close();
|
||||
}
|
||||
|
||||
public static class FooAdvice extends AbstractRequestHandlerAdvice {
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
http://www.springframework.org/schema/integration/jms
|
||||
http://www.springframework.org/schema/integration/jms/spring-integration-jms.xsd">
|
||||
|
||||
<jms:inbound-channel-adapter channel="output"
|
||||
<jms:inbound-channel-adapter id="adapter"
|
||||
channel="output"
|
||||
connection-factory="testConnectionFactory"
|
||||
destination="testDestination"/>
|
||||
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
|
||||
<jms:inbound-channel-adapter id="inboundAdapter" jms-template="jmsTemplate" channel="output"/>
|
||||
|
||||
<jms:inbound-channel-adapter id="inboundAdapterWithoutJmsTemplate" channel="outputA" acknowledge="transacted"
|
||||
<jms:inbound-channel-adapter id="inboundAdapterWithoutJmsTemplate" channel="outputA"
|
||||
session-transacted="true"
|
||||
destination-name="foo.bar"/>
|
||||
|
||||
<integration:channel id="outputA"/>
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
<jms:outbound-channel-adapter id="adapter"
|
||||
channel="input"
|
||||
connection-factory="testConnectionFactory"
|
||||
destination="testDestination"/>
|
||||
destination="testDestination"
|
||||
session-transacted="true" />
|
||||
|
||||
<bean id="testConnectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">
|
||||
<constructor-arg>
|
||||
|
||||
@@ -75,6 +75,28 @@
|
||||
<int:poller fixed-rate="30000"/>
|
||||
</int-jms:inbound-channel-adapter>]]></programlisting>
|
||||
</para>
|
||||
<section id="jms-ib-transactions">
|
||||
<title>Transactions</title>
|
||||
<para>
|
||||
Starting with <emphasis>version 4.0</emphasis>, the inbound channel adapter supports the
|
||||
<code>session-transacted</code> attribute. In earlier versions, you had to inject a
|
||||
<classname>JmsTemplate</classname> with <code>sessionTransacted</code> set to <code>true</code>.
|
||||
(The adapter did allow the <code>acknowledge</code> attribute to be set to
|
||||
<code>transacted</code> but this was incorrect and did not work).
|
||||
</para>
|
||||
<para>
|
||||
Note, however, that setting <code>session-transacted</code> to <code>true</code> has
|
||||
little value because the transaction is committed immediately after the <code>receive()</code>
|
||||
and before the message is sent to the <code>channel</code>,
|
||||
</para>
|
||||
<para>
|
||||
If you want the entire flow to be transactional (for example if there is a downstream
|
||||
outbound channel adapter), you must use a <code>transactional</code> poller, with a
|
||||
<classname>JmsTransactionManager</classname>. Or, consider using a
|
||||
<code>jms-message-driven-channel-adapter</code> with <code>acknowledge</code>
|
||||
set to <code>transacted</code>.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section id="jms-message-driven-channel-adapter">
|
||||
@@ -151,6 +173,18 @@
|
||||
those cases, it's the JMS properties mapping <emphasis>to</emphasis> Spring Integration MessageHeaders).
|
||||
</note>
|
||||
</para>
|
||||
<section id="jms-ob-transactions">
|
||||
<title>Transactions</title>
|
||||
<para>
|
||||
Starting with <emphasis>version 4.0</emphasis>, the outbound channel adapter supports the
|
||||
<code>session-transacted</code> attribute. In earlier versions, you had to inject a
|
||||
<classname>JmsTemplate</classname> with <code>sessionTransacted</code> set to <code>true</code>.
|
||||
The attribute now sets the property on the built-in default <classname>JmsTemplate</classname>.
|
||||
If a transaction exists (perhaps from an upstream <code>message-driven-channel-adapter</code>)
|
||||
the send will be performed within the same transaction. Otherwise a new transaction will
|
||||
be started.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section id="jms-inbound-gateway">
|
||||
|
||||
@@ -107,6 +107,24 @@
|
||||
considered for outbound messages. For more information see <xref linkend="jms-header-mapping"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="4.0-jms-ob">
|
||||
<title>JMS Outbound Channel Adapter</title>
|
||||
<para>
|
||||
The JMS outbound channel adapter now supports the <code>session-transacted</code> attribute
|
||||
(default false). Previously, you had to inject a customized <classname>JmsTemplate</classname>
|
||||
to use transactions. See <xref linkend="jms-outbound-channel-adapter"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="4.0-jms-ib">
|
||||
<title>JMS Inbound Channel Adapter</title>
|
||||
<para>
|
||||
The JMS inbound channel adapter now supports the <code>session-transacted</code> attribute
|
||||
(default false). Previously, you had to inject a customized <classname>JmsTemplate</classname>
|
||||
to use transactions (the adapter allowed 'transacted' in the acknowledgeMode which was
|
||||
incorrect, and didn't work; this value is no longer allowed). See
|
||||
<xref linkend="jms-inbound-channel-adapter"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="4.0-datatype-channel">
|
||||
<title>Datatype Channels</title>
|
||||
<para>
|
||||
|
||||
Reference in New Issue
Block a user