diff --git a/spring-batch-execution/src/main/java/org/springframework/batch/execution/listener/RepeatListenerItemReadListenerAdapter.java b/spring-batch-execution/src/main/java/org/springframework/batch/execution/listener/RepeatListenerItemReadListenerAdapter.java new file mode 100644 index 000000000..97672b49d --- /dev/null +++ b/spring-batch-execution/src/main/java/org/springframework/batch/execution/listener/RepeatListenerItemReadListenerAdapter.java @@ -0,0 +1,100 @@ +/* + * 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.execution.listener; + +import org.springframework.batch.core.domain.ItemReadListener; +import org.springframework.batch.repeat.RepeatContext; +import org.springframework.batch.repeat.RepeatListener; +import org.springframework.batch.repeat.synch.RepeatSynchronizationManager; +import org.springframework.util.ObjectUtils; + +/** + * Adapts a {@link RepeatListener} to the {@link ItemReadListener} interface. + * The {@link RepeatListener} is assumed to be targeted at the chunk operations + * in a step, so after an item corresponds to the after method in + * {@link RepeatListener}.
+ * + * The open and close methods on the {@link RepeatListener} are also invoked. + * The open method is called on the first call to {@link #beforeRead()}, and + * the close is registered as a destruction callback in the + * {@link RepeatContext}.
+ * + * A {@link RepeatContext} is obtained as needed from the + * {@link RepeatSynchronizationManager}. + * + * @author Dave Syer + * + */ +public class RepeatListenerItemReadListenerAdapter implements ItemReadListener { + + private RepeatListener delegate; + + /** + * @param delegate + */ + public RepeatListenerItemReadListenerAdapter(RepeatListener delegate) { + super(); + this.delegate = delegate; + } + + /** + * Does nothing. + * + * @see org.springframework.batch.core.domain.ItemReadListener#afterRead(java.lang.Object) + */ + public void afterRead(Object item) { + // NO-OP + + } + + /** + * Calls the delegate {@link RepeatListener#before}. Also calls + * {@link RepeatListener#open} if it hasn't been called yet on this context. + * + * @see org.springframework.batch.core.domain.ItemReadListener#beforeRead() + */ + public void beforeRead() { + RepeatContext context = RepeatSynchronizationManager.getContext(); + maybeOpen(context); + delegate.before(context); + } + + /** + * @param context + */ + private void maybeOpen(final RepeatContext context) { + String identity = ObjectUtils.identityToString(delegate); + if (!context.hasAttribute(identity)) { + context.setAttribute(identity, Boolean.TRUE); + delegate.open(context); + context.registerDestructionCallback(identity, new Runnable() { + public void run() { + delegate.close(context); + } + }); + } + } + + /** + * Calls the delegate {@link RepeatListener#onError}. + * + * @see org.springframework.batch.core.domain.ItemReadListener#onReadError(java.lang.Exception) + */ + public void onReadError(Exception ex) { + delegate.onError(RepeatSynchronizationManager.getContext(), ex); + } + +} diff --git a/spring-batch-execution/src/main/java/org/springframework/batch/execution/listener/RepeatListenerItemWriteListenerAdapter.java b/spring-batch-execution/src/main/java/org/springframework/batch/execution/listener/RepeatListenerItemWriteListenerAdapter.java new file mode 100644 index 000000000..00b8ad68c --- /dev/null +++ b/spring-batch-execution/src/main/java/org/springframework/batch/execution/listener/RepeatListenerItemWriteListenerAdapter.java @@ -0,0 +1,77 @@ +/* + * 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.execution.listener; + +import org.springframework.batch.core.domain.ItemWriteListener; +import org.springframework.batch.repeat.ExitStatus; +import org.springframework.batch.repeat.RepeatContext; +import org.springframework.batch.repeat.RepeatListener; +import org.springframework.batch.repeat.synch.RepeatSynchronizationManager; + +/** + * Adapts a {@link RepeatListener} to the {@link ItemWriteListener} interface. + * The {@link RepeatListener} is assumed to be targeted at the chunk operations + * in a step, so after an item corresponds to the after method in + * {@link RepeatListener}.
+ * + * A {@link RepeatContext} is obtained as needed from the + * {@link RepeatSynchronizationManager}. + * + * @author Dave Syer + * + */ +public class RepeatListenerItemWriteListenerAdapter implements ItemWriteListener { + + private RepeatListener delegate; + + /** + * @param delegate + */ + public RepeatListenerItemWriteListenerAdapter(RepeatListener delegate) { + super(); + this.delegate = delegate; + } + + /** + * Calls the delegate {@link RepeatListener#after} with + * {@link ExitStatus#CONTINUABLE}. + * + * @see org.springframework.batch.core.domain.ItemWriteListener#afterWrite() + */ + public void afterWrite() { + delegate.after(RepeatSynchronizationManager.getContext(), ExitStatus.CONTINUABLE); + } + + /** + * Does nothing. + * + * @see org.springframework.batch.core.domain.ItemWriteListener#beforeWrite(java.lang.Object) + */ + public void beforeWrite(Object item) { + // NO-OP + } + + /** + * Calls the delegate {@link RepeatListener#onError} ignoring the item. + * + * @see org.springframework.batch.core.domain.ItemWriteListener#onWriteError(java.lang.Exception, + * java.lang.Object) + */ + public void onWriteError(Exception ex, Object item) { + delegate.onError(RepeatSynchronizationManager.getContext(), ex); + } + +} diff --git a/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/ItemOrientedStep.java b/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/ItemOrientedStep.java index ee3984af6..b3437f508 100644 --- a/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/ItemOrientedStep.java +++ b/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/ItemOrientedStep.java @@ -295,7 +295,6 @@ public class ItemOrientedStep extends AbstractStep implements InitializingBean { listener.update(stepExecution.getExecutionContext()); try { - stepExecution.setStatus(BatchStatus.COMPLETED); jobRepository.saveOrUpdateExecutionContext(stepExecution); } catch (Exception e) { diff --git a/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/support/ListenerMulticaster.java b/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/support/ListenerMulticaster.java index 444ee3194..c765e9a72 100644 --- a/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/support/ListenerMulticaster.java +++ b/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/support/ListenerMulticaster.java @@ -25,11 +25,15 @@ import org.springframework.batch.execution.listener.CompositeChunkListener; import org.springframework.batch.execution.listener.CompositeItemReadListener; import org.springframework.batch.execution.listener.CompositeItemWriteListener; import org.springframework.batch.execution.listener.CompositeStepListener; +import org.springframework.batch.execution.listener.RepeatListenerItemReadListenerAdapter; +import org.springframework.batch.execution.listener.RepeatListenerItemWriteListenerAdapter; import org.springframework.batch.item.ExecutionContext; import org.springframework.batch.item.ItemStream; import org.springframework.batch.item.exception.StreamException; import org.springframework.batch.item.stream.CompositeItemStream; import org.springframework.batch.repeat.ExitStatus; +import org.springframework.batch.repeat.RepeatListener; +import org.springframework.batch.repeat.interceptor.CompositeRepeatListener; /** * @author Dave Syer @@ -48,6 +52,17 @@ public class ListenerMulticaster implements ItemStream, StepListener, ChunkListe private CompositeItemWriteListener itemWriteListener = new CompositeItemWriteListener(); + private CompositeRepeatListener repeatListener = new CompositeRepeatListener(); + + /** + * Initialise the listener instance. + */ + public ListenerMulticaster() { + super(); + itemWriteListener.register(new RepeatListenerItemWriteListenerAdapter(repeatListener)); + itemReadListener.register(new RepeatListenerItemReadListenerAdapter(repeatListener)); + } + /** * Register each of the objects as listeners. Once registered, calls to the * {@link ListenerMulticaster} broadcast to the individual listeners. @@ -82,6 +97,9 @@ public class ListenerMulticaster implements ItemStream, StepListener, ChunkListe if (listener instanceof ItemWriteListener) { this.itemWriteListener.register((ItemWriteListener) listener); } + if (listener instanceof RepeatListener) { + this.repeatListener.register((RepeatListener) listener); + } } /** diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/io/support/HibernateAwareItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/io/support/HibernateAwareItemWriter.java index f5087cf6d..c53c751cc 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/io/support/HibernateAwareItemWriter.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/io/support/HibernateAwareItemWriter.java @@ -22,11 +22,10 @@ import org.hibernate.SessionFactory; import org.springframework.batch.item.ItemWriter; import org.springframework.batch.item.exception.ClearFailedException; import org.springframework.batch.item.exception.FlushFailedException; -import org.springframework.batch.repeat.ExitStatus; import org.springframework.batch.repeat.RepeatContext; import org.springframework.batch.repeat.RepeatListener; +import org.springframework.batch.repeat.synch.RepeatSynchronizationManager; import org.springframework.beans.factory.InitializingBean; -import org.springframework.core.AttributeAccessor; import org.springframework.orm.hibernate3.HibernateOperations; import org.springframework.orm.hibernate3.HibernateTemplate; import org.springframework.transaction.support.TransactionSynchronizationManager; @@ -46,19 +45,13 @@ import org.springframework.util.Assert; * @author Dave Syer * */ -public class HibernateAwareItemWriter implements ItemWriter, RepeatListener, InitializingBean { +public class HibernateAwareItemWriter implements ItemWriter, InitializingBean { /** * Key for items processed in the current transaction {@link RepeatContext}. */ private static final String ITEMS_PROCESSED = HibernateAwareItemWriter.class.getName() + ".ITEMS_PROCESSED"; - /** - * Key for {@link RepeatContext} in transaction resource context. - */ - private static final String WRITER_REPEAT_CONTEXT = HibernateAwareItemWriter.class.getName() - + ".WRITER_REPEAT_CONTEXT"; - private Set failed = new HashSet(); private ItemWriter delegate; @@ -112,99 +105,21 @@ public class HibernateAwareItemWriter implements ItemWriter, RepeatListener, Ini * @see org.springframework.batch.io.OutputSource#write(java.lang.Object) */ public void write(Object output) throws Exception { + bindTransactionResources(); getProcessed().add(output); delegate.write(output); flushIfNecessary(output); } - /** - * Does nothing unless the delegate is also a {@link RepeatListener} in - * which case pass on the call to him. - * - * @see org.springframework.batch.repeat.RepeatListener#before(org.springframework.batch.repeat.RepeatContext) - */ - public void before(RepeatContext context) { - if (delegate instanceof RepeatListener) { - RepeatListener interceptor = (RepeatListener) delegate; - interceptor.before(context); - } - } - - /** - * Does nothing unless the delegate is also a {@link RepeatListener} in - * which case pass on the call to him. - * - * @see org.springframework.batch.repeat.RepeatListener#after(org.springframework.batch.repeat.RepeatContext, - * org.springframework.batch.repeat.ExitStatus) - */ - public void after(RepeatContext context, ExitStatus result) { - if (delegate instanceof RepeatListener) { - RepeatListener interceptor = (RepeatListener) delegate; - interceptor.after(context, result); - } - } - - /** - * If the delegate is also a {@link RepeatListener} then it will be given - * the call before flushing. - * - * - * @see org.springframework.batch.repeat.RepeatListener#close(org.springframework.batch.repeat.RepeatContext) - */ - public void close(RepeatContext context) { - try { - flushInContext(); - } - finally { - unsetContext(); - if (delegate instanceof RepeatListener) { - RepeatListener interceptor = (RepeatListener) delegate; - interceptor.close(context); - } - } - } - - /** - * Does nothing unless the delegate is also a {@link RepeatListener} in - * which case pass on the call to him. - * - * @see org.springframework.batch.repeat.RepeatListener#onError(org.springframework.batch.repeat.RepeatContext, - * java.lang.Throwable) - */ - public void onError(RepeatContext context, Throwable e) { - if (delegate instanceof RepeatListener) { - RepeatListener interceptor = (RepeatListener) delegate; - interceptor.onError(context, e); - } - } - - /** - * Sets up the context as a transaction resource so that we can store state - * and refer back to it in the {@link #write(Object)} method. If the - * delegate is also a {@link RepeatListener} then it will be given the call - * afterwards. - * - * @see org.springframework.batch.repeat.RepeatListener#open(org.springframework.batch.repeat.RepeatContext) - */ - public void open(RepeatContext context) { - this.setContext(context); - getProcessed().clear(); - if (delegate instanceof RepeatListener) { - RepeatListener interceptor = (RepeatListener) delegate; - interceptor.open(context); - } - } - /** * Accessor for the list of processed items in this transaction. * * @return the processed */ private Set getProcessed() { - Assert.state(TransactionSynchronizationManager.hasResource(WRITER_REPEAT_CONTEXT), - "RepeatContext not bound to transaction."); - Set processed = (Set) ((AttributeAccessor) TransactionSynchronizationManager.getResource(WRITER_REPEAT_CONTEXT)) - .getAttribute(ITEMS_PROCESSED); + Assert.state(TransactionSynchronizationManager.hasResource(ITEMS_PROCESSED), + "Processed items not bound to transaction."); + Set processed = (Set) TransactionSynchronizationManager.getResource(ITEMS_PROCESSED); return processed; } @@ -213,22 +128,21 @@ public class HibernateAwareItemWriter implements ItemWriter, RepeatListener, Ini * * @param context the context to set */ - private void setContext(RepeatContext context) { - if (TransactionSynchronizationManager.hasResource(WRITER_REPEAT_CONTEXT)) { + private void bindTransactionResources() { + if (TransactionSynchronizationManager.hasResource(ITEMS_PROCESSED)) { return; } - TransactionSynchronizationManager.bindResource(WRITER_REPEAT_CONTEXT, context); - context.setAttribute(ITEMS_PROCESSED, new HashSet()); + TransactionSynchronizationManager.bindResource(ITEMS_PROCESSED, new HashSet()); } /** * Remove the transaction resource associated with this context. */ - private void unsetContext() { - if (!TransactionSynchronizationManager.hasResource(WRITER_REPEAT_CONTEXT)) { + private void unbindTransactionResources() { + if (!TransactionSynchronizationManager.hasResource(ITEMS_PROCESSED)) { return; } - TransactionSynchronizationManager.unbindResource(WRITER_REPEAT_CONTEXT); + TransactionSynchronizationManager.unbindResource(ITEMS_PROCESSED); } /** @@ -244,23 +158,21 @@ public class HibernateAwareItemWriter implements ItemWriter, RepeatListener, Ini flush = failed.contains(output); } if (flush) { - RepeatContext context = (RepeatContext) TransactionSynchronizationManager - .getResource(WRITER_REPEAT_CONTEXT); + RepeatContext context = RepeatSynchronizationManager.getContext(); // Force early completion to commit aggressively if we encounter a // failed item (from a failed chunk but we don't know which one was // the problem). context.setCompleteOnly(); // Flush now, so that if there is a failure this record can be // skipped. - flushInContext(); + doHibernateFlush(); } - } /** - * + * Flush the hibernate session from within a repeat context. */ - private void flushInContext() { + private void doHibernateFlush() { try { hibernateTemplate.flush(); // This should happen when the transaction commits anyway, but to be @@ -277,25 +189,28 @@ public class HibernateAwareItemWriter implements ItemWriter, RepeatListener, Ini } } - /* - * (non-Javadoc) + /** + * Call the delegate clear() method, and then clear the hibernate session. + * * @see org.springframework.batch.item.ItemWriter#clear() */ public void clear() throws ClearFailedException { - if (delegate != null) { - delegate.clear(); - } + unbindTransactionResources(); hibernateTemplate.clear(); + delegate.clear(); } /** - * Flush the Hibernate session. The delegate flush will also be called - * before finishing. + * Flush the Hibernate session and record failures if there are any. The + * delegate flush will also be called. + * + * @see org.springframework.batch.item.ItemWriter#flush() */ public void flush() throws FlushFailedException { - if (delegate != null) { - delegate.flush(); - } + bindTransactionResources(); + doHibernateFlush(); + unbindTransactionResources(); + delegate.flush(); } } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/repeat/interceptor/CompositeRepeatListener.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/repeat/interceptor/CompositeRepeatListener.java new file mode 100644 index 000000000..ca548df04 --- /dev/null +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/repeat/interceptor/CompositeRepeatListener.java @@ -0,0 +1,105 @@ +/* + * 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.repeat.interceptor; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import org.springframework.batch.repeat.ExitStatus; +import org.springframework.batch.repeat.RepeatContext; +import org.springframework.batch.repeat.RepeatListener; + +/** + * @author Dave Syer + * + */ +public class CompositeRepeatListener implements RepeatListener { + + private List listeners = new ArrayList(); + + /** + * Public setter for the listeners. + * + * @param listeners + */ + public void setListeners(RepeatListener[] listeners) { + this.listeners = Arrays.asList(listeners); + } + + /** + * Register additional listener. + * + * @param itemReaderListener + */ + public void register(RepeatListener listener) { + if (!listeners.contains(listener)) { + listeners.add(listener); + } + } + + /* (non-Javadoc) + * @see org.springframework.batch.repeat.RepeatListener#after(org.springframework.batch.repeat.RepeatContext, org.springframework.batch.repeat.ExitStatus) + */ + public void after(RepeatContext context, ExitStatus result) { + for (Iterator iterator = listeners.iterator(); iterator.hasNext();) { + RepeatListener listener = (RepeatListener) iterator.next(); + listener.after(context, result); + } + } + + /* (non-Javadoc) + * @see org.springframework.batch.repeat.RepeatListener#before(org.springframework.batch.repeat.RepeatContext) + */ + public void before(RepeatContext context) { + for (Iterator iterator = listeners.iterator(); iterator.hasNext();) { + RepeatListener listener = (RepeatListener) iterator.next(); + listener.before(context); + } + } + + /* (non-Javadoc) + * @see org.springframework.batch.repeat.RepeatListener#close(org.springframework.batch.repeat.RepeatContext) + */ + public void close(RepeatContext context) { + for (Iterator iterator = listeners.iterator(); iterator.hasNext();) { + RepeatListener listener = (RepeatListener) iterator.next(); + listener.close(context); + } + } + + /* (non-Javadoc) + * @see org.springframework.batch.repeat.RepeatListener#onError(org.springframework.batch.repeat.RepeatContext, java.lang.Throwable) + */ + public void onError(RepeatContext context, Throwable e) { + for (Iterator iterator = listeners.iterator(); iterator.hasNext();) { + RepeatListener listener = (RepeatListener) iterator.next(); + listener.onError(context, e); + } + } + + /* (non-Javadoc) + * @see org.springframework.batch.repeat.RepeatListener#open(org.springframework.batch.repeat.RepeatContext) + */ + public void open(RepeatContext context) { + for (Iterator iterator = listeners.iterator(); iterator.hasNext();) { + RepeatListener listener = (RepeatListener) iterator.next(); + listener.open(context); + } + } + +} diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/io/support/HibernateAwareItemWriterTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/io/support/HibernateAwareItemWriterTests.java index ccabf4e70..f34349c8b 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/io/support/HibernateAwareItemWriterTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/io/support/HibernateAwareItemWriterTests.java @@ -25,10 +25,8 @@ import junit.framework.TestCase; import org.springframework.batch.item.ItemWriter; import org.springframework.batch.item.exception.ClearFailedException; import org.springframework.batch.item.exception.FlushFailedException; -import org.springframework.batch.repeat.ExitStatus; -import org.springframework.batch.repeat.RepeatContext; -import org.springframework.batch.repeat.RepeatListener; import org.springframework.batch.repeat.context.RepeatContextSupport; +import org.springframework.batch.repeat.synch.RepeatSynchronizationManager; import org.springframework.dao.DataAccessException; import org.springframework.orm.hibernate3.HibernateTemplate; import org.springframework.transaction.support.TransactionSynchronizationManager; @@ -48,40 +46,17 @@ public class HibernateAwareItemWriterTests extends TestCase { }; } - private class StubItemWriter implements ItemWriter, RepeatListener { + private class StubItemWriter implements ItemWriter { public void write(Object item) { list.add(item); } - public void after(RepeatContext context, ExitStatus result) { - list.add(result); - } - - public void before(RepeatContext context) { - list.add(context); - } - - public void close(RepeatContext context) { - list.add(context); - } - - public void onError(RepeatContext context, Throwable e) { - list.add(e); - } - - public void open(RepeatContext context) { - list.add(context); - } - - public void close() throws Exception { - } - public void clear() throws ClearFailedException { - list.add("clear"); + list.add("delegateClear"); } public void flush() throws FlushFailedException { - list.add("flush"); + list.add("delegateFlush"); } } @@ -99,7 +74,7 @@ public class HibernateAwareItemWriterTests extends TestCase { protected void setUp() throws Exception { writer.setDelegate(new StubItemWriter()); context = new RepeatContextSupport(null); - writer.open(context); + RepeatSynchronizationManager.register(context); writer.setHibernateTemplate(new HibernateTemplateWrapper()); list.clear(); } @@ -158,7 +133,7 @@ public class HibernateAwareItemWriterTests extends TestCase { * Test method for * {@link org.springframework.batch.io.support.HibernateAwareItemWriter#write(java.lang.Object)}. */ - public void testCloseWithFailure() throws Exception{ + public void testFlushWithFailure() throws Exception{ final RuntimeException ex = new RuntimeException("bar"); writer.setHibernateTemplate(new HibernateTemplate() { public void flush() throws DataAccessException { @@ -166,14 +141,11 @@ public class HibernateAwareItemWriterTests extends TestCase { } }); try { - writer.close(context); + writer.flush(); fail("Expected RuntimeException"); } catch (RuntimeException e) { assertEquals("bar", e.getMessage()); } - assertEquals(1, list.size()); - System.err.println(list); - assertTrue(list.contains(context)); } /** @@ -181,7 +153,7 @@ public class HibernateAwareItemWriterTests extends TestCase { * {@link org.springframework.batch.io.support.HibernateAwareItemWriter#write(java.lang.Object)}. * @throws Exception */ - public void testWriteAndCloseWithFailure() throws Exception { + public void testWriteAndFlushWithFailure() throws Exception { final RuntimeException ex = new RuntimeException("bar"); writer.setHibernateTemplate(new HibernateTemplateWrapper() { public void flush() throws DataAccessException { @@ -190,41 +162,22 @@ public class HibernateAwareItemWriterTests extends TestCase { }); writer.write("foo"); try { - writer.close(context); + writer.flush(); fail("Expected RuntimeException"); } catch (RuntimeException e) { assertEquals("bar", e.getMessage()); } - assertEquals(2, list.size()); - assertTrue(list.contains(context)); + assertEquals(1, list.size()); writer.setHibernateTemplate(new HibernateTemplateWrapper() { public void flush() throws DataAccessException { list.add("flush"); } }); - writer.open(context); writer.write("foo"); - assertEquals(6, list.size()); + assertEquals(4, list.size()); assertTrue(list.contains("flush")); assertTrue(list.contains("clear")); - } - - /** - * Test method for - * {@link org.springframework.batch.io.support.HibernateAwareItemWriter#before(org.springframework.batch.repeat.RepeatContext)}. - */ - public void testBefore() { - writer.before(context); - assertEquals(1, list.size()); - } - - /** - * Test method for - * {@link org.springframework.batch.io.support.HibernateAwareItemWriter#after(org.springframework.batch.repeat.RepeatContext, org.springframework.batch.repeat.ExitStatus)}. - */ - public void testAfter() { - writer.after(context, ExitStatus.FINISHED); - assertEquals(1, list.size()); + assertTrue(context.isCompleteOnly()); } /** @@ -233,40 +186,20 @@ public class HibernateAwareItemWriterTests extends TestCase { */ public void testFlush() throws Exception{ writer.flush(); - assertEquals(1, list.size()); + assertEquals(3, list.size()); assertTrue(list.contains("flush")); + assertTrue(list.contains("clear")); + assertTrue(list.contains("delegateFlush")); } /** * Test method for * {@link org.springframework.batch.io.support.HibernateAwareItemWriter#close(org.springframework.batch.repeat.RepeatContext)}. */ - public void testCloseAfterClear() throws Exception{ - Map map = TransactionSynchronizationManager.getResourceMap(); - String key = (String) map.keySet().iterator().next(); - TransactionSynchronizationManager.unbindResource(key); - writer.close(context); - assertEquals(3, list.size()); - assertTrue(list.contains("flush")); + public void testClear() throws Exception{ + writer.clear(); + assertEquals(2, list.size()); assertTrue(list.contains("clear")); + assertTrue(list.contains("delegateClear")); } - - /** - * Test method for - * {@link org.springframework.batch.io.support.HibernateAwareItemWriter#onError(org.springframework.batch.repeat.RepeatContext, java.lang.Throwable)}. - */ - public void testOnError() { - writer.onError(context, new Exception()); - assertEquals(1, list.size()); - } - - /** - * Test method for - * {@link org.springframework.batch.io.support.HibernateAwareItemWriter#open(org.springframework.batch.repeat.RepeatContext)}. - */ - public void testOpen() { - writer.open(context); - assertEquals(1, list.size()); - } - } diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/repeat/interceptor/CompositeRepeatListenerTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/repeat/interceptor/CompositeRepeatListenerTests.java new file mode 100644 index 000000000..792162796 --- /dev/null +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/repeat/interceptor/CompositeRepeatListenerTests.java @@ -0,0 +1,98 @@ +/* + * 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.repeat.interceptor; + +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestCase; + +import org.springframework.batch.repeat.RepeatContext; +import org.springframework.batch.repeat.RepeatListener; +import org.springframework.batch.repeat.context.RepeatContextSupport; + +/** + * @author Dave Syer + * + */ +public class CompositeRepeatListenerTests extends TestCase { + + private CompositeRepeatListener listener = new CompositeRepeatListener(); + private RepeatContext context = new RepeatContextSupport(null); + + private List list = new ArrayList(); + + /** + * Test method for + * {@link org.springframework.batch.execution.listener.CompositeStepListener#setListeners(org.springframework.batch.core.domain.StepListener[])}. + */ + public void testSetListeners() { + listener.setListeners(new RepeatListener[] { new RepeatListenerSupport() { + public void open(RepeatContext context) { + list.add("fail"); + } + }, new RepeatListenerSupport() { + public void open(RepeatContext context) { + list.add("continue"); + } + } }); + listener.open(context); + assertEquals(2, list.size()); + } + + /** + * Test method for + * {@link org.springframework.batch.execution.listener.CompositeStepListener#setListener(org.springframework.batch.core.domain.StepListener)}. + */ + public void testSetListener() { + listener.register(new RepeatListenerSupport() { + public void before(RepeatContext context) { + list.add("fail"); + } + }); + listener.before(context); + assertEquals(1, list.size()); + } + + /** + * Test method for + * {@link org.springframework.batch.execution.listener.CompositeStepListener#beforeStep(StepExecution)}. + */ + public void testClose() { + listener.register(new RepeatListenerSupport() { + public void close(RepeatContext context) { + list.add("foo"); + } + }); + listener.close(context); + assertEquals(1, list.size()); + } + + /** + * Test method for + * {@link org.springframework.batch.execution.listener.CompositeStepListener#beforeStep(StepExecution)}. + */ + public void testOnError() { + listener.register(new RepeatListenerSupport() { + public void onError(RepeatContext context, Throwable e) { + list.add(e); + } + }); + listener.onError(context, new RuntimeException("foo")); + assertEquals(1, list.size()); + } + +} diff --git a/spring-batch-samples/src/main/java/org/springframework/batch/sample/item/writer/CustomerCreditIncreaseWriter.java b/spring-batch-samples/src/main/java/org/springframework/batch/sample/item/writer/CustomerCreditIncreaseWriter.java index f8f575bdc..fa0d5517f 100644 --- a/spring-batch-samples/src/main/java/org/springframework/batch/sample/item/writer/CustomerCreditIncreaseWriter.java +++ b/spring-batch-samples/src/main/java/org/springframework/batch/sample/item/writer/CustomerCreditIncreaseWriter.java @@ -34,7 +34,4 @@ public class CustomerCreditIncreaseWriter extends AbstractItemWriter { customerCreditDao.writeCredit(customerCredit); } - public void close() throws Exception { - } - } diff --git a/spring-batch-samples/src/main/resources/jobs/hibernateJob.xml b/spring-batch-samples/src/main/resources/jobs/hibernateJob.xml index a8ad1b233..7c44c7e7c 100644 --- a/spring-batch-samples/src/main/resources/jobs/hibernateJob.xml +++ b/spring-batch-samples/src/main/resources/jobs/hibernateJob.xml @@ -10,29 +10,23 @@ http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> Example for Hibernate integration. - - - + + + - + - + - - - - - - + class="org.springframework.batch.execution.step.support.AlwaysSkipItemSkipPolicy" /> +