From 514f4ad30085178aa5ba7edc4e95252fa1502a75 Mon Sep 17 00:00:00 2001 From: lucasward Date: Thu, 30 Oct 2008 04:38:27 +0000 Subject: [PATCH] OPEN - issue BATCH-891: Create Annotations http://jira.springframework.org/browse/BATCH-891 A first try at using a BeanPostProcessor to detect annotated listeners. --- ...obListenerAnnotationBeanPostProcessor.java | 127 ++++++++++++++++++ ...tenerAnnotationBeanPostProcessorTests.java | 91 +++++++++++++ 2 files changed, 218 insertions(+) create mode 100644 spring-batch-core/src/main/java/org/springframework/batch/core/job/JobListenerAnnotationBeanPostProcessor.java create mode 100644 spring-batch-core/src/test/java/org/springframework/batch/core/job/JobListenerAnnotationBeanPostProcessorTests.java diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/job/JobListenerAnnotationBeanPostProcessor.java b/spring-batch-core/src/main/java/org/springframework/batch/core/job/JobListenerAnnotationBeanPostProcessor.java new file mode 100644 index 000000000..1d5fb05e8 --- /dev/null +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/job/JobListenerAnnotationBeanPostProcessor.java @@ -0,0 +1,127 @@ +/** + * + */ +package org.springframework.batch.core.job; + +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.listener.JobExecutionListenerSupport; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.core.annotation.AnnotationUtils; + + +/** + * {@link BeanPostProcessor} that registers any declared beans to the provided job + * that have any methods annotated with {@link BeforeJob} or {@link AfterJob} + * + * @author Lucas Ward + * @since 2.0 + * @see SimpleJob + */ +public class JobListenerAnnotationBeanPostProcessor implements BeanPostProcessor { + + private AbstractJob job; + + public JobListenerAnnotationBeanPostProcessor(AbstractJob job) { + this.job = job; + } + + public Object postProcessAfterInitialization(Object bean, String beanName) + throws BeansException { + return null; + } + + public Object postProcessBeforeInitialization(Object bean, String beanName) + throws BeansException { + + for(Method method : bean.getClass().getMethods()){ + BeforeJob beforeJob = AnnotationUtils.findAnnotation(method, BeforeJob.class); + if(beforeJob != null){ + JobExecutionListener listener = new BeforeJobProxy(method, bean, supportsJobExecutionPassing(method)); + job.registerJobExecutionListener(listener); + } + AfterJob afterJob = AnnotationUtils.findAnnotation(method, AfterJob.class); + if(afterJob != null){ + JobExecutionListener listener = new AfterJobProxy(method, bean, supportsJobExecutionPassing(method)); + job.registerJobExecutionListener(listener); + } + } + + return null; + } + + private class BeforeJobProxy extends JobExecutionListenerSupport{ + + Method method; + Object bean; + boolean passThroughExecution; + + public BeforeJobProxy(Method method, Object bean, boolean passThroughExecution) { + this.method = method; + this.bean = bean; + this.passThroughExecution = passThroughExecution; + } + + @Override + public void beforeJob(JobExecution jobExecution) { + try{ + if(passThroughExecution){ + method.invoke(bean, jobExecution); + } + else{ + method.invoke(bean); + } + } + catch(Exception ex){ + throw new IllegalStateException("Unable to invoke annotated method: [" + method, ex); + } + } + } + + private class AfterJobProxy extends JobExecutionListenerSupport{ + + Method method; + Object bean; + boolean passThroughExecution; + + public AfterJobProxy(Method method, Object bean, boolean passThroughExecution) { + this.method = method; + this.bean = bean; + this.passThroughExecution = passThroughExecution; + } + + @Override + public void afterJob(JobExecution jobExecution) { + try{ + if(passThroughExecution){ + method.invoke(bean, jobExecution); + } + else{ + method.invoke(bean); + } + } + catch(Exception ex){ + throw new IllegalStateException("Unable to invoke annotated method: [" + method, ex); + } + } + } + + private boolean supportsJobExecutionPassing(Method method){ + Class[] parameters = method.getParameterTypes(); + if(parameters.length == 0 || parameters.length > 1){ + return false; + } + + Class parameter = parameters[0]; + if(parameter == JobExecution.class){ + return true; + } + + return false; + } +} diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/job/JobListenerAnnotationBeanPostProcessorTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/job/JobListenerAnnotationBeanPostProcessorTests.java new file mode 100644 index 000000000..c9bb2e898 --- /dev/null +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/job/JobListenerAnnotationBeanPostProcessorTests.java @@ -0,0 +1,91 @@ +/** + * + */ +package org.springframework.batch.core.job; + +import static org.easymock.EasyMock.createMock; +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.JobExecutionException; +import org.springframework.batch.core.StepExecution; +import org.springframework.batch.core.annotation.AfterJob; +import org.springframework.batch.core.annotation.BeforeJob; +import org.springframework.batch.core.repository.JobRepository; + +/** + * @author Lucas Ward + * + */ +public class JobListenerAnnotationBeanPostProcessorTests { + + JobListenerAnnotationBeanPostProcessor postProcessor; + AnnotatedClassStub annotatedClass; + StubJob job; + JobExecution jobExecution; + + @Before + public void init(){ + job = new StubJob(); + annotatedClass = new AnnotatedClassStub(); + postProcessor = new JobListenerAnnotationBeanPostProcessor(job); + jobExecution = new JobExecution(1L); + } + + @Test + public void testBeforeJob(){ + + postProcessor.postProcessBeforeInitialization(annotatedClass, "test"); + job.execute(jobExecution); + assertTrue(annotatedClass.beforeJobCalled); + assertTrue(annotatedClass.beforeJobWithExecutionCalled); + assertTrue(annotatedClass.afterJobCalled); + assertTrue(annotatedClass.afterJobWithExecutionCalled); + } + + private class AnnotatedClassStub{ + + boolean beforeJobCalled = false; + boolean beforeJobWithExecutionCalled = false; + + boolean afterJobCalled = false; + boolean afterJobWithExecutionCalled = false; + + @BeforeJob + public void beforeJobMethod(){ + beforeJobCalled = true; + }; + + @BeforeJob + public void beforeJobMethodWithExecution(JobExecution jobExecution){ + beforeJobWithExecutionCalled = true; + } + + @AfterJob + public void afterJobMethod(){ + afterJobCalled = true; + } + + @AfterJob + public void afterJobMethodWithExecution(JobExecution jobExecution){ + afterJobWithExecutionCalled = true; + } + } + + private class StubJob extends AbstractJob{ + + public StubJob() { + super.setJobRepository(createMock(JobRepository.class)); + } + + @Override + protected StepExecution doExecute(JobExecution execution) + throws JobExecutionException { + return null; + } + + + } +}