diff --git a/spring-integration-jms/src/main/java/org/springframework/integration/jms/config/JmsAdapterParserUtils.java b/spring-integration-jms/src/main/java/org/springframework/integration/jms/config/JmsAdapterParserUtils.java index f3c522b360..b3f1e01546 100644 --- a/spring-integration-jms/src/main/java/org/springframework/integration/jms/config/JmsAdapterParserUtils.java +++ b/spring-integration-jms/src/main/java/org/springframework/integration/jms/config/JmsAdapterParserUtils.java @@ -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(); } diff --git a/spring-integration-jms/src/main/java/org/springframework/integration/jms/config/JmsInboundChannelAdapterParser.java b/spring-integration-jms/src/main/java/org/springframework/integration/jms/config/JmsInboundChannelAdapterParser.java index a9decccdd7..de58758448 100644 --- a/spring-integration-jms/src/main/java/org/springframework/integration/jms/config/JmsInboundChannelAdapterParser.java +++ b/spring-integration-jms/src/main/java/org/springframework/integration/jms/config/JmsInboundChannelAdapterParser.java @@ -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); diff --git a/spring-integration-jms/src/main/java/org/springframework/integration/jms/config/JmsOutboundChannelAdapterParser.java b/spring-integration-jms/src/main/java/org/springframework/integration/jms/config/JmsOutboundChannelAdapterParser.java index e55f7196a6..d10151e070 100644 --- a/spring-integration-jms/src/main/java/org/springframework/integration/jms/config/JmsOutboundChannelAdapterParser.java +++ b/spring-integration-jms/src/main/java/org/springframework/integration/jms/config/JmsOutboundChannelAdapterParser.java @@ -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 " + diff --git a/spring-integration-jms/src/main/resources/org/springframework/integration/jms/config/spring-integration-jms-4.0.xsd b/spring-integration-jms/src/main/resources/org/springframework/integration/jms/config/spring-integration-jms-4.0.xsd index 2afcbcb39b..f1d739066c 100644 --- a/spring-integration-jms/src/main/resources/org/springframework/integration/jms/config/spring-integration-jms-4.0.xsd +++ b/spring-integration-jms/src/main/resources/org/springframework/integration/jms/config/spring-integration-jms-4.0.xsd @@ -548,6 +548,14 @@ + + + + + @@ -1178,6 +1186,16 @@ ]]> + + + + + diff --git a/spring-integration-jms/src/test/java/org/springframework/integration/jms/ActiveMQMultiContextTests.java b/spring-integration-jms/src/test/java/org/springframework/integration/jms/ActiveMQMultiContextTests.java index 9b6475385f..28cefde979 100644 --- a/spring-integration-jms/src/test/java/org/springframework/integration/jms/ActiveMQMultiContextTests.java +++ b/spring-integration-jms/src/test/java/org/springframework/integration/jms/ActiveMQMultiContextTests.java @@ -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(); } + } diff --git a/spring-integration-jms/src/test/java/org/springframework/integration/jms/JmsInboundChannelAdapterTests-context.xml b/spring-integration-jms/src/test/java/org/springframework/integration/jms/JmsInboundChannelAdapterTests-context.xml new file mode 100644 index 0000000000..46cbab445a --- /dev/null +++ b/spring-integration-jms/src/test/java/org/springframework/integration/jms/JmsInboundChannelAdapterTests-context.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + diff --git a/spring-integration-jms/src/test/java/org/springframework/integration/jms/JmsInboundChannelAdapterTests.java b/spring-integration-jms/src/test/java/org/springframework/integration/jms/JmsInboundChannelAdapterTests.java new file mode 100644 index 0000000000..6364e1d041 --- /dev/null +++ b/spring-integration-jms/src/test/java/org/springframework/integration/jms/JmsInboundChannelAdapterTests.java @@ -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; + } + } + +} diff --git a/spring-integration-jms/src/test/java/org/springframework/integration/jms/JmsOutboundChannelAdapterTests-context.xml b/spring-integration-jms/src/test/java/org/springframework/integration/jms/JmsOutboundChannelAdapterTests-context.xml new file mode 100644 index 0000000000..af8c0b1cfd --- /dev/null +++ b/spring-integration-jms/src/test/java/org/springframework/integration/jms/JmsOutboundChannelAdapterTests-context.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + diff --git a/spring-integration-jms/src/test/java/org/springframework/integration/jms/JmsOutboundChannelAdapterTests.java b/spring-integration-jms/src/test/java/org/springframework/integration/jms/JmsOutboundChannelAdapterTests.java new file mode 100644 index 0000000000..1eda51e267 --- /dev/null +++ b/spring-integration-jms/src/test/java/org/springframework/integration/jms/JmsOutboundChannelAdapterTests.java @@ -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"); + } + } + } + +} diff --git a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsInboundChannelAdapterParserTests.java b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsInboundChannelAdapterParserTests.java index fe4859d670..aa502f8007 100644 --- a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsInboundChannelAdapterParserTests.java +++ b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsInboundChannelAdapterParserTests.java @@ -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(); } } diff --git a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsOutboundChannelAdapterParserTests.java b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsOutboundChannelAdapterParserTests.java index 3d4371fcbf..7178593440 100644 --- a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsOutboundChannelAdapterParserTests.java +++ b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsOutboundChannelAdapterParserTests.java @@ -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("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 { diff --git a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/jmsInboundWithConnectionFactoryAndDestination.xml b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/jmsInboundWithConnectionFactoryAndDestination.xml index d15818e15f..30844bd9a9 100644 --- a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/jmsInboundWithConnectionFactoryAndDestination.xml +++ b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/jmsInboundWithConnectionFactoryAndDestination.xml @@ -10,7 +10,8 @@ http://www.springframework.org/schema/integration/jms http://www.springframework.org/schema/integration/jms/spring-integration-jms.xsd"> - diff --git a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/jmsInboundWithJmsTemplate.xml b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/jmsInboundWithJmsTemplate.xml index 6e1c3a1a5f..8f4ce253d0 100644 --- a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/jmsInboundWithJmsTemplate.xml +++ b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/jmsInboundWithJmsTemplate.xml @@ -14,7 +14,8 @@ - diff --git a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/jmsOutboundWithConnectionFactoryAndDestination.xml b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/jmsOutboundWithConnectionFactoryAndDestination.xml index 7681480200..eab56aa780 100644 --- a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/jmsOutboundWithConnectionFactoryAndDestination.xml +++ b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/jmsOutboundWithConnectionFactoryAndDestination.xml @@ -15,7 +15,8 @@ + destination="testDestination" + session-transacted="true" /> diff --git a/src/reference/docbook/jms.xml b/src/reference/docbook/jms.xml index d89adefea8..731cd6dd17 100644 --- a/src/reference/docbook/jms.xml +++ b/src/reference/docbook/jms.xml @@ -75,6 +75,28 @@ ]]> +
+ Transactions + + Starting with version 4.0, the inbound channel adapter supports the + session-transacted attribute. In earlier versions, you had to inject a + JmsTemplate with sessionTransacted set to true. + (The adapter did allow the acknowledge attribute to be set to + transacted but this was incorrect and did not work). + + + Note, however, that setting session-transacted to true has + little value because the transaction is committed immediately after the receive() + and before the message is sent to the channel, + + + If you want the entire flow to be transactional (for example if there is a downstream + outbound channel adapter), you must use a transactional poller, with a + JmsTransactionManager. Or, consider using a + jms-message-driven-channel-adapter with acknowledge + set to transacted. + +
@@ -151,6 +173,18 @@ those cases, it's the JMS properties mapping to Spring Integration MessageHeaders). +
+ Transactions + + Starting with version 4.0, the outbound channel adapter supports the + session-transacted attribute. In earlier versions, you had to inject a + JmsTemplate with sessionTransacted set to true. + The attribute now sets the property on the built-in default JmsTemplate. + If a transaction exists (perhaps from an upstream message-driven-channel-adapter) + the send will be performed within the same transaction. Otherwise a new transaction will + be started. + +
diff --git a/src/reference/docbook/whats-new.xml b/src/reference/docbook/whats-new.xml index 688bef819a..e2aca6ccf9 100644 --- a/src/reference/docbook/whats-new.xml +++ b/src/reference/docbook/whats-new.xml @@ -107,6 +107,24 @@ considered for outbound messages. For more information see .
+
+ JMS Outbound Channel Adapter + + The JMS outbound channel adapter now supports the session-transacted attribute + (default false). Previously, you had to inject a customized JmsTemplate + to use transactions. See . + +
+
+ JMS Inbound Channel Adapter + + The JMS inbound channel adapter now supports the session-transacted attribute + (default false). Previously, you had to inject a customized JmsTemplate + to use transactions (the adapter allowed 'transacted' in the acknowledgeMode which was + incorrect, and didn't work; this value is no longer allowed). See + . + +
Datatype Channels