diff --git a/README.md b/README.md
index 2965315..b4e59e5 100644
--- a/README.md
+++ b/README.md
@@ -476,6 +476,34 @@ class Service {
}
```
+To resolve conflicts between multiple methods that can be picked for recovery. You can explicitly specify recovery method name.
+The following example shows how to do so:
+
+```java
+@Service
+class Service {
+ @Retryable(recover = "service1Recover", value = RemoteAccessException.class)
+ public void service1(String str1, String str2) {
+ // ... do something
+ }
+
+ @Retryable(recover = "service2Recover", value = RemoteAccessException.class)
+ public void service2(String str1, String str2) {
+ // ... do something
+ }
+
+ @Recover
+ public void service1Recover(RemoteAccessException e, String str1, String str2) {
+ // ... error handling making use of original args if required
+ }
+
+ @Recover
+ public void service2Recover(RemoteAccessException e, String str1, String str2) {
+ // ... error handling making use of original args if required
+ }
+}
+```
+
Version 1.2 introduces the ability to use expressions for certain properties. The
following example show how to use expressions this way:
diff --git a/src/main/java/org/springframework/retry/annotation/RecoverAnnotationRecoveryHandler.java b/src/main/java/org/springframework/retry/annotation/RecoverAnnotationRecoveryHandler.java
index f986735..0684b01 100644
--- a/src/main/java/org/springframework/retry/annotation/RecoverAnnotationRecoveryHandler.java
+++ b/src/main/java/org/springframework/retry/annotation/RecoverAnnotationRecoveryHandler.java
@@ -26,6 +26,7 @@ import org.springframework.retry.ExhaustedRetryException;
import org.springframework.retry.interceptor.MethodInvocationRecoverer;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.MethodCallback;
+import org.springframework.util.StringUtils;
/**
* A recoverer for method invocations based on the @Recover annotation. A
@@ -43,6 +44,7 @@ import org.springframework.util.ReflectionUtils.MethodCallback;
* @author Aldo Sinanaj
* @author Randell Callahan
* @author Nathanaƫl Roberts
+ * @author Maksim Kita
* @param the type of the return value from the recovery
*/
public class RecoverAnnotationRecoveryHandler implements MethodInvocationRecoverer {
@@ -53,6 +55,8 @@ public class RecoverAnnotationRecoveryHandler implements MethodInvocationReco
private Object target;
+ private String recoverMethodName;
+
public RecoverAnnotationRecoveryHandler(Object target, Method method) {
this.target = target;
init(target, method);
@@ -81,25 +85,43 @@ public class RecoverAnnotationRecoveryHandler implements MethodInvocationReco
}
private Method findClosestMatch(Object[] args, Class extends Throwable> cause) {
- int min = Integer.MAX_VALUE;
Method result = null;
- for (Method method : this.methods.keySet()) {
- SimpleMetadata meta = this.methods.get(method);
- Class extends Throwable> type = meta.getType();
- if (type == null) {
- type = Throwable.class;
- }
- if (type.isAssignableFrom(cause)) {
- int distance = calculateDistance(cause, type);
- if (distance < min) {
- min = distance;
- result = method;
+
+ if (StringUtils.isEmpty(recoverMethodName)) {
+ int min = Integer.MAX_VALUE;
+ for (Map.Entry entry : this.methods.entrySet()) {
+ Method method = entry.getKey();
+ SimpleMetadata meta = entry.getValue();
+ Class extends Throwable> type = meta.getType();
+ if (type == null) {
+ type = Throwable.class;
}
- else if (distance == min) {
- boolean parametersMatch = compareParameters(args, meta.getArgCount(), method.getParameterTypes());
- if (parametersMatch) {
+ if (type.isAssignableFrom(cause)) {
+ int distance = calculateDistance(cause, type);
+ if (distance < min) {
+ min = distance;
result = method;
}
+ else if (distance == min) {
+ boolean parametersMatch = compareParameters(args, meta.getArgCount(),
+ method.getParameterTypes());
+ if (parametersMatch) {
+ result = method;
+ }
+ }
+ }
+ }
+ }
+ else {
+ for (Map.Entry entry : this.methods.entrySet()) {
+ 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())) {
+ result = method;
+ break;
+ }
}
}
}
@@ -139,6 +161,10 @@ public class RecoverAnnotationRecoveryHandler implements MethodInvocationReco
private void init(Object target, Method method) {
final Map, Method> types = new HashMap, Method>();
final Method failingMethod = method;
+ Retryable retryable = method.getAnnotation(Retryable.class);
+ if (retryable != null) {
+ this.recoverMethodName = retryable.recover();
+ }
ReflectionUtils.doWithMethods(failingMethod.getDeclaringClass(), new MethodCallback() {
@Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
diff --git a/src/main/java/org/springframework/retry/annotation/Retryable.java b/src/main/java/org/springframework/retry/annotation/Retryable.java
index 9096915..a556e4d 100644
--- a/src/main/java/org/springframework/retry/annotation/Retryable.java
+++ b/src/main/java/org/springframework/retry/annotation/Retryable.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 the original author or authors.
+ * Copyright 2014-2019 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.
@@ -28,6 +28,7 @@ import java.lang.annotation.Target;
* @author Dave Syer
* @author Artem Bilan
* @author Gary Russell
+ * @author Maksim Kita
* @since 1.1
*
*/
@@ -36,6 +37,13 @@ import java.lang.annotation.Target;
@Documented
public @interface Retryable {
+ /**
+ * Name of method in this class to use for recover.
+ * Method had to be marked with {@link Recover} annotation.
+ * @return the name of recover method
+ */
+ String recover() default "";
+
/**
* Retry interceptor bean name to be applied for retryable method. Is mutually
* exclusive with other attributes.
diff --git a/src/test/java/org/springframework/retry/annotation/RecoverAnnotationRecoveryHandlerTests.java b/src/test/java/org/springframework/retry/annotation/RecoverAnnotationRecoveryHandlerTests.java
index 3a31696..a81ad65 100644
--- a/src/test/java/org/springframework/retry/annotation/RecoverAnnotationRecoveryHandlerTests.java
+++ b/src/test/java/org/springframework/retry/annotation/RecoverAnnotationRecoveryHandlerTests.java
@@ -34,6 +34,7 @@ import static org.junit.Assert.assertEquals;
* @author Aldo Sinanaj
* @author Randell Callahan
* @author Nathanaƫl Roberts
+ * @author Maksim Kita
*/
public class RecoverAnnotationRecoveryHandlerTests {
@@ -161,6 +162,14 @@ public class RecoverAnnotationRecoveryHandlerTests {
handler.recover(new Object[] { new ArrayList() }, new IllegalArgumentException("Planned")));
}
+ @Test
+ public void recoverByRetryableName() {
+ Method foo = ReflectionUtils.findMethod(RecoverByRetryableName.class, "foo", String.class);
+ RecoverAnnotationRecoveryHandler> handler = new RecoverAnnotationRecoveryHandler(
+ new RecoverByRetryableName(), foo);
+ assertEquals(2, handler.recover(new Object[] { "Kevin" }, new RuntimeException("Planned")));
+ }
+
private static class InAccessibleRecover {
@Retryable
@@ -394,4 +403,23 @@ public class RecoverAnnotationRecoveryHandlerTests {
}
+ protected static class RecoverByRetryableName {
+
+ @Retryable(recover = "barRecover")
+ public int foo(String name) {
+ return 0;
+ }
+
+ @Recover
+ public int fooRecover(Throwable throwable, String name) {
+ return 1;
+ }
+
+ @Recover
+ public int barRecover(Throwable throwable, String name) {
+ return 2;
+ }
+
+ }
+
}