OPEN - issue BATCH-673: Add new Java 5.0 features
http://jira.springframework.org/browse/BATCH-673 First draft of StepListener annotation support.
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Marks a method to be called after a chunk is executed.
|
||||
*
|
||||
* @author Lucas Ward
|
||||
* @since 2.0
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD})
|
||||
public @interface AfterChunk {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.springframework.batch.core.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.batch.item.ItemProcessor;
|
||||
|
||||
/**
|
||||
* Marks a method to be called after an item is passed to an {@link ItemProcessor}
|
||||
*
|
||||
* @author Lucas Ward
|
||||
* @since 2.0
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD})
|
||||
public @interface AfterProcess {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
|
||||
/**
|
||||
* Marks a method to be called after an item is read from an {@link ItemReader}
|
||||
*
|
||||
* @author Lucas Ward
|
||||
* @since 2.0
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD})
|
||||
public @interface AfterRead {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.springframework.batch.core.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.batch.item.ItemWriter;
|
||||
|
||||
/**
|
||||
* Marks a method to be called after an item is passed to an {@link ItemWriter}
|
||||
*
|
||||
* @author Lucas Ward
|
||||
* @since 2.0
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD})
|
||||
public @interface AfterWrite {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Marks a method to be called before a chunk is executed.
|
||||
*
|
||||
* @author Lucas Ward
|
||||
* @since 2.0
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD})
|
||||
public @interface BeforeChunk {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.batch.item.ItemProcessor;
|
||||
|
||||
/**
|
||||
* Marks a method to be called before an item is passed to an {@link ItemProcessor}
|
||||
*
|
||||
* @author Lucas Ward
|
||||
* @since 2.0
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD})
|
||||
public @interface BeforeProcess {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
|
||||
/**
|
||||
* Marks a method to be called before an item is read from an {@link ItemReader}
|
||||
*
|
||||
* @author Lucas Ward
|
||||
* @since 2.0
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD})
|
||||
public @interface BeforeRead {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.springframework.batch.core.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.batch.item.ItemWriter;
|
||||
|
||||
/**
|
||||
* Marks a method to be called before an item is passed to an {@link ItemWriter}
|
||||
*
|
||||
* @author Lucas Ward
|
||||
* @since 2.0
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD})
|
||||
public @interface BeforeWrite {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.springframework.batch.core.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.batch.item.ItemProcessor;
|
||||
|
||||
/**
|
||||
* Marks a method to be called if an exception is thrown by an {@link ItemProcessor}
|
||||
*
|
||||
* @author Lucas Ward
|
||||
* @since 2.0
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD})
|
||||
public @interface OnProcessError {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
|
||||
/**
|
||||
* Marks a method to be called if an exception is thrown by an {@link ItemReader}
|
||||
*
|
||||
* @author Lucas Ward
|
||||
* @since 2.0
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD})
|
||||
public @interface OnReadError {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.springframework.batch.core.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.batch.item.ItemProcessor;
|
||||
|
||||
/**
|
||||
* Marks a method to be called when an item is skipped due to an exception thrown in the
|
||||
* {@link ItemProcessor}
|
||||
*
|
||||
* @author Lucas Ward
|
||||
* @since 2.0
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD})
|
||||
public @interface OnSkipInProcess {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.springframework.batch.core.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
|
||||
/**
|
||||
* Marks a method to be called when an item is skipped due to an exception thrown in the
|
||||
* {@link ItemReader}
|
||||
*
|
||||
* @author Lucas Ward
|
||||
* @since 2.0
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD})
|
||||
public @interface OnSkipInRead {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.springframework.batch.core.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.batch.item.ItemWriter;
|
||||
|
||||
/**
|
||||
* Marks a method to be called when an item is skipped due to an exception thrown in the
|
||||
* {@link ItemWriter}
|
||||
*
|
||||
* @author Lucas Ward
|
||||
* @since 2.0
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD})
|
||||
public @interface OnSkipInWrite {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.springframework.batch.core.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.batch.item.ItemWriter;
|
||||
|
||||
/**
|
||||
* Marks a method to be called if an exception is thrown by an {@link ItemWriter}
|
||||
*
|
||||
* @author Lucas Ward
|
||||
* @since 2.0
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD})
|
||||
public @interface OnWriteError {
|
||||
|
||||
}
|
||||
@@ -33,6 +33,7 @@ package org.springframework.batch.core.configuration.util;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.apache.commons.lang.builder.HashCodeBuilder;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
@@ -49,6 +50,22 @@ public class SimpleMethodInvoker implements MethodInvoker {
|
||||
private final Object object;
|
||||
private Method method;
|
||||
|
||||
public static MethodInvoker createMethodInvokerByName(Object object, String methodName, boolean paramsRequired, Class<?>... paramTypes){
|
||||
Assert.notNull(object, "Object to invoke must not be null");
|
||||
Method method = ClassUtils.getMethodIfAvailable(object.getClass(), methodName, paramTypes);
|
||||
//if no method was found for the given parameters, and the parameters aren't required
|
||||
if(method == null && !paramsRequired){
|
||||
//try with no params
|
||||
method = ClassUtils.getMethodIfAvailable(object.getClass(), methodName, new Class[]{});
|
||||
}
|
||||
if(method == null){
|
||||
return null;
|
||||
}
|
||||
else{
|
||||
return new SimpleMethodInvoker(object, 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");
|
||||
@@ -93,4 +110,22 @@ public class SimpleMethodInvoker implements MethodInvoker {
|
||||
object + "] with arguments: [" + args + "]");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if(!(obj instanceof SimpleMethodInvoker)){
|
||||
return false;
|
||||
}
|
||||
|
||||
if(obj == this){
|
||||
return true;
|
||||
}
|
||||
SimpleMethodInvoker rhs = (SimpleMethodInvoker) obj;
|
||||
return (rhs.method == this.method) && (rhs.object == this.object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return new HashCodeBuilder(25, 37).append(object).append(method).toHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.batch.core.ChunkListener;
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
import org.springframework.batch.core.ItemProcessListener;
|
||||
import org.springframework.batch.core.ItemReadListener;
|
||||
import org.springframework.batch.core.ItemWriteListener;
|
||||
import org.springframework.batch.core.SkipListener;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.StepExecutionListener;
|
||||
import org.springframework.batch.core.configuration.util.MethodInvoker;
|
||||
|
||||
/**
|
||||
* @author Lucas Ward
|
||||
*
|
||||
*/
|
||||
public class StepExecutionListenerAdapter<T, S> implements StepExecutionListener, ChunkListener,
|
||||
ItemReadListener<T>, ItemWriteListener<S>, ItemProcessListener<T, S>, SkipListener<T, S>{
|
||||
|
||||
private Set<MethodInvoker> beforeStepMethodInvokers = new HashSet<MethodInvoker>();
|
||||
private Set<MethodInvoker> afterMethodInvokers = new HashSet<MethodInvoker>();
|
||||
|
||||
public void setAfterMethodInvokers(Set<MethodInvoker> afterMethodInvokers) {
|
||||
this.afterMethodInvokers = afterMethodInvokers;
|
||||
}
|
||||
|
||||
public void setBeforeMethodInvokers(Set<MethodInvoker> beforeMethodInvokers) {
|
||||
this.beforeStepMethodInvokers = beforeMethodInvokers;
|
||||
}
|
||||
|
||||
public ExitStatus afterStep(StepExecution stepExecution) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void beforeStep(StepExecution stepExecution) {
|
||||
for(MethodInvoker invoker : beforeStepMethodInvokers){
|
||||
invoker.invokeMethod(stepExecution);
|
||||
}
|
||||
}
|
||||
|
||||
public void afterChunk() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
public void beforeChunk() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
public void afterRead(T item) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
public void beforeRead() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
public void onReadError(Exception ex) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
public void afterWrite(List<? extends S> items) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
public void beforeWrite(List<? extends S> items) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
public void onWriteError(Exception exception, List<? extends S> items) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
public void afterProcess(T item, S result) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
public void beforeProcess(T item) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
public void onProcessError(T item, Exception e) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
public void onSkipInProcess(T item, Throwable t) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
public void onSkipInRead(Throwable t) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
public void onSkipInWrite(S item, Throwable t) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* 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.lang.reflect.Method;
|
||||
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.StepListener;
|
||||
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.FactoryBean;
|
||||
|
||||
/**
|
||||
* {@link FactoryBean} implementation that builds a {@link StepListener} based on the
|
||||
* various lifecycle methods or annotations that are provided.
|
||||
*
|
||||
* @author Lucas Ward
|
||||
*
|
||||
*/
|
||||
public class StepListenerFactoryBean implements FactoryBean{
|
||||
|
||||
private Object delegate;
|
||||
private Map<StepListenerMetaData, String> metaDataMap;
|
||||
|
||||
public Object getObject() throws Exception {
|
||||
|
||||
Map<String, Set<MethodInvoker>> invokerMap = new HashMap<String, Set<MethodInvoker>>();
|
||||
if(metaDataMap == null){
|
||||
metaDataMap = new HashMap<StepListenerMetaData, String>();
|
||||
}
|
||||
for(StepListenerMetaData metaData : StepListenerMetaData.values()){
|
||||
if(!metaDataMap.containsKey(metaData)){
|
||||
//put null so that the annotation and interface is checked
|
||||
metaDataMap.put(metaData, null);
|
||||
}
|
||||
}
|
||||
Set<Class<? extends StepListener>> listenerInterfaces = new HashSet<Class<? extends StepListener>>();
|
||||
|
||||
for(Entry<StepListenerMetaData, String> entry : metaDataMap.entrySet()){
|
||||
StepListenerMetaData metaData = entry.getKey();
|
||||
Set<MethodInvoker> invokers = new NullIgnoringSet<MethodInvoker>();
|
||||
invokers.add(getMethodInvokerByName(entry.getValue(), delegate, metaData.getParamTypes()));
|
||||
invokers.add(getMethodInvokerForInterface(metaData.getListenerInterface(), metaData.getMethodName(),
|
||||
delegate, metaData.getParamTypes()));
|
||||
invokers.add(getMethodInvokerByAnnotation(delegate, metaData.getAnnotation()));
|
||||
if(!invokers.isEmpty()){
|
||||
invokerMap.put(metaData.getMethodName(), invokers);
|
||||
listenerInterfaces.add(metaData.getListenerInterface());
|
||||
}
|
||||
}
|
||||
|
||||
ProxyFactory proxyFactory = new ProxyFactory();
|
||||
proxyFactory.setInterfaces(listenerInterfaces.toArray(new Class[0]));
|
||||
proxyFactory.addAdvisor(new DefaultPointcutAdvisor(new StepListenerMethodInterceptor(invokerMap)));
|
||||
return proxyFactory.getProxy();
|
||||
}
|
||||
|
||||
private MethodInvoker getMethodInvokerByName(String methodName, Object candidate, Class<?>... params){
|
||||
if(methodName != null){
|
||||
return SimpleMethodInvoker.createMethodInvokerByName(candidate, methodName, false, params);
|
||||
}
|
||||
else{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private MethodInvoker getMethodInvokerForInterface(Class<? extends StepListener> iFace, String methodName,
|
||||
Object candidate, Class<?>... params){
|
||||
|
||||
if(candidate.getClass().isAssignableFrom(iFace)){
|
||||
return SimpleMethodInvoker.createMethodInvokerByName(candidate, methodName, true, params);
|
||||
}
|
||||
else{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private MethodInvoker getMethodInvokerByAnnotation(Object candidate, Class<? extends Annotation> annotation){
|
||||
|
||||
MethodResolver resolver = new AnnotationMethodResolver(annotation);
|
||||
Method method = resolver.findMethod(candidate);
|
||||
|
||||
if(method != null){
|
||||
return new SimpleMethodInvoker(candidate, method);
|
||||
}
|
||||
else{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Class getObjectType() {
|
||||
return StepListener.class;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setDelegate(Object delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
public void setMetaDataMap(Map<StepListenerMetaData, String> metaDataMap) {
|
||||
this.metaDataMap = metaDataMap;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extension of HashSet that ignores nulls, rather than putting them into
|
||||
* the set.
|
||||
*/
|
||||
private 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,99 @@
|
||||
/*
|
||||
* 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 org.springframework.batch.core.ChunkListener;
|
||||
import org.springframework.batch.core.ItemProcessListener;
|
||||
import org.springframework.batch.core.ItemReadListener;
|
||||
import org.springframework.batch.core.ItemWriteListener;
|
||||
import org.springframework.batch.core.SkipListener;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.StepExecutionListener;
|
||||
import org.springframework.batch.core.StepListener;
|
||||
import org.springframework.batch.core.annotation.AfterChunk;
|
||||
import org.springframework.batch.core.annotation.AfterProcess;
|
||||
import org.springframework.batch.core.annotation.AfterRead;
|
||||
import org.springframework.batch.core.annotation.AfterStep;
|
||||
import org.springframework.batch.core.annotation.AfterWrite;
|
||||
import org.springframework.batch.core.annotation.BeforeChunk;
|
||||
import org.springframework.batch.core.annotation.BeforeProcess;
|
||||
import org.springframework.batch.core.annotation.BeforeRead;
|
||||
import org.springframework.batch.core.annotation.BeforeStep;
|
||||
import org.springframework.batch.core.annotation.BeforeWrite;
|
||||
import org.springframework.batch.core.annotation.OnProcessError;
|
||||
import org.springframework.batch.core.annotation.OnReadError;
|
||||
import org.springframework.batch.core.annotation.OnSkipInProcess;
|
||||
import org.springframework.batch.core.annotation.OnSkipInRead;
|
||||
import org.springframework.batch.core.annotation.OnSkipInWrite;
|
||||
import org.springframework.batch.core.annotation.OnWriteError;
|
||||
|
||||
/**
|
||||
* Enumeration for {@link StepListener} meta data, which ties together the names
|
||||
* of methods, their interfaces, annotation, and expected arguments.
|
||||
*
|
||||
* @author Lucas Ward
|
||||
* @since 2.0
|
||||
* @see StepListenerFactoryBean
|
||||
*/
|
||||
public enum StepListenerMetaData {
|
||||
|
||||
BEFORE_STEP("beforeStep", BeforeStep.class, StepExecutionListener.class, StepExecution.class),
|
||||
AFTER_STEP("afterStep", AfterStep.class, StepExecutionListener.class, StepExecution.class),
|
||||
BEFORE_CHUNK("beforeChunk", BeforeChunk.class, ChunkListener.class),
|
||||
AFTER_CHUNK("afterChunk", AfterChunk.class, ChunkListener.class),
|
||||
BEFORE_READ("beforeRead", BeforeRead.class, ItemReadListener.class),
|
||||
AFTER_READ("afterRead", AfterRead.class, ItemReadListener.class, Object.class),
|
||||
ON_READ_ERROR("onReadError", OnReadError.class, ItemReadListener.class, Exception.class),
|
||||
BEFORE_PROCESS("beforeProcess", BeforeProcess.class, ItemProcessListener.class, Object.class),
|
||||
AFTER_PROCESS("afterProcess", AfterProcess.class, ItemProcessListener.class, Object.class),
|
||||
ON_PROCESS_ERROR("onProcessError", OnProcessError.class, ItemProcessListener.class, Object.class, Exception.class),
|
||||
BEFORE_WRITE("beforeWrite", BeforeWrite.class, ItemWriteListener.class, Object.class),
|
||||
AFTER_WRITE("afterWrite", AfterWrite.class, ItemWriteListener.class, Object.class),
|
||||
ON_WRITE_ERROR("onWriteError", OnWriteError.class, ItemWriteListener.class, Object.class, Exception.class),
|
||||
ON_SKIP_IN_READ("onSkipInRead", OnSkipInRead.class, SkipListener.class, Object.class, Throwable.class),
|
||||
ON_SKIP_IN_PROCESS("onSkipInProcess", OnSkipInProcess.class, SkipListener.class, Object.class, Throwable.class),
|
||||
ON_SKIP_IN_WRITE("onSkipInWrite", OnSkipInWrite.class, SkipListener.class, Object.class, Throwable.class);
|
||||
|
||||
private final String methodName;
|
||||
private final Class<? extends Annotation> annotation;
|
||||
private final Class<? extends StepListener> listenerInterface;
|
||||
private final Class<?>[] paramTypes;
|
||||
|
||||
StepListenerMetaData(String methodName, Class<? extends Annotation> annotation, Class<? extends StepListener> listenerInterface, Class<?>... paramTypes) {
|
||||
this.methodName = methodName;
|
||||
this.annotation = annotation;
|
||||
this.listenerInterface = listenerInterface;
|
||||
this.paramTypes = paramTypes;
|
||||
}
|
||||
|
||||
public String getMethodName() {
|
||||
return methodName;
|
||||
}
|
||||
|
||||
public Class<? extends Annotation> getAnnotation() {
|
||||
return annotation;
|
||||
}
|
||||
|
||||
public Class<? extends StepListener> getListenerInterface() {
|
||||
return listenerInterface;
|
||||
}
|
||||
|
||||
public Class<?>[] getParamTypes() {
|
||||
return paramTypes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.springframework.batch.core.configuration.util.MethodInvoker;
|
||||
|
||||
/**
|
||||
* @author Lucas Ward
|
||||
*
|
||||
*/
|
||||
public class StepListenerMethodInterceptor implements MethodInterceptor{
|
||||
|
||||
private final Map<String, Set<MethodInvoker>> invokerMap;
|
||||
|
||||
public StepListenerMethodInterceptor(Map<String, Set<MethodInvoker>> invokerMap) {
|
||||
this.invokerMap = invokerMap;
|
||||
}
|
||||
|
||||
public Object invoke(MethodInvocation invocation) throws Throwable {
|
||||
|
||||
String methodName = invocation.getMethod().getName();
|
||||
Set<MethodInvoker> invokers = invokerMap.get(methodName);
|
||||
|
||||
if(invokers == null){
|
||||
return null;
|
||||
}
|
||||
|
||||
for(MethodInvoker invoker : invokers){
|
||||
invoker.invokeMethod(invocation.getArguments());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* 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 junit.framework.Assert.assertTrue;
|
||||
import static org.springframework.batch.core.listener.StepListenerMetaData.AFTER_CHUNK;
|
||||
import static org.springframework.batch.core.listener.StepListenerMetaData.AFTER_STEP;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.batch.core.ChunkListener;
|
||||
import org.springframework.batch.core.ItemProcessListener;
|
||||
import org.springframework.batch.core.ItemReadListener;
|
||||
import org.springframework.batch.core.ItemWriteListener;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.SkipListener;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.StepExecutionListener;
|
||||
import org.springframework.batch.core.StepListener;
|
||||
import org.springframework.batch.core.annotation.AfterProcess;
|
||||
import org.springframework.batch.core.annotation.AfterRead;
|
||||
import org.springframework.batch.core.annotation.AfterWrite;
|
||||
import org.springframework.batch.core.annotation.BeforeChunk;
|
||||
import org.springframework.batch.core.annotation.BeforeProcess;
|
||||
import org.springframework.batch.core.annotation.BeforeRead;
|
||||
import org.springframework.batch.core.annotation.BeforeStep;
|
||||
import org.springframework.batch.core.annotation.BeforeWrite;
|
||||
import org.springframework.batch.core.annotation.OnProcessError;
|
||||
import org.springframework.batch.core.annotation.OnReadError;
|
||||
import org.springframework.batch.core.annotation.OnSkipInProcess;
|
||||
import org.springframework.batch.core.annotation.OnSkipInRead;
|
||||
import org.springframework.batch.core.annotation.OnSkipInWrite;
|
||||
import org.springframework.batch.core.annotation.OnWriteError;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Lucas Ward
|
||||
*
|
||||
*/
|
||||
public class StepListenerFactoryBeanTests {
|
||||
|
||||
StepListenerFactoryBean factoryBean;
|
||||
TestClass testClass;
|
||||
JobExecution jobExecution = new JobExecution(11L);
|
||||
StepExecution stepExecution = new StepExecution("testStep", jobExecution);
|
||||
|
||||
@Before
|
||||
public void setUp(){
|
||||
factoryBean = new StepListenerFactoryBean();
|
||||
testClass = new TestClass();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testStepAndChunk() throws Exception{
|
||||
|
||||
factoryBean.setDelegate(testClass);
|
||||
Map<StepListenerMetaData, String> metaDataMap = new HashMap<StepListenerMetaData, String>();;
|
||||
metaDataMap.put(AFTER_STEP, "destroy");
|
||||
metaDataMap.put(AFTER_CHUNK, "afterChunk");
|
||||
factoryBean.setMetaDataMap(metaDataMap);
|
||||
Object item = new Object();
|
||||
List<Object> items = new ArrayList<Object>();
|
||||
items.add(item);
|
||||
StepListener listener = (StepListener) factoryBean.getObject();
|
||||
((StepExecutionListener)listener).beforeStep(stepExecution);
|
||||
((StepExecutionListener)listener).afterStep(stepExecution);
|
||||
((ChunkListener)listener).beforeChunk();
|
||||
((ChunkListener)listener).afterChunk();
|
||||
((ItemReadListener<Object>)listener).beforeRead();
|
||||
//((ItemReadListener<Object>)listener).afterRead(item);
|
||||
((ItemReadListener)listener).onReadError(new Exception());
|
||||
((ItemProcessListener<Object, Object>)listener).beforeProcess(item);
|
||||
((ItemProcessListener<Object, Object>)listener).afterProcess(item, item);
|
||||
((ItemProcessListener<Object, Object>)listener).onProcessError(item, new Exception());
|
||||
((ItemWriteListener<Object>)listener).beforeWrite(items);
|
||||
((ItemWriteListener<Object>)listener).afterWrite(items);
|
||||
((ItemWriteListener<Object>)listener).onWriteError(new Exception(), items);
|
||||
((SkipListener<Object, Object>)listener).onSkipInRead(new Throwable());
|
||||
((SkipListener<Object, Object>)listener).onSkipInProcess(item, new Throwable());
|
||||
((SkipListener<Object, Object>)listener).onSkipInWrite(item, new Throwable());
|
||||
assertTrue(testClass.beforeStepCalled);
|
||||
assertTrue(testClass.beforeChunkCalled);
|
||||
assertTrue(testClass.afterChunkCalled);
|
||||
assertTrue(testClass.beforeReadCalled);
|
||||
//assertTrue(testClass.afterReadCalled);
|
||||
assertTrue(testClass.onReadErrorCalled);
|
||||
assertTrue(testClass.beforeProcessCalled);
|
||||
assertTrue(testClass.afterProcessCalled);
|
||||
assertTrue(testClass.onProcessErrorCalled);
|
||||
assertTrue(testClass.beforeWriteCalled);
|
||||
assertTrue(testClass.afterWriteCalled);
|
||||
assertTrue(testClass.onWriteErrorCalled);
|
||||
assertTrue(testClass.onSkipInReadCalled);
|
||||
assertTrue(testClass.onSkipInProcessCalled);
|
||||
assertTrue(testClass.onSkipInWriteCalled);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private class TestClass implements SkipListener<Object, Object>{
|
||||
|
||||
boolean beforeStepCalled = false;
|
||||
boolean afterStepCalled = false;
|
||||
boolean beforeChunkCalled = false;
|
||||
boolean afterChunkCalled = false;
|
||||
boolean beforeReadCalled = false;
|
||||
boolean afterReadCalled = false;
|
||||
boolean onReadErrorCalled = false;
|
||||
boolean beforeProcessCalled = false;
|
||||
boolean afterProcessCalled = false;
|
||||
boolean onProcessErrorCalled = false;
|
||||
boolean beforeWriteCalled = false;
|
||||
boolean afterWriteCalled = false;
|
||||
boolean onWriteErrorCalled = false;
|
||||
boolean onSkipInReadCalled = false;
|
||||
boolean onSkipInProcessCalled = false;
|
||||
boolean onSkipInWriteCalled = false;
|
||||
|
||||
@BeforeStep
|
||||
public void initStep(){
|
||||
beforeStepCalled = true;
|
||||
}
|
||||
|
||||
public void destroy(){
|
||||
afterStepCalled = true;
|
||||
}
|
||||
|
||||
@BeforeChunk
|
||||
public void before(){
|
||||
beforeChunkCalled = true;
|
||||
}
|
||||
|
||||
public void afterChunk(){
|
||||
afterChunkCalled = true;
|
||||
}
|
||||
|
||||
@BeforeRead
|
||||
public void beforeReadMethod(){
|
||||
beforeReadCalled = true;
|
||||
}
|
||||
|
||||
@AfterRead
|
||||
public void afterReadMethod(Object item){
|
||||
Assert.notNull(item);
|
||||
afterReadCalled = true;
|
||||
}
|
||||
|
||||
@OnReadError
|
||||
public void onErrorInRead(){
|
||||
onReadErrorCalled = true;
|
||||
}
|
||||
|
||||
@BeforeProcess
|
||||
public void beforeProcess(){
|
||||
beforeProcessCalled = true;
|
||||
}
|
||||
|
||||
@AfterProcess
|
||||
public void afterProcess(){
|
||||
afterProcessCalled = true;
|
||||
}
|
||||
|
||||
@OnProcessError
|
||||
public void processError(){
|
||||
onProcessErrorCalled = true;
|
||||
}
|
||||
|
||||
@BeforeWrite
|
||||
public void beforeWrite(){
|
||||
beforeWriteCalled = true;
|
||||
}
|
||||
|
||||
@AfterWrite
|
||||
public void afterWrite(){
|
||||
afterWriteCalled = true;
|
||||
}
|
||||
|
||||
@OnWriteError
|
||||
public void writeError(){
|
||||
onWriteErrorCalled = true;
|
||||
}
|
||||
|
||||
@OnSkipInProcess
|
||||
public void onSkipInProcess(Object item, Throwable t) {
|
||||
onSkipInProcessCalled = true;
|
||||
}
|
||||
|
||||
@OnSkipInRead
|
||||
public void onSkipInRead(Throwable t) {
|
||||
onSkipInReadCalled = true;
|
||||
}
|
||||
|
||||
@OnSkipInWrite
|
||||
public void onSkipInWrite(Object item, Throwable t) {
|
||||
onSkipInWriteCalled = true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user