diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/util/MethodInvokerUtils.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/util/MethodInvokerUtils.java index efbf3142f..082b2bec3 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/util/MethodInvokerUtils.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/util/MethodInvokerUtils.java @@ -21,7 +21,6 @@ import java.lang.annotation.Target; import java.lang.reflect.Method; import java.util.concurrent.atomic.AtomicReference; -import org.springframework.batch.core.StepListener; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -62,7 +61,7 @@ public class MethodInvokerUtils { } } - public static MethodInvoker getMethodInvokerForInterface(Class iFace, String methodName, + public static MethodInvoker getMethodInvokerForInterface(Class iFace, String methodName, Object candidate, Class... params){ if(iFace.isAssignableFrom(candidate.getClass())){ diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/JobExecutionListenerParser.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/JobExecutionListenerParser.java index bb79010b6..d554b166a 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/JobExecutionListenerParser.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/JobExecutionListenerParser.java @@ -22,13 +22,14 @@ import java.util.ArrayList; import java.util.List; import org.springframework.batch.core.JobExecutionListener; -import org.springframework.batch.core.listener.JobExecutionListenerFactoryBean; +import org.springframework.batch.core.listener.JobListenerFactoryBean; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.config.BeanReference; import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.ManagedList; +import org.springframework.beans.factory.support.ManagedMap; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; @@ -45,15 +46,13 @@ import org.w3c.dom.NamedNodeMap; */ public class JobExecutionListenerParser { - + @SuppressWarnings("unchecked") public ManagedList parse(Element element, ParserContext parserContext) { List listeners = new ArrayList(); - - @SuppressWarnings("unchecked") List listenerElements = (List) DomUtils.getChildElementsByTagName(element, "listener"); - for(Element listenerElement : listenerElements){ - BeanDefinitionBuilder listenerBuilder = BeanDefinitionBuilder.genericBeanDefinition(JobExecutionListenerFactoryBean.class); + for(Element listenerElement : listenerElements){ + BeanDefinitionBuilder listenerBuilder = BeanDefinitionBuilder.genericBeanDefinition(JobListenerFactoryBean.class); String id = listenerElement.getAttribute("id"); String listenerRef = listenerElement.getAttribute("ref"); String className = listenerElement.getAttribute("class"); @@ -76,26 +75,25 @@ public class JobExecutionListenerParser { } else if(hasText(className)){ RootBeanDefinition beanDef = new RootBeanDefinition(className, null, null); - if (!StringUtils.hasText(id)) { - id = parserContext.getReaderContext().generateBeanName(beanDef); - } - parserContext.getRegistry().registerBeanDefinition(id, beanDef); - listenerBuilder.addPropertyReference("delegate", id); + String delegateId = parserContext.getReaderContext().generateBeanName(beanDef); + parserContext.getRegistry().registerBeanDefinition(delegateId, beanDef); + listenerBuilder.addPropertyReference("delegate", delegateId); } else { throw new BeanCreationException("Neither 'ref' or 'class' specified for <" + listenerElement.getTagName() + "> element"); } - + ManagedMap metaDataMap = new ManagedMap(); String beforeMethod = listenerElement.getAttribute("before-method"); if(StringUtils.hasText(beforeMethod)){ - listenerBuilder.addPropertyValue("beforeMethod", beforeMethod); + metaDataMap.put("beforeMethod", beforeMethod); } String afterMethod = listenerElement.getAttribute("after-method"); if(StringUtils.hasText(beforeMethod)){ - listenerBuilder.addPropertyValue("afterMethod", afterMethod); + metaDataMap.put("afterMethod", afterMethod); } + listenerBuilder.addPropertyValue("metaDataMap", metaDataMap); AbstractBeanDefinition beanDef = listenerBuilder.getBeanDefinition(); if (!StringUtils.hasText(id)) { id = parserContext.getReaderContext().generateBeanName(beanDef); @@ -106,8 +104,7 @@ public class JobExecutionListenerParser { } ManagedList managedList = new ManagedList(); - @SuppressWarnings( { "unchecked", "unused" }) - boolean dummy = managedList.addAll(listeners); + managedList.addAll(listeners); return managedList; } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/JobParser.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/JobParser.java index 8e59c28ff..691bf9a8a 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/JobParser.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/JobParser.java @@ -24,6 +24,7 @@ import org.springframework.beans.factory.support.ManagedList; import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.util.StringUtils; +import org.springframework.util.xml.DomUtils; import org.w3c.dom.Element; /** @@ -71,8 +72,11 @@ public class JobParser extends AbstractBeanDefinitionParser { builder.addPropertyValue("flow", flowDef); JobExecutionListenerParser listenerParser = new JobExecutionListenerParser(); - ManagedList managedList = listenerParser.parse(element, parserContext); - builder.addPropertyValue("jobExecutionListeners", managedList); + Element listenersElement = (Element)DomUtils.getChildElementByTagName(element, "listeners"); + if(listenersElement != null){ + ManagedList managedList = listenerParser.parse(listenersElement, parserContext); + builder.addPropertyValue("jobExecutionListeners", managedList); + } return builder.getBeanDefinition(); } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/StepParser.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/StepParser.java index 1dd8c3bf2..b7a8d327c 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/StepParser.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/StepParser.java @@ -24,7 +24,6 @@ import org.springframework.batch.core.Step; import org.springframework.batch.core.job.flow.support.StateTransition; import org.springframework.batch.core.job.flow.support.state.EndState; import org.springframework.batch.core.job.flow.support.state.StepState; -import org.springframework.batch.core.listener.JobExecutionListenerFactoryBean; import org.springframework.batch.core.listener.StepListenerFactoryBean; import org.springframework.batch.core.listener.StepListenerMetaData; import org.springframework.beans.factory.BeanCreationException; diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/listener/JobExecutionListenerAdapter.java b/spring-batch-core/src/main/java/org/springframework/batch/core/listener/JobExecutionListenerAdapter.java deleted file mode 100644 index 4e5de7793..000000000 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/listener/JobExecutionListenerAdapter.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2002-2008 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.core.listener; - -import java.lang.reflect.Method; - -import org.springframework.batch.core.JobExecution; -import org.springframework.batch.core.JobExecutionListener; -import org.springframework.batch.core.annotation.AfterJob; -import org.springframework.batch.core.annotation.BeforeJob; -import org.springframework.batch.core.configuration.util.AnnotationMethodResolver; -import org.springframework.batch.core.configuration.util.MethodInvoker; -import org.springframework.batch.core.configuration.util.MethodResolver; -import org.springframework.batch.core.configuration.util.SimpleMethodInvoker; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.util.Assert; - -/** - * {@link JobExecutionListener} implementation that adapts a delegate object to the - * {@link JobExecutionListener} interface, using either string method names, or the - * {@link BeforeJob} and {@link AfterJob} annotations. It should be noted that priority - * is given to method names. - * - * @author Lucas Ward - * @since 2.0 - */ -public class JobExecutionListenerAdapter implements JobExecutionListener, InitializingBean{ - - private final Object delegate; - - private String beforeMethod; - private MethodInvoker beforeInvoker; - - private String afterMethod; - private MethodInvoker afterInvoker; - - public JobExecutionListenerAdapter(Object delegate) { - Assert.notNull(delegate, "Delegate must not be null"); - this.delegate = delegate; - } - - public void afterPropertiesSet() throws Exception { - if(beforeMethod != null){ - beforeInvoker = new SimpleMethodInvoker(delegate, beforeMethod, JobExecution.class); - } - else{ - MethodResolver resolver = new AnnotationMethodResolver(BeforeJob.class); - Method method = resolver.findMethod(delegate); - if(method != null){ - beforeInvoker = new SimpleMethodInvoker(delegate, method); - } - } - - if(afterMethod != null){ - afterInvoker = new SimpleMethodInvoker(delegate, afterMethod, JobExecution.class); - } - else{ - MethodResolver resolver = new AnnotationMethodResolver(AfterJob.class); - Method method = resolver.findMethod(delegate); - if(method != null){ - afterInvoker = new SimpleMethodInvoker(delegate, method); - } - } - - if(beforeInvoker == null && afterInvoker == null){ - throw new IllegalArgumentException("No methods found with the provided method name or appropriate annotations"); - } - } - - public void setBeforeMethod(String beforeMethod) { - this.beforeMethod = beforeMethod; - } - - public void setAfterMethod(String afterMethod) { - this.afterMethod = afterMethod; - } - - public void afterJob(JobExecution jobExecution) { - if(afterInvoker != null){ - afterInvoker.invokeMethod(jobExecution); - } - } - - public void beforeJob(JobExecution jobExecution) { - if(beforeInvoker != null){ - beforeInvoker.invokeMethod(jobExecution); - } - } - -} diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/listener/JobExecutionListenerFactoryBean.java b/spring-batch-core/src/main/java/org/springframework/batch/core/listener/JobExecutionListenerFactoryBean.java deleted file mode 100644 index 783408b03..000000000 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/listener/JobExecutionListenerFactoryBean.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2002-2008 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.core.listener; - -import org.springframework.batch.core.JobExecutionListener; -import org.springframework.beans.factory.FactoryBean; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.util.Assert; - -/** - * {@link FactoryBean} implementation that accepts a delgate, checking whether - * or not it implements the {@link JobExecutionListener} interface, and if so, passes directly - * through. However, if it does not implement the interface, {@link JobExecutionListenerAdapter} - * is used to - * - * @author Lucas Ward - * - */ -public class JobExecutionListenerFactoryBean implements FactoryBean, InitializingBean{ - - private Object delegate; - private String beforeMethod; - private String afterMethod; - - public void setDelegate(Object delegate) { - this.delegate = delegate; - } - - public void setBeforeMethod(String beforeMethod) { - this.beforeMethod = beforeMethod; - } - - public void setAfterMethod(String afterMethod) { - this.afterMethod = afterMethod; - } - - public Object getObject() throws Exception { - if(delegate instanceof JobExecutionListener){ - return delegate; - } - else{ - JobExecutionListenerAdapter listenerAdapter = new JobExecutionListenerAdapter(delegate); - listenerAdapter.setBeforeMethod(beforeMethod); - listenerAdapter.setAfterMethod(afterMethod); - listenerAdapter.afterPropertiesSet(); - return listenerAdapter; - } - } - - @SuppressWarnings("unchecked") - public Class getObjectType() { - return JobExecutionListener.class; - } - - public boolean isSingleton() { - return false; - } - - public void afterPropertiesSet() throws Exception { - Assert.notNull(delegate, "Delegate listener must not be null"); - } -} 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 new file mode 100644 index 000000000..10c81476f --- /dev/null +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/listener/JobListenerFactoryBean.java @@ -0,0 +1,145 @@ +/* + * Copyright 2002-2008 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.core.listener; + +import static org.springframework.batch.core.configuration.util.MethodInvokerUtils.getMethodInvokerByAnnotation; +import static org.springframework.batch.core.configuration.util.MethodInvokerUtils.getMethodInvokerForInterface; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; + +import org.springframework.aop.framework.ProxyFactory; +import org.springframework.aop.support.DefaultPointcutAdvisor; +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.JobExecutionListener; +import org.springframework.batch.core.configuration.util.MethodInvoker; +import org.springframework.batch.core.configuration.util.MethodInvokerUtils; +import org.springframework.beans.factory.FactoryBean; +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: + * + *
    + *
  • 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. + * + * @author Lucas Ward + * @since 2.0 + * @see JobListenerMetaData + */ +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 { + Map> invokerMap = new HashMap>(); + 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 + 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()){ + 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(getMethodInvokerByAnnotation(metaData.getAnnotation(), delegate)); + if(!invokers.isEmpty()){ + invokerMap.put(metaData.getMethodName(), invokers); + } + } + + //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.addAdvisor(new DefaultPointcutAdvisor(new MethodInvokerMethodInterceptor(invokerMap))); + return proxyFactory.getProxy(); + } + + private MethodInvoker getMethodInvokerByName(String methodName, Object candidate, Class... params){ + if(methodName != null){ + return MethodInvokerUtils.createMethodInvokerByName(candidate, methodName, false, params); + } + else{ + return null; + } + } + + @SuppressWarnings("unchecked") + public Class getObjectType() { + return JobExecutionListener.class; + } + + public boolean isSingleton() { + return false; + } + + public void afterPropertiesSet() throws Exception { + Assert.notNull(delegate, "Delegate listener must not be null"); + } + + /* + * Extension of HashSet that ignores nulls, rather than putting them into + * the set. + */ + private static class NullIgnoringSet extends HashSet{ + + @Override + public boolean add(E e) { + if(e == null){ + return false; + } + else{ + return super.add(e); + } + }; + } +} diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/listener/JobListenerMetaData.java b/spring-batch-core/src/main/java/org/springframework/batch/core/listener/JobListenerMetaData.java new file mode 100644 index 000000000..ba1c4c36f --- /dev/null +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/listener/JobListenerMetaData.java @@ -0,0 +1,80 @@ +/* + * Copyright 2002-2008 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.core.listener; + +import java.lang.annotation.Annotation; +import java.util.HashMap; +import java.util.Map; + +import org.springframework.batch.core.JobExecutionListener; +import org.springframework.batch.core.annotation.AfterJob; +import org.springframework.batch.core.annotation.BeforeJob; + +/** + * Enumeration for {@link JobExecutionListener} meta data, which ties together the names + * of methods, their interfaces, annotation, and expected arguments. + * + * @author Lucas Ward + * @since 2.0 + * @see JobListenerFactoryBean + */ +public enum JobListenerMetaData { + + BEFORE_JOB("beforeJob", "before-job-method", BeforeJob.class), + AFTER_JOB("afterJob", "after-job-method", AfterJob.class); + + + private final String methodName; + private final String propertyName; + private final Class annotation; + private static final Map propertyMap; + + JobListenerMetaData(String methodName, String propertyName, Class annotation) { + this.methodName = methodName; + this.propertyName = propertyName; + this.annotation = annotation; + } + + static{ + propertyMap = new HashMap(); + for(JobListenerMetaData metaData : values()){ + propertyMap.put(metaData.getPropertyName(), metaData); + } + } + + public String getMethodName() { + return methodName; + } + + public Class getAnnotation() { + return annotation; + } + + + public String getPropertyName() { + return propertyName; + } + + /** + * Return the relevant meta data for the provided property name. + * + * @param propertyName + * @return meta data with supplied property name, null if none exists. + */ + public static JobListenerMetaData fromPropertyName(String propertyName){ + return propertyMap.get(propertyName); + } +} diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/listener/StepListenerMethodInterceptor.java b/spring-batch-core/src/main/java/org/springframework/batch/core/listener/MethodInvokerMethodInterceptor.java similarity index 93% rename from spring-batch-core/src/main/java/org/springframework/batch/core/listener/StepListenerMethodInterceptor.java rename to spring-batch-core/src/main/java/org/springframework/batch/core/listener/MethodInvokerMethodInterceptor.java index ab7c32be4..9c756fac6 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/listener/StepListenerMethodInterceptor.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/listener/MethodInvokerMethodInterceptor.java @@ -36,11 +36,11 @@ import org.springframework.batch.core.configuration.util.MethodInvoker; * @since 2.0 * @see MethodInvoker */ -public class StepListenerMethodInterceptor implements MethodInterceptor{ +public class MethodInvokerMethodInterceptor implements MethodInterceptor{ private final Map> invokerMap; - public StepListenerMethodInterceptor(Map> invokerMap) { + public MethodInvokerMethodInterceptor(Map> invokerMap) { this.invokerMap = invokerMap; } 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 7a12e17b2..462850238 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 @@ -95,7 +95,7 @@ public class StepListenerFactoryBean implements FactoryBean, InitializingBean{ //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 StepListenerMethodInterceptor(invokerMap))); + proxyFactory.addAdvisor(new DefaultPointcutAdvisor(new MethodInvokerMethodInterceptor(invokerMap))); return proxyFactory.getProxy(); } diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/JobExecutionListenerParserTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/JobExecutionListenerParserTests.java index 43a96f959..71a6bf054 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/JobExecutionListenerParserTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/JobExecutionListenerParserTests.java @@ -15,11 +15,17 @@ */ package org.springframework.batch.core.configuration.xml; -import junit.framework.Assert; +import static junit.framework.Assert.assertTrue; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.batch.core.Job; +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.JobParameters; +import org.springframework.batch.core.JobParametersBuilder; +import org.springframework.batch.core.annotation.AfterJob; +import org.springframework.batch.core.annotation.BeforeJob; +import org.springframework.batch.core.repository.JobRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -32,12 +38,35 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) public class JobExecutionListenerParserTests { + public static boolean beforeCalled = false; + public static boolean afterCalled = false; + @Autowired Job job; + @Autowired + JobRepository jobRepository; + @Test - public void testListners(){ - Assert.assertNotNull(job); + public void testListeners() throws Exception{ + JobExecution jobExecution = jobRepository.createJobExecution("testJob", new JobParametersBuilder().addLong("now", + System.currentTimeMillis()).toJobParameters()); + job.execute(jobExecution); + assertTrue(beforeCalled); + assertTrue(afterCalled); + } + + public static class TestComponent{ + + @BeforeJob + public void before(JobExecution jobExecution){ + beforeCalled = true; + } + + @AfterJob + public void after(){ + afterCalled = true; + } } } diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/listener/JobExecutionListenerAdapterTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/listener/JobExecutionListenerAdapterTests.java deleted file mode 100644 index 02da9fc77..000000000 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/listener/JobExecutionListenerAdapterTests.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2002-2008 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.core.listener; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import org.junit.Before; -import org.junit.Test; -import org.springframework.batch.core.JobExecution; -import org.springframework.batch.core.annotation.AfterJob; -import org.springframework.batch.core.annotation.BeforeJob; - -/** - * @author Lucas Ward - * - */ -public class JobExecutionListenerAdapterTests { - - private TestClass testClass; - private AnnotatedTestClass annotatedTestClass; - private JobExecution jobExecution = new JobExecution(11L); - - - @Before - public void setUp(){ - testClass = new TestClass(); - annotatedTestClass = new AnnotatedTestClass(); - } - - @Test - public void testBeforeJob() throws Exception{ - JobExecutionListenerAdapter adapter = new JobExecutionListenerAdapter(testClass); - adapter.setBeforeMethod("beforeJob"); - adapter.afterPropertiesSet(); - adapter.beforeJob(jobExecution); - adapter.afterJob(jobExecution); - assertTrue(testClass.beforeJobCalled); - assertFalse(testClass.afterJobCalled); - } - - @Test - public void testAfterJob() throws Exception{ - JobExecutionListenerAdapter adapter = new JobExecutionListenerAdapter(testClass); - adapter.setAfterMethod("afterJob"); - adapter.afterPropertiesSet(); - adapter.beforeJob(jobExecution); - adapter.afterJob(jobExecution); - assertFalse(testClass.beforeJobCalled); - assertTrue(testClass.afterJobCalled); - } - - @Test - public void testBoth() throws Exception{ - JobExecutionListenerAdapter adapter = new JobExecutionListenerAdapter(testClass); - adapter.setAfterMethod("afterJob"); - adapter.setBeforeMethod("beforeJob"); - adapter.afterPropertiesSet(); - adapter.beforeJob(jobExecution); - adapter.afterJob(jobExecution); - assertTrue(testClass.beforeJobCalled); - assertTrue(testClass.afterJobCalled); - } - - @Test(expected=IllegalArgumentException.class) - public void testNeither() throws Exception{ - JobExecutionListenerAdapter adapter = new JobExecutionListenerAdapter(testClass); - adapter.afterPropertiesSet(); - } - - @Test - public void testAnnotation() throws Exception{ - JobExecutionListenerAdapter adapter = new JobExecutionListenerAdapter(annotatedTestClass); - adapter.afterPropertiesSet(); - adapter.beforeJob(jobExecution); - adapter.afterJob(jobExecution); - assertTrue(annotatedTestClass.beforeJobCalled); - assertTrue(annotatedTestClass.afterJobCalled); - } - - private class TestClass{ - - boolean beforeJobCalled = false; - boolean afterJobCalled = false; - - public void beforeJob(JobExecution jobExecution){ - beforeJobCalled = true; - } - - public void afterJob(JobExecution jobExecution){ - afterJobCalled = true; - } - } - - private class AnnotatedTestClass extends TestClass{ - - @BeforeJob - public void before(){ - super.beforeJobCalled = true; - } - - @AfterJob - public void after(){ - super.afterJobCalled = true; - } - } - -} diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/listener/JobExecutionListnerFactoryBeanTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/listener/JobListenerFactoryBeanTests.java similarity index 79% rename from spring-batch-core/src/test/java/org/springframework/batch/core/listener/JobExecutionListnerFactoryBeanTests.java rename to spring-batch-core/src/test/java/org/springframework/batch/core/listener/JobListenerFactoryBeanTests.java index a16dd01c5..19b451f8d 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/listener/JobExecutionListnerFactoryBeanTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/listener/JobListenerFactoryBeanTests.java @@ -28,20 +28,25 @@ import org.springframework.batch.core.annotation.BeforeJob; * @author Lucas Ward * */ -public class JobExecutionListnerFactoryBeanTests { +public class JobListenerFactoryBeanTests { - JobExecutionListenerFactoryBean factoryBean; + JobListenerFactoryBean factoryBean; @Before public void setUp(){ - factoryBean = new JobExecutionListenerFactoryBean(); + factoryBean = new JobListenerFactoryBean(); } @Test public void testWithInterface() throws Exception{ JobListenerWithInterface delegate = new JobListenerWithInterface(); factoryBean.setDelegate(delegate); - assertEquals(delegate,factoryBean.getObject()); + JobExecutionListener listener = (JobExecutionListener) factoryBean.getObject(); + JobExecution jobExecution = new JobExecution(11L); + listener.beforeJob(jobExecution); + listener.afterJob(jobExecution); + assertTrue(delegate.beforeJobCalled); + assertTrue(delegate.afterJobCalled); } @Test @@ -58,10 +63,15 @@ public class JobExecutionListnerFactoryBeanTests { private class JobListenerWithInterface implements JobExecutionListener{ + boolean beforeJobCalled = false; + boolean afterJobCalled = false; + public void afterJob(JobExecution jobExecution) { + beforeJobCalled = true; } public void beforeJob(JobExecution jobExecution) { + afterJobCalled = true; } } diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/listener/StepListenerMethodInterceptorTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/listener/StepListenerMethodInterceptorTests.java index 503785a75..daf00c04e 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/listener/StepListenerMethodInterceptorTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/listener/StepListenerMethodInterceptorTests.java @@ -19,7 +19,7 @@ import org.springframework.batch.core.configuration.util.SimpleMethodInvoker; public class StepListenerMethodInterceptorTests { - StepListenerMethodInterceptor interceptor; + MethodInvokerMethodInterceptor interceptor; TestClass testClass; @Before @@ -34,7 +34,7 @@ public class StepListenerMethodInterceptorTests { for(Method method : TestClass.class.getMethods()){ invokerMap.put(method.getName(), asSet( new SimpleMethodInvoker(testClass, method))); } - interceptor = new StepListenerMethodInterceptor(invokerMap); + interceptor = new MethodInvokerMethodInterceptor(invokerMap); interceptor.invoke(new StubMethodInvocation(TestClass.class.getMethod("method1"))); assertEquals(1, testClass.method1Count); interceptor.invoke(new StubMethodInvocation(TestClass.class.getMethod("method2"))); @@ -48,7 +48,7 @@ public class StepListenerMethodInterceptorTests { Set invokers = asSet(MethodInvokerUtils.createMethodInvokerByName(testClass, "method1", false)); invokers.add(MethodInvokerUtils.createMethodInvokerByName(testClass, "method2", false)); invokerMap.put("method1", invokers); - interceptor = new StepListenerMethodInterceptor(invokerMap); + interceptor = new MethodInvokerMethodInterceptor(invokerMap); interceptor.invoke(new StubMethodInvocation(TestClass.class.getMethod("method1"))); assertEquals(1, testClass.method1Count); interceptor.invoke(new StubMethodInvocation(TestClass.class.getMethod("method2"))); @@ -61,7 +61,7 @@ public class StepListenerMethodInterceptorTests { Set invokers = asSet(MethodInvokerUtils.createMethodInvokerByName(testClass, "method3", false)); invokers.add(MethodInvokerUtils.createMethodInvokerByName(testClass, "method3", false)); invokerMap.put("method3", invokers); - interceptor = new StepListenerMethodInterceptor(invokerMap); + interceptor = new MethodInvokerMethodInterceptor(invokerMap); assertEquals(ExitStatus.FINISHED, interceptor.invoke(new StubMethodInvocation(TestClass.class.getMethod("method3")))); } diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/JobExecutionListenerParserTests-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/JobExecutionListenerParserTests-context.xml index 6b3238942..7594b5f35 100644 --- a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/JobExecutionListenerParserTests-context.xml +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/JobExecutionListenerParserTests-context.xml @@ -6,13 +6,14 @@ - + + @@ -21,4 +22,5 @@ + \ No newline at end of file