diff --git a/src/main/java/org/springframework/retry/annotation/RecoverAnnotationRecoveryHandler.java b/src/main/java/org/springframework/retry/annotation/RecoverAnnotationRecoveryHandler.java index 250d49e..fd2db51 100644 --- a/src/main/java/org/springframework/retry/annotation/RecoverAnnotationRecoveryHandler.java +++ b/src/main/java/org/springframework/retry/annotation/RecoverAnnotationRecoveryHandler.java @@ -16,10 +16,6 @@ package org.springframework.retry.annotation; -import java.lang.reflect.Method; -import java.util.HashMap; -import java.util.Map; - import org.springframework.classify.SubclassClassifier; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.retry.ExhaustedRetryException; @@ -27,6 +23,10 @@ import org.springframework.retry.interceptor.MethodInvocationRecoverer; import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils.MethodCallback; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + /** * A recoverer for method invocations based on the @Recover annotation. A * suitable recovery method is one with a Throwable type as the first parameter and the @@ -37,10 +37,11 @@ import org.springframework.util.ReflectionUtils.MethodCallback; * class hierarchy is chosen, so for instance if an IllegalArgumentException is being * handled and there is a method whose first argument is RuntimeException, then it will be * preferred over a method whose first argument is Throwable. - * + * * @author Dave Syer * @author Josh Long * @author Aldo Sinanaj + * @author Randell Callahan */ public class RecoverAnnotationRecoveryHandler implements MethodInvocationRecoverer { @@ -55,7 +56,7 @@ public class RecoverAnnotationRecoveryHandler implements MethodInvocationReco @Override public T recover(Object[] args, Throwable cause) { - Method method = findClosestMatch(cause.getClass()); + Method method = findClosestMatch(args, cause.getClass()); if (method == null) { throw new ExhaustedRetryException("Cannot locate recovery method", cause); } @@ -75,7 +76,7 @@ public class RecoverAnnotationRecoveryHandler implements MethodInvocationReco } } - private Method findClosestMatch(Class cause) { + private Method findClosestMatch(Object[] args, Class cause) { int min = Integer.MAX_VALUE; Method result = null; for (Method method : methods.keySet()) { @@ -90,6 +91,13 @@ public class RecoverAnnotationRecoveryHandler implements MethodInvocationReco min = distance; result = method; } + else if (distance == min) { + boolean parametersMatch = compareParameters( args, meta.getArgCount(), + method.getParameterTypes() ); + if (parametersMatch) { + result = method; + } + } } } return result; @@ -106,6 +114,22 @@ public class RecoverAnnotationRecoveryHandler implements MethodInvocationReco return result; } + private boolean compareParameters(Object[] args, int argCount, Class[] parameterTypes) { + if (argCount == (args.length + 1)) { + int startingIndex = 0; + if (parameterTypes.length > 0 && parameterTypes[0] == Throwable.class) { + startingIndex = 1; + } + for (int i = startingIndex; i < parameterTypes.length; i++) { + if (parameterTypes[i] != args[i-1].getClass()) { + return false; + } + } + return true; + } + return false; + } + private void init(Object target, Method method) { final Map, Method> types = new HashMap, Method>(); final Method failingMethod = method; diff --git a/src/test/java/org/springframework/retry/annotation/RecoverAnnotationRecoveryHandlerTests.java b/src/test/java/org/springframework/retry/annotation/RecoverAnnotationRecoveryHandlerTests.java index 29b7c4b..5b4da88 100644 --- a/src/test/java/org/springframework/retry/annotation/RecoverAnnotationRecoveryHandlerTests.java +++ b/src/test/java/org/springframework/retry/annotation/RecoverAnnotationRecoveryHandlerTests.java @@ -22,9 +22,6 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.springframework.retry.ExhaustedRetryException; -import org.springframework.retry.annotation.Recover; -import org.springframework.retry.annotation.RecoverAnnotationRecoveryHandler; -import org.springframework.retry.annotation.Retryable; import org.springframework.util.ReflectionUtils; import java.lang.reflect.Method; @@ -32,6 +29,7 @@ import java.lang.reflect.Method; /** * @author Dave Syer * @author Aldo Sinanaj + * @author Randell Callahan */ public class RecoverAnnotationRecoveryHandlerTests { @@ -116,6 +114,28 @@ public class RecoverAnnotationRecoveryHandlerTests { } + @Test + public void multipleQualifyingRecoverMethods (){ + Method foo = ReflectionUtils.findMethod(MultipleQualifyingRecovers.class, + "foo", String.class); + RecoverAnnotationRecoveryHandler handler = new RecoverAnnotationRecoveryHandler( + new MultipleQualifyingRecovers(), foo); + assertEquals(1, + handler.recover(new Object[] { "Randell" }, new RuntimeException("Planned"))); + + } + + @Test + public void multipleQualifyingRecoverMethodsReOrdered (){ + Method foo = ReflectionUtils.findMethod(MultipleQualifyingRecoversReOrdered.class, + "foo", String.class); + RecoverAnnotationRecoveryHandler handler = new RecoverAnnotationRecoveryHandler( + new MultipleQualifyingRecoversReOrdered(), foo); + assertEquals(3, + handler.recover(new Object[] { "Randell" }, new RuntimeException("Planned"))); + + } + private static class InAccessibleRecover { @Retryable @@ -232,4 +252,52 @@ public class RecoverAnnotationRecoveryHandlerTests { } + protected static class MultipleQualifyingRecovers { + + @Retryable + public int foo(String name) { + return 0; + } + + @Recover + public int fooRecover(Throwable e, String name) { + return 1; + } + + @Recover + public int fooRecover(Throwable e) { + return 2; + } + + @Recover + public int barRecover(Throwable e, int number) { + return 3; + } + + } + + protected static class MultipleQualifyingRecoversReOrdered { + + @Retryable + public int foo(String name) { + return 0; + } + + @Recover + public int fooRecover(Throwable e) { + return 1; + } + + @Recover + public int barRecover(Throwable e, int number) { + return 2; + } + + @Recover + public int fooRecover(Throwable e, String name) { + return 3; + } + + } + }