From 2ca2d7a60cbe6f8cefe5c7af0bb78d35e153c5fb Mon Sep 17 00:00:00 2001 From: dsyer Date: Wed, 14 Nov 2007 16:11:42 +0000 Subject: [PATCH] RESOLVED - issue BATCH-165: delegating ItemProvider and ItemProcessor http://opensource.atlassian.com/projects/spring/browse/BATCH-165 Patch applied from JIRA. --- infrastructure/.springBeans | 3 + .../DynamicMethodInvocationException.java | 23 +++ .../processor/DelegatingItemProcessor.java | 38 ++++ ...ertyExtractingDelegatingItemProcessor.java | 68 +++++++ .../item/provider/DelegatingItemProvider.java | 47 +++++ .../batch/support/AbstractDelegator.java | 175 ++++++++++++++++++ .../batch/io/sample/domain/FooService.java | 53 ++++++ ...legatingItemProcessorIntegrationTests.java | 55 ++++++ ...egatingItemProccessorIntegrationTests.java | 58 ++++++ ...elegatingItemProviderIntegrationTests.java | 69 +++++++ .../batch/support/AbstractDelegatorTests.java | 172 +++++++++++++++++ .../processor/delegating-item-processor.xml | 12 ++ .../pe-delegating-item-processor.xml | 13 ++ .../provider/delegating-item-provider.xml | 12 ++ samples/.springBeans | 9 +- .../batch/sample/domain/PersonService.java | 79 ++++++++ .../src/main/resources/jobs/delegatingJob.xml | 52 ++++++ .../sample/DelegatingJobFunctionalTests.java | 25 +++ 18 files changed, 958 insertions(+), 5 deletions(-) create mode 100644 infrastructure/src/main/java/org/springframework/batch/io/exception/DynamicMethodInvocationException.java create mode 100644 infrastructure/src/main/java/org/springframework/batch/item/processor/DelegatingItemProcessor.java create mode 100644 infrastructure/src/main/java/org/springframework/batch/item/processor/PropertyExtractingDelegatingItemProcessor.java create mode 100644 infrastructure/src/main/java/org/springframework/batch/item/provider/DelegatingItemProvider.java create mode 100644 infrastructure/src/main/java/org/springframework/batch/support/AbstractDelegator.java create mode 100644 infrastructure/src/test/java/org/springframework/batch/io/sample/domain/FooService.java create mode 100644 infrastructure/src/test/java/org/springframework/batch/item/processor/DelegatingItemProcessorIntegrationTests.java create mode 100644 infrastructure/src/test/java/org/springframework/batch/item/processor/PropertyExtractingDelegatingItemProccessorIntegrationTests.java create mode 100644 infrastructure/src/test/java/org/springframework/batch/item/provider/DelegatingItemProviderIntegrationTests.java create mode 100644 infrastructure/src/test/java/org/springframework/batch/support/AbstractDelegatorTests.java create mode 100644 infrastructure/src/test/resources/org/springframework/batch/item/processor/delegating-item-processor.xml create mode 100644 infrastructure/src/test/resources/org/springframework/batch/item/processor/pe-delegating-item-processor.xml create mode 100644 infrastructure/src/test/resources/org/springframework/batch/item/provider/delegating-item-provider.xml create mode 100644 samples/src/main/java/org/springframework/batch/sample/domain/PersonService.java create mode 100644 samples/src/main/resources/jobs/delegatingJob.xml create mode 100644 samples/src/test/java/org/springframework/batch/sample/DelegatingJobFunctionalTests.java 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. + + + + + + + + + + + + + + + + + + + + + + + + firstName + address.city + + + + + + + + + + + + + \ 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; + } + + +}