OPEN - issue BATCH-378: RepeatListener is confusing and too generic to use for 'intercepting' a step

http://jira.springframework.org/browse/BATCH-378

Fix hibernate aware item writer - no need for listener interface now that ItemWRiter has flush/clear
This commit is contained in:
dsyer
2008-02-29 16:15:52 +00:00
parent b1ae2efaae
commit e5f4413101
10 changed files with 453 additions and 217 deletions

View File

@@ -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}.<br/>
*
* 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}.<br/>
*
* 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);
}
}

View File

@@ -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}.<br/>
*
* 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);
}
}

View File

@@ -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) {

View File

@@ -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);
}
}
/**

View File

@@ -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();
}
}

View File

@@ -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);
}
}
}

View File

@@ -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());
}
}

View File

@@ -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());
}
}

View File

@@ -34,7 +34,4 @@ public class CustomerCreditIncreaseWriter extends AbstractItemWriter {
customerCreditDao.writeCredit(customerCredit);
}
public void close() throws Exception {
}
}

View File

@@ -10,29 +10,23 @@
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<description>Example for Hibernate integration.</description>
<import resource="../simple-container-definition.xml" />
<import resource="../simple-container-definition.xml" />
<bean id="hibernateJob" parent="simpleJob">
<!-- set restartable=false so that this job can be used by more than one test -->
<property name="restartable" value="false" />
<property name="steps">
<bean id="step1" parent="simpleStep">
<property name="itemReader" ref="hibernateItemReader" />
<property name="itemReader" ref="hibernateItemReader" />
<property name="itemWriter" ref="hibernateOutputSource" />
<property name="commitInterval" value="3" />
<property name="itemSkipPolicy">
<bean class="org.springframework.batch.execution.step.support.AlwaysSkipItemSkipPolicy"/>
</property>
<property name="chunkOperations">
<bean
class="org.springframework.batch.repeat.support.RepeatTemplate">
<property name="listeners"
ref="hibernateOutputSource" />
</bean>
class="org.springframework.batch.execution.step.support.AlwaysSkipItemSkipPolicy" />
</property>
</bean>
</property>
</bean>