RESOLVED - issue BATCH-205: support for processing chain

http://opensource.atlassian.com/projects/spring/browse/BATCH-205

Patch applied.
This commit is contained in:
dsyer
2007-11-20 11:04:23 +00:00
parent 33371c51cc
commit 2b175e21e7
7 changed files with 291 additions and 6 deletions

View File

@@ -0,0 +1,42 @@
package org.springframework.batch.item.processor;
import java.util.Iterator;
import java.util.List;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
/**
* Composite {@link ItemTransformer} that passes the item through a sequence
* of injected <code>ItemTransformer</code>s (return value of previous transformation
* is the entry value of the next).
*
* @author Robert Kasanicky
*/
public class CompositeItemTransformer implements ItemTransformer, InitializingBean {
private List itemTransformers;
public Object transform(Object item) {
Object result = item;
for (Iterator iterator = itemTransformers.listIterator(); iterator.hasNext();) {
result = ((ItemTransformer)iterator.next()).transform(result);
}
return result;
}
public void afterPropertiesSet() throws Exception {
Assert.notEmpty(itemTransformers);
for (Iterator iterator = itemTransformers.iterator(); iterator.hasNext();) {
Assert.isInstanceOf(ItemTransformer.class, iterator.next());
}
}
/**
* @param itemTransformers will be chained to produce a composite transformation.
*/
public void setItemTransformers(List itemTransformers) {
this.itemTransformers = itemTransformers;
}
}

View File

@@ -0,0 +1,11 @@
package org.springframework.batch.item.processor;
/**
* Interface for item transformations during processing phase.
*
* @author Robert Kasanicky
*/
public interface ItemTransformer {
Object transform(Object item);
}

View File

@@ -9,6 +9,7 @@ import org.springframework.batch.restart.GenericRestartData;
import org.springframework.batch.restart.RestartData;
import org.springframework.batch.restart.Restartable;
import org.springframework.batch.statistics.StatisticsProvider;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
/**
@@ -16,23 +17,33 @@ import org.springframework.util.Assert;
* {@link StatisticsProvider} where the {@link OutputSource} does.
*
* @author Dave Syer
* @author Robert Kasanicky
*/
public class OutputSourceItemProcessor implements ItemProcessor, Restartable, Skippable,
StatisticsProvider {
StatisticsProvider, InitializingBean {
private OutputSource source;
/* (non-Javadoc)
/**
* Calls {@link #doProcess(Object)} and then writes the result to the output source.
*
* @see org.springframework.batch.item.ItemProcessor#process(java.lang.Object)
*/
public void process(Object data) throws Exception {
source.write(data);
final public void process(Object item) throws Exception {
Object result = doProcess(item);
source.write(result);
}
/**
* By default returns the argument. This method is an extension point
* meant to be overridden by subclasses that implement processing logic.
*/
protected Object doProcess(Object item) {
return item;
}
/**
* Setter for output source.
*
* @param source
*/
public void setOutputSource(OutputSource source) {
this.source = source;
@@ -85,4 +96,9 @@ public class OutputSourceItemProcessor implements ItemProcessor, Restartable, Sk
}
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(source);
}
}

View File

@@ -0,0 +1,35 @@
package org.springframework.batch.item.processor;
import org.springframework.batch.io.OutputSource;
import org.springframework.util.Assert;
/**
* Transforms the item using injected {@link ItemTransformer}
* before it is written to output by {@link OutputSource}.
*
* @author Robert Kasanicky
*/
public class TransformerOutputSourceItemProcessor extends OutputSourceItemProcessor {
private ItemTransformer itemTransformer;
/**
* Transform the item using the {@link #itemTransformer}.
*/
protected Object doProcess(Object item) {
return itemTransformer.transform(item);
}
/**
* @param itemTransformer will transform the item before
* it is passed to {@link OutputSource}.
*/
public void setItemTransformer(ItemTransformer itemTransformer) {
this.itemTransformer = itemTransformer;
}
public void afterPropertiesSet() throws Exception {
super.afterPropertiesSet();
Assert.notNull(itemTransformer);
}
}

View File

@@ -0,0 +1,95 @@
package org.springframework.batch.item.processor;
import java.util.ArrayList;
import junit.framework.TestCase;
import org.easymock.MockControl;
/**
* Tests for {@link CompositeItemTransformer}.
*
* @author Robert Kasanicky
*/
public class CompositeItemTransformerTests extends TestCase {
private CompositeItemTransformer composite = new CompositeItemTransformer();
private ItemTransformer transformer1;
private ItemTransformer transformer2;
private MockControl tControl1 = MockControl.createControl(ItemTransformer.class);
private MockControl tControl2 = MockControl.createControl(ItemTransformer.class);
protected void setUp() throws Exception {
transformer1 = (ItemTransformer) tControl1.getMock();
transformer2 = (ItemTransformer) tControl2 .getMock();
composite.setItemTransformers(new ArrayList() {{
add(transformer1); add(transformer2);
}});
composite.afterPropertiesSet();
}
/**
* Regular usage scenario - item is passed through the processing chain,
* return value of the of the last transformation is returned by the composite.
*/
public void testTransform() throws Exception {
Object item = new Object();
Object itemAfterFirstTransfromation = new Object();
Object itemAfterSecondTransformation = new Object();
transformer1.transform(item);
tControl1.setReturnValue(itemAfterFirstTransfromation);
transformer2.transform(itemAfterFirstTransfromation);
tControl2.setReturnValue(itemAfterSecondTransformation);
tControl1.replay();
tControl2.replay();
assertSame(itemAfterSecondTransformation, composite.transform(item));
tControl1.verify();
tControl2.verify();
}
/**
* The list of transformers must not be null or empty and
* can contain only instances of {@link ItemTransformer}.
*/
public void testAfterPropertiesSet() throws Exception {
// value not set
composite.setItemTransformers(null);
try {
composite.afterPropertiesSet();
fail();
}
catch (IllegalArgumentException e) {
// expected
}
// empty list
composite.setItemTransformers(new ArrayList());
try {
composite.afterPropertiesSet();
fail();
}
catch (IllegalArgumentException e) {
// expected
}
// invalid list member
composite.setItemTransformers(new ArrayList() {{ add(new Object()); }});
try {
composite.afterPropertiesSet();
fail();
}
catch (IllegalArgumentException e) {
// expected
}
}
}

View File

@@ -47,6 +47,7 @@ public class OutputSourceItemProcessorTests extends TestCase {
protected void setUp() throws Exception {
source = new MockOutputSource("test");
processor.setOutputSource(source);
processor.afterPropertiesSet();
}
public void testProcess() throws Exception {
@@ -126,6 +127,20 @@ public class OutputSourceItemProcessorTests extends TestCase {
assertEquals("after skip", list.get(0));
}
/**
* Output source property must be set.
*/
public void testAfterPropertiesSet() throws Exception {
processor.setOutputSource(null);
try {
processor.afterPropertiesSet();
fail();
}
catch (IllegalArgumentException e) {
// expected
}
}
private List list = new ArrayList();
/**

View File

@@ -0,0 +1,71 @@
package org.springframework.batch.item.processor;
import junit.framework.TestCase;
import org.easymock.MockControl;
import org.springframework.batch.io.OutputSource;
/**
* Tests for {@link TransformerOutputSourceItemProcessor}.
*
* @author Robert Kasanicky
*/
public class TransformerOutputSourceItemProcessorTests extends TestCase {
private TransformerOutputSourceItemProcessor processor = new TransformerOutputSourceItemProcessor();
private ItemTransformer transformer;
private OutputSource outputSource;
private MockControl tControl = MockControl.createControl(ItemTransformer.class);
private MockControl outControl = MockControl.createControl(OutputSource.class);
protected void setUp() throws Exception {
transformer = (ItemTransformer) tControl.getMock();
outputSource = (OutputSource) outControl.getMock();
processor.setItemTransformer(transformer);
processor.setOutputSource(outputSource);
processor.afterPropertiesSet();
}
/**
* Regular usage scenario - item is passed to transformer
* and the result of transformation is passed to output source.
*/
public void testProcess() throws Exception {
Object item = new Object();
Object itemAfterTransformation = new Object();
transformer.transform(item);
tControl.setReturnValue(itemAfterTransformation);
outputSource.write(itemAfterTransformation);
outControl.setVoidCallable();
tControl.replay();
outControl.replay();
processor.process(item);
tControl.verify();
outControl.verify();
}
/**
* Item transformer must be set.
*/
public void testAfterPropertiesSet() throws Exception {
// value not set
processor.setItemTransformer(null);
try {
processor.afterPropertiesSet();
fail();
}
catch (IllegalArgumentException e) {
// expected
}
}
}