OPEN - issue BATCH-673: Add new Java 5.0 features
http://jira.springframework.org/browse/BATCH-673 Modified job listeners to use same approach as step listeners. Fixed test errors in samples.
This commit is contained in:
@@ -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<? extends StepListener> iFace, String methodName,
|
||||
public static MethodInvoker getMethodInvokerForInterface(Class<?> iFace, String methodName,
|
||||
Object candidate, Class<?>... params){
|
||||
|
||||
if(iFace.isAssignableFrom(candidate.getClass())){
|
||||
|
||||
@@ -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<BeanReference> listeners = new ArrayList<BeanReference>();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Element> listenerElements = (List<Element>) 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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Interface implementation: By implementing JobExecutionListener, methods on said
|
||||
* interface will be called.
|
||||
* <li>Annotations: Annotating a method will result in registration.
|
||||
* <li>String name of the method to be called, which is tied to {@link JobListenerMetaData} in the
|
||||
* metaDatMap.
|
||||
* </ul>
|
||||
*
|
||||
* 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<String, String> metaDataMap;
|
||||
|
||||
public void setDelegate(Object delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
public void setMetaDataMap(Map<String, String> metaDataMap) {
|
||||
this.metaDataMap = metaDataMap;
|
||||
}
|
||||
|
||||
public Object getObject() throws Exception {
|
||||
Map<String, Set<MethodInvoker>> invokerMap = new HashMap<String, Set<MethodInvoker>>();
|
||||
if(metaDataMap == null){
|
||||
metaDataMap = new HashMap<String, String>();
|
||||
}
|
||||
//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<String, String> entry : metaDataMap.entrySet()){
|
||||
JobListenerMetaData metaData = JobListenerMetaData.fromPropertyName(entry.getKey());
|
||||
Set<MethodInvoker> invokers = new NullIgnoringSet<MethodInvoker>();
|
||||
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<E> extends HashSet<E>{
|
||||
|
||||
@Override
|
||||
public boolean add(E e) {
|
||||
if(e == null){
|
||||
return false;
|
||||
}
|
||||
else{
|
||||
return super.add(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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<? extends Annotation> annotation;
|
||||
private static final Map<String, JobListenerMetaData> propertyMap;
|
||||
|
||||
JobListenerMetaData(String methodName, String propertyName, Class<? extends Annotation> annotation) {
|
||||
this.methodName = methodName;
|
||||
this.propertyName = propertyName;
|
||||
this.annotation = annotation;
|
||||
}
|
||||
|
||||
static{
|
||||
propertyMap = new HashMap<String, JobListenerMetaData>();
|
||||
for(JobListenerMetaData metaData : values()){
|
||||
propertyMap.put(metaData.getPropertyName(), metaData);
|
||||
}
|
||||
}
|
||||
|
||||
public String getMethodName() {
|
||||
return methodName;
|
||||
}
|
||||
|
||||
public Class<? extends Annotation> 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);
|
||||
}
|
||||
}
|
||||
@@ -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<String, Set<MethodInvoker>> invokerMap;
|
||||
|
||||
public StepListenerMethodInterceptor(Map<String, Set<MethodInvoker>> invokerMap) {
|
||||
public MethodInvokerMethodInterceptor(Map<String, Set<MethodInvoker>> invokerMap) {
|
||||
this.invokerMap = invokerMap;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<MethodInvoker> 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<MethodInvoker> 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"))));
|
||||
}
|
||||
|
||||
|
||||
@@ -6,13 +6,14 @@
|
||||
|
||||
<beans:import resource="common-context.xml" />
|
||||
|
||||
<job id="job" incrementer="testIncrementer">
|
||||
<job id="job" incrementer="testIncrementer" repository="jobRepository">
|
||||
<step name="step1">
|
||||
<simple-task tasklet="tasklet"/>
|
||||
</step>
|
||||
<listeners>
|
||||
<listener after-method="afterJob" ref="testListener"/>
|
||||
<listener after-method="afterJob" class="org.springframework.batch.core.configuration.xml.TestJobListener"/>
|
||||
<listener class="org.springframework.batch.core.configuration.xml.JobExecutionListenerParserTests$TestComponent"/>
|
||||
</listeners>
|
||||
</job>
|
||||
|
||||
@@ -21,4 +22,5 @@
|
||||
<beans:bean id="testListener" class="org.springframework.batch.core.configuration.xml.TestJobListener" />
|
||||
|
||||
<beans:bean id="testIncrementer" class="org.springframework.batch.core.configuration.xml.TestIncrementer" />
|
||||
|
||||
</beans:beans>
|
||||
Reference in New Issue
Block a user