diff --git a/spring-batch-integration/src/main/java/org/springframework/batch/container/jms/BatchMessageListenerContainer.java b/spring-batch-integration/src/main/java/org/springframework/batch/container/jms/BatchMessageListenerContainer.java index 6ac73c399..918d0333c 100644 --- a/spring-batch-integration/src/main/java/org/springframework/batch/container/jms/BatchMessageListenerContainer.java +++ b/spring-batch-integration/src/main/java/org/springframework/batch/container/jms/BatchMessageListenerContainer.java @@ -111,7 +111,7 @@ public class BatchMessageListenerContainer extends DefaultMessageListenerContain * Extension point for subclasses. Do anything necessary to recover from the * exception, which was raised when the message was being processed. * @param session the current JMS session. - * @param message the message just receieved and failed to process. + * @param message the message just received and failed to process. * @param ex the exception thrown during message processing. */ protected void recover(Session session, Message message, Throwable ex) throws JMSException { @@ -140,10 +140,34 @@ public class BatchMessageListenerContainer extends DefaultMessageListenerContain } else if (ex instanceof Error) { // Just re-throw Error instances because otherwise unit tests just - // swallow expections from EasyMock and JUnit. + // swallow exceptions from EasyMock and JUnit. throw (Error) ex; } } + + /** + * Override base class to prevent exceptions from being swallowed. + * + * @see org.springframework.jms.listener.AbstractMessageListenerContainer#handleListenerException(java.lang.Throwable) + */ + protected void handleListenerException(Throwable ex) { + if (!isSessionTransacted()) { + // Log the exceptions in base class if not transactional anyway + super.handleListenerException(ex); + return; + } + logger.debug("Re-throwing exception in container."); + if (ex instanceof RuntimeException) { + // We need to re-throw so that an enclosing non-JMS transaction can + // rollback... + throw (RuntimeException) ex; + } + else if (ex instanceof Error) { + // Just re-throw Error instances because otherwise unit tests just + // swallow exceptions from EasyMock and JUnit. + throw (Error) ex; + } + } /** * Override base class method to wrap call in a batch. diff --git a/spring-batch-integration/src/test/java/org/springframework/batch/config/DatasourceTests.java b/spring-batch-integration/src/test/java/org/springframework/batch/config/DatasourceTests.java index 7710abc12..4ddc5f28a 100644 --- a/spring-batch-integration/src/test/java/org/springframework/batch/config/DatasourceTests.java +++ b/spring-batch-integration/src/test/java/org/springframework/batch/config/DatasourceTests.java @@ -16,12 +16,14 @@ package org.springframework.batch.config; +import org.springframework.batch.jms.ExternalRetryInBatchTests; import org.springframework.test.AbstractTransactionalDataSourceSpringContextTests; +import org.springframework.util.ClassUtils; public class DatasourceTests extends AbstractTransactionalDataSourceSpringContextTests { protected String[] getConfigLocations() { - return new String[] { "/org/springframework/batch/jms/jms-context.xml" }; + return new String[] { ClassUtils.addResourcePathToPackagePath(ExternalRetryInBatchTests.class, "jms-context.xml") }; } public void testTemplate() throws Exception { diff --git a/spring-batch-integration/src/test/java/org/springframework/batch/config/MessagingTests.java b/spring-batch-integration/src/test/java/org/springframework/batch/config/MessagingTests.java index e9d3a3c42..f38ecde90 100644 --- a/spring-batch-integration/src/test/java/org/springframework/batch/config/MessagingTests.java +++ b/spring-batch-integration/src/test/java/org/springframework/batch/config/MessagingTests.java @@ -19,8 +19,10 @@ package org.springframework.batch.config; import java.util.ArrayList; import java.util.List; +import org.springframework.batch.jms.ExternalRetryInBatchTests; import org.springframework.jms.core.JmsTemplate; import org.springframework.test.AbstractDependencyInjectionSpringContextTests; +import org.springframework.util.ClassUtils; public class MessagingTests extends AbstractDependencyInjectionSpringContextTests { @@ -31,7 +33,8 @@ public class MessagingTests extends AbstractDependencyInjectionSpringContextTest } protected String[] getConfigLocations() { - return new String[] { "/org/springframework/batch/jms/jms-context.xml" }; + return new String[] { ClassUtils.addResourcePathToPackagePath(ExternalRetryInBatchTests.class, + "jms-context.xml") }; } protected void onSetUp() throws Exception { diff --git a/spring-batch-integration/src/test/java/org/springframework/batch/container/jms/BatchMessageListenerContainerIntegrationTests.java b/spring-batch-integration/src/test/java/org/springframework/batch/container/jms/BatchMessageListenerContainerIntegrationTests.java new file mode 100644 index 000000000..2908c5fdb --- /dev/null +++ b/spring-batch-integration/src/test/java/org/springframework/batch/container/jms/BatchMessageListenerContainerIntegrationTests.java @@ -0,0 +1,188 @@ +/* + * Copyright 2006-2007 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.batch.container.jms; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.TextMessage; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.batch.item.AbstractItemWriter; +import org.springframework.batch.item.ItemKeyGenerator; +import org.springframework.batch.item.ItemRecoverer; +import org.springframework.batch.jms.ExternalRetryInBatchTests; +import org.springframework.batch.retry.callback.ItemWriterRetryCallback; +import org.springframework.batch.retry.policy.ItemWriterRetryPolicy; +import org.springframework.batch.retry.policy.NeverRetryPolicy; +import org.springframework.batch.retry.support.RetryTemplate; +import org.springframework.jms.core.JmsTemplate; +import org.springframework.test.AbstractDependencyInjectionSpringContextTests; +import org.springframework.util.ClassUtils; + +/** + * @author Dave Syer + * + */ +public class BatchMessageListenerContainerIntegrationTests extends AbstractDependencyInjectionSpringContextTests { + + private final Log logger = LogFactory.getLog(getClass()); + + private JmsTemplate jmsTemplate; + + private BatchMessageListenerContainer container; + + private int recovered; + + private int count; + + /** + * Public setter for the {@link BatchMessageListenerContainer}. + * @param container the container to set + */ + public void setContainer(BatchMessageListenerContainer container) { + this.container = container; + } + + /** + * Public setter for the JmsTemplate. + * @param jmsTemplate the jmsTemplate to set + */ + public void setJmsTemplate(JmsTemplate jmsTemplate) { + this.jmsTemplate = jmsTemplate; + } + + /* + * (non-Javadoc) + * @see org.springframework.test.AbstractSingleSpringContextTests#getConfigLocations() + */ + protected String[] getConfigLocations() { + // Share config with other test so that ActiveMQ only starts up once. + return new String[] { ClassUtils.addResourcePathToPackagePath(ExternalRetryInBatchTests.class, "jms-context.xml") }; + } + + /* (non-Javadoc) + * @see org.springframework.test.AbstractSingleSpringContextTests#onSetUp() + */ + protected void onSetUp() throws Exception { + while(jmsTemplate.receiveAndConvert("queue")!=null) { + // do nothing + } + } + + /* + * (non-Javadoc) + * @see org.springframework.test.AbstractSingleSpringContextTests#onTearDown() + */ + protected void onTearDown() throws Exception { + container.stop(); + while(jmsTemplate.receiveAndConvert("queue")!=null) { + // do nothing + } + } + + public void testConfiguration() throws Exception { + assertNotNull(container); + } + + public void testSendAndReceive() throws Exception { + container.setMessageListener(new MessageListener() { + public void onMessage(Message msg) { + count++; + } + }); + container.start(); + jmsTemplate.convertAndSend("queue", "foo"); + jmsTemplate.convertAndSend("queue", "bar"); + int waiting = 0; + while (count < 2 && waiting++ < 10) { + Thread.sleep(100L); + } + if (count < 2) { + fail("Expected message to be processed."); + } + } + + public void testFailureAndRepresent() throws Exception { + container.setMessageListener(new MessageListener() { + public void onMessage(Message msg) { + logger.debug("Message: "+msg); + count++; + throw new RuntimeException("planned failure: " + msg); + } + }); + container.start(); + jmsTemplate.convertAndSend("queue", "foo"); + int waiting = 0; + while (count < 2 && waiting++ < 10) { + Thread.sleep(100L); + } + if (count < 2) { + fail("Expected message to be processed twice."); + } + } + + public void testFailureAndRecovery() throws Exception { + final RetryTemplate retryTemplate = new RetryTemplate(); + retryTemplate.setRetryPolicy(new ItemWriterRetryPolicy(new NeverRetryPolicy())); + container.setMessageListener(new MessageListener() { + public void onMessage(final Message msg) { + try { + ItemWriterRetryCallback callback = new ItemWriterRetryCallback(msg, new AbstractItemWriter() { + public void write(Object item) throws Exception { + logger.debug("Message: "+item); + count++; + throw new RuntimeException("planned failure: " + msg); + } + }); + callback.setKeyGenerator(new ItemKeyGenerator() { + public Object getKey(Object item) { + String text; + try { + text = ((TextMessage)item).getJMSMessageID(); + } + catch (JMSException e) { + text = ""+item; + } + logger.debug("Key for message: "+text); + return text; + } + }); + callback.setRecoverer(new ItemRecoverer() { + public boolean recover(Object data, Throwable cause) { + recovered++; + logger.debug("Recovered: " + data); + return true; + } + }); + retryTemplate.execute(callback); + } + catch (Exception e) { + throw (RuntimeException) e; + } + } + }); + container.start(); + jmsTemplate.convertAndSend("queue", "foo"); + int waiting = 0; + while ((count < 1 || recovered < 1) && waiting++ < 1000) { + Thread.sleep(100L); + } + assertEquals(1, count); + assertEquals(1, recovered); + } +} diff --git a/spring-batch-integration/src/test/java/org/springframework/batch/container/jms/BatchMessageListenerContainerTests.java b/spring-batch-integration/src/test/java/org/springframework/batch/container/jms/BatchMessageListenerContainerTests.java index f768ad356..83bf092d4 100644 --- a/spring-batch-integration/src/test/java/org/springframework/batch/container/jms/BatchMessageListenerContainerTests.java +++ b/spring-batch-integration/src/test/java/org/springframework/batch/container/jms/BatchMessageListenerContainerTests.java @@ -125,8 +125,13 @@ public class BatchMessageListenerContainerTests extends TestCase { template.setCompletionPolicy(new SimpleCompletionPolicy(2)); container = getContainer(template); container.setSessionTransacted(true); - boolean received = doTestWithException(new IllegalStateException("No way!"), true, 2); - assertFalse("Message received", received); + try { + boolean received = doTestWithException(new IllegalStateException("No way!"), true, 2); + assertFalse("Message received", received); + fail("Expected IllegalStateException"); + } catch (IllegalStateException e) { + assertEquals("No way!", e.getMessage()); + } } public void testNonTransactionalReceiveAndExecuteWithCallbackThrowingException() throws Exception { diff --git a/spring-batch-integration/src/test/java/org/springframework/batch/jms/ExternalRetryInBatchTests.java b/spring-batch-integration/src/test/java/org/springframework/batch/jms/ExternalRetryInBatchTests.java index d6dc42009..14d5ad17d 100644 --- a/spring-batch-integration/src/test/java/org/springframework/batch/jms/ExternalRetryInBatchTests.java +++ b/spring-batch-integration/src/test/java/org/springframework/batch/jms/ExternalRetryInBatchTests.java @@ -40,6 +40,7 @@ import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionTemplate; +import org.springframework.util.ClassUtils; public class ExternalRetryInBatchTests extends AbstractDependencyInjectionSpringContextTests { private JmsTemplate jmsTemplate; @@ -71,7 +72,7 @@ public class ExternalRetryInBatchTests extends AbstractDependencyInjectionSpring } protected String[] getConfigLocations() { - return new String[] { "/org/springframework/batch/jms/jms-context.xml" }; + return new String[] { ClassUtils.addResourcePathToPackagePath(getClass(), "jms-context.xml" )}; } protected void onSetUp() throws Exception { diff --git a/spring-batch-integration/src/test/java/org/springframework/batch/repeat/jms/AsynchronousTests.java b/spring-batch-integration/src/test/java/org/springframework/batch/repeat/jms/AsynchronousTests.java index da7a4c27e..3f50db69b 100644 --- a/spring-batch-integration/src/test/java/org/springframework/batch/repeat/jms/AsynchronousTests.java +++ b/spring-batch-integration/src/test/java/org/springframework/batch/repeat/jms/AsynchronousTests.java @@ -25,15 +25,18 @@ import javax.jms.Session; import javax.jms.TextMessage; import org.springframework.batch.container.jms.BatchMessageListenerContainer; +import org.springframework.batch.jms.ExternalRetryInBatchTests; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jms.core.JmsTemplate; import org.springframework.jms.listener.SessionAwareMessageListener; import org.springframework.test.AbstractDependencyInjectionSpringContextTests; +import org.springframework.util.ClassUtils; public class AsynchronousTests extends AbstractDependencyInjectionSpringContextTests { protected String[] getConfigLocations() { - return new String[] { "/org/springframework/batch/jms/jms-context.xml" }; + return new String[] { ClassUtils.addResourcePathToPackagePath(ExternalRetryInBatchTests.class, + "jms-context.xml") }; } private BatchMessageListenerContainer container; diff --git a/spring-batch-integration/src/test/java/org/springframework/batch/repeat/jms/SynchronousTests.java b/spring-batch-integration/src/test/java/org/springframework/batch/repeat/jms/SynchronousTests.java index 7ea713098..ff7021daf 100644 --- a/spring-batch-integration/src/test/java/org/springframework/batch/repeat/jms/SynchronousTests.java +++ b/spring-batch-integration/src/test/java/org/springframework/batch/repeat/jms/SynchronousTests.java @@ -23,6 +23,7 @@ import javax.jms.ConnectionFactory; import javax.jms.JMSException; import javax.jms.Session; +import org.springframework.batch.jms.ExternalRetryInBatchTests; import org.springframework.batch.repeat.ExitStatus; import org.springframework.batch.repeat.RepeatCallback; import org.springframework.batch.repeat.RepeatContext; @@ -33,6 +34,7 @@ import org.springframework.jms.core.SessionCallback; import org.springframework.test.AbstractTransactionalDataSourceSpringContextTests; import org.springframework.transaction.support.TransactionSynchronizationAdapter; import org.springframework.transaction.support.TransactionSynchronizationManager; +import org.springframework.util.ClassUtils; public class SynchronousTests extends AbstractTransactionalDataSourceSpringContextTests { @@ -49,7 +51,8 @@ public class SynchronousTests extends AbstractTransactionalDataSourceSpringConte } protected String[] getConfigLocations() { - return new String[] { "/org/springframework/batch/jms/jms-context.xml" }; + return new String[] { ClassUtils.addResourcePathToPackagePath(ExternalRetryInBatchTests.class, + "jms-context.xml") }; } protected void onSetUpBeforeTransaction() throws Exception { @@ -85,7 +88,7 @@ public class SynchronousTests extends AbstractTransactionalDataSourceSpringConte String text = (String) jmsTemplate.receiveAndConvert("queue"); list.add(text); jdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", new Object[] { - new Integer(list.size()), text }); + new Integer(list.size()), text }); return new ExitStatus(text != null); } }); @@ -116,7 +119,7 @@ public class SynchronousTests extends AbstractTransactionalDataSourceSpringConte String text = (String) jmsTemplate.receiveAndConvert("queue"); list.add(text); jdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", new Object[] { - new Integer(list.size()), text }); + new Integer(list.size()), text }); return new ExitStatus(text != null); } }); @@ -146,7 +149,7 @@ public class SynchronousTests extends AbstractTransactionalDataSourceSpringConte // The JmsTemplate is used elsewhere outside a transaction, so // we need to use one here that is transaction aware. final JmsTemplate jmsTemplate = new JmsTemplate((ConnectionFactory) applicationContext - .getBean("txAwareConnectionFactory")); + .getBean("txAwareConnectionFactory")); jmsTemplate.setReceiveTimeout(100L); jmsTemplate.setSessionTransacted(true); @@ -156,7 +159,7 @@ public class SynchronousTests extends AbstractTransactionalDataSourceSpringConte String text = (String) jmsTemplate.receiveAndConvert("queue"); list.add(text); jdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", new Object[] { - new Integer(list.size()), text }); + new Integer(list.size()), text }); return new ExitStatus(text != null); } }); @@ -170,9 +173,11 @@ public class SynchronousTests extends AbstractTransactionalDataSourceSpringConte try { assertTrue("Not a SessionProxy - wrong spring version?", session instanceof SessionProxy); ((SessionProxy) session).getTargetSession().rollback(); - } catch (JMSException e) { + } + catch (JMSException e) { throw e; - } catch (Exception e) { + } + catch (Exception e) { // swallow it e.printStackTrace(); } diff --git a/spring-batch-integration/src/test/java/org/springframework/batch/retry/jms/ExternalRetryTests.java b/spring-batch-integration/src/test/java/org/springframework/batch/retry/jms/ExternalRetryTests.java index ee4105f05..60cbbd634 100644 --- a/spring-batch-integration/src/test/java/org/springframework/batch/retry/jms/ExternalRetryTests.java +++ b/spring-batch-integration/src/test/java/org/springframework/batch/retry/jms/ExternalRetryTests.java @@ -24,6 +24,7 @@ import javax.sql.DataSource; import org.springframework.batch.item.AbstractItemReader; import org.springframework.batch.item.AbstractItemWriter; import org.springframework.batch.item.ItemRecoverer; +import org.springframework.batch.jms.ExternalRetryInBatchTests; import org.springframework.batch.retry.callback.ItemWriterRetryCallback; import org.springframework.batch.retry.policy.ItemWriterRetryPolicy; import org.springframework.batch.retry.support.RetryTemplate; @@ -34,6 +35,7 @@ import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionTemplate; +import org.springframework.util.ClassUtils; public class ExternalRetryTests extends AbstractDependencyInjectionSpringContextTests { @@ -60,7 +62,8 @@ public class ExternalRetryTests extends AbstractDependencyInjectionSpringContext } protected String[] getConfigLocations() { - return new String[] { "/org/springframework/batch/jms/jms-context.xml" }; + return new String[] { ClassUtils.addResourcePathToPackagePath(ExternalRetryInBatchTests.class, + "jms-context.xml") }; } protected void onSetUp() throws Exception { @@ -93,7 +96,8 @@ public class ExternalRetryTests extends AbstractDependencyInjectionSpringContext private List recovered = new ArrayList(); /** - * Message processing is successful on the second attempt but must receive the message again. + * Message processing is successful on the second attempt but must receive + * the message again. * * @throws Exception */ @@ -106,7 +110,7 @@ public class ExternalRetryTests extends AbstractDependencyInjectionSpringContext final AbstractItemWriter writer = new AbstractItemWriter() { public void write(final Object text) { jdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", new Object[] { - new Integer(list.size()), text }); + new Integer(list.size()), text }); if (list.size() == 1) { throw new RuntimeException("Rollback!"); } @@ -120,13 +124,15 @@ public class ExternalRetryTests extends AbstractDependencyInjectionSpringContext try { ItemWriterRetryCallback callback = new ItemWriterRetryCallback(provider.read(), writer); return retryTemplate.execute(callback); - } catch (Exception e) { + } + catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } } }); fail("Expected Exception"); - } catch (Exception e) { + } + catch (Exception e) { assertEquals("Rollback!", e.getMessage()); @@ -140,7 +146,8 @@ public class ExternalRetryTests extends AbstractDependencyInjectionSpringContext try { ItemWriterRetryCallback callback = new ItemWriterRetryCallback(provider.read(), writer); return retryTemplate.execute(callback); - } catch (Exception e) { + } + catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } } @@ -170,7 +177,7 @@ public class ExternalRetryTests extends AbstractDependencyInjectionSpringContext final ItemWriterRetryCallback callback = new ItemWriterRetryCallback(provider.read(), new AbstractItemWriter() { public void write(final Object text) { jdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", new Object[] { - new Integer(list.size()), text }); + new Integer(list.size()), text }); throw new RuntimeException("Rollback!"); } }); @@ -184,12 +191,14 @@ public class ExternalRetryTests extends AbstractDependencyInjectionSpringContext public Object doInTransaction(TransactionStatus status) { try { return retryTemplate.execute(callback); - } catch (Exception e) { + } + catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } } }); - } catch (Exception e) { + } + catch (Exception e) { if (i < 3) assertEquals("Rollback!", e.getMessage()); diff --git a/spring-batch-integration/src/test/java/org/springframework/batch/retry/jms/SynchronousTests.java b/spring-batch-integration/src/test/java/org/springframework/batch/retry/jms/SynchronousTests.java index 24f213003..ba9db883a 100644 --- a/spring-batch-integration/src/test/java/org/springframework/batch/retry/jms/SynchronousTests.java +++ b/spring-batch-integration/src/test/java/org/springframework/batch/retry/jms/SynchronousTests.java @@ -21,6 +21,7 @@ import java.util.List; import org.springframework.batch.item.AbstractItemWriter; import org.springframework.batch.item.jms.JmsItemReader; +import org.springframework.batch.jms.ExternalRetryInBatchTests; import org.springframework.batch.retry.RetryCallback; import org.springframework.batch.retry.RetryContext; import org.springframework.batch.retry.callback.ItemWriterRetryCallback; @@ -30,6 +31,7 @@ import org.springframework.test.AbstractTransactionalDataSourceSpringContextTest import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionTemplate; +import org.springframework.util.ClassUtils; public class SynchronousTests extends AbstractTransactionalDataSourceSpringContextTests { @@ -42,7 +44,8 @@ public class SynchronousTests extends AbstractTransactionalDataSourceSpringConte } protected String[] getConfigLocations() { - return new String[] { "/org/springframework/batch/jms/jms-context.xml" }; + return new String[] { ClassUtils.addResourcePathToPackagePath(ExternalRetryInBatchTests.class, + "jms-context.xml") }; } protected void onSetUpBeforeTransaction() throws Exception { @@ -73,7 +76,8 @@ public class SynchronousTests extends AbstractTransactionalDataSourceSpringConte List list = new ArrayList(); /** - * Message processing is successful on the second attempt without having to receive the message again. + * Message processing is successful on the second attempt without having to + * receive the message again. * * @throws Exception */ @@ -82,9 +86,11 @@ public class SynchronousTests extends AbstractTransactionalDataSourceSpringConte assertInitialState(); /* - * We either want the JMS receive to be outside a transaction, or we need the database transaction in the retry - * to be PROPAGATION_NESTED. Otherwise JMS will roll back when the retry callback is eventually successful - * because of the previous exception. PROPAGATION_REQUIRES_NEW is wrong because it doesn't allow the outer + * We either want the JMS receive to be outside a transaction, or we + * need the database transaction in the retry to be PROPAGATION_NESTED. + * Otherwise JMS will roll back when the retry callback is eventually + * successful because of the previous exception. + * PROPAGATION_REQUIRES_NEW is wrong because it doesn't allow the outer * transaction to fail and rollback the inner one. */ final String text = (String) jmsTemplate.receiveAndConvert("queue"); @@ -101,7 +107,7 @@ public class SynchronousTests extends AbstractTransactionalDataSourceSpringConte list.add(text); System.err.println("Inserting: [" + list.size() + "," + text + "]"); jdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", new Object[] { - new Integer(list.size()), text }); + new Integer(list.size()), text }); if (list.size() == 1) { throw new RuntimeException("Rollback!"); } @@ -130,8 +136,8 @@ public class SynchronousTests extends AbstractTransactionalDataSourceSpringConte } /** - * Message processing is successful on the second attempt without having to receive the message again - uses - * JmsItemProvider internally. + * Message processing is successful on the second attempt without having to + * receive the message again - uses JmsItemProvider internally. * * @throws Exception */ @@ -155,7 +161,7 @@ public class SynchronousTests extends AbstractTransactionalDataSourceSpringConte list.add(text); System.err.println("Inserting: [" + list.size() + "," + text + "]"); jdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", new Object[] { - new Integer(list.size()), text }); + new Integer(list.size()), text }); if (list.size() == 1) { throw new RuntimeException("Rollback!"); } @@ -185,7 +191,8 @@ public class SynchronousTests extends AbstractTransactionalDataSourceSpringConte } /** - * Message processing is successful on the second attempt without having to receive the message again. + * Message processing is successful on the second attempt without having to + * receive the message again. * * @throws Exception */ @@ -194,9 +201,11 @@ public class SynchronousTests extends AbstractTransactionalDataSourceSpringConte assertInitialState(); /* - * We either want the JMS receive to be outside a transaction, or we need the database transaction in the retry - * to be PROPAGATION_NESTED. Otherwise JMS will roll back when the retry callback is eventually successful - * because of the previous exception. PROPAGATION_REQUIRES_NEW is wrong because it doesn't allow the outer + * We either want the JMS receive to be outside a transaction, or we + * need the database transaction in the retry to be PROPAGATION_NESTED. + * Otherwise JMS will roll back when the retry callback is eventually + * successful because of the previous exception. + * PROPAGATION_REQUIRES_NEW is wrong because it doesn't allow the outer * transaction to fail and rollback the inner one. */ final String text = (String) jmsTemplate.receiveAndConvert("queue"); @@ -212,7 +221,7 @@ public class SynchronousTests extends AbstractTransactionalDataSourceSpringConte list.add(text); System.err.println("Inserting: [" + list.size() + "," + text + "]"); jdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", new Object[] { - new Integer(list.size()), text }); + new Integer(list.size()), text }); return text; } @@ -241,7 +250,8 @@ public class SynchronousTests extends AbstractTransactionalDataSourceSpringConte } /** - * Message processing is successful on the second attempt but must receive the message again. + * Message processing is successful on the second attempt but must receive + * the message again. * * @throws Exception */ @@ -266,7 +276,7 @@ public class SynchronousTests extends AbstractTransactionalDataSourceSpringConte final String text = (String) jmsTemplate.receiveAndConvert("queue"); list.add(text); jdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", new Object[] { - new Integer(list.size()), text }); + new Integer(list.size()), text }); if (list.size() == 1) { throw new RuntimeException("Rollback!"); } @@ -318,7 +328,7 @@ public class SynchronousTests extends AbstractTransactionalDataSourceSpringConte final String text = (String) jmsTemplate.receiveAndConvert("queue"); list.add(text); jdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", - new Object[] { new Integer(list.size()), text }); + new Object[] { new Integer(list.size()), text }); throw new RuntimeException("Rollback!"); } @@ -328,12 +338,13 @@ public class SynchronousTests extends AbstractTransactionalDataSourceSpringConte }); /* - * N.B. the message can be re-directed to an error queue by setting an error destination in a - * JmsItemProvider. + * N.B. the message can be re-directed to an error queue by setting + * an error destination in a JmsItemProvider. */ fail("Expected RuntimeException"); - } catch (RuntimeException e) { + } + catch (RuntimeException e) { assertEquals("Rollback!", e.getMessage()); // expected } diff --git a/spring-batch-integration/src/test/java/org/springframework/retry/jms/SynchronousTests.java b/spring-batch-integration/src/test/java/org/springframework/retry/jms/SynchronousTests.java index 5cb92b2c5..e68363f43 100644 --- a/spring-batch-integration/src/test/java/org/springframework/retry/jms/SynchronousTests.java +++ b/spring-batch-integration/src/test/java/org/springframework/retry/jms/SynchronousTests.java @@ -21,6 +21,7 @@ import java.util.List; import org.springframework.batch.item.AbstractItemWriter; import org.springframework.batch.item.jms.JmsItemReader; +import org.springframework.batch.jms.ExternalRetryInBatchTests; import org.springframework.batch.retry.RetryCallback; import org.springframework.batch.retry.RetryContext; import org.springframework.batch.retry.callback.ItemWriterRetryCallback; @@ -30,6 +31,7 @@ import org.springframework.test.AbstractTransactionalDataSourceSpringContextTest import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionTemplate; +import org.springframework.util.ClassUtils; public class SynchronousTests extends AbstractTransactionalDataSourceSpringContextTests { @@ -42,7 +44,8 @@ public class SynchronousTests extends AbstractTransactionalDataSourceSpringConte } protected String[] getConfigLocations() { - return new String[] { "/org/springframework/batch/jms/jms-context.xml" }; + return new String[] { ClassUtils.addResourcePathToPackagePath(ExternalRetryInBatchTests.class, + "jms-context.xml") }; } protected void onSetUpBeforeTransaction() throws Exception { @@ -70,7 +73,8 @@ public class SynchronousTests extends AbstractTransactionalDataSourceSpringConte List list = new ArrayList(); /** - * Message processing is successful on the second attempt without having to receive the message again. + * Message processing is successful on the second attempt without having to + * receive the message again. * * @throws Exception */ @@ -79,9 +83,11 @@ public class SynchronousTests extends AbstractTransactionalDataSourceSpringConte assertInitialState(); /* - * We either want the JMS receive to be outside a transaction, or we need the database transaction in the retry - * to be PROPAGATION_NESTED. Otherwise JMS will roll back when the retry callback is eventually successful - * because of the previous exception. PROPAGATION_REQUIRES_NEW is wrong because it doesn't allow the outer + * We either want the JMS receive to be outside a transaction, or we + * need the database transaction in the retry to be PROPAGATION_NESTED. + * Otherwise JMS will roll back when the retry callback is eventually + * successful because of the previous exception. + * PROPAGATION_REQUIRES_NEW is wrong because it doesn't allow the outer * transaction to fail and rollback the inner one. */ final String text = (String) jmsTemplate.receiveAndConvert("queue"); @@ -97,7 +103,7 @@ public class SynchronousTests extends AbstractTransactionalDataSourceSpringConte list.add(text); System.err.println("Inserting: [" + list.size() + "," + text + "]"); jdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", new Object[] { - new Integer(list.size()), text }); + new Integer(list.size()), text }); if (list.size() == 1) { throw new RuntimeException("Rollback!"); } @@ -126,8 +132,8 @@ public class SynchronousTests extends AbstractTransactionalDataSourceSpringConte } /** - * Message processing is successful on the second attempt without having to receive the message again - uses - * JmsItemProvider internally. + * Message processing is successful on the second attempt without having to + * receive the message again - uses JmsItemProvider internally. * * @throws Exception */ @@ -151,7 +157,7 @@ public class SynchronousTests extends AbstractTransactionalDataSourceSpringConte list.add(text); System.err.println("Inserting: [" + list.size() + "," + text + "]"); jdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", new Object[] { - new Integer(list.size()), text }); + new Integer(list.size()), text }); if (list.size() == 1) { throw new RuntimeException("Rollback!"); } @@ -181,7 +187,8 @@ public class SynchronousTests extends AbstractTransactionalDataSourceSpringConte } /** - * Message processing is successful on the second attempt without having to receive the message again. + * Message processing is successful on the second attempt without having to + * receive the message again. * * @throws Exception */ @@ -190,9 +197,11 @@ public class SynchronousTests extends AbstractTransactionalDataSourceSpringConte assertInitialState(); /* - * We either want the JMS receive to be outside a transaction, or we need the database transaction in the retry - * to be PROPAGATION_NESTED. Otherwise JMS will roll back when the retry callback is eventually successful - * because of the previous exception. PROPAGATION_REQUIRES_NEW is wrong because it doesn't allow the outer + * We either want the JMS receive to be outside a transaction, or we + * need the database transaction in the retry to be PROPAGATION_NESTED. + * Otherwise JMS will roll back when the retry callback is eventually + * successful because of the previous exception. + * PROPAGATION_REQUIRES_NEW is wrong because it doesn't allow the outer * transaction to fail and rollback the inner one. */ final String text = (String) jmsTemplate.receiveAndConvert("queue"); @@ -208,7 +217,7 @@ public class SynchronousTests extends AbstractTransactionalDataSourceSpringConte list.add(text); System.err.println("Inserting: [" + list.size() + "," + text + "]"); jdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", new Object[] { - new Integer(list.size()), text }); + new Integer(list.size()), text }); return text; } @@ -237,7 +246,8 @@ public class SynchronousTests extends AbstractTransactionalDataSourceSpringConte } /** - * Message processing is successful on the second attempt but must receive the message again. + * Message processing is successful on the second attempt but must receive + * the message again. * * @throws Exception */ @@ -262,7 +272,7 @@ public class SynchronousTests extends AbstractTransactionalDataSourceSpringConte final String text = (String) jmsTemplate.receiveAndConvert("queue"); list.add(text); jdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", new Object[] { - new Integer(list.size()), text }); + new Integer(list.size()), text }); if (list.size() == 1) { throw new RuntimeException("Rollback!"); } @@ -314,7 +324,7 @@ public class SynchronousTests extends AbstractTransactionalDataSourceSpringConte final String text = (String) jmsTemplate.receiveAndConvert("queue"); list.add(text); jdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", - new Object[] { new Integer(list.size()), text }); + new Object[] { new Integer(list.size()), text }); throw new RuntimeException("Rollback!"); } @@ -325,7 +335,8 @@ public class SynchronousTests extends AbstractTransactionalDataSourceSpringConte fail("Expected RuntimeException"); - } catch (RuntimeException e) { + } + catch (RuntimeException e) { assertEquals("Rollback!", e.getMessage()); // expected } diff --git a/spring-batch-integration/src/test/resources/log4j.properties b/spring-batch-integration/src/test/resources/log4j.properties index 45e88b404..b31319400 100644 --- a/spring-batch-integration/src/test/resources/log4j.properties +++ b/spring-batch-integration/src/test/resources/log4j.properties @@ -2,7 +2,8 @@ log4j.rootCategory=INFO, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n +log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{1}:%L - %m%n log4j.category.org.apache.activemq=ERROR # log4j.category.org.springframework=DEBUG +log4j.category.org.springframework.batch.container.jms=DEBUG diff --git a/spring-batch-integration/src/test/resources/org/springframework/batch/jms/jms-context.xml b/spring-batch-integration/src/test/resources/org/springframework/batch/jms/jms-context.xml index 31492e460..921cb6d37 100644 --- a/spring-batch-integration/src/test/resources/org/springframework/batch/jms/jms-context.xml +++ b/spring-batch-integration/src/test/resources/org/springframework/batch/jms/jms-context.xml @@ -52,6 +52,7 @@ + diff --git a/spring-batch-samples/.springBeans b/spring-batch-samples/.springBeans index 3e96106b5..de51a46c0 100644 --- a/spring-batch-samples/.springBeans +++ b/spring-batch-samples/.springBeans @@ -1,10 +1,11 @@ 1 - + + src/main/resources/jobs/fixedLengthImportJob.xml src/main/resources/jobs/multilineJob.xml