OPEN - issue BATCH-673: Add new Java 5.0 features
http://jira.springframework.org/browse/BATCH-673 Fixed a couple of small implementation bugs, and cleaned up a bit.
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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.configuration.util;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.ElementType;
|
||||
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;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Utility methods for create MethodInvoker instances.
|
||||
*
|
||||
* @author Lucas Ward
|
||||
* @since 2.0
|
||||
*/
|
||||
public class MethodInvokerUtils {
|
||||
|
||||
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 static MethodInvoker getMethodInvokerForInterface(Class<? extends StepListener> iFace, String methodName,
|
||||
Object candidate, Class<?>... params){
|
||||
|
||||
if(iFace.isAssignableFrom(candidate.getClass())){
|
||||
return MethodInvokerUtils.createMethodInvokerByName(candidate, methodName, true, params);
|
||||
}
|
||||
else{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static MethodInvoker getMethodInvokerByAnnotation(final Class<? extends Annotation> annotationType, final Object candidate){
|
||||
Assert.notNull(candidate, "class must not be null");
|
||||
Assert.notNull(annotationType, "annotationType must not be null");
|
||||
Assert.isTrue(ObjectUtils.containsElement(
|
||||
annotationType.getAnnotation(Target.class).value(), ElementType.METHOD),
|
||||
"Annotation [" + annotationType + "] is not a Method-level annotation.");
|
||||
final AtomicReference<Method> annotatedMethod = new AtomicReference<Method>();
|
||||
ReflectionUtils.doWithMethods(candidate.getClass(), new ReflectionUtils.MethodCallback() {
|
||||
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
|
||||
Annotation annotation = AnnotationUtils.findAnnotation(method, annotationType);
|
||||
if (annotation != null) {
|
||||
Assert.isNull(annotatedMethod.get(), "found more than one method on target class ["
|
||||
+ candidate + "] with the annotation type [" + candidate + "]");
|
||||
annotatedMethod.set(method);
|
||||
}
|
||||
}
|
||||
});
|
||||
Method method = annotatedMethod.get();
|
||||
if(method == null){
|
||||
return null;
|
||||
}
|
||||
else{
|
||||
return new SimpleMethodInvoker(candidate, annotatedMethod.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,22 +50,6 @@ 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");
|
||||
@@ -100,6 +84,9 @@ public class SimpleMethodInvoker implements MethodInvoker {
|
||||
else if(parameterTypes.length > args.length){
|
||||
throw new IllegalArgumentException("Wrong number of arguments, expected no more than: [" + parameterTypes.length + "]");
|
||||
}
|
||||
else{
|
||||
invokeArgs = args;
|
||||
}
|
||||
|
||||
method.setAccessible(true);
|
||||
|
||||
@@ -121,11 +108,11 @@ public class SimpleMethodInvoker implements MethodInvoker {
|
||||
return true;
|
||||
}
|
||||
SimpleMethodInvoker rhs = (SimpleMethodInvoker) obj;
|
||||
return (rhs.method == this.method) && (rhs.object == this.object);
|
||||
return (rhs.method.equals(this.method)) && (rhs.object.equals(this.object));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return new HashCodeBuilder(25, 37).append(object).append(method).toHashCode();
|
||||
return new HashCodeBuilder(25, 37).append(object.hashCode()).append(method.hashCode()).toHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,9 @@
|
||||
*/
|
||||
package org.springframework.batch.core.listener;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
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;
|
||||
@@ -26,18 +27,32 @@ 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.batch.core.configuration.util.MethodInvokerUtils;
|
||||
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.
|
||||
* various lifecycle methods or annotations that are provided. There are three possible ways of having
|
||||
* a method called as part of a {@link StepListener} lifecyle:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Interface implementation: By implementing any of the subclasses of StepListener, 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 StepListenerMetaData} in the
|
||||
* metaDatMap.
|
||||
* </ul>
|
||||
*
|
||||
* It should be noted that methods obtained by name or annotation that don't match the StepListener 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 StepListenerMetaData
|
||||
*/
|
||||
public class StepListenerFactoryBean implements FactoryBean{
|
||||
|
||||
@@ -50,27 +65,32 @@ public class StepListenerFactoryBean implements FactoryBean{
|
||||
if(metaDataMap == null){
|
||||
metaDataMap = new HashMap<StepListenerMetaData, String>();
|
||||
}
|
||||
//Because all annotations and interfaces should be checked for, make sure that each meta data
|
||||
//entry is represented.
|
||||
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 every entry in th emap, try and find a method by interface, name, or annotation. If the same
|
||||
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()));
|
||||
invokers.add(getMethodInvokerByAnnotation(metaData.getAnnotation(), delegate));
|
||||
if(!invokers.isEmpty()){
|
||||
invokerMap.put(metaData.getMethodName(), invokers);
|
||||
listenerInterfaces.add(metaData.getListenerInterface());
|
||||
}
|
||||
}
|
||||
|
||||
//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)));
|
||||
@@ -79,37 +99,12 @@ public class StepListenerFactoryBean implements FactoryBean{
|
||||
|
||||
private MethodInvoker getMethodInvokerByName(String methodName, Object candidate, Class<?>... params){
|
||||
if(methodName != null){
|
||||
return SimpleMethodInvoker.createMethodInvokerByName(candidate, methodName, false, params);
|
||||
return MethodInvokerUtils.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() {
|
||||
|
||||
@@ -65,7 +65,7 @@ public enum StepListenerMetaData {
|
||||
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_READ("onSkipInRead", OnSkipInRead.class, SkipListener.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);
|
||||
|
||||
|
||||
@@ -15,17 +15,26 @@
|
||||
*/
|
||||
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.ExitStatus;
|
||||
import org.springframework.batch.core.StepExecutionListener;
|
||||
import org.springframework.batch.core.configuration.util.MethodInvoker;
|
||||
|
||||
/**
|
||||
* {@link MethodInterceptor} that, given a map of method names and {@link MethodInvoker}s,
|
||||
* will execute all methods tied to a particular method name, with the provided
|
||||
* arguments. The only possible return value that is handled is of type ExitStatus, since
|
||||
* the only StepListener implementation that isn't void is
|
||||
* {@link StepExecutionListener#afterStep(org.springframework.batch.core.StepExecution)}, which
|
||||
* returns ExitStatus.
|
||||
*
|
||||
* @author Lucas Ward
|
||||
*
|
||||
* @since 2.0
|
||||
* @see MethodInvoker
|
||||
*/
|
||||
public class StepListenerMethodInterceptor implements MethodInterceptor{
|
||||
|
||||
@@ -43,12 +52,20 @@ public class StepListenerMethodInterceptor implements MethodInterceptor{
|
||||
if(invokers == null){
|
||||
return null;
|
||||
}
|
||||
|
||||
ExitStatus status = null;
|
||||
for(MethodInvoker invoker : invokers){
|
||||
invoker.invokeMethod(invocation.getArguments());
|
||||
Object retVal = invoker.invokeMethod(invocation.getArguments());
|
||||
if(retVal instanceof ExitStatus){
|
||||
if(status != null){
|
||||
status = status.and((ExitStatus) retVal);
|
||||
}
|
||||
else{
|
||||
status = (ExitStatus) retVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobExecutionListener;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Lucas Ward
|
||||
@@ -101,9 +102,27 @@ public class SimpleMethodInvokerTests {
|
||||
assertFalse(testClass.beforeJobCalled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMethodWithArgument() throws Exception{
|
||||
MethodInvoker methodInvoker = new SimpleMethodInvoker(testClass, "argumentTest", Object.class);
|
||||
methodInvoker.invokeMethod(new Object());
|
||||
assertTrue(testClass.argumentTestCalled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEquals() throws Exception{
|
||||
Method method = TestClass.class.getMethod("beforeJobWithExecution", JobExecution.class);
|
||||
MethodInvoker methodInvoker = new SimpleMethodInvoker(testClass, method);
|
||||
|
||||
method = TestClass.class.getMethod("beforeJobWithExecution", JobExecution.class);
|
||||
MethodInvoker methodInvoker2 = new SimpleMethodInvoker(testClass, method);
|
||||
assertEquals(methodInvoker, methodInvoker2);
|
||||
}
|
||||
|
||||
private class TestClass{
|
||||
|
||||
boolean beforeJobCalled = false;
|
||||
boolean argumentTestCalled = false;
|
||||
|
||||
public void beforeJob(){
|
||||
beforeJobCalled = true;
|
||||
@@ -116,5 +135,10 @@ public class SimpleMethodInvokerTests {
|
||||
public void beforeJobWithTooManyArguments(JobExecution jobExecution, int someInt){
|
||||
beforeJobCalled = true;
|
||||
}
|
||||
|
||||
public void argumentTest(Object object){
|
||||
Assert.notNull(object);
|
||||
argumentTestCalled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.batch.core.listener;
|
||||
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.springframework.batch.core.listener.StepListenerMetaData.AFTER_CHUNK;
|
||||
import static org.springframework.batch.core.listener.StepListenerMetaData.AFTER_STEP;
|
||||
|
||||
@@ -27,6 +28,7 @@ import java.util.Map;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
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;
|
||||
@@ -37,6 +39,7 @@ 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.AfterStep;
|
||||
import org.springframework.batch.core.annotation.AfterWrite;
|
||||
import org.springframework.batch.core.annotation.BeforeChunk;
|
||||
import org.springframework.batch.core.annotation.BeforeProcess;
|
||||
@@ -45,9 +48,6 @@ 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;
|
||||
|
||||
@@ -86,7 +86,7 @@ public class StepListenerFactoryBeanTests {
|
||||
((ChunkListener)listener).beforeChunk();
|
||||
((ChunkListener)listener).afterChunk();
|
||||
((ItemReadListener<Object>)listener).beforeRead();
|
||||
//((ItemReadListener<Object>)listener).afterRead(item);
|
||||
((ItemReadListener<Object>)listener).afterRead(item);
|
||||
((ItemReadListener)listener).onReadError(new Exception());
|
||||
((ItemProcessListener<Object, Object>)listener).beforeProcess(item);
|
||||
((ItemProcessListener<Object, Object>)listener).afterProcess(item, item);
|
||||
@@ -101,7 +101,7 @@ public class StepListenerFactoryBeanTests {
|
||||
assertTrue(testClass.beforeChunkCalled);
|
||||
assertTrue(testClass.afterChunkCalled);
|
||||
assertTrue(testClass.beforeReadCalled);
|
||||
//assertTrue(testClass.afterReadCalled);
|
||||
assertTrue(testClass.afterReadCalled);
|
||||
assertTrue(testClass.onReadErrorCalled);
|
||||
assertTrue(testClass.beforeProcessCalled);
|
||||
assertTrue(testClass.afterProcessCalled);
|
||||
@@ -114,7 +114,74 @@ public class StepListenerFactoryBeanTests {
|
||||
assertTrue(testClass.onSkipInWriteCalled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAllThreeTypes() throws Exception{
|
||||
//Test to make sure if someone has annotated a method, implemented the interface, and given a string
|
||||
//method name, that all three will be called
|
||||
ThreeStepExecutionListener delegate = new ThreeStepExecutionListener();
|
||||
factoryBean.setDelegate(delegate);
|
||||
Map<StepListenerMetaData, String> metaDataMap = new HashMap<StepListenerMetaData, String>();;
|
||||
metaDataMap.put(AFTER_STEP, "destroy");
|
||||
factoryBean.setMetaDataMap(metaDataMap);
|
||||
StepListener listener = (StepListener) factoryBean.getObject();
|
||||
((StepExecutionListener)listener).afterStep(stepExecution);
|
||||
assertEquals(3, delegate.callcount);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAnnotatingInterfaceResultsInOneCall() throws Exception{
|
||||
MultipleAfterStep delegate = new MultipleAfterStep();
|
||||
factoryBean.setDelegate(delegate);
|
||||
Map<StepListenerMetaData, String> metaDataMap = new HashMap<StepListenerMetaData, String>();;
|
||||
metaDataMap.put(AFTER_STEP, "afterStep");
|
||||
factoryBean.setMetaDataMap(metaDataMap);
|
||||
StepListener listener = (StepListener) factoryBean.getObject();
|
||||
((StepExecutionListener)listener).afterStep(stepExecution);
|
||||
assertEquals(1, delegate.callcount);
|
||||
}
|
||||
|
||||
private class MultipleAfterStep implements StepExecutionListener{
|
||||
|
||||
int callcount = 0;
|
||||
|
||||
@AfterStep
|
||||
public ExitStatus afterStep(StepExecution stepExecution) {
|
||||
Assert.notNull(stepExecution);
|
||||
callcount++;
|
||||
return null;
|
||||
}
|
||||
|
||||
public void beforeStep(StepExecution stepExecution) {
|
||||
callcount++;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private class ThreeStepExecutionListener implements StepExecutionListener{
|
||||
|
||||
int callcount = 0;
|
||||
|
||||
public ExitStatus afterStep(StepExecution stepExecution) {
|
||||
Assert.notNull(stepExecution);
|
||||
callcount++;
|
||||
return null;
|
||||
}
|
||||
|
||||
public void beforeStep(StepExecution stepExecution) {
|
||||
callcount++;
|
||||
}
|
||||
|
||||
public void destroy(){
|
||||
callcount++;
|
||||
}
|
||||
|
||||
@AfterStep
|
||||
public void after(){
|
||||
callcount++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class TestClass implements SkipListener<Object, Object>{
|
||||
|
||||
@@ -199,17 +266,14 @@ public class StepListenerFactoryBeanTests {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
package org.springframework.batch.core.listener;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
import org.springframework.batch.core.configuration.util.MethodInvoker;
|
||||
import org.springframework.batch.core.configuration.util.MethodInvokerUtils;
|
||||
import org.springframework.batch.core.configuration.util.SimpleMethodInvoker;
|
||||
|
||||
public class StepListenerMethodInterceptorTests {
|
||||
|
||||
StepListenerMethodInterceptor interceptor;
|
||||
TestClass testClass;
|
||||
|
||||
@Before
|
||||
public void setUp(){
|
||||
testClass = new TestClass();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNormalCase() throws Throwable{
|
||||
|
||||
Map<String, Set<MethodInvoker>> invokerMap = new HashMap<String, Set<MethodInvoker>>();
|
||||
for(Method method : TestClass.class.getMethods()){
|
||||
invokerMap.put(method.getName(), asSet( new SimpleMethodInvoker(testClass, method)));
|
||||
}
|
||||
interceptor = new StepListenerMethodInterceptor(invokerMap);
|
||||
interceptor.invoke(new StubMethodInvocation(TestClass.class.getMethod("method1")));
|
||||
assertEquals(1, testClass.method1Count);
|
||||
interceptor.invoke(new StubMethodInvocation(TestClass.class.getMethod("method2")));
|
||||
assertEquals(1, testClass.method2Count);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleInvokersPerName() throws Throwable{
|
||||
|
||||
Map<String, Set<MethodInvoker>> invokerMap = new HashMap<String, Set<MethodInvoker>>();
|
||||
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.invoke(new StubMethodInvocation(TestClass.class.getMethod("method1")));
|
||||
assertEquals(1, testClass.method1Count);
|
||||
interceptor.invoke(new StubMethodInvocation(TestClass.class.getMethod("method2")));
|
||||
assertEquals(1, testClass.method2Count);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExitStatusReturn() throws Throwable{
|
||||
Map<String, Set<MethodInvoker>> invokerMap = new HashMap<String, Set<MethodInvoker>>();
|
||||
Set<MethodInvoker> invokers = asSet(MethodInvokerUtils.createMethodInvokerByName(testClass, "method3", false));
|
||||
invokers.add(MethodInvokerUtils.createMethodInvokerByName(testClass, "method3", false));
|
||||
invokerMap.put("method3", invokers);
|
||||
interceptor = new StepListenerMethodInterceptor(invokerMap);
|
||||
assertEquals(ExitStatus.FINISHED, interceptor.invoke(new StubMethodInvocation(TestClass.class.getMethod("method3"))));
|
||||
}
|
||||
|
||||
public Set<MethodInvoker> asSet(MethodInvoker methodInvoker){
|
||||
Set<MethodInvoker> invokerSet = new HashSet<MethodInvoker>();
|
||||
invokerSet.add(methodInvoker);
|
||||
return invokerSet;
|
||||
}
|
||||
|
||||
private class TestClass{
|
||||
|
||||
int method1Count = 0;
|
||||
int method2Count = 0;
|
||||
int method3Count = 0;
|
||||
|
||||
public void method1(){
|
||||
method1Count++;
|
||||
}
|
||||
|
||||
public void method2(){
|
||||
method2Count++;
|
||||
}
|
||||
|
||||
public ExitStatus method3(){
|
||||
method3Count++;
|
||||
return ExitStatus.FINISHED;
|
||||
}
|
||||
}
|
||||
|
||||
private class StubMethodInvocation implements MethodInvocation{
|
||||
|
||||
Method method;
|
||||
Object[] args;
|
||||
|
||||
public StubMethodInvocation(Method method, Object... args) {
|
||||
this.method = method;
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
public Method getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public Object[] getArguments() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
public AccessibleObject getStaticPart() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
public Object getThis() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
public Object proceed() throws Throwable {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user