IN PROGRESS - issue BATCH-7: Remove transaction synchronization and state management from input/output sources (formerly buffering)

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

Remove ResourceLifecycle and make existing implementations into ItemStream
This commit is contained in:
dsyer
2008-01-31 00:44:50 +00:00
parent bebab9c8d2
commit 9d1ebb42c7
39 changed files with 416 additions and 449 deletions

View File

@@ -22,8 +22,8 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.io.exception.FlatFileParsingException;
import org.springframework.batch.io.file.mapping.DefaultFieldSet;
import org.springframework.batch.io.file.mapping.FieldSetMapper;
import org.springframework.batch.io.file.mapping.FieldSet;
import org.springframework.batch.io.file.mapping.FieldSetMapper;
import org.springframework.batch.io.file.separator.LineReader;
import org.springframework.batch.io.file.separator.RecordSeparatorPolicy;
import org.springframework.batch.io.file.separator.ResourceLineReader;
@@ -170,8 +170,10 @@ public class SimpleFlatFileItemReader extends AbstractItemReader implements Item
}
/**
* Close and null out the delegate line reader.
* Close and null out the reader.
* @throws Exception
*
* @see ResourceLifecycle
*/
public void close() throws Exception {
try {

View File

@@ -20,7 +20,6 @@ import java.util.Set;
import org.hibernate.SessionFactory;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.stream.ItemStreamAdapter;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.batch.repeat.RepeatContext;
import org.springframework.batch.repeat.RepeatInterceptor;
@@ -45,7 +44,7 @@ import org.springframework.util.Assert;
* @author Dave Syer
*
*/
public class HibernateAwareItemWriter extends ItemStreamAdapter implements ItemWriter, RepeatInterceptor,
public class HibernateAwareItemWriter implements ItemWriter, RepeatInterceptor,
InitializingBean {
/**

View File

@@ -42,7 +42,7 @@ import org.springframework.util.Assert;
*
* @author Robert Kasanicky
*/
public class StaxEventItemReader extends AbstractItemReader implements ItemReader,
public class StaxEventItemReader extends AbstractItemReader implements ItemReader,
Skippable, ItemStream, StatisticsProvider, InitializingBean, DisposableBean {
public static final String READ_COUNT_STATISTICS_NAME = "StaxEventReaderItemReader.readCount";

View File

@@ -38,7 +38,7 @@ import org.springframework.batch.item.reader.AbstractItemReader;
* @author Lucas Ward
* @since 1.0
*/
public interface ItemReader extends ItemStream {
public interface ItemReader {
/**
* Reads a piece of input data and advance to the next one. Implementations
@@ -51,4 +51,12 @@ public interface ItemReader extends ItemStream {
*/
Object read() throws Exception;
/**
* Close the reader, freeing any resources that may have been allocated
* since the first call to read().
*
* TODO: this is only used in sandbox?
*
*/
void close() throws Exception;
}

View File

@@ -35,16 +35,6 @@ package org.springframework.batch.item;
*
*/
public interface ItemStream {
void open() throws Exception;
/**
* Close the reader, freeing any resources that may have been allocated
* since the first call to read().
*
* @throws Exception if an underlying resource is unavailable
*/
void close() throws Exception;
/**
* Get {@link StreamContext} representing this object's current state.
@@ -60,4 +50,16 @@ public interface ItemStream {
* @param data
*/
void restoreFrom(StreamContext data);
/**
* If any resources are needed for the stream to operate they need to be
* initialised here.
*/
void open() throws Exception;
/**
* If any resources are needed for the stream to operate they need to be
* destroyed here.
*/
void close() throws Exception;
}

View File

@@ -25,7 +25,7 @@ package org.springframework.batch.item;
* @author Dave Syer
* @author Lucas Ward
*/
public interface ItemWriter extends ItemStream {
public interface ItemWriter {
/**
* Process the supplied data element. Will be called multiple times during a
@@ -38,4 +38,10 @@ public interface ItemWriter extends ItemStream {
*/
public void write(Object item) throws Exception;
/**
* Close the writer, allowing all allocated resources to be cleaned up.
*
* @throws Exception
*/
void close() throws Exception;
}

View File

@@ -17,13 +17,19 @@
package org.springframework.batch.item.reader;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.stream.ItemStreamAdapter;
/**
* Base class for {@link ItemReader} implementations.
* @author Dave Syer
*
*/
public abstract class AbstractItemReader extends ItemStreamAdapter implements ItemReader {
public abstract class AbstractItemReader implements ItemReader {
/**
* Do nothing.
* @see org.springframework.batch.item.ItemReader#close()
*/
public void close() throws Exception {
}
}

View File

@@ -30,7 +30,7 @@ import org.springframework.util.Assert;
*
* @author Dave Syer
*/
public class DelegatingItemReader extends AbstractItemReader implements ItemStream, Skippable, InitializingBean{
public class DelegatingItemReader extends AbstractItemReader implements Skippable, InitializingBean, ItemStream {
private ItemReader inputSource;
@@ -87,4 +87,22 @@ public class DelegatingItemReader extends AbstractItemReader implements ItemStre
((Skippable)inputSource).skip();
}
}
/* (non-Javadoc)
* @see org.springframework.batch.item.ItemStream#open()
*/
public void open() throws Exception {
if (inputSource instanceof ItemStream) {
((ItemStream) inputSource).open();
}
}
/* (non-Javadoc)
* @see org.springframework.batch.item.ItemStream#open()
*/
public void close() throws Exception {
if (inputSource instanceof ItemStream) {
((ItemStream) inputSource).close();
}
}
}

View File

@@ -16,11 +16,7 @@
package org.springframework.batch.item.reader;
import java.util.Properties;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.StreamContext;
import org.springframework.batch.item.stream.GenericStreamContext;
import org.springframework.batch.support.AbstractMethodInvokingDelegator;
/**
@@ -39,33 +35,10 @@ public class ItemReaderAdapter extends AbstractMethodInvokingDelegator implement
/**
* Do nothing.
* @see org.springframework.batch.item.ItemStream#open()
*/
public void open() throws Exception {
// no-op
}
/**
* Do nothing.
* @see org.springframework.batch.item.ItemStream#close()
*
* @see org.springframework.batch.item.ItemReader#close()
*/
public void close() throws Exception {
// no-op
}
/**
* Return empty {@link StreamContext}.
* @see org.springframework.batch.item.ItemStream#getRestartData()
*/
public StreamContext getRestartData() {
return new GenericStreamContext(new Properties());
}
/**
* Do nothing.
* @see org.springframework.batch.item.ItemStream#restoreFrom(org.springframework.batch.item.StreamContext)
*/
public void restoreFrom(StreamContext data) {
}

View File

@@ -0,0 +1,158 @@
/*
* 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.item.stream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemStream;
import org.springframework.batch.item.StreamContext;
/**
* @author Dave Syer
*
*/
public class CompositeItemStream implements ItemStream {
private static final String SEPARATOR = "#";
private List delegates;
public void setDelegates(List delegates) {
this.delegates = delegates;
}
/**
* Compound restart data of all injected (Restartable) ItemProcessors,
* property keys are prefixed with list index of the ItemProcessor.
*/
public StreamContext getRestartData() {
Properties props = createCompoundProperties(new PropertiesExtractor() {
public Properties extractProperties(Object o) {
if (o instanceof ItemStream) {
return ((ItemStream) o).getRestartData().getProperties();
}
else {
return null;
}
}
});
return new GenericStreamContext(props);
}
/**
* @param data contains values of restart data, property keys are expected
* to be prefixed with list index of the ItemProcessor.
*/
public void restoreFrom(StreamContext data) {
if (data == null || data.getProperties() == null) {
// do nothing
return;
}
List restartDataList = parseProperties(data.getProperties());
// iterators would make the loop below less readable
for (int i = 0; i < delegates.size(); i++) {
if (delegates.get(i) instanceof ItemStream) {
((ItemStream) delegates.get(i)).restoreFrom((StreamContext) restartDataList.get(i));
}
}
}
/**
* Parses compound properties into a list of RestartData.
*/
private List parseProperties(Properties props) {
List restartDataList = new ArrayList(delegates.size());
for (int i = 0; i < delegates.size(); i++) {
restartDataList.add(new GenericStreamContext(new Properties()));
}
for (Iterator iterator = props.entrySet().iterator(); iterator.hasNext();) {
Map.Entry entry = (Map.Entry) iterator.next();
String key = (String) entry.getKey();
String value = (String) entry.getValue();
int separatorIndex = key.indexOf(SEPARATOR);
int i = Integer.valueOf(key.substring(0, separatorIndex)).intValue();
((StreamContext) restartDataList.get(i)).getProperties().setProperty(key.substring(separatorIndex + 1),
value);
}
return restartDataList;
}
/**
* @param extractor used to extract Properties from {@link ItemReader}s
* @return compound Properties containing all the Properties from injected
* delegates with property keys prefixed by list index.
*/
private Properties createCompoundProperties(PropertiesExtractor extractor) {
Properties stats = new Properties();
int index = 0;
for (Iterator iterator = delegates.listIterator(); iterator.hasNext();) {
Properties writerStats = extractor.extractProperties(iterator.next());
if (writerStats != null) {
for (Iterator iterator2 = writerStats.entrySet().iterator(); iterator2.hasNext();) {
Map.Entry entry = (Map.Entry) iterator2.next();
stats.setProperty("" + index + SEPARATOR + entry.getKey(), (String) entry.getValue());
}
}
index++;
}
return stats;
}
/**
* Extracts information from given object in the form of {@link Properties}.
* If the information is not available (e.g. unexpected object class) return
* null.
*/
private interface PropertiesExtractor {
Properties extractProperties(Object o);
}
public void close() throws Exception {
for (Iterator iterator = delegates.listIterator(); iterator.hasNext();) {
Object delegate = iterator.next();
if (delegate instanceof ItemStream) {
((ItemStream) delegate).close();
}
}
}
public void open() throws Exception {
for (Iterator iterator = delegates.listIterator(); iterator.hasNext();) {
Object delegate = iterator.next();
if (delegate instanceof ItemStream) {
((ItemStream) delegate).open();
}
}
}
/**
* Public getter for the list of delegates.
* @return the delegates
*/
public List getDelegates() {
return delegates;
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2006-2008 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.item.writer;
import org.springframework.batch.item.ItemWriter;
/**
* Abstract {@link ItemWriter} that allows for base classes to only
* implement the close method if they need it.
*
* @author Lucas Ward
*
*/
public abstract class AbstractItemWriter implements ItemWriter{
public void close() throws Exception {
}
}

View File

@@ -1,131 +1,24 @@
package org.springframework.batch.item.writer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemStream;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.StreamContext;
import org.springframework.batch.item.stream.GenericStreamContext;
import org.springframework.batch.item.stream.ItemStreamAdapter;
import org.springframework.batch.item.stream.CompositeItemStream;
/**
* Runs a collection of ItemProcessors in fixed-order sequence.
*
* @author Robert Kasanicky
*/
public class CompositeItemWriter extends ItemStreamAdapter implements ItemWriter, ItemStream {
private static final String SEPARATOR = "#";
private List delegates;
public class CompositeItemWriter extends CompositeItemStream implements ItemWriter {
/**
* Calls injected ItemProcessors in order.
*/
public void write(Object data) throws Exception {
for (Iterator iterator = delegates.listIterator(); iterator.hasNext();) {
for (Iterator iterator = getDelegates().listIterator(); iterator.hasNext();) {
((ItemWriter) iterator.next()).write(data);
}
}
/**
* Compound restart data of all injected (Restartable) ItemProcessors,
* property keys are prefixed with list index of the ItemProcessor.
*/
public StreamContext getRestartData() {
Properties props = createCompoundProperties(new PropertiesExtractor() {
public Properties extractProperties(ItemStream o) {
return o.getRestartData().getProperties();
}
});
return new GenericStreamContext(props);
}
/**
* @param data contains values of restart data, property keys are expected
* to be prefixed with list index of the ItemProcessor.
*/
public void restoreFrom(StreamContext data) {
if (data == null || data.getProperties() == null) {
// do nothing
return;
}
List restartDataList = parseProperties(data.getProperties());
// iterators would make the loop below less readable
for (int i = 0; i < delegates.size(); i++) {
if (delegates.get(i) instanceof ItemStream) {
((ItemStream) delegates.get(i)).restoreFrom((StreamContext) restartDataList.get(i));
}
}
}
public void setItemWriters(List itemProcessors) {
this.delegates = itemProcessors;
}
/**
* Parses compound properties into a list of RestartData.
*/
private List parseProperties(Properties props) {
List restartDataList = new ArrayList(delegates.size());
for (int i = 0; i < delegates.size(); i++) {
restartDataList.add(new GenericStreamContext(new Properties()));
}
for (Iterator iterator = props.entrySet().iterator(); iterator.hasNext();) {
Map.Entry entry = (Map.Entry) iterator.next();
String key = (String) entry.getKey();
String value = (String) entry.getValue();
int separatorIndex = key.indexOf(SEPARATOR);
int i = Integer.valueOf(key.substring(0, separatorIndex)).intValue();
((StreamContext) restartDataList.get(i)).getProperties()
.setProperty(key.substring(separatorIndex + 1), value);
}
return restartDataList;
}
/**
* @param extractor used to extract Properties from {@link ItemReader}s
* @return compound Properties containing all the Properties from injected
* {@link ItemWriter}s with property keys prefixed by list index.
*/
private Properties createCompoundProperties(PropertiesExtractor extractor) {
Properties stats = new Properties();
int index = 0;
for (Iterator iterator = delegates.listIterator(); iterator.hasNext();) {
Properties writerStats = extractor.extractProperties((ItemStream) iterator.next());
if (writerStats != null) {
for (Iterator iterator2 = writerStats.entrySet().iterator(); iterator2.hasNext();) {
Map.Entry entry = (Map.Entry) iterator2.next();
stats.setProperty("" + index + SEPARATOR + entry.getKey(), (String) entry.getValue());
}
}
index++;
}
return stats;
}
/**
* Extracts information from given object in the form of {@link Properties}.
* If the information is not available (e.g. unexpected object class) return
* null.
*/
private interface PropertiesExtractor {
Properties extractProperties(ItemStream o);
}
public void close() throws Exception {
for (Iterator iterator = delegates.listIterator(); iterator.hasNext();) {
((ItemWriter) iterator.next()).close();
}
}
}

View File

@@ -7,7 +7,6 @@ import org.springframework.batch.item.ItemStream;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.StreamContext;
import org.springframework.batch.item.stream.GenericStreamContext;
import org.springframework.batch.item.stream.ItemStreamAdapter;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
@@ -18,7 +17,7 @@ import org.springframework.util.Assert;
* @author Dave Syer
* @author Robert Kasanicky
*/
public class DelegatingItemWriter extends ItemStreamAdapter implements ItemWriter, Skippable, InitializingBean {
public class DelegatingItemWriter implements ItemWriter, Skippable, InitializingBean {
private ItemWriter writer;
@@ -88,6 +87,7 @@ public class DelegatingItemWriter extends ItemStreamAdapter implements ItemWrite
Assert.notNull(writer);
}
// YODO: remove
public void close() throws Exception {
writer.close();
}

View File

@@ -16,11 +16,7 @@
package org.springframework.batch.item.writer;
import java.util.Properties;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.StreamContext;
import org.springframework.batch.item.stream.GenericStreamContext;
import org.springframework.batch.support.AbstractMethodInvokingDelegator;
@@ -38,35 +34,13 @@ public class ItemWriterAdapter extends AbstractMethodInvokingDelegator implement
invokeDelegateMethodWithArgument(item);
}
/**
* Do nothing.
* @see org.springframework.batch.item.ItemStream#open()
*/
public void open() throws Exception {
// no-op
}
/**
* Do nothing.
* @see org.springframework.batch.item.ItemStream#close()
*/
/*
* No-op, can't call more than one method.
*
* (non-Javadoc)
* @see org.springframework.batch.item.ItemWriter#close()
*/
public void close() throws Exception {
// no-op
}
/**
* Return empty {@link StreamContext}.
* @see org.springframework.batch.item.ItemStream#getRestartData()
*/
public StreamContext getRestartData() {
return new GenericStreamContext(new Properties());
}
/**
* Do nothing.
* @see org.springframework.batch.item.ItemStream#restoreFrom(org.springframework.batch.item.StreamContext)
*/
public void restoreFrom(StreamContext data) {
}

View File

@@ -16,11 +16,7 @@
package org.springframework.batch.item.writer;
import java.util.Properties;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.StreamContext;
import org.springframework.batch.item.stream.GenericStreamContext;
import org.springframework.batch.support.AbstractMethodInvokingDelegator;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
@@ -70,35 +66,7 @@ public class PropertyExtractingDelegatingItemWriter extends AbstractMethodInvoki
this.fieldsUsedAsTargetMethodArguments = fieldsUsedAsMethodArguments;
}
/**
* Do nothing.
* @see org.springframework.batch.item.ItemStream#open()
*/
public void open() throws Exception {
// no-op
}
/**
* Do nothing.
* @see org.springframework.batch.item.ItemStream#close()
*/
public void close() throws Exception {
// no-op
}
/**
* Return empty {@link StreamContext}.
* @see org.springframework.batch.item.ItemStream#getRestartData()
*/
public StreamContext getRestartData() {
return new GenericStreamContext(new Properties());
}
/**
* Do nothing.
* @see org.springframework.batch.item.ItemStream#restoreFrom(org.springframework.batch.item.StreamContext)
*/
public void restoreFrom(StreamContext data) {
public void close() throws Exception {
}
}

View File

@@ -8,7 +8,7 @@ import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jdbc.core.JdbcTemplate;
class FooItemReader extends AbstractItemReader implements ItemReader, ItemStream, DisposableBean, InitializingBean{
class FooItemReader extends AbstractItemReader implements ItemStream, ItemReader, DisposableBean, InitializingBean{
DrivingQueryItemReader inputSource;
FooDao fooDao = new SingleKeyFooDao();
@@ -44,5 +44,8 @@ class FooItemReader extends AbstractItemReader implements ItemReader, ItemStream
}
public void afterPropertiesSet() throws Exception {
}
public void open() throws Exception {
};
}

View File

@@ -22,7 +22,7 @@ import java.util.Map;
import junit.framework.TestCase;
import org.springframework.batch.item.writer.AbstractItemWriter;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.batch.repeat.RepeatContext;
import org.springframework.batch.repeat.RepeatInterceptor;
@@ -41,13 +41,12 @@ public class HibernateAwareItemWriterTests extends TestCase {
public void flush() throws DataAccessException {
list.add("flush");
}
public void clear() {
list.add("clear");
list.add("clear");
};
}
private class StubItemWriter extends AbstractItemWriter implements RepeatInterceptor {
private class StubItemWriter implements ItemWriter, RepeatInterceptor {
public void write(Object item) {
list.add(item);
}
@@ -71,10 +70,13 @@ public class HibernateAwareItemWriterTests extends TestCase {
public void open(RepeatContext context) {
list.add(context);
}
public void close() throws Exception {
}
}
HibernateAwareItemWriter writer = new HibernateAwareItemWriter();
final List list = new ArrayList();
private RepeatContextSupport context;
@@ -91,16 +93,15 @@ public class HibernateAwareItemWriterTests extends TestCase {
writer.setHibernateTemplate(new HibernateTemplateWrapper());
list.clear();
}
/*
* (non-Javadoc)
/* (non-Javadoc)
* @see junit.framework.TestCase#tearDown()
*/
protected void tearDown() throws Exception {
Map map = TransactionSynchronizationManager.getResourceMap();
for (Iterator iterator = map.keySet().iterator(); iterator.hasNext();) {
String key = (String) iterator.next();
TransactionSynchronizationManager.unbindResource(key);
TransactionSynchronizationManager.unbindResource(key);
}
}
@@ -115,10 +116,10 @@ public class HibernateAwareItemWriterTests extends TestCase {
try {
writer.afterPropertiesSet();
fail("Expected IllegalArgumentException");
}
catch (IllegalArgumentException e) {
} catch (IllegalArgumentException e) {
// expected
assertTrue("Wrong message for exception: " + e.getMessage(), e.getMessage().indexOf("delegate") >= 0);
assertTrue("Wrong message for exception: " + e.getMessage(), e
.getMessage().indexOf("delegate") >= 0);
}
}
@@ -135,7 +136,7 @@ public class HibernateAwareItemWriterTests extends TestCase {
/**
* Test method for
* {@link org.springframework.batch.io.support.HibernateAwareItemWriter#write(java.lang.Object)}.
* @throws Exception
* @throws Exception
*/
public void testWrite() throws Exception {
writer.write("foo");
@@ -157,8 +158,7 @@ public class HibernateAwareItemWriterTests extends TestCase {
try {
writer.close(context);
fail("Expected RuntimeException");
}
catch (RuntimeException e) {
} catch (RuntimeException e) {
assertEquals("bar", e.getMessage());
}
assertEquals(2, list.size());
@@ -169,7 +169,7 @@ public class HibernateAwareItemWriterTests extends TestCase {
/**
* Test method for
* {@link org.springframework.batch.io.support.HibernateAwareItemWriter#write(java.lang.Object)}.
* @throws Exception
* @throws Exception
*/
public void testWriteAndCloseWithFailure() throws Exception {
final RuntimeException ex = new RuntimeException("bar");
@@ -182,8 +182,7 @@ public class HibernateAwareItemWriterTests extends TestCase {
try {
writer.close(context);
fail("Expected RuntimeException");
}
catch (RuntimeException e) {
} catch (RuntimeException e) {
assertEquals("bar", e.getMessage());
}
assertEquals(3, list.size());

View File

@@ -8,10 +8,11 @@ import java.util.Properties;
import junit.framework.TestCase;
import org.easymock.MockControl;
import org.springframework.batch.item.ItemStream;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.StreamContext;
import org.springframework.batch.item.stream.GenericStreamContext;
import org.springframework.batch.item.stream.ItemStreamAdapter;
import org.springframework.batch.item.writer.CompositeItemWriter;
import org.springframework.batch.statistics.StatisticsProvider;
/**
@@ -48,7 +49,7 @@ public class CompositeItemWriterTests extends TestCase {
controls.add(control);
}
itemProcessor.setItemWriters(processors);
itemProcessor.setDelegates(processors);
itemProcessor.write(data);
for (Iterator iterator = controls.iterator(); iterator.hasNext();) {
@@ -61,22 +62,27 @@ public class CompositeItemWriterTests extends TestCase {
* All Restartable processors should be restarted, not-Restartable processors should be ignored.
*/
public void testRestart() {
final ItemWriter p2 = new ItemWriterStub();
final ItemWriter p3 = new ItemWriterStub();
//this mock with undefined behaviour makes sure not-Restartable processor is ignored
MockControl p1c = MockControl.createStrictControl(ItemWriter.class);
final ItemWriter p1 = (ItemWriter) p1c.getMock();
final ItemWriter p2 = new StubItemWriter();
final ItemWriter p3 = new StubItemWriter();
List itemProcessors = new ArrayList(){{
add(p1);
add(p2);
add(p3);
}};
itemProcessor.setItemWriters(itemProcessors);
itemProcessor.setDelegates(itemProcessors);
StreamContext rd = itemProcessor.getRestartData();
itemProcessor.restoreFrom(rd);
for (Iterator iterator = itemProcessors.iterator(); iterator.hasNext();) {
ItemWriter processor = (ItemWriter) iterator.next();
if (processor instanceof ItemWriterStub) {
if (processor instanceof StubItemWriter) {
assertTrue("Injected processors are restarted",
((ItemWriterStub)processor).restarted);
((StubItemWriter)processor).restarted);
}
}
@@ -86,35 +92,34 @@ public class CompositeItemWriterTests extends TestCase {
final int NUMBER_OF_PROCESSORS = 10;
List controls = new ArrayList(NUMBER_OF_PROCESSORS);
List processors = new ArrayList(NUMBER_OF_PROCESSORS);
final List list = new ArrayList(NUMBER_OF_PROCESSORS);
for (int i = 0; i < NUMBER_OF_PROCESSORS; i++) {
MockControl control = MockControl.createStrictControl(ItemWriter.class);
ItemWriter processor = (ItemWriter) control.getMock();
ItemWriter processor = new AbstractItemWriter() {
public void write(Object item) throws Exception {
throw new IllegalStateException("No way!");
}
public void close() throws Exception {
list.add(this);
}
};
processor.close();
control.setVoidCallable();
control.replay();
processors.add(processor);
controls.add(control);
}
itemProcessor.setItemWriters(processors);
itemProcessor.setDelegates(processors);
itemProcessor.close();
for (Iterator iterator = controls.iterator(); iterator.hasNext();) {
MockControl control = (MockControl) iterator.next();
control.verify();
}
assertEquals(NUMBER_OF_PROCESSORS, list.size());
}
/**
* Stub for testing restart. Checks the restart data received is the same that was returned by
* <code>getRestartData()</code>
*/
private static class ItemWriterStub extends ItemStreamAdapter implements ItemWriter, StatisticsProvider {
private static class StubItemWriter implements ItemWriter, ItemStream, StatisticsProvider {
private static final String RESTART_KEY = "restartData";
private static final String STATS_KEY = "stats";
@@ -149,9 +154,10 @@ public class CompositeItemWriterTests extends TestCase {
}
public void close() throws Exception {
}
public void open() throws Exception {
}
}
}

View File

@@ -19,9 +19,9 @@ package org.springframework.batch.repeat.support;
import java.util.HashSet;
import java.util.Set;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.batch.repeat.RepeatCallback;
import org.springframework.batch.repeat.RepeatContext;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.batch.repeat.callback.ItemReaderRepeatCallback;
import org.springframework.core.task.SimpleAsyncTaskExecutor;