diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/util/MethodInvokerUtils.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/util/MethodInvokerUtils.java new file mode 100644 index 000000000..ae9d97ccd --- /dev/null +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/util/MethodInvokerUtils.java @@ -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 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 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 annotatedMethod = new AtomicReference(); + 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()); + } + } +} diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/util/SimpleMethodInvoker.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/util/SimpleMethodInvoker.java index 48e5c0248..8086278cd 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/util/SimpleMethodInvoker.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/util/SimpleMethodInvoker.java @@ -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(); } } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/listener/StepListenerFactoryBean.java b/spring-batch-core/src/main/java/org/springframework/batch/core/listener/StepListenerFactoryBean.java index 39b7cfdcc..c6e1dbcf2 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/listener/StepListenerFactoryBean.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/listener/StepListenerFactoryBean.java @@ -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: + * + * + * + * 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(); } + //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> listenerInterfaces = new HashSet>(); + //For every entry in th emap, try and find a method by interface, name, or annotation. If the same for(Entry entry : metaDataMap.entrySet()){ StepListenerMetaData metaData = entry.getKey(); Set invokers = new NullIgnoringSet(); 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 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 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() { diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/listener/StepListenerMetaData.java b/spring-batch-core/src/main/java/org/springframework/batch/core/listener/StepListenerMetaData.java index 722d3c041..9b784c32e 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/listener/StepListenerMetaData.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/listener/StepListenerMetaData.java @@ -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); diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/listener/StepListenerMethodInterceptor.java b/spring-batch-core/src/main/java/org/springframework/batch/core/listener/StepListenerMethodInterceptor.java index 8fb83de62..ab7c32be4 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/listener/StepListenerMethodInterceptor.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/listener/StepListenerMethodInterceptor.java @@ -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; } diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/util/SimpleMethodInvokerTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/util/SimpleMethodInvokerTests.java index feb41f6da..cbefc80a8 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/util/SimpleMethodInvokerTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/util/SimpleMethodInvokerTests.java @@ -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; + } } } diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/listener/StepListenerFactoryBeanTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/listener/StepListenerFactoryBeanTests.java index 2c8013fa1..43ae3998d 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/listener/StepListenerFactoryBeanTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/listener/StepListenerFactoryBeanTests.java @@ -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)listener).beforeRead(); - //((ItemReadListener)listener).afterRead(item); + ((ItemReadListener)listener).afterRead(item); ((ItemReadListener)listener).onReadError(new Exception()); ((ItemProcessListener)listener).beforeProcess(item); ((ItemProcessListener)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 metaDataMap = new HashMap();; + 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 metaDataMap = new HashMap();; + 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{ @@ -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; } diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/listener/StepListenerMethodInterceptorTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/listener/StepListenerMethodInterceptorTests.java new file mode 100644 index 000000000..503785a75 --- /dev/null +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/listener/StepListenerMethodInterceptorTests.java @@ -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> invokerMap = new HashMap>(); + 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> invokerMap = new HashMap>(); + Set 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> invokerMap = new HashMap>(); + Set 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 asSet(MethodInvoker methodInvoker){ + Set invokerSet = new HashSet(); + 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; + } + + } +}