IN PROGRESS - issue BATCH-891: Create Annotations
http://jira.springframework.org/browse/BATCH-891 Added a post processor that checks for annotations. (still under development)
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package org.springframework.batch.core.annotation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.support.DefaultIntroductionAdvisor;
|
||||
import org.springframework.aop.support.DelegatingIntroductionInterceptor;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.StepExecutionListener;
|
||||
import org.springframework.batch.core.step.AbstractStep;
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
|
||||
|
||||
/**
|
||||
* @author Lucas Ward
|
||||
*
|
||||
*/
|
||||
public class StepComponentBeanPostProcessor implements BeanPostProcessor, Ordered {
|
||||
|
||||
private AbstractStep step;
|
||||
private String basePackage;
|
||||
|
||||
public StepComponentBeanPostProcessor(AbstractStep step, String basePackage) {
|
||||
this.step = step;
|
||||
this.basePackage = basePackage;
|
||||
}
|
||||
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
// if it implements the StepExecutionListener interface, we'll register
|
||||
// it directly as such
|
||||
|
||||
if (bean instanceof StepExecutionListener) {
|
||||
step.registerStepExecutionListener((StepExecutionListener) bean);
|
||||
}
|
||||
|
||||
Class<?> clazz = bean.getClass();
|
||||
if(bean instanceof FactoryBean){
|
||||
clazz = ((FactoryBean)bean).getObjectType();
|
||||
}
|
||||
|
||||
// If the class isn't of the correct package, isn't annotated with the
|
||||
// BatchComponent annotation, we won't put it in.
|
||||
if (!clazz.getName().startsWith(basePackage)
|
||||
|| AnnotationUtils.findAnnotation(clazz, BatchComponent.class) == null) {
|
||||
return bean;
|
||||
}
|
||||
|
||||
ProxyFactory proxyFactory = new ProxyFactory(
|
||||
new Class[] { StepExecutionListener.class });
|
||||
|
||||
StepExecutionListenerMixin listenerMixin = new StepExecutionListenerMixin(bean);
|
||||
|
||||
Method[] methods = clazz.getMethods();
|
||||
for (Method method : methods) {
|
||||
if(AnnotationUtils.findAnnotation(method, BeforeStep.class) != null){
|
||||
listenerMixin.setBeforeStepMethod(method);
|
||||
}
|
||||
|
||||
if(AnnotationUtils.findAnnotation(method, AfterStep.class) != null){
|
||||
listenerMixin.setAfterStepMethod(method);
|
||||
}
|
||||
}
|
||||
|
||||
proxyFactory.addAdvisor(new DefaultIntroductionAdvisor(listenerMixin, StepExecutionListener.class));
|
||||
StepExecutionListener listener = (StepExecutionListener)proxyFactory.getProxy();
|
||||
step.registerStepExecutionListener(listener);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
|
||||
return bean;
|
||||
}
|
||||
|
||||
private class StepExecutionListenerMixin extends DelegatingIntroductionInterceptor implements StepExecutionListener{
|
||||
|
||||
Object bean;
|
||||
Method afterStepMethod;
|
||||
Method beforeStepMethod;
|
||||
|
||||
public StepExecutionListenerMixin(Object bean) {
|
||||
this.bean = bean;
|
||||
}
|
||||
|
||||
public void setBeforeStepMethod(Method beforeStepMethod) {
|
||||
this.beforeStepMethod = beforeStepMethod;
|
||||
}
|
||||
|
||||
public void setAfterStepMethod(Method afterStepMethod) {
|
||||
this.afterStepMethod = afterStepMethod;
|
||||
}
|
||||
|
||||
public ExitStatus afterStep(StepExecution stepExecution) {
|
||||
if(afterStepMethod != null){
|
||||
invokeMethod(afterStepMethod, bean);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void beforeStep(StepExecution stepExecution) {
|
||||
if(beforeStepMethod != null){
|
||||
invokeMethod(beforeStepMethod, bean);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Object invokeMethod(Method method, Object bean, Object... args){
|
||||
|
||||
try {
|
||||
return method.invoke(bean, args);
|
||||
} catch (Exception e) {
|
||||
if(e instanceof RuntimeException){
|
||||
throw (RuntimeException)e;
|
||||
}
|
||||
throw new IllegalArgumentException("Failed to invoke method: [" + method + "]");
|
||||
}
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return Ordered.LOWEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.springframework.batch.core.annotation;
|
||||
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.batch.core.JobParameters;
|
||||
import org.springframework.batch.core.job.SimpleJob;
|
||||
import org.springframework.batch.core.launch.JobLauncher;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations={"component-scan-context.xml"})
|
||||
public class StepComponentBeanPostProcessorIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
private SimpleJob job;
|
||||
|
||||
@Autowired
|
||||
private JobLauncher jobLauncher;
|
||||
|
||||
@Test
|
||||
public void testListener() throws Exception{
|
||||
|
||||
jobLauncher.run(job, new JobParameters());
|
||||
|
||||
// assertTrue(TestComponent.isAfterStepCalled());
|
||||
// assertTrue(TestComponent.isBeforeStepCalled());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package org.springframework.batch.core.annotation;
|
||||
|
||||
import static org.easymock.EasyMock.createMock;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.core.step.AbstractStep;
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
|
||||
|
||||
/**
|
||||
* @author Lucas Ward
|
||||
*
|
||||
*/
|
||||
public class StepComponentBeanPostProcessorTests {
|
||||
|
||||
@BatchComponent
|
||||
private class TestComponent {
|
||||
|
||||
boolean afterStepCalled = false;
|
||||
|
||||
@AfterStep
|
||||
public void testMethod(){
|
||||
afterStepCalled = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNormalCase() throws Exception{
|
||||
AbstractStep step = new StubStep();
|
||||
step.setJobRepository(createMock(JobRepository.class));
|
||||
StepComponentBeanPostProcessor postProcessor = new StepComponentBeanPostProcessor(step, "org.springframework.batch.core.annotation");
|
||||
TestComponent testComponent = new TestComponent();
|
||||
postProcessor.postProcessAfterInitialization(testComponent, "testComponent");
|
||||
step.execute(new StepExecution("teststep", new JobExecution(11L)));
|
||||
assertTrue(testComponent.afterStepCalled);
|
||||
}
|
||||
|
||||
private class StubStep extends AbstractStep{
|
||||
|
||||
@Override
|
||||
protected ExitStatus doExecute(StepExecution stepExecution)
|
||||
throws Exception {
|
||||
// TODO Auto-generated method stub
|
||||
return ExitStatus.FINISHED;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -10,4 +10,25 @@ package org.springframework.batch.core.annotation;
|
||||
@BatchComponent
|
||||
public class TestComponent {
|
||||
|
||||
private static boolean beforeStepCalled = false;
|
||||
|
||||
private static boolean afterStepCalled = false;
|
||||
|
||||
public static boolean isBeforeStepCalled(){
|
||||
return beforeStepCalled;
|
||||
}
|
||||
|
||||
public static boolean isAfterStepCalled(){
|
||||
return afterStepCalled;
|
||||
}
|
||||
|
||||
@BeforeStep
|
||||
public void beforeStep(){
|
||||
beforeStepCalled = true;
|
||||
}
|
||||
|
||||
@AfterStep
|
||||
public void afterStep(){
|
||||
afterStepCalled = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
|
||||
|
||||
<context:component-scan base-package="org.springframework.batch.core.annotation"
|
||||
scope-resolver="org.springframework.batch.core.annotation.BatchComponentScopeMetaDataResolver"
|
||||
>
|
||||
<context:include-filter type="annotation" expression="org.springframework.batch.core.annotation.BatchComponent"/>
|
||||
|
||||
</context:component-scan>
|
||||
|
||||
<bean id="job" class="org.springframework.batch.core.job.SimpleJob" >
|
||||
<property name="steps">
|
||||
<list>
|
||||
<ref bean="step"/>
|
||||
</list>
|
||||
</property>
|
||||
<property name="jobRepository" ref="jobRepository" />
|
||||
</bean>
|
||||
|
||||
<bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean" >
|
||||
<property name="transactionManager">
|
||||
<bean class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="step" class="org.springframework.batch.core.step.tasklet.TaskletStep" >
|
||||
<property name="tasklet">
|
||||
<bean class="org.springframework.batch.core.configuration.xml.DummyTasklet" />
|
||||
</property>
|
||||
<property name="jobRepository" ref="jobRepository" />
|
||||
<property name="transactionManager">
|
||||
<bean class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean class="org.springframework.batch.core.annotation.StepComponentBeanPostProcessor" >
|
||||
<constructor-arg ref="step" />
|
||||
<constructor-arg value="org.springframework.batch.core.annotation" />
|
||||
</bean>
|
||||
|
||||
<bean class="org.springframework.batch.core.launch.support.SimpleJobLauncher" >
|
||||
<property name="jobRepository" ref="jobRepository" />
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
Reference in New Issue
Block a user