diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/annotation/BeforeJob.java b/spring-batch-core/src/main/java/org/springframework/batch/core/annotation/BeforeJob.java index ebc071adf..ba531acce 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/annotation/BeforeJob.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/annotation/BeforeJob.java @@ -12,6 +12,7 @@ import org.springframework.batch.core.Job; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.JobExecutionListener; import org.springframework.batch.core.Step; +import org.springframework.beans.factory.annotation.Qualifier; /** * Marks a method to be called before a {@link Job} is executed, which comes @@ -24,6 +25,7 @@ import org.springframework.batch.core.Step; */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) +@Qualifier("JobExecutionListener") public @interface BeforeJob { } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/util/AnnotationMethodResolver.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/util/AnnotationMethodResolver.java new file mode 100644 index 000000000..37dd88a00 --- /dev/null +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/util/AnnotationMethodResolver.java @@ -0,0 +1,104 @@ +/* + * 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.configuration.util; + +import java.lang.annotation.Annotation; +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; +import java.lang.reflect.Method; +import java.util.concurrent.atomic.AtomicReference; + +import org.springframework.aop.support.AopUtils; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; +import org.springframework.util.ReflectionUtils; + +/** + * MethodResolver implementation that finds a single Method on the + * given Class that contains the specified annotation type. + * + * @author Mark Fisher + */ +public class AnnotationMethodResolver implements MethodResolver { + + private Class annotationType; + + + /** + * Create a MethodResolver for the specified Method-level annotation type + */ + public AnnotationMethodResolver(Class annotationType) { + Assert.notNull(annotationType, "annotationType must not be null"); + Assert.isTrue(ObjectUtils.containsElement( + annotationType.getAnnotation(Target.class).value(), ElementType.METHOD), + "Annotation [" + annotationType + "] is not a Method-level annotation."); + this.annotationType = annotationType; + } + + + /** + * Find a single Method on the Class of the given candidate object + * that contains the annotation type for which this resolver is searching. + * + * @param candidate the instance whose Class will be checked for the + * annotation + * + * @return a single matching Method instance or null if the + * candidate's Class contains no Methods with the specified annotation + * + * @throws IllegalArgumentException if more than one Method has the + * specified annotation + */ + public Method findMethod(Object candidate) { + Assert.notNull(candidate, "candidate object must not be null"); + Class targetClass = AopUtils.getTargetClass(candidate); + if (targetClass == null) { + targetClass = candidate.getClass(); + } + return this.findMethod(targetClass); + } + + /** + * Find a single Method on the given Class that contains the + * annotation type for which this resolver is searching. + * + * @param clazz the Class instance to check for the annotation + * + * @return a single matching Method instance or null if the + * Class contains no Methods with the specified annotation + * + * @throws IllegalArgumentException if more than one Method has the + * specified annotation + */ + public Method findMethod(final Class clazz) { + Assert.notNull(clazz, "class must not be null"); + final AtomicReference annotatedMethod = new AtomicReference(); + ReflectionUtils.doWithMethods(clazz, new ReflectionUtils.MethodCallback() { + public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException { + Annotation annotation = AnnotationUtils.findAnnotation(method, annotationType); + if (annotation != null) { + Assert.isNull(annotatedMethod.get(), "found more than one method on target class [" + + clazz + "] with the annotation type [" + annotationType + "]"); + annotatedMethod.set(method); + } + } + }); + return annotatedMethod.get(); + } + +} diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/util/MethodInvoker.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/util/MethodInvoker.java new file mode 100644 index 000000000..0b797095a --- /dev/null +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/util/MethodInvoker.java @@ -0,0 +1,29 @@ +/* + * 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.configuration.util; + +/** + * A strategy interface for invoking a method. + * Typically used by adapters. + * + * @author Mark Fisher + */ +public interface MethodInvoker { + + Object invokeMethod(Object ... args); + +} diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/util/MethodResolver.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/util/MethodResolver.java new file mode 100644 index 000000000..cf63278ff --- /dev/null +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/util/MethodResolver.java @@ -0,0 +1,57 @@ +/* + * 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.configuration.util; + +import java.lang.reflect.Method; + +/** + * Strategy interface for detecting a single Method on a Class. + * + * @author Mark Fisher + */ +public interface MethodResolver { + + /** + * Find a single Method on the provided Object that matches this resolver's + * criteria. + * + * @param candidate the candidate Object whose Class should be searched for + * a Method + * + * @return a single Method or null if no Method matching this + * resolver's criteria can be found. + * + * @throws IllegalArgumentException if more than one Method defined on the + * given candidate's Class matches this resolver's criteria + */ + Method findMethod(Object candidate) throws IllegalArgumentException; + + /** + * Find a single Method on the given Class that matches this + * resolver's criteria. + * + * @param clazz the Class instance on which to search for a Method + * + * @return a single Method or null if no Method matching this + * resolver's criteria can be found. + * + * @throws IllegalArgumentException if more than one Method defined on the + * given Class matches this resolver's criteria + */ + Method findMethod(Class clazz); + +} diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/util/SimpleMethodInvoker.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/util/SimpleMethodInvoker.java new file mode 100644 index 000000000..6f6a6f68e --- /dev/null +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/util/SimpleMethodInvoker.java @@ -0,0 +1,96 @@ +/* + * 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. + */ + +/* + * 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.configuration.util; + +import java.lang.reflect.Method; + +import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; + +/** + * Simple implementation of the {@link MethodInvoker} interface that invokes a method on an + * object. If the method has no arguments, but arguments are provided, they are ignored and the method + * is invoked anyway. If there are more arguments than there are provided, then an exception is thrown. + * + * @author Lucas Ward + * @since 2.0 + */ +public class SimpleMethodInvoker implements MethodInvoker { + + private final Object object; + private Method method; + + public SimpleMethodInvoker(Object object, Method method) { + Assert.notNull(object, "Object to invoke must not be null"); + Assert.notNull(method, "Method to invoke must not be null"); + this.object = object; + this.method = method; + } + + public SimpleMethodInvoker(Object object, String methodName, Class... paramTypes){ + Assert.notNull(object, "Object to invoke must not be null"); + this.object = object; + this.method = ClassUtils.getMethodIfAvailable(object.getClass(), methodName, paramTypes); + if(this.method == null){ + //try with no params + this.method = ClassUtils.getMethodIfAvailable(object.getClass(), methodName, new Class[]{}); + } + if(this.method == null){ + throw new IllegalArgumentException("No methods found for name: [" + methodName + "] in class: [" + + object.getClass() + "] with arguments of type: [" + paramTypes + "]"); + } + } + + /* (non-Javadoc) + * @see org.springframework.batch.core.configuration.util.MethodInvoker#invokeMethod(java.lang.Object[]) + */ + public Object invokeMethod(Object... args) { + + Class[] parameterTypes = method.getParameterTypes(); + Object[] invokeArgs = new Object[parameterTypes.length]; + if(parameterTypes.length == 0){ + invokeArgs = new Object[]{}; + } + else if(parameterTypes.length > args.length){ + throw new IllegalArgumentException("Wrong number of arguments, expected no more than: [" + parameterTypes.length + "]"); + } + + method.setAccessible(true); + + try { + return method.invoke(object, invokeArgs); + } catch (Exception e) { + throw new IllegalArgumentException("Unable to invoke method: [" + method + "] on object: [" + + object + "] with arguments: [" + args + "]"); + } + } +} 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 new file mode 100644 index 000000000..0f92d4064 --- /dev/null +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/xml/JobExecutionListenerParser.java @@ -0,0 +1,81 @@ +/* + * 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.configuration.xml; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.batch.core.JobExecutionListener; +import org.springframework.batch.core.listener.JobExecutionListenerAdapter; +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.xml.BeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.util.StringUtils; +import org.springframework.util.xml.DomUtils; +import org.w3c.dom.Element; + +/** + * {@link BeanDefinitionParser} for {@link JobExecutionListener}s + * + * @author Lucas Ward + * + */ +public class JobExecutionListenerParser { + + + 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(JobExecutionListenerAdapter.class); + String delegateName = listenerElement.getAttribute("ref"); + listenerBuilder.addConstructorArgReference(delegateName); + + String beforeMethod = listenerElement.getAttribute("before-method"); + if(StringUtils.hasText(beforeMethod)){ + listenerBuilder.addPropertyValue("beforeMethod", beforeMethod); + } + + String afterMethod = listenerElement.getAttribute("after-method"); + if(StringUtils.hasText(beforeMethod)){ + listenerBuilder.addPropertyValue("afterMethod", afterMethod); + } + AbstractBeanDefinition beanDef = listenerBuilder.getBeanDefinition(); + String id = listenerElement.getAttribute("id"); + if (!StringUtils.hasText(id)) { + id = parserContext.getReaderContext().generateBeanName(beanDef); + } + parserContext.getRegistry().registerBeanDefinition(id, beanDef); + BeanReference bean = new RuntimeBeanReference(id); + listeners.add(bean); + } + + ManagedList managedList = new ManagedList(); + @SuppressWarnings( { "unchecked", "unused" }) + boolean dummy = 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 a475ae384..3579562be 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 @@ -20,6 +20,7 @@ import org.springframework.batch.core.job.flow.FlowJob; import org.springframework.batch.core.repository.JobRepository; 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.xml.AbstractBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.util.StringUtils; @@ -62,9 +63,12 @@ public class JobParser extends AbstractBeanDefinitionParser { FlowParser flowParser = new FlowParser(); AbstractBeanDefinition flowDef = flowParser.parse(element, parserContext, jobName); - builder.addPropertyValue("flow", flowDef); - + + JobExecutionListenerParser listenerParser = new JobExecutionListenerParser(); + ManagedList managedList = listenerParser.parse(element, parserContext); + builder.addPropertyValue("jobExecutionListeners", managedList); + return builder.getBeanDefinition(); } 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 new file mode 100644 index 000000000..4e5de7793 --- /dev/null +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/listener/JobExecutionListenerAdapter.java @@ -0,0 +1,104 @@ +/* + * 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/resources/org/springframework/batch/core/configuration/xml/spring-batch-2.0.xsd b/spring-batch-core/src/main/resources/org/springframework/batch/core/configuration/xml/spring-batch-2.0.xsd index 2e4d140b3..3eb398659 100644 --- a/spring-batch-core/src/main/resources/org/springframework/batch/core/configuration/xml/spring-batch-2.0.xsd +++ b/spring-batch-core/src/main/resources/org/springframework/batch/core/configuration/xml/spring-batch-2.0.xsd @@ -20,8 +20,10 @@ Defines a job composed of a set of steps and transitions between steps. The job will be exposed - in the enclosing bean factory as a component of type Job - that can be launched using a JobLauncher. + in the enclosing + bean factory as a component of type Job + that can be launched using a + JobLauncher. @@ -37,7 +39,8 @@ - + - + @@ -104,8 +107,10 @@ - Defines a stage in job processing backed by a Step. The name - attribute has to match the id of a bean definition for a Step. + Defines a stage in job processing backed by a + Step. The name + attribute has to match the id of a bean definition + for a Step. The next attribute is a synonym for <next on="*" .../> @@ -167,6 +172,28 @@ + + + + + + + + + + + + + + + + + + + @@ -492,4 +519,10 @@ + + + + + + \ No newline at end of file diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/util/SimpleMethodInvokerTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/util/SimpleMethodInvokerTests.java new file mode 100644 index 000000000..feb41f6da --- /dev/null +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/util/SimpleMethodInvokerTests.java @@ -0,0 +1,120 @@ +/* + * 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. + */ + +/* + * 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.configuration.util; + +import static org.junit.Assert.*; + +import java.lang.reflect.Method; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.JobExecutionListener; + +/** + * @author Lucas Ward + * + */ +public class SimpleMethodInvokerTests { + + TestClass testClass; + JobExecution jobExecution = new JobExecution(11L); + + @Before + public void setUp(){ + testClass = new TestClass(); + } + + @Test + public void testMethod() throws Exception{ + + Method method = TestClass.class.getMethod("beforeJob"); + MethodInvoker methodInvoker = new SimpleMethodInvoker(testClass, method); + methodInvoker.invokeMethod(jobExecution); + assertTrue(testClass.beforeJobCalled); + } + + @Test + public void testMethodByName() throws Exception{ + + MethodInvoker methodInvoker = new SimpleMethodInvoker(testClass, "beforeJob", JobExecutionListener.class); + methodInvoker.invokeMethod(jobExecution); + assertTrue(testClass.beforeJobCalled); + } + + @Test + public void testMethodWithExecution() throws Exception{ + Method method = TestClass.class.getMethod("beforeJobWithExecution", JobExecution.class); + MethodInvoker methodInvoker = new SimpleMethodInvoker(testClass, method); + methodInvoker.invokeMethod(jobExecution); + assertTrue(testClass.beforeJobCalled); + } + + @Test + public void testMethodByNameWithExecution() throws Exception{ + MethodInvoker methodInvoker = new SimpleMethodInvoker(testClass, "beforeJobWithExecution", JobExecution.class); + methodInvoker.invokeMethod(jobExecution); + assertTrue(testClass.beforeJobCalled); + } + + @Test(expected=IllegalArgumentException.class) + public void testMethodWithTooManyArguments() throws Exception{ + Method method = TestClass.class.getMethod("beforeJobWithTooManyArguments", JobExecution.class, int.class); + MethodInvoker methodInvoker = new SimpleMethodInvoker(testClass, method); + methodInvoker.invokeMethod(jobExecution); + assertFalse(testClass.beforeJobCalled); + } + + @Test(expected=IllegalArgumentException.class) + public void testMethodByNameWithTooManyArguments() throws Exception{ + MethodInvoker methodInvoker = new SimpleMethodInvoker(testClass, "beforeJobWithTooManyArguments", JobExecution.class); + methodInvoker.invokeMethod(jobExecution); + assertFalse(testClass.beforeJobCalled); + } + + private class TestClass{ + + boolean beforeJobCalled = false; + + public void beforeJob(){ + beforeJobCalled = true; + } + + public void beforeJobWithExecution(JobExecution jobExecution){ + beforeJobCalled = true; + } + + public void beforeJobWithTooManyArguments(JobExecution jobExecution, int someInt){ + beforeJobCalled = true; + } + } +} 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 new file mode 100644 index 000000000..43a96f959 --- /dev/null +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/JobExecutionListenerParserTests.java @@ -0,0 +1,43 @@ +/* + * 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.configuration.xml; + +import junit.framework.Assert; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.batch.core.Job; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @author Lucas Ward + * + */ +@ContextConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +public class JobExecutionListenerParserTests { + + @Autowired + Job job; + + @Test + public void testListners(){ + Assert.assertNotNull(job); + } + +} diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/TestJobListener.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/TestJobListener.java new file mode 100644 index 000000000..4b14b0725 --- /dev/null +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/xml/TestJobListener.java @@ -0,0 +1,15 @@ +package org.springframework.batch.core.configuration.xml; + +import org.springframework.batch.core.annotation.BeforeJob; + +public class TestJobListener { + + @BeforeJob + public void beforeJob(){ + + } + + public void afterJob(){ + + } +} 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 new file mode 100644 index 000000000..97367c7a8 --- /dev/null +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/listener/JobExecutionListenerAdapterTests.java @@ -0,0 +1,119 @@ +/* + * 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/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 new file mode 100644 index 000000000..547e0b16c --- /dev/null +++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/xml/JobExecutionListenerParserTests-context.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file