diff --git a/archetypes/simple-cli/pom.xml b/archetypes/simple-cli/pom.xml index 29f9c251f..0709ee244 100644 --- a/archetypes/simple-cli/pom.xml +++ b/archetypes/simple-cli/pom.xml @@ -138,9 +138,9 @@ com.springsource.bundlor com.springsource.bundlor.maven + 1.0.0.BUILD-SNAPSHOT ${project.basedir}/src/main/resources/META-INF/MANIFEST.MF - ${project.basedir}/template.mf diff --git a/archetypes/simple-cli/src/main/resources/META-INF/MANIFEST.MF b/archetypes/simple-cli/src/main/resources/META-INF/MANIFEST.MF index 4bc828571..bdd712f9e 100644 Binary files a/archetypes/simple-cli/src/main/resources/META-INF/MANIFEST.MF and b/archetypes/simple-cli/src/main/resources/META-INF/MANIFEST.MF differ diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/ExitStatus.java b/spring-batch-core/src/main/java/org/springframework/batch/core/ExitStatus.java index 19f9b8de0..29e55b888 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/ExitStatus.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/ExitStatus.java @@ -17,12 +17,11 @@ package org.springframework.batch.core; import java.io.Serializable; -import org.springframework.batch.repeat.RepeatOperations; import org.springframework.util.StringUtils; /** * Value object used to carry information about the status of a - * {@link RepeatOperations}. + * job or step execution. * * ExitStatus is immutable and therefore thread-safe. * diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/listener/JobListenerFactoryBean.java b/spring-batch-core/src/main/java/org/springframework/batch/core/listener/JobListenerFactoryBean.java index 10c81476f..e2bdbfd4b 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/listener/JobListenerFactoryBean.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/listener/JobListenerFactoryBean.java @@ -35,80 +35,88 @@ import org.springframework.beans.factory.InitializingBean; import org.springframework.util.Assert; /** - * {@link FactoryBean} implementation that builds a {@link JobExecutionListener} based on the - * various lifecycle methods or annotations that are provided. There are three possible ways of having - * a method called as part of a {@link JobExecutionListener} lifecyle: + * {@link FactoryBean} implementation that builds a {@link JobExecutionListener} + * based on the various lifecycle methods or annotations that are provided. + * There are three possible ways of having a method called as part of a + * {@link JobExecutionListener} lifecyle: * * + *
  • Interface implementation: By implementing JobExecutionListener, methods + * on said interface will be called. + *
  • Annotations: Annotating a method will result in registration. + *
  • String name of the method to be called, which is tied to + * {@link JobListenerMetaData} in the metaDatMap. + * * - * It should be noted that methods obtained by name or annotation that don't match the listener method - * signatures to which they belong, will cause errors. However, it is acceptable to have no parameters at all. - * If the same method is marked in more than one way. (i.e. the method name is given and it's annotated) the - * method will only be called once. However, if the same class has multiple methods tied to a particular - * listener, each method will be called. + * It should be noted that methods obtained by name or annotation that don't + * match the listener method signatures to which they belong, will cause errors. + * However, it is acceptable to have no parameters at all. If the same method is + * marked in more than one way. (i.e. the method name is given and it's + * annotated) the method will only be called once. However, if the same class + * has multiple methods tied to a particular listener, each method will be + * called. * * @author Lucas Ward * @since 2.0 * @see JobListenerMetaData */ -public class JobListenerFactoryBean implements FactoryBean, InitializingBean{ +public class JobListenerFactoryBean implements FactoryBean, InitializingBean { private Object delegate; + private Map metaDataMap; - + public void setDelegate(Object delegate) { this.delegate = delegate; } - + public void setMetaDataMap(Map metaDataMap) { this.metaDataMap = metaDataMap; } - - public Object getObject() throws Exception { + + public Object getObject() { Map> invokerMap = new HashMap>(); - if(metaDataMap == null){ + if (metaDataMap == null) { metaDataMap = new HashMap(); } - //Because all annotations and interfaces should be checked for, make sure that each meta data - //entry is represented. - for(JobListenerMetaData metaData : JobListenerMetaData.values()){ - if(!metaDataMap.containsKey(metaData.getPropertyName())){ - //put null so that the annotation and interface is checked + // Because all annotations and interfaces should be checked for, make + // sure that each meta data + // entry is represented. + for (JobListenerMetaData metaData : JobListenerMetaData.values()) { + if (!metaDataMap.containsKey(metaData.getPropertyName())) { + // put null so that the annotation and interface is checked metaDataMap.put(metaData.getPropertyName(), null); } } - - //For every entry in th emap, try and find a method by interface, name, or annotation. If the same - for(Entry entry : metaDataMap.entrySet()){ + + // For every entry in the map, try and find a method by interface, name, + // or annotation. If the same + for (Entry entry : metaDataMap.entrySet()) { JobListenerMetaData metaData = JobListenerMetaData.fromPropertyName(entry.getKey()); Set invokers = new NullIgnoringSet(); invokers.add(getMethodInvokerByName(entry.getValue(), delegate, JobExecution.class)); - invokers.add(getMethodInvokerForInterface(JobExecutionListener.class, metaData.getMethodName(), - delegate, JobExecution.class)); + invokers.add(getMethodInvokerForInterface(JobExecutionListener.class, metaData.getMethodName(), delegate, + JobExecution.class)); invokers.add(getMethodInvokerByAnnotation(metaData.getAnnotation(), delegate)); - if(!invokers.isEmpty()){ + if (!invokers.isEmpty()) { invokerMap.put(metaData.getMethodName(), invokers); } } - - //create a proxy listener for only the interfaces that have methods to be called + + // create a proxy listener for only the interfaces that have methods to + // be called ProxyFactory proxyFactory = new ProxyFactory(); - proxyFactory.setInterfaces(new Class[]{JobExecutionListener.class}); + proxyFactory.setTarget(delegate); + proxyFactory.setInterfaces(new Class[] { JobExecutionListener.class }); proxyFactory.addAdvisor(new DefaultPointcutAdvisor(new MethodInvokerMethodInterceptor(invokerMap))); return proxyFactory.getProxy(); } - - private MethodInvoker getMethodInvokerByName(String methodName, Object candidate, Class... params){ - if(methodName != null){ + + private MethodInvoker getMethodInvokerByName(String methodName, Object candidate, Class... params) { + if (methodName != null) { return MethodInvokerUtils.createMethodInvokerByName(candidate, methodName, false, params); } - else{ + else { return null; } } @@ -125,19 +133,52 @@ public class JobListenerFactoryBean implements FactoryBean, InitializingBean{ public void afterPropertiesSet() throws Exception { Assert.notNull(delegate, "Delegate listener must not be null"); } - + + /** + * Convenience method to wrap any object and expose the appropriate + * {@link JobExecutionListener} interfaces. + * @param delegate a delegate object + * @return a JobListener instance constructed from the delegate + */ + public static JobExecutionListener getListener(Object delegate) { + JobListenerFactoryBean factory = new JobListenerFactoryBean(); + factory.setDelegate(delegate); + return (JobExecutionListener) factory.getObject(); + } + + /** + * Convenience method to check whether the given object is or can be made + * into a {@link JobExecutionListener}. + * @param delegate the object to check + * @return true if the delegate is an instance of + * {@link JobExecutionListener}, or contains the marker annotations + */ + public static boolean isListener(Object delegate) { + if (delegate instanceof JobExecutionListener) { + return true; + } + for (JobListenerMetaData metaData : JobListenerMetaData.values()) { + Set invokers = new NullIgnoringSet(); + invokers.add(getMethodInvokerByAnnotation(metaData.getAnnotation(), delegate)); + if (!invokers.isEmpty()) { + return true; + } + } + return false; + } + /* * Extension of HashSet that ignores nulls, rather than putting them into * the set. */ - private static class NullIgnoringSet extends HashSet{ - + private static class NullIgnoringSet extends HashSet { + @Override public boolean add(E e) { - if(e == null){ + if (e == null) { return false; } - else{ + else { return super.add(e); } }; diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/listener/MethodInvokerMethodInterceptor.java b/spring-batch-core/src/main/java/org/springframework/batch/core/listener/MethodInvokerMethodInterceptor.java index 9c756fac6..08c20ce20 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/listener/MethodInvokerMethodInterceptor.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/listener/MethodInvokerMethodInterceptor.java @@ -25,48 +25,68 @@ import org.springframework.batch.core.StepExecutionListener; import org.springframework.batch.core.configuration.util.MethodInvoker; /** - * {@link MethodInterceptor} that, given a map of method names and {@link MethodInvoker}s, - * will execute all methods tied to a particular method name, with the provided - * arguments. The only possible return value that is handled is of type ExitStatus, since - * the only StepListener implementation that isn't void is - * {@link StepExecutionListener#afterStep(org.springframework.batch.core.StepExecution)}, which - * returns ExitStatus. + * {@link MethodInterceptor} that, given a map of method names and + * {@link MethodInvoker}s, will execute all methods tied to a particular method + * name, with the provided arguments. The only possible return value that is + * handled is of type ExitStatus, since the only StepListener implementation + * that isn't void is + * {@link StepExecutionListener#afterStep(org.springframework.batch.core.StepExecution)} + * , which returns ExitStatus. * * @author Lucas Ward * @since 2.0 * @see MethodInvoker */ -public class MethodInvokerMethodInterceptor implements MethodInterceptor{ +public class MethodInvokerMethodInterceptor implements MethodInterceptor { private final Map> invokerMap; public MethodInvokerMethodInterceptor(Map> invokerMap) { this.invokerMap = invokerMap; } - + public Object invoke(MethodInvocation invocation) throws Throwable { - + String methodName = invocation.getMethod().getName(); Set invokers = invokerMap.get(methodName); - - if(invokers == null){ + + if (invokers == null) { return null; } ExitStatus status = null; - for(MethodInvoker invoker : invokers){ + for (MethodInvoker invoker : invokers) { Object retVal = invoker.invokeMethod(invocation.getArguments()); - if(retVal instanceof ExitStatus){ - if(status != null){ + if (retVal instanceof ExitStatus) { + if (status != null) { status = status.and((ExitStatus) retVal); } - else{ + else { status = (ExitStatus) retVal; } } } - + return status; } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof MethodInvokerMethodInterceptor)) { + return false; + } + MethodInvokerMethodInterceptor other = (MethodInvokerMethodInterceptor) obj; + return invokerMap.equals(other.invokerMap); + } - + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return invokerMap.hashCode(); + } + } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/listener/StepListenerFactoryBean.java b/spring-batch-core/src/main/java/org/springframework/batch/core/listener/StepListenerFactoryBean.java index 724170c9a..e3b242443 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/listener/StepListenerFactoryBean.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/listener/StepListenerFactoryBean.java @@ -34,76 +34,87 @@ import org.springframework.beans.factory.InitializingBean; import org.springframework.util.Assert; /** - * {@link FactoryBean} implementation that builds a {@link StepListener} based on the - * various lifecycle methods or annotations that are provided. There are three possible ways of having - * a method called as part of a {@link StepListener} lifecyle: + * {@link FactoryBean} implementation that builds a {@link StepListener} based + * on the various lifecycle methods or annotations that are provided. There are + * three possible ways of having a method called as part of a + * {@link StepListener} lifecyle: * *
      - *
    • Interface implementation: By implementing any of the subclasses of StepListener, methods on said - * interface will be called - *
    • Annotations: Annotating a method will result in registration. - *
    • String name of the method to be called, which is tied to {@link StepListenerMetaData} in the - * metaDatMap. - *
    + *
  • Interface implementation: By implementing any of the subclasses of + * StepListener, methods on said interface will be called + *
  • Annotations: Annotating a method will result in registration. + *
  • String name of the method to be called, which is tied to + * {@link StepListenerMetaData} in the metaDatMap. + * * - * It should be noted that methods obtained by name or annotation that don't match the StepListener method - * signatures to which they belong, will cause errors. However, it is acceptable to have no parameters at all. - * If the same method is marked in more than one way. (i.e. the method name is given and it's annotated) the - * method will only be called once. However, if the same class has multiple methods tied to a particular - * listener, each method will be called. + * It should be noted that methods obtained by name or annotation that don't + * match the StepListener method signatures to which they belong, will cause + * errors. However, it is acceptable to have no parameters at all. If the same + * method is marked in more than one way. (i.e. the method name is given and + * it's annotated) the method will only be called once. However, if the same + * class has multiple methods tied to a particular listener, each method will be + * called. * * @author Lucas Ward * @since 2.0 * @see StepListenerMetaData */ -public class StepListenerFactoryBean implements FactoryBean, InitializingBean{ +public class StepListenerFactoryBean implements FactoryBean, InitializingBean { private Object delegate; + private Map metaDataMap; - - public Object getObject() throws Exception { - + + public Object getObject() { + Map> invokerMap = new HashMap>(); - if(metaDataMap == null){ + if (metaDataMap == null) { metaDataMap = new HashMap(); } - //Because all annotations and interfaces should be checked for, make sure that each meta data - //entry is represented. - for(StepListenerMetaData metaData : StepListenerMetaData.values()){ - if(!metaDataMap.containsKey(metaData.getPropertyName())){ - //put null so that the annotation and interface is checked + // Because all annotations and interfaces should be checked for, make + // sure that each meta data + // entry is represented. + for (StepListenerMetaData metaData : StepListenerMetaData.values()) { + if (!metaDataMap.containsKey(metaData.getPropertyName())) { + // put null so that the annotation and interface is checked metaDataMap.put(metaData.getPropertyName(), null); } } - + Set> listenerInterfaces = new HashSet>(); - - //For every entry in the map, try and find a method by interface, name, or annotation. If the same - for(Entry entry : metaDataMap.entrySet()){ + + // For every entry in the map, try and find a method by interface, name, + // or annotation. If the same + for (Entry entry : metaDataMap.entrySet()) { StepListenerMetaData metaData = StepListenerMetaData.fromPropertyName(entry.getKey()); Set invokers = new NullIgnoringSet(); invokers.add(getMethodInvokerByName(entry.getValue(), delegate, metaData.getParamTypes())); - invokers.add(getMethodInvokerForInterface(metaData.getListenerInterface(), metaData.getMethodName(), + invokers.add(getMethodInvokerForInterface(metaData.getListenerInterface(), metaData.getMethodName(), delegate, metaData.getParamTypes())); invokers.add(getMethodInvokerByAnnotation(metaData.getAnnotation(), delegate)); - if(!invokers.isEmpty()){ + if (!invokers.isEmpty()) { invokerMap.put(metaData.getMethodName(), invokers); listenerInterfaces.add(metaData.getListenerInterface()); } } - - //create a proxy listener for only the interfaces that have methods to be called + + if (listenerInterfaces.isEmpty()) { + listenerInterfaces.add(StepListener.class); + } + + // create a proxy listener for only the interfaces that have methods to + // be called ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.setInterfaces(listenerInterfaces.toArray(new Class[0])); proxyFactory.addAdvisor(new DefaultPointcutAdvisor(new MethodInvokerMethodInterceptor(invokerMap))); return proxyFactory.getProxy(); } - - private MethodInvoker getMethodInvokerByName(String methodName, Object candidate, Class... params){ - if(methodName != null){ + + private MethodInvoker getMethodInvokerByName(String methodName, Object candidate, Class... params) { + if (methodName != null) { return MethodInvokerUtils.createMethodInvokerByName(candidate, methodName, false, params); } - else{ + else { return null; } } @@ -116,27 +127,60 @@ public class StepListenerFactoryBean implements FactoryBean, InitializingBean{ public boolean isSingleton() { return false; } - + public void setDelegate(Object delegate) { this.delegate = delegate; } - + public void setMetaDataMap(Map metaDataMap) { this.metaDataMap = metaDataMap; } - + + /** + * Convenience method to wrap any object and expose the appropriate + * {@link StepListener} interfaces. + * @param delegate a delegate object + * @return a StepListener instance constructed from the delegate + */ + public static StepListener getListener(Object delegate) { + StepListenerFactoryBean factory = new StepListenerFactoryBean(); + factory.setDelegate(delegate); + return (StepListener) factory.getObject(); + } + + /** + * Convenience method to check whether the given object is or can be made + * into a {@link StepListener}. + * @param delegate the object to check + * @return true if the delegate is an instance of any of the + * {@link StepListener} interfaces, or contains the marker annotations + */ + public static boolean isListener(Object delegate) { + if (delegate instanceof StepListener) { + return true; + } + for (StepListenerMetaData metaData : StepListenerMetaData.values()) { + Set invokers = new NullIgnoringSet(); + invokers.add(getMethodInvokerByAnnotation(metaData.getAnnotation(), delegate)); + if (!invokers.isEmpty()) { + return true; + } + } + return false; + } + /* * Extension of HashSet that ignores nulls, rather than putting them into * the set. */ - private static class NullIgnoringSet extends HashSet{ - + private static class NullIgnoringSet extends HashSet { + @Override public boolean add(E e) { - if(e == null){ + if (e == null) { return false; } - else{ + else { return super.add(e); } }; diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/SimpleStepFactoryBean.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/SimpleStepFactoryBean.java index 51a3bb2f4..1b83bdf40 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/SimpleStepFactoryBean.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/SimpleStepFactoryBean.java @@ -29,6 +29,7 @@ import org.springframework.batch.core.SkipListener; import org.springframework.batch.core.Step; import org.springframework.batch.core.StepExecutionListener; import org.springframework.batch.core.StepListener; +import org.springframework.batch.core.listener.StepListenerFactoryBean; import org.springframework.batch.core.repository.JobRepository; import org.springframework.batch.core.step.tasklet.TaskletStep; import org.springframework.batch.item.ItemProcessor; @@ -121,10 +122,21 @@ public class SimpleStepFactoryBean implements FactoryBean, BeanNameAware { super(); } + /** + * Flag to signal that the reader is transactional (usually a JMS consumer) + * so that items are re-presented after a rollback. The default is false and + * readers are assumed to be forward-only. + * + * @param isReaderTransactionalQueue the value of the flag + */ public void setIsReaderTransactionalQueue(boolean isReaderTransactionalQueue) { this.isReaderTransactionalQueue = isReaderTransactionalQueue; } + /** + * Convenience method for subclasses. + * @return true if the flag is set (default false) + */ protected boolean isReaderTransactionalQueue() { return isReaderTransactionalQueue; } @@ -142,7 +154,7 @@ public class SimpleStepFactoryBean implements FactoryBean, BeanNameAware { } /** - * Public getter for the String. + * Public getter for the name of the step. * @return the name */ public String getName() { @@ -150,7 +162,7 @@ public class SimpleStepFactoryBean implements FactoryBean, BeanNameAware { } /** - * Public setter for the startLimit. + * Public setter for the start limit for the step. * * @param startLimit the startLimit to set */ @@ -159,7 +171,8 @@ public class SimpleStepFactoryBean implements FactoryBean, BeanNameAware { } /** - * Public setter for the shouldAllowStartIfComplete. + * Public setter for the flag to indicate that the step should be replayed + * on a restart, even if successful the first time. * * @param allowStartIfComplete the shouldAllowStartIfComplete to set */ @@ -168,21 +181,21 @@ public class SimpleStepFactoryBean implements FactoryBean, BeanNameAware { } /** - * @param itemReader the itemReader to set + * @param itemReader the {@link ItemReader} to set */ public void setItemReader(ItemReader itemReader) { this.itemReader = itemReader; } /** - * @param itemWriter the itemWriter to set + * @param itemWriter the {@link ItemWriter} to set */ public void setItemWriter(ItemWriter itemWriter) { this.itemWriter = itemWriter; } /** - * @param itemProcessor the itemProcessor to set + * @param itemProcessor the {@link ItemProcessor} to set */ public void setItemProcessor(ItemProcessor itemProcessor) { this.itemProcessor = itemProcessor; @@ -516,11 +529,14 @@ public class SimpleStepFactoryBean implements FactoryBean, BeanNameAware { if (itemHandler instanceof ItemStream) { step.registerStream((ItemStream) itemHandler); } - if (itemHandler instanceof StepExecutionListener) { - step.registerStepExecutionListener((StepExecutionListener) itemHandler); - } - if (itemHandler instanceof ChunkListener) { - chunkListeners.add((StepListener) itemHandler); + if (StepListenerFactoryBean.isListener(itemHandler)) { + StepListener listener = StepListenerFactoryBean.getListener(itemHandler); + if (listener instanceof StepExecutionListener) { + step.registerStepExecutionListener((StepExecutionListener) listener); + } + if (listener instanceof ChunkListener) { + chunkListeners.add((StepListener) listener); + } } } @@ -551,17 +567,20 @@ public class SimpleStepFactoryBean implements FactoryBean, BeanNameAware { // auto-register reader, processor and writer for (Object itemHandler : new Object[] { getItemReader(), getItemWriter(), getItemProcessor() }) { - if (itemHandler instanceof SkipListener) { - chunkProvider.registerListener((StepListener) itemHandler); - chunkProcessor.registerListener((StepListener) itemHandler); - // already registered with both so avoid double-registering - continue; - } - if (itemHandler instanceof ItemReadListener) { - chunkProvider.registerListener((StepListener) itemHandler); - } - if (itemHandler instanceof ItemProcessListener || itemHandler instanceof ItemWriteListener) { - chunkProcessor.registerListener((StepListener) itemHandler); + if (StepListenerFactoryBean.isListener(itemHandler)) { + StepListener listener = StepListenerFactoryBean.getListener(itemHandler); + if (listener instanceof SkipListener) { + chunkProvider.registerListener(listener); + chunkProcessor.registerListener(listener); + // already registered with both so avoid double-registering + continue; + } + if (listener instanceof ItemReadListener) { + chunkProvider.registerListener(listener); + } + if (listener instanceof ItemProcessListener || listener instanceof ItemWriteListener) { + chunkProcessor.registerListener(listener); + } } } } diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/listener/JobListenerFactoryBeanTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/listener/JobListenerFactoryBeanTests.java index 19b451f8d..d4b7ea297 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/listener/JobListenerFactoryBeanTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/listener/JobListenerFactoryBeanTests.java @@ -15,7 +15,11 @@ */ package org.springframework.batch.core.listener; -import static org.junit.Assert.*; +import static junit.framework.Assert.assertTrue; +import static org.junit.Assert.assertEquals; + +import java.util.HashSet; +import java.util.Set; import org.junit.Before; import org.junit.Test; @@ -26,19 +30,19 @@ import org.springframework.batch.core.annotation.BeforeJob; /** * @author Lucas Ward - * + * */ public class JobListenerFactoryBeanTests { JobListenerFactoryBean factoryBean; - + @Before - public void setUp(){ + public void setUp() { factoryBean = new JobListenerFactoryBean(); } - + @Test - public void testWithInterface() throws Exception{ + public void testWithInterface() throws Exception { JobListenerWithInterface delegate = new JobListenerWithInterface(); factoryBean.setDelegate(delegate); JobExecutionListener listener = (JobExecutionListener) factoryBean.getObject(); @@ -48,9 +52,9 @@ public class JobListenerFactoryBeanTests { assertTrue(delegate.beforeJobCalled); assertTrue(delegate.afterJobCalled); } - + @Test - public void testWithAnnotations() throws Exception{ + public void testWithAnnotations() throws Exception { AnnotatedTestClass delegate = new AnnotatedTestClass(); factoryBean.setDelegate(delegate); JobExecutionListener listener = (JobExecutionListener) factoryBean.getObject(); @@ -60,34 +64,81 @@ public class JobListenerFactoryBeanTests { assertTrue(delegate.beforeJobCalled); assertTrue(delegate.afterJobCalled); } - - private class JobListenerWithInterface implements JobExecutionListener{ + + @Test + public void testFactoryMethod() throws Exception { + JobListenerWithInterface delegate = new JobListenerWithInterface(); + Object listener = JobListenerFactoryBean.getListener(delegate); + assertTrue(listener instanceof JobExecutionListener); + ((JobExecutionListener) listener).afterJob(new JobExecution(11L)); + assertTrue(delegate.afterJobCalled); + } + + @Test + public void testUseInHashSet() throws Exception { + JobListenerWithInterface delegate = new JobListenerWithInterface(); + Object listener = JobListenerFactoryBean.getListener(delegate); + Object other = JobListenerFactoryBean.getListener(delegate); + assertTrue(listener instanceof JobExecutionListener); + Set listeners = new HashSet(); + listeners.add((JobExecutionListener) listener); + listeners.add((JobExecutionListener) other); + assertTrue(listeners.contains(listener)); + assertEquals(1, listeners.size()); + } + + @Test + public void testAnnotationsIsListener() throws Exception { + assertTrue(JobListenerFactoryBean.isListener(new Object() { + @SuppressWarnings("unused") + @BeforeJob + public void foo(JobExecution execution) { + } + })); + } + + @Test + public void testInterfaceIsListener() throws Exception { + assertTrue(JobListenerFactoryBean.isListener(new JobListenerWithInterface())); + } + + @Test + public void testEqualityOfProxies() throws Exception { + JobListenerWithInterface delegate = new JobListenerWithInterface(); + Object listener1 = JobListenerFactoryBean.getListener(delegate); + Object listener2 = JobListenerFactoryBean.getListener(delegate); + assertEquals(listener1, listener2); + } + + private class JobListenerWithInterface implements JobExecutionListener { boolean beforeJobCalled = false; + boolean afterJobCalled = false; - + public void afterJob(JobExecution jobExecution) { - beforeJobCalled = true; + afterJobCalled = true; } public void beforeJob(JobExecution jobExecution) { - afterJobCalled = true; - } - - } - - private class AnnotatedTestClass { - - boolean beforeJobCalled = false; - boolean afterJobCalled = false; - - @BeforeJob - public void before(){ beforeJobCalled = true; } - + + } + + private class AnnotatedTestClass { + + boolean beforeJobCalled = false; + + boolean afterJobCalled = false; + + @BeforeJob + public void before() { + beforeJobCalled = true; + } + @AfterJob - public void after(){ + public void after() { afterJobCalled = true; } } diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/listener/StepListenerFactoryBeanTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/listener/StepListenerFactoryBeanTests.java index 0889bd571..ad90ac0e8 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/listener/StepListenerFactoryBeanTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/listener/StepListenerFactoryBeanTests.java @@ -17,6 +17,7 @@ package org.springframework.batch.core.listener; import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.springframework.batch.core.listener.StepListenerMetaData.AFTER_CHUNK; import static org.springframework.batch.core.listener.StepListenerMetaData.AFTER_STEP; @@ -57,22 +58,22 @@ import org.springframework.util.Assert; */ public class StepListenerFactoryBeanTests { - StepListenerFactoryBean factoryBean; - TestClass testClass; - JobExecution jobExecution = new JobExecution(11L); - StepExecution stepExecution = new StepExecution("testStep", jobExecution); + private StepListenerFactoryBean factoryBean; + private TestListener testListener; + private JobExecution jobExecution = new JobExecution(11L); + private StepExecution stepExecution = new StepExecution("testStep", jobExecution); @Before public void setUp(){ factoryBean = new StepListenerFactoryBean(); - testClass = new TestClass(); + testListener = new TestListener(); } @Test @SuppressWarnings("unchecked") public void testStepAndChunk() throws Exception{ - factoryBean.setDelegate(testClass); + factoryBean.setDelegate(testListener); Map metaDataMap = new HashMap();; metaDataMap.put(AFTER_STEP.getPropertyName(), "destroy"); metaDataMap.put(AFTER_CHUNK.getPropertyName(), "afterChunk"); @@ -97,21 +98,21 @@ public class StepListenerFactoryBeanTests { ((SkipListener)listener).onSkipInRead(new Throwable()); ((SkipListener)listener).onSkipInProcess(item, new Throwable()); ((SkipListener)listener).onSkipInWrite(item, new Throwable()); - assertTrue(testClass.beforeStepCalled); - assertTrue(testClass.beforeChunkCalled); - assertTrue(testClass.afterChunkCalled); - assertTrue(testClass.beforeReadCalled); - assertTrue(testClass.afterReadCalled); - assertTrue(testClass.onReadErrorCalled); - assertTrue(testClass.beforeProcessCalled); - assertTrue(testClass.afterProcessCalled); - assertTrue(testClass.onProcessErrorCalled); - assertTrue(testClass.beforeWriteCalled); - assertTrue(testClass.afterWriteCalled); - assertTrue(testClass.onWriteErrorCalled); - assertTrue(testClass.onSkipInReadCalled); - assertTrue(testClass.onSkipInProcessCalled); - assertTrue(testClass.onSkipInWriteCalled); + assertTrue(testListener.beforeStepCalled); + assertTrue(testListener.beforeChunkCalled); + assertTrue(testListener.afterChunkCalled); + assertTrue(testListener.beforeReadCalled); + assertTrue(testListener.afterReadCalled); + assertTrue(testListener.onReadErrorCalled); + assertTrue(testListener.beforeProcessCalled); + assertTrue(testListener.afterProcessCalled); + assertTrue(testListener.onProcessErrorCalled); + assertTrue(testListener.beforeWriteCalled); + assertTrue(testListener.afterWriteCalled); + assertTrue(testListener.onWriteErrorCalled); + assertTrue(testListener.onSkipInReadCalled); + assertTrue(testListener.onSkipInProcessCalled); + assertTrue(testListener.onSkipInWriteCalled); } @Test @@ -132,7 +133,7 @@ public class StepListenerFactoryBeanTests { public void testAnnotatingInterfaceResultsInOneCall() throws Exception{ MultipleAfterStep delegate = new MultipleAfterStep(); factoryBean.setDelegate(delegate); - Map metaDataMap = new HashMap();; + Map metaDataMap = new HashMap(); metaDataMap.put(AFTER_STEP.getPropertyName(), "afterStep"); factoryBean.setMetaDataMap(metaDataMap); StepListener listener = (StepListener) factoryBean.getObject(); @@ -140,7 +141,55 @@ public class StepListenerFactoryBeanTests { assertEquals(1, delegate.callcount); } - private class MultipleAfterStep implements StepExecutionListener{ + @Test + public void testVanillaInterface() throws Exception{ + MultipleAfterStep delegate = new MultipleAfterStep(); + factoryBean.setDelegate(delegate); + Object listener = factoryBean.getObject(); + assertTrue(listener instanceof StepExecutionListener); + ((StepExecutionListener)listener).beforeStep(stepExecution); + assertEquals(1, delegate.callcount); + } + + @Test + public void testFactoryMethod() throws Exception{ + MultipleAfterStep delegate = new MultipleAfterStep(); + Object listener = StepListenerFactoryBean.getListener(delegate); + assertTrue(listener instanceof StepExecutionListener); + assertFalse(listener instanceof ChunkListener); + ((StepExecutionListener)listener).beforeStep(stepExecution); + assertEquals(1, delegate.callcount); + } + + @Test + public void testInterfaceIsListener() throws Exception { + assertTrue(StepListenerFactoryBean.isListener(new ThreeStepExecutionListener())); + } + + @Test + public void testAnnotationsIsListener() throws Exception { + assertTrue(StepListenerFactoryBean.isListener(new Object() { + @SuppressWarnings("unused") + @BeforeStep + public void foo(StepExecution execution) { + } + })); + } + + @Test + public void testMixedIsListener() throws Exception { + assertTrue(StepListenerFactoryBean.isListener(new MultipleAfterStep())); + } + + @Test + public void testNonListener() throws Exception{ + Object delegate = new Object(); + factoryBean.setDelegate(delegate); + StepListener listener = (StepListener) factoryBean.getObject(); + assertTrue(listener instanceof StepListener); + } + + private class MultipleAfterStep implements StepExecutionListener { int callcount = 0; @@ -183,7 +232,7 @@ public class StepListenerFactoryBeanTests { } - private class TestClass implements SkipListener{ + private class TestListener implements SkipListener{ boolean beforeStepCalled = false; boolean afterStepCalled = false; diff --git a/spring-batch-samples/src/main/resources/jobs/skipSampleJob.xml b/spring-batch-samples/src/main/resources/jobs/skipSampleJob.xml index 65848a676..02e24166a 100644 --- a/spring-batch-samples/src/main/resources/jobs/skipSampleJob.xml +++ b/spring-batch-samples/src/main/resources/jobs/skipSampleJob.xml @@ -39,7 +39,6 @@ - diff --git a/spring-batch-samples/src/test/java/org/springframework/batch/sample/common/CustomItemReaderTests.java b/spring-batch-samples/src/test/java/org/springframework/batch/sample/common/CustomItemReaderTests.java index 9cfa40739..612c55bcb 100644 --- a/spring-batch-samples/src/test/java/org/springframework/batch/sample/common/CustomItemReaderTests.java +++ b/spring-batch-samples/src/test/java/org/springframework/batch/sample/common/CustomItemReaderTests.java @@ -15,11 +15,14 @@ */ package org.springframework.batch.sample.common; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + import java.util.ArrayList; import java.util.List; -import junit.framework.TestCase; - +import org.junit.Before; +import org.junit.Test; import org.springframework.batch.item.ExecutionContext; import org.springframework.batch.item.ItemReader; import org.springframework.batch.item.ItemStream; @@ -34,15 +37,15 @@ import org.springframework.batch.item.UnexpectedInputException; * @author Lucas Ward * */ -public class CustomItemReaderTests extends TestCase { +public class CustomItemReaderTests { ItemReader itemReader; /* (non-Javadoc) * @see junit.framework.TestCase#setUp() */ - protected void setUp() throws Exception { - super.setUp(); + @Before + public void setUp() throws Exception { List items = new ArrayList(); items.add("1"); @@ -52,6 +55,7 @@ public class CustomItemReaderTests extends TestCase { itemReader = new CustomItemReader(items); } + @Test public void testRead() throws Exception{ assertEquals("1", itemReader.read()); @@ -60,6 +64,7 @@ public class CustomItemReaderTests extends TestCase { assertNull(itemReader.read()); } + @Test public void testRestart() throws Exception{ ExecutionContext executionContext = new ExecutionContext(); diff --git a/spring-batch-samples/src/test/java/org/springframework/batch/sample/common/StagingItemReaderTests.java b/spring-batch-samples/src/test/java/org/springframework/batch/sample/common/StagingItemReaderTests.java index 28dc8871f..39d0d0961 100644 --- a/spring-batch-samples/src/test/java/org/springframework/batch/sample/common/StagingItemReaderTests.java +++ b/spring-batch-samples/src/test/java/org/springframework/batch/sample/common/StagingItemReaderTests.java @@ -41,7 +41,7 @@ public class StagingItemReaderTests { @Autowired private StagingItemReader reader; - private Long jobId = 11L; + private Long jobId = 113L; @Autowired public void setDataSource(DataSource dataSource) { diff --git a/spring-batch-samples/src/test/java/org/springframework/batch/sample/support/AbstractRowMapperTests.java b/spring-batch-samples/src/test/java/org/springframework/batch/sample/support/AbstractRowMapperTests.java index 6ce1fde3f..5ffa94b60 100644 --- a/spring-batch-samples/src/test/java/org/springframework/batch/sample/support/AbstractRowMapperTests.java +++ b/spring-batch-samples/src/test/java/org/springframework/batch/sample/support/AbstractRowMapperTests.java @@ -1,12 +1,13 @@ package org.springframework.batch.sample.support; -import static org.easymock.EasyMock.*; +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.replay; +import static org.junit.Assert.assertEquals; import java.sql.ResultSet; import java.sql.SQLException; -import junit.framework.TestCase; - +import org.junit.Test; import org.springframework.jdbc.core.RowMapper; /** @@ -14,38 +15,38 @@ import org.springframework.jdbc.core.RowMapper; * * @author Robert Kasanicky */ -public abstract class AbstractRowMapperTests extends TestCase { +public abstract class AbstractRowMapperTests { - //row number should be irrelevant + // row number should be irrelevant private static final int IGNORED_ROW_NUMBER = 0; - - //mock result set + + // mock result set private ResultSet rs = createMock(ResultSet.class); /** - * @return Expected result of mapping the mock ResultSet by - * the mapper being tested. + * @return Expected result of mapping the mock ResultSet by the + * mapper being tested. */ abstract protected Object expectedDomainObject(); - + /** * @return RowMapper implementation that is being tested. */ abstract protected RowMapper rowMapper(); - + /* * Define the behaviour of mock ResultSet. */ abstract protected void setUpResultSetMock(ResultSet rs) throws SQLException; - - + /* * Regular usage scenario. */ + @Test public void testRegularUse() throws SQLException { setUpResultSetMock(rs); replay(rs); - + assertEquals(expectedDomainObject(), rowMapper().mapRow(rs, IGNORED_ROW_NUMBER)); } }