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:
lucasward
2008-11-07 16:35:09 +00:00
parent 97ce100ff9
commit 9ac28ef106
5 changed files with 290 additions and 0 deletions

View File

@@ -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;
}
}

View File

@@ -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());
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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>