GH-85: Allow recover by name in the @Retryable
Fixes https://github.com/spring-projects/spring-retry/issues/85 * Added recover name in `Retryable` annotation. Added name in Recover annotation. Updated tests. * Removed unneccessary parameter name from Recover annotation * Updated entry iteration. Updated Copyrights. Added more explicit documentation. * Updated javadoc. Updated Readme. * Fixed Readme * Fixed tabs. Updated javadoc.
This commit is contained in:
28
README.md
28
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:
|
||||
|
||||
|
||||
@@ -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 <code>@Recover</code> 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 <T> the type of the return value from the recovery
|
||||
*/
|
||||
public class RecoverAnnotationRecoveryHandler<T> implements MethodInvocationRecoverer<T> {
|
||||
@@ -53,6 +55,8 @@ public class RecoverAnnotationRecoveryHandler<T> 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<T> 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<Method, SimpleMetadata> 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<Method, SimpleMetadata> 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<T> implements MethodInvocationReco
|
||||
private void init(Object target, Method method) {
|
||||
final Map<Class<? extends Throwable>, Method> types = new HashMap<Class<? extends Throwable>, 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 {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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<String>() }, new IllegalArgumentException("Planned")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void recoverByRetryableName() {
|
||||
Method foo = ReflectionUtils.findMethod(RecoverByRetryableName.class, "foo", String.class);
|
||||
RecoverAnnotationRecoveryHandler<?> handler = new RecoverAnnotationRecoveryHandler<Integer>(
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user