diff --git a/src/main/java/org/springframework/retry/annotation/RecoverAnnotationRecoveryHandler.java b/src/main/java/org/springframework/retry/annotation/RecoverAnnotationRecoveryHandler.java index 0684b01..8cdc82f 100644 --- a/src/main/java/org/springframework/retry/annotation/RecoverAnnotationRecoveryHandler.java +++ b/src/main/java/org/springframework/retry/annotation/RecoverAnnotationRecoveryHandler.java @@ -45,6 +45,7 @@ import org.springframework.util.StringUtils; * @author Randell Callahan * @author Nathanaƫl Roberts * @author Maksim Kita + * @author Gary Russell * @param the type of the return value from the recovery */ public class RecoverAnnotationRecoveryHandler implements MethodInvocationRecoverer { @@ -87,7 +88,7 @@ public class RecoverAnnotationRecoveryHandler implements MethodInvocationReco private Method findClosestMatch(Object[] args, Class cause) { Method result = null; - if (StringUtils.isEmpty(recoverMethodName)) { + if (StringUtils.isEmpty(this.recoverMethodName)) { int min = Integer.MAX_VALUE; for (Map.Entry entry : this.methods.entrySet()) { Method method = entry.getKey(); @@ -103,8 +104,8 @@ public class RecoverAnnotationRecoveryHandler implements MethodInvocationReco result = method; } else if (distance == min) { - boolean parametersMatch = compareParameters(args, meta.getArgCount(), - method.getParameterTypes()); + boolean parametersMatch = compareParameters(args, + meta.getArgCount(), method.getParameterTypes()); if (parametersMatch) { result = method; } @@ -117,8 +118,8 @@ public class RecoverAnnotationRecoveryHandler implements MethodInvocationReco Method method = entry.getKey(); if (method.getName().equals(this.recoverMethodName)) { SimpleMetadata meta = entry.getValue(); - if (meta.type.isAssignableFrom(cause) - && compareParameters(args, meta.getArgCount(), method.getParameterTypes())) { + if (meta.type.isAssignableFrom(cause) && compareParameters(args, + meta.getArgCount(), method.getParameterTypes())) { result = method; break; } @@ -128,7 +129,8 @@ public class RecoverAnnotationRecoveryHandler implements MethodInvocationReco return result; } - private int calculateDistance(Class cause, Class type) { + private int calculateDistance(Class cause, + Class type) { int result = 0; Class current = cause; while (current != type && current != Throwable.class) { @@ -138,14 +140,17 @@ public class RecoverAnnotationRecoveryHandler implements MethodInvocationReco return result; } - private boolean compareParameters(Object[] args, int argCount, Class[] parameterTypes) { + private boolean compareParameters(Object[] args, int argCount, + Class[] parameterTypes) { if (argCount == (args.length + 1)) { int startingIndex = 0; - if (parameterTypes.length > 0 && Throwable.class.isAssignableFrom(parameterTypes[0])) { + if (parameterTypes.length > 0 + && Throwable.class.isAssignableFrom(parameterTypes[0])) { startingIndex = 1; } for (int i = startingIndex; i < parameterTypes.length; i++) { - final Object argument = i - startingIndex < args.length ? args[i - startingIndex] : null; + final Object argument = i - startingIndex < args.length + ? args[i - startingIndex] : null; if (argument == null) { continue; } @@ -165,31 +170,48 @@ public class RecoverAnnotationRecoveryHandler implements MethodInvocationReco if (retryable != null) { this.recoverMethodName = retryable.recover(); } - ReflectionUtils.doWithMethods(failingMethod.getDeclaringClass(), new MethodCallback() { - @Override - public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException { - Recover recover = AnnotationUtils.findAnnotation(method, Recover.class); - if (recover != null && method.getReturnType().isAssignableFrom(failingMethod.getReturnType())) { - Class[] parameterTypes = method.getParameterTypes(); - if (parameterTypes.length > 0 && Throwable.class.isAssignableFrom(parameterTypes[0])) { - @SuppressWarnings("unchecked") - Class type = (Class) parameterTypes[0]; - types.put(type, method); - RecoverAnnotationRecoveryHandler.this.methods.put(method, - new SimpleMetadata(parameterTypes.length, type)); + ReflectionUtils.doWithMethods(failingMethod.getDeclaringClass(), + new MethodCallback() { + @Override + public void doWith(Method method) + throws IllegalArgumentException, IllegalAccessException { + Recover recover = AnnotationUtils.findAnnotation(method, + Recover.class); + if (recover != null && method.getReturnType() + .isAssignableFrom(failingMethod.getReturnType())) { + Class[] parameterTypes = method.getParameterTypes(); + if (parameterTypes.length > 0 && Throwable.class + .isAssignableFrom(parameterTypes[0])) { + @SuppressWarnings("unchecked") + Class type = (Class) parameterTypes[0]; + types.put(type, method); + RecoverAnnotationRecoveryHandler.this.methods.put(method, + new SimpleMetadata(parameterTypes.length, type)); + } + else { + RecoverAnnotationRecoveryHandler.this.classifier + .setDefaultValue(method); + RecoverAnnotationRecoveryHandler.this.methods.put(method, + new SimpleMetadata(parameterTypes.length, null)); + } + } } - else { - RecoverAnnotationRecoveryHandler.this.classifier.setDefaultValue(method); - RecoverAnnotationRecoveryHandler.this.methods.put(method, - new SimpleMetadata(parameterTypes.length, null)); - } - } - } - }); + }); this.classifier.setTypeMap(types); optionallyFilterMethodsBy(failingMethod.getReturnType()); } + private Recover findAnnotationOnTarget(Object target, Method method) { + try { + Method targetMethod = target.getClass().getMethod(method.getName(), + method.getParameterTypes()); + return AnnotationUtils.findAnnotation(targetMethod, Recover.class); + } + catch (Exception e) { + return null; + } + } + private void optionallyFilterMethodsBy(Class returnClass) { Map filteredMethods = new HashMap(); for (Method method : this.methods.keySet()) { @@ -229,7 +251,8 @@ public class RecoverAnnotationRecoveryHandler implements MethodInvocationReco result[0] = t; startArgs = 1; } - int length = result.length - startArgs > args.length ? args.length : result.length - startArgs; + int length = result.length - startArgs > args.length ? args.length + : result.length - startArgs; if (length == 0) { return result; } diff --git a/src/test/java/org/springframework/retry/annotation/EnableRetryTests.java b/src/test/java/org/springframework/retry/annotation/EnableRetryTests.java index 83cd119..2658879 100644 --- a/src/test/java/org/springframework/retry/annotation/EnableRetryTests.java +++ b/src/test/java/org/springframework/retry/annotation/EnableRetryTests.java @@ -176,6 +176,8 @@ public class EnableRetryTests { service.service1(); service.service2(); assertEquals(4, service.getCount()); + service.service3(); + assertTrue(service.isRecovered()); context.close(); } @@ -582,12 +584,20 @@ public class EnableRetryTests { int getCount(); + void service3(); + + void recover(Exception e); + + boolean isRecovered(); + } public static class TheClass implements TheInterface { private int count = 0; + private boolean recovered; + @Override @Retryable public void service1() { @@ -608,6 +618,23 @@ public class EnableRetryTests { return count; } + @Override + @Retryable + public void service3() { + throw new RuntimeException("planned"); + } + + @Override + @Recover + public void recover(Exception e) { + this.recovered = true; + } + + @Override + public boolean isRecovered() { + return this.recovered; + } + } public static interface NotAnnotatedInterface {