diff --git a/infrastructure/.springBeans b/infrastructure/.springBeans
index da47de4bd..29e2bef61 100644
--- a/infrastructure/.springBeans
+++ b/infrastructure/.springBeans
@@ -6,6 +6,9 @@
src/test/resources/org/springframework/batch/io/file/support/mapping/bean-wrapper.xml
src/test/resources/org/springframework/batch/io/sql/data-source-context.xml
+ src/test/resources/org/springframework/batch/item/processor/delegating-item-processor.xml
+ src/test/resources/org/springframework/batch/item/processor/pe-delegating-item-processor.xml
+ src/test/resources/org/springframework/batch/item/provider/delegating-item-provider.xml
src/test/resources/org/springframework/batch/retry/aop/retry-transaction-test.xml
diff --git a/infrastructure/src/main/java/org/springframework/batch/io/exception/DynamicMethodInvocationException.java b/infrastructure/src/main/java/org/springframework/batch/io/exception/DynamicMethodInvocationException.java
new file mode 100644
index 000000000..495995b29
--- /dev/null
+++ b/infrastructure/src/main/java/org/springframework/batch/io/exception/DynamicMethodInvocationException.java
@@ -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);
+ }
+}
diff --git a/infrastructure/src/main/java/org/springframework/batch/item/processor/DelegatingItemProcessor.java b/infrastructure/src/main/java/org/springframework/batch/item/processor/DelegatingItemProcessor.java
new file mode 100644
index 000000000..bda01f4bf
--- /dev/null
+++ b/infrastructure/src/main/java/org/springframework/batch/item/processor/DelegatingItemProcessor.java
@@ -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);
+ }
+
+}
+
diff --git a/infrastructure/src/main/java/org/springframework/batch/item/processor/PropertyExtractingDelegatingItemProcessor.java b/infrastructure/src/main/java/org/springframework/batch/item/processor/PropertyExtractingDelegatingItemProcessor.java
new file mode 100644
index 000000000..277dff2b0
--- /dev/null
+++ b/infrastructure/src/main/java/org/springframework/batch/item/processor/PropertyExtractingDelegatingItemProcessor.java
@@ -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. address.city
+ */
+ public void setFieldsUsedAsTargetMethodArguments(String[] fieldsUsedAsMethodArguments) {
+ this.fieldsUsedAsTargetMethodArguments = fieldsUsedAsMethodArguments;
+ }
+}
diff --git a/infrastructure/src/main/java/org/springframework/batch/item/provider/DelegatingItemProvider.java b/infrastructure/src/main/java/org/springframework/batch/item/provider/DelegatingItemProvider.java
new file mode 100644
index 000000000..592a3dc4f
--- /dev/null
+++ b/infrastructure/src/main/java/org/springframework/batch/item/provider/DelegatingItemProvider.java
@@ -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;
+ }
+
+}
+
diff --git a/infrastructure/src/main/java/org/springframework/batch/support/AbstractDelegator.java b/infrastructure/src/main/java/org/springframework/batch/support/AbstractDelegator.java
new file mode 100644
index 000000000..f3fdab5a8
--- /dev/null
+++ b/infrastructure/src/main/java/org/springframework/batch/support/AbstractDelegator.java
@@ -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;
+ }
+}
diff --git a/infrastructure/src/test/java/org/springframework/batch/io/sample/domain/FooService.java b/infrastructure/src/test/java/org/springframework/batch/io/sample/domain/FooService.java
new file mode 100644
index 000000000..9c9e44bf9
--- /dev/null
+++ b/infrastructure/src/test/java/org/springframework/batch/io/sample/domain/FooService.java
@@ -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;
+ }
+
+
+}
diff --git a/infrastructure/src/test/java/org/springframework/batch/item/processor/DelegatingItemProcessorIntegrationTests.java b/infrastructure/src/test/java/org/springframework/batch/item/processor/DelegatingItemProcessorIntegrationTests.java
new file mode 100644
index 000000000..3ad6d30f1
--- /dev/null
+++ b/infrastructure/src/test/java/org/springframework/batch/item/processor/DelegatingItemProcessorIntegrationTests.java
@@ -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;
+ }
+}
diff --git a/infrastructure/src/test/java/org/springframework/batch/item/processor/PropertyExtractingDelegatingItemProccessorIntegrationTests.java b/infrastructure/src/test/java/org/springframework/batch/item/processor/PropertyExtractingDelegatingItemProccessorIntegrationTests.java
new file mode 100644
index 000000000..8bf4a91b3
--- /dev/null
+++ b/infrastructure/src/test/java/org/springframework/batch/item/processor/PropertyExtractingDelegatingItemProccessorIntegrationTests.java
@@ -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;
+ }
+
+}
diff --git a/infrastructure/src/test/java/org/springframework/batch/item/provider/DelegatingItemProviderIntegrationTests.java b/infrastructure/src/test/java/org/springframework/batch/item/provider/DelegatingItemProviderIntegrationTests.java
new file mode 100644
index 000000000..f7bf58c01
--- /dev/null
+++ b/infrastructure/src/test/java/org/springframework/batch/item/provider/DelegatingItemProviderIntegrationTests.java
@@ -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
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/infrastructure/src/test/resources/org/springframework/batch/item/processor/pe-delegating-item-processor.xml b/infrastructure/src/test/resources/org/springframework/batch/item/processor/pe-delegating-item-processor.xml
new file mode 100644
index 000000000..ba22e035e
--- /dev/null
+++ b/infrastructure/src/test/resources/org/springframework/batch/item/processor/pe-delegating-item-processor.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/infrastructure/src/test/resources/org/springframework/batch/item/provider/delegating-item-provider.xml b/infrastructure/src/test/resources/org/springframework/batch/item/provider/delegating-item-provider.xml
new file mode 100644
index 000000000..741704896
--- /dev/null
+++ b/infrastructure/src/test/resources/org/springframework/batch/item/provider/delegating-item-provider.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/.springBeans b/samples/.springBeans
index e5728c0c6..1a06c56cc 100644
--- a/samples/.springBeans
+++ b/samples/.springBeans
@@ -1,10 +1,8 @@
- 1
-
-
-
-
+
+ xml
+
src/main/resources/jobs/fixedLengthImportJob.xml
src/main/resources/jobs/multilineJob.xml
@@ -28,6 +26,7 @@
src/main/resources/jobs/nflJob.xml
src/main/resources/jobs/simpleTaskletJob.xml
src/main/resources/beanRefContext.xml
+ src/main/resources/jobs/delegatingJob.xml
diff --git a/samples/src/main/java/org/springframework/batch/sample/domain/PersonService.java b/samples/src/main/java/org/springframework/batch/sample/domain/PersonService.java
new file mode 100644
index 000000000..df8da60ee
--- /dev/null
+++ b/samples/src/main/java/org/springframework/batch/sample/domain/PersonService.java
@@ -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} (getData()) and
+ * {@link ItemProcessor} (processData(..)).
+ *
+ * @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;
+ }
+}
diff --git a/samples/src/main/resources/jobs/delegatingJob.xml b/samples/src/main/resources/jobs/delegatingJob.xml
new file mode 100644
index 000000000..9a1cb2c60
--- /dev/null
+++ b/samples/src/main/resources/jobs/delegatingJob.xml
@@ -0,0 +1,52 @@
+
+
+
+
+ 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.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/src/test/java/org/springframework/batch/sample/DelegatingJobFunctionalTests.java b/samples/src/test/java/org/springframework/batch/sample/DelegatingJobFunctionalTests.java
new file mode 100644
index 000000000..d926f92b8
--- /dev/null
+++ b/samples/src/test/java/org/springframework/batch/sample/DelegatingJobFunctionalTests.java
@@ -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;
+ }
+
+
+}