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.bundlorcom.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.
- *
+ *
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 extends T> itemReader) {
this.itemReader = itemReader;
}
/**
- * @param itemWriter the itemWriter to set
+ * @param itemWriter the {@link ItemWriter} to set
*/
public void setItemWriter(ItemWriter super S> itemWriter) {
this.itemWriter = itemWriter;
}
/**
- * @param itemProcessor the itemProcessor to set
+ * @param itemProcessor the {@link ItemProcessor} to set
*/
public void setItemProcessor(ItemProcessor super T, ? extends S> 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