RESOLVED - issue BATCH-165: delegating ItemProvider and ItemProcessor

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

Patch applied from JIRA.
This commit is contained in:
dsyer
2007-11-14 16:11:42 +00:00
parent 40d75b07e1
commit 2ca2d7a60c
18 changed files with 958 additions and 5 deletions

View File

@@ -6,6 +6,9 @@
<configs>
<config>src/test/resources/org/springframework/batch/io/file/support/mapping/bean-wrapper.xml</config>
<config>src/test/resources/org/springframework/batch/io/sql/data-source-context.xml</config>
<config>src/test/resources/org/springframework/batch/item/processor/delegating-item-processor.xml</config>
<config>src/test/resources/org/springframework/batch/item/processor/pe-delegating-item-processor.xml</config>
<config>src/test/resources/org/springframework/batch/item/provider/delegating-item-provider.xml</config>
<config>src/test/resources/org/springframework/batch/retry/aop/retry-transaction-test.xml</config>
</configs>
<configSets>

View File

@@ -0,0 +1,23 @@
package org.springframework.batch.io.exception;
import org.springframework.util.MethodInvoker;
/**
* Indicates an error has been encountered
* while trying to dynamically call a method e.g. using {@link MethodInvoker}.
*
* @author Robert Kasanicky
*/
public class DynamicMethodInvocationException extends RuntimeException {
//generated value
private static final long serialVersionUID = -6056786139731564040L;
public DynamicMethodInvocationException(Throwable cause){
super(cause);
}
public DynamicMethodInvocationException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,38 @@
/*
* 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.processor;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.support.AbstractDelegator;
/**
* Delegates item processing to a custom method -
* passes the item as an argument for the delegate method.
*
* @see PropertyExtractingDelegatingItemProcessor
*
* @author Robert Kasanicky
*/
public class DelegatingItemProcessor extends AbstractDelegator implements ItemProcessor {
public void process(Object item) throws Exception {
invokeDelegateMethodWithArgument(item);
}
}

View File

@@ -0,0 +1,68 @@
/*
* 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.processor;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.support.AbstractDelegator;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.util.Assert;
/**
* Delegates processing to a custom method - extracts property values
* from item object and uses them as arguments for the delegate method.
*
* @see DelegatingItemProcessor
*
* @author Robert Kasanicky
*/
public class PropertyExtractingDelegatingItemProcessor extends AbstractDelegator implements ItemProcessor {
private String[] fieldsUsedAsTargetMethodArguments;
/**
* Extracts values from item's fields named in fieldsUsedAsTargetMethodArguments
* and passes them as arguments to the delegate method.
*/
public void process(Object item) throws Exception {
// helper for extracting property values from a bean
BeanWrapper beanWrapper = new BeanWrapperImpl();
beanWrapper.setWrappedInstance(item);
Object[] methodArguments = new Object[fieldsUsedAsTargetMethodArguments.length];
for (int i = 0; i < fieldsUsedAsTargetMethodArguments.length; i++) {
methodArguments[i] = beanWrapper.getPropertyValue(fieldsUsedAsTargetMethodArguments[i]);
}
invokeDelegateMethodWithArguments(methodArguments);
}
public void afterPropertiesSet() throws Exception {
super.afterPropertiesSet();
Assert.notEmpty(fieldsUsedAsTargetMethodArguments);
}
/**
* @param fieldsUsedAsTargetMethodArguments the values of the these item's fields
* will be used as arguments for the delegate method. Nested property values are
* supported, e.g. <code>address.city</code>
*/
public void setFieldsUsedAsTargetMethodArguments(String[] fieldsUsedAsMethodArguments) {
this.fieldsUsedAsTargetMethodArguments = fieldsUsedAsMethodArguments;
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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.provider;
import org.springframework.batch.item.ItemProvider;
import org.springframework.batch.support.AbstractDelegator;
/**
* Invokes a custom method which provides an item.
*
* @author Robert Kasanicky
*/
public class DelegatingItemProvider extends AbstractDelegator implements ItemProvider {
/**
* @return return value of the target method.
*/
public Object next() throws Exception {
return invokeDelegateMethod();
}
//harmless implementation of method required by ItemProvider interface
public Object getKey(Object item) {
return item;
}
//harmless implementation of method required by ItemProvider interface
public boolean recover(Object data, Throwable cause) {
return false;
}
}

View File

@@ -0,0 +1,175 @@
/*
* 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.support;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.springframework.batch.io.exception.DynamicMethodInvocationException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import org.springframework.util.MethodInvoker;
/**
* Superclass for delegating classes which dynamically call a
* custom method of injected object.
* Provides convenient API for dynamic method invocation shielding
* subclasses from low-level details and exception handling.
*
* @author Robert Kasanicky
*/
public class AbstractDelegator implements InitializingBean {
private Object targetObject;
private String targetMethod;
private Object[] arguments;
/**
* Invoker the target method with no arguments.
* @return object returned by invoked method
* @throws DynamicMethodInvocationException if the {@link MethodInvoker} used throws exception
*/
protected Object invokeDelegateMethod() {
MethodInvoker invoker = createMethodInvoker(targetObject, targetMethod);
invoker.setArguments(arguments);
return doInvoke(invoker);
}
/**
* Invokes the target method with given argument.
* @param object argument for the target method
* @return object returned by target method
* @throws DynamicMethodInvocationException if the {@link MethodInvoker} used throws exception
*/
protected Object invokeDelegateMethodWithArgument(Object object) {
MethodInvoker invoker = createMethodInvoker(targetObject, targetMethod);
invoker.setArguments(new Object[]{object});
return doInvoke(invoker);
}
/**
* Invokes the target method with given arguments.
* @param args arguments for the invoked method
* @return object returned by invoked method
* @throws DynamicMethodInvocationException if the {@link MethodInvoker} used throws exception
*/
protected Object invokeDelegateMethodWithArguments(Object[] args) {
MethodInvoker invoker = createMethodInvoker(targetObject, targetMethod);
invoker.setArguments(args);
return doInvoke(invoker);
}
/**
* Create a new configured instance of {@link MethodInvoker}.
*/
private MethodInvoker createMethodInvoker(Object targetObject, String targetMethod) {
MethodInvoker invoker = new MethodInvoker();
invoker.setTargetObject(targetObject);
invoker.setTargetMethod(targetMethod);
invoker.setArguments(arguments);
return invoker;
}
/**
* Prepare and invoke the invoker, rethrow checked exceptions as unchecked.
* @param invoker configured invoker
* @return return value of the invoked method
*/
private Object doInvoke(MethodInvoker invoker) {
try {
invoker.prepare();
}
catch (ClassNotFoundException e) {
throw new DynamicMethodInvocationException(e);
}
catch (NoSuchMethodException e) {
throw new DynamicMethodInvocationException(e);
}
try {
return invoker.invoke();
}
catch (InvocationTargetException e) {
throw new DynamicMethodInvocationException(e);
}
catch (IllegalAccessException e) {
throw new DynamicMethodInvocationException(e);
}
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(targetObject);
Assert.hasLength(targetMethod);
Assert.state(targetClassDeclaresTargetMethod(),
"target class must declare a method with name matching the target method");
}
/**
* @return true if target class declares a method matching target method name
* with given number of arguments of appropriate type.
*/
private boolean targetClassDeclaresTargetMethod() {
MethodInvoker invoker = createMethodInvoker(targetObject, targetMethod);
Method[] methods = invoker.getTargetClass().getDeclaredMethods();
String targetMethodName = invoker.getTargetMethod();
for (int i=0; i < methods.length; i++) {
if (methods[i].getName().equals(targetMethodName)) {
Class[] params = methods[i].getParameterTypes();
if (arguments == null) {
return true;
} else if (arguments.length == params.length) {
boolean argumentsMatchParameters = true;
for (int j = 0; j < params.length; j++) {
if (!(params[j].isAssignableFrom(arguments[j].getClass()))) {
argumentsMatchParameters = false;
}
}
if (argumentsMatchParameters) return true;
}
}
}
return false;
}
/**
* @param targetObject the delegate - bean id can be used to set this value in Spring configuration
*/
public void setTargetObject(Object targetObject) {
this.targetObject = targetObject;
}
/**
* @param targetMethod name of the method to be invoked on {@link #targetObject}.
*/
public void setTargetMethod(String targetMethod) {
this.targetMethod = targetMethod;
}
/**
* @param arguments arguments values for the {{@link #targetMethod}.
* These are not expected to change during the lifetime of the delegator
* and will be used only when the subclass tries to invoke the target method
* without providing explicit argument values.
*/
public void setArguments(Object[] arguments) {
this.arguments = arguments;
}
}

View File

@@ -0,0 +1,53 @@
package org.springframework.batch.io.sample.domain;
import java.util.ArrayList;
import java.util.List;
/**
* Custom class that contains the logic of providing and processing {@link Foo}
* objects. It serves the purpose to show how providing/processing logic contained in a
* custom class can be reused by the framework.
*
* @author Robert Kasanicky
*/
public class FooService {
public static final int GENERATION_LIMIT = 10;
private int counter = 0;
private List generatedFoos = new ArrayList(GENERATION_LIMIT);
private List processedFoos = new ArrayList(GENERATION_LIMIT);
private List processedFooNameValuePairs = new ArrayList(GENERATION_LIMIT);
public Foo generateFoo() {
if (counter++ >= GENERATION_LIMIT) return null;
Foo foo = new Foo(counter, "foo" + counter, counter);
generatedFoos.add(foo);
return foo;
}
public void processFoo(Foo foo) {
processedFoos.add(foo);
}
public void processNameValuePair(String name, int value) {
processedFooNameValuePairs.add(new Foo(0, name, value));
}
public List getGeneratedFoos() {
return generatedFoos;
}
public List getProcessedFoos() {
return processedFoos;
}
public List getProcessedFooNameValuePairs() {
return processedFooNameValuePairs;
}
}

View File

@@ -0,0 +1,55 @@
package org.springframework.batch.item.processor;
import java.util.List;
import org.springframework.batch.io.sample.domain.Foo;
import org.springframework.batch.io.sample.domain.FooService;
import org.springframework.test.AbstractDependencyInjectionSpringContextTests;
/**
* Tests for {@link DelegatingItemProcessor}.
*
* @author Robert Kasanicky
*/
public class DelegatingItemProcessorIntegrationTests extends AbstractDependencyInjectionSpringContextTests {
private DelegatingItemProcessor processor;
private FooService fooService;
protected String getConfigPath() {
return "delegating-item-processor.xml";
}
/**
* Regular usage scenario - input object should be passed to
* the service the injected invoker points to.
*/
public void testProcess() throws Exception {
Foo foo;
while ((foo = fooService.generateFoo()) != null) {
processor.process(foo);
}
List input = fooService.getGeneratedFoos();
List processed = fooService.getProcessedFoos();
assertEquals(input.size(), processed.size());
assertFalse(fooService.getProcessedFoos().isEmpty());
for (int i = 0; i < input.size(); i++) {
assertSame(input.get(i), processed.get(i));
}
}
//setter for auto-injection
public void setProcessor(DelegatingItemProcessor processor) {
this.processor = processor;
}
//setter for auto-injection
public void setFooService(FooService fooService) {
this.fooService = fooService;
}
}

View File

@@ -0,0 +1,58 @@
package org.springframework.batch.item.processor;
import java.util.List;
import org.springframework.batch.io.sample.domain.Foo;
import org.springframework.batch.io.sample.domain.FooService;
import org.springframework.test.AbstractDependencyInjectionSpringContextTests;
/**
* Tests for {@link PropertyExtractingDelegatingItemProcessor}
*
* @author Robert Kasanicky
*/
public class PropertyExtractingDelegatingItemProccessorIntegrationTests
extends AbstractDependencyInjectionSpringContextTests {
private PropertyExtractingDelegatingItemProcessor processor;
private FooService fooService;
protected String getConfigPath() {
return "pe-delegating-item-processor.xml";
}
/**
* Regular usage scenario - input object should be passed to
* the service the injected invoker points to.
*/
public void testProcess() throws Exception {
Foo foo;
while ((foo = fooService.generateFoo()) != null) {
processor.process(foo);
}
List input = fooService.getGeneratedFoos();
List processed = fooService.getProcessedFooNameValuePairs();
assertEquals(input.size(), processed.size());
assertFalse(fooService.getProcessedFooNameValuePairs().isEmpty());
for (int i = 0; i < input.size(); i++) {
Foo inputFoo = (Foo) input.get(i);
Foo outputFoo = (Foo) processed.get(i);
assertEquals(inputFoo.getName(), outputFoo.getName());
assertEquals(inputFoo.getValue(), outputFoo.getValue());
assertEquals(0, outputFoo.getId());
}
}
public void setProcessor(PropertyExtractingDelegatingItemProcessor processor) {
this.processor = processor;
}
public void setFooService(FooService fooService) {
this.fooService = fooService;
}
}

View File

@@ -0,0 +1,69 @@
package org.springframework.batch.item.provider;
import java.util.ArrayList;
import java.util.List;
import org.springframework.batch.io.sample.domain.FooService;
import org.springframework.test.AbstractDependencyInjectionSpringContextTests;
/**
* Tests for {@link DelegatingItemProvider}.
*
* @author Robert Kasanicky
*/
public class DelegatingItemProviderIntegrationTests extends AbstractDependencyInjectionSpringContextTests {
private DelegatingItemProvider provider;
private FooService fooService;
protected String getConfigPath() {
return "delegating-item-provider.xml";
}
/**
* Regular usage scenario - items are retrieved from
* the service injected invoker points to.
*/
public void testNext() throws Exception {
List returnedItems = new ArrayList();
Object item;
while ((item = provider.next()) != null) {
returnedItems.add(item);
}
List input = fooService.getGeneratedFoos();
assertEquals(input.size(), returnedItems.size());
assertFalse(returnedItems.isEmpty());
for (int i = 0; i<input.size(); i++) {
assertSame(input.get(i), returnedItems.get(i));
}
}
/**
* getKey(..) is implemented trivially.
*/
public void testGetKey() {
Object item = new Object();
assertSame(item, provider.getKey(item));
}
/**
* Recover not supported.
*/
public void testRecover() {
assertFalse(provider.recover(null, null));
}
public void setProvider(DelegatingItemProvider provider) {
this.provider = provider;
}
public void setFooService(FooService fooService) {
this.fooService = fooService;
}
}

View File

@@ -0,0 +1,172 @@
package org.springframework.batch.support;
import junit.framework.TestCase;
import org.springframework.batch.io.exception.DynamicMethodInvocationException;
import org.springframework.batch.io.sample.domain.Foo;
import org.springframework.batch.io.sample.domain.FooService;
import org.springframework.util.Assert;
/**
* Tests for {@link AbstractDelegator}
*
* @author Robert Kasanicky
*/
public class AbstractDelegatorTests extends TestCase {
private static class ConcreteDelegator extends AbstractDelegator {
}
private AbstractDelegator delegator = new ConcreteDelegator();
private Foo foo = new Foo(0, "foo", 1);
protected void setUp() throws Exception {
delegator.setTargetObject(foo);
delegator.setArguments(null);
}
/**
* Regular use - calling methods directly and via delegator leads to same
* results
*/
public void testDelegation() throws Exception {
delegator.setTargetMethod("getName");
delegator.afterPropertiesSet();
assertEquals(foo.getName(), delegator.invokeDelegateMethod());
}
/**
* Regular use - calling methods directly and via delegator leads to same
* results
*/
public void testDelegationWithArgument() throws Exception {
delegator.setTargetMethod("setName");
final String NEW_FOO_NAME = "newFooName";
delegator.afterPropertiesSet();
delegator.invokeDelegateMethodWithArgument(NEW_FOO_NAME);
assertEquals(NEW_FOO_NAME, foo.getName());
// using the arguments setter should work equally well
foo.setName("foo");
Assert.state(!foo.getName().equals(NEW_FOO_NAME));
delegator.setArguments(new Object[] { NEW_FOO_NAME });
delegator.afterPropertiesSet();
delegator.invokeDelegateMethod();
assertEquals(NEW_FOO_NAME, foo.getName());
}
/**
* Regular use - calling methods directly and via delegator leads to same
* results
*/
public void testDelegationWithMultipleArguments() throws Exception {
FooService fooService = new FooService();
delegator.setTargetObject(fooService);
delegator.setTargetMethod("processNameValuePair");
delegator.afterPropertiesSet();
final String FOO_NAME = "fooName";
final int FOO_VALUE = 12345;
delegator.invokeDelegateMethodWithArguments(new Object[]{FOO_NAME, new Integer(FOO_VALUE)});
Foo foo = (Foo) fooService.getProcessedFooNameValuePairs().get(0);
assertEquals(FOO_NAME, foo.getName());
assertEquals(FOO_VALUE, foo.getValue());
}
/**
* Exception scenario - target method is not declared by target object.
*/
public void testInvalidMethodName() throws Exception {
delegator.setTargetMethod("not-existing-method-name");
try {
delegator.afterPropertiesSet();
fail();
}
catch (IllegalStateException e) {
// expected
}
try {
delegator.invokeDelegateMethod();
fail();
}
catch (DynamicMethodInvocationException e) {
// expected
}
}
/**
* Exception scenario - target method is called with invalid arguments.
*/
public void testInvalidArgumentsForExistingMethod() throws Exception {
delegator.setTargetMethod("setName");
delegator.afterPropertiesSet();
try {
delegator.invokeDelegateMethodWithArgument(new Object());
fail();
}
catch (DynamicMethodInvocationException e) {
// expected
}
}
/**
* Exception scenario - target method is called with incorrect number of
* arguments.
*/
public void testIncorrectArgumentCount() throws Exception {
delegator.setTargetMethod("setName");
delegator.afterPropertiesSet();
try {
// single argument expected but none provided
delegator.invokeDelegateMethod();
fail();
}
catch (DynamicMethodInvocationException e) {
// expected
}
try {
// single argument expected but two provided
delegator.invokeDelegateMethodWithArguments(new Object[]{"name", "anotherName"});
fail();
}
catch (DynamicMethodInvocationException e) {
// expected
}
}
/**
* Exception scenario - incorrect static arguments set.
*/
public void testIncorrectNumberOfStaticArguments() throws Exception {
delegator.setTargetMethod("setName");
// incorrect argument count
delegator.setArguments(new Object[]{"first", "second"});
try {
delegator.afterPropertiesSet();
fail();
}
catch (IllegalStateException e) {
// expected
}
// correct argument count, but invalid argument type
delegator.setArguments(new Object[]{new Object()});
try {
delegator.afterPropertiesSet();
fail();
}
catch (IllegalStateException e) {
// expected
}
}
}

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="processor" class="org.springframework.batch.item.processor.DelegatingItemProcessor">
<property name="targetObject" ref="fooService" />
<property name="targetMethod" value="processFoo" />
</bean>
<bean id="fooService" class="org.springframework.batch.io.sample.domain.FooService" />
</beans>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="processor" class="org.springframework.batch.item.processor.PropertyExtractingDelegatingItemProcessor">
<property name="targetObject" ref="fooService" />
<property name="targetMethod" value="processNameValuePair" />
<property name="fieldsUsedAsTargetMethodArguments" value="name,value" />
</bean>
<bean id="fooService" class="org.springframework.batch.io.sample.domain.FooService" />
</beans>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="provider" class="org.springframework.batch.item.provider.DelegatingItemProvider">
<property name="targetObject" ref="fooService" />
<property name="targetMethod" value="generateFoo" />
</bean>
<bean id="fooService" class="org.springframework.batch.io.sample.domain.FooService" />
</beans>

View File

@@ -1,10 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<beansProjectDescription>
<version>1</version>
<pluginVersion><![CDATA[2.0.2.v200710312100]]></pluginVersion>
<configSuffixes>
<configSuffix><![CDATA[xml]]></configSuffix>
</configSuffixes>
<configExtensions>
<configExtension>xml</configExtension>
</configExtensions>
<configs>
<config>src/main/resources/jobs/fixedLengthImportJob.xml</config>
<config>src/main/resources/jobs/multilineJob.xml</config>
@@ -28,6 +26,7 @@
<config>src/main/resources/jobs/nflJob.xml</config>
<config>src/main/resources/jobs/simpleTaskletJob.xml</config>
<config>src/main/resources/beanRefContext.xml</config>
<config>src/main/resources/jobs/delegatingJob.xml</config>
</configs>
<configSets>
<configSet>

View File

@@ -0,0 +1,79 @@
/*
* 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.sample.domain;
import java.util.ArrayList;
import java.util.List;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemProvider;
/**
* Custom class that contains logic that would normally be
* be contained in {@link ItemProvider} (<code>getData()</code>) and
* {@link ItemProcessor} (<code>processData(..)</code>).
*
* @author tomas.slanina
* @author Robert Kasanicky
*/
public class PersonService {
private static final int GENERATION_LIMIT = 10;
private int generatedCounter = 0;
private int processedCounter = 0;
public Person getData() {
if (generatedCounter >= GENERATION_LIMIT) return null;
Person person = new Person();
Address address = new Address();
Child child = new Child();
List children = new ArrayList(1);
children.add(child);
person.setFirstName("John" + generatedCounter);
person.setAge(20 + generatedCounter);
address.setCity("Johnsville" + generatedCounter);
child.setName("Little Johny" + generatedCounter);
person.setAddress(address);
person.setChildren(children);
generatedCounter++;
return person;
}
/**
* Badly designed method signature which accepts multiple implicitly related
* arguments instead of a single Person argument.
*/
public void processPerson(String name, String city) {
processedCounter++;
}
public int getReturnedCount() {
return generatedCounter;
}
public int getReceivedCount() {
return processedCounter;
}
}

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<description>
The intent is to to give an example of how existing bean
definitions (e.g. from custom application's domain layer)
can be integrated into a batch job.
</description>
<bean class="org.springframework.batch.execution.configuration.JobConfigurationRegistryBeanPostProcessor">
<property name="jobConfigurationRegistry" ref="jobConfigurationRegistry"/>
</bean>
<bean id="jobConfiguration" parent="simpleJob">
<property name="steps">
<bean id="step1" parent="simpleStep">
<property name="tasklet">
<bean
class="org.springframework.batch.execution.tasklet.ItemProviderProcessTasklet">
<property name="itemProvider">
<bean class="org.springframework.batch.item.provider.DelegatingItemProvider">
<property name="targetObject" ref="delegatingObject" />
<property name="targetMethod" value="getData" />
</bean>
</property>
<property name="itemProcessor">
<bean class="org.springframework.batch.item.processor.PropertyExtractingDelegatingItemProcessor">
<property name="targetObject" ref="delegatingObject" />
<property name="targetMethod" value="processPerson" />
<property name="fieldsUsedAsTargetMethodArguments">
<list>
<value>firstName</value>
<value>address.city</value>
</list>
</property>
</bean>
</property>
</bean>
</property>
</bean>
</property>
</bean>
<bean id="delegatingObject" class="org.springframework.batch.sample.domain.PersonService" />
</beans>

View File

@@ -0,0 +1,25 @@
package org.springframework.batch.sample;
import org.springframework.batch.sample.domain.PersonService;
public class DelegatingJobFunctionalTests extends AbstractValidatingBatchLauncherTests {
private PersonService personService;
protected String[] getConfigLocations() {
return new String[] {"jobs/delegatingJob.xml"};
}
protected void validatePostConditions() throws Exception {
assertTrue(personService.getReturnedCount() > 0);
assertEquals(personService.getReturnedCount(), personService.getReceivedCount());
}
// setter for auto-injection
public void setPersonService(PersonService personService) {
this.personService = personService;
}
}