Apply format plugin

This commit is contained in:
Dave Syer
2019-03-20 17:03:08 +00:00
parent 0a1c3cb3c1
commit 84e906fbe6
127 changed files with 1956 additions and 1854 deletions

17
pom.xml
View File

@@ -23,6 +23,7 @@
<properties>
<maven.test.failure.ignore>true</maven.test.failure.ignore>
<spring.framework.version>4.3.22.RELEASE</spring.framework.version>
<disable.checks>false</disable.checks>
</properties>
<profiles>
<profile>
@@ -348,6 +349,22 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>io.spring.javaformat</groupId>
<artifactId>spring-javaformat-maven-plugin</artifactId>
<executions>
<execution>
<phase>validate</phase>
<configuration>
<skip>${disable.checks}</skip>
</configuration>
<goals>
<goal>apply</goal>
<goal>validate</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -18,11 +18,11 @@ package org.springframework.classify;
import java.util.Map;
/**
* A special purpose {@link Classifier} with easy configuration options for
* mapping from one arbitrary type of object to another via a pattern matcher.
*
* A special purpose {@link Classifier} with easy configuration options for mapping from
* one arbitrary type of object to another via a pattern matcher.
*
* @author Dave Syer
*
*
*/
@SuppressWarnings("serial")
public class BackToBackPatternClassifier<C, T> implements Classifier<C, T> {
@@ -32,28 +32,26 @@ public class BackToBackPatternClassifier<C, T> implements Classifier<C, T> {
private Classifier<String, T> matcher;
/**
* Default constructor, provided as a convenience for people using setter
* injection.
* Default constructor, provided as a convenience for people using setter injection.
*/
public BackToBackPatternClassifier() {
}
/**
* Set up a classifier with input to the router and output from the matcher.
*
* @param router see {@link #setRouterDelegate(Object)}
* @param matcher see {@link #setMatcherMap(Map)}
*/
public BackToBackPatternClassifier(Classifier<C, String> router, Classifier<String, T> matcher) {
public BackToBackPatternClassifier(Classifier<C, String> router,
Classifier<String, T> matcher) {
super();
this.router = router;
this.matcher = matcher;
}
/**
* A convenience method for creating a pattern matching classifier for the
* matcher component.
*
* A convenience method for creating a pattern matching classifier for the matcher
* component.
* @param map maps pattern keys with wildcards to output values
*/
public void setMatcherMap(Map<String, T> map) {
@@ -61,21 +59,19 @@ public class BackToBackPatternClassifier<C, T> implements Classifier<C, T> {
}
/**
* A convenience method of creating a router classifier based on a plain old
* Java Object. The object provided must have precisely one public method
* that either has the <code>@Classifier</code> annotation or accepts a single argument
* and outputs a String. This will be used to create an input classifier for
* the router component.
*
* A convenience method of creating a router classifier based on a plain old Java
* Object. The object provided must have precisely one public method that either has
* the <code>@Classifier</code> annotation or accepts a single argument and outputs a
* String. This will be used to create an input classifier for the router component.
* @param delegate the delegate object used to create a router classifier
*/
public void setRouterDelegate(Object delegate) {
this.router = new ClassifierAdapter<C,String>(delegate);
this.router = new ClassifierAdapter<C, String>(delegate);
}
/**
* Classify the input and map to a String, then take that and put it into a
* pattern matcher to match to an output value.
* Classify the input and map to a String, then take that and put it into a pattern
* matcher to match to an output value.
*/
public T classify(C classifiable) {
return matcher.classify(router.classify(classifiable));

View File

@@ -21,14 +21,12 @@ import java.util.HashMap;
import java.util.Map;
/**
* A {@link Classifier} for exceptions that has only two classes (true and
* false). Classifies objects according to their inheritance relation with the
* supplied types. If the object to be classified is one of the provided types,
* or is a subclass of one of the types, then the non-default value is returned
* (usually true).
* A {@link Classifier} for exceptions that has only two classes (true and false).
* Classifies objects according to their inheritance relation with the supplied types. If
* the object to be classified is one of the provided types, or is a subclass of one of
* the types, then the non-default value is returned (usually true).
*
* @see SubclassClassifier
*
* @author Dave Syer
* @author Gary Russell
*
@@ -44,14 +42,13 @@ public class BinaryExceptionClassifier extends SubclassClassifier<Throwable, Boo
public static BinaryExceptionClassifier newDefaultClassifier() {
// create new instance for each call due to mutability
return new BinaryExceptionClassifier(
Collections.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true), false
);
return new BinaryExceptionClassifier(Collections
.<Class<? extends Throwable>, Boolean>singletonMap(Exception.class, true),
false);
}
/**
* Create a binary exception classifier with the provided default value.
*
* @param defaultValue defaults to false
*/
public BinaryExceptionClassifier(boolean defaultValue) {
@@ -60,13 +57,13 @@ public class BinaryExceptionClassifier extends SubclassClassifier<Throwable, Boo
/**
* Create a binary exception classifier with the provided classes and their
* subclasses. The mapped value for these exceptions will be the one
* provided (which will be the opposite of the default).
*
* subclasses. The mapped value for these exceptions will be the one provided (which
* will be the opposite of the default).
* @param exceptionClasses the exceptions to classify among
* @param value the value to classify
*/
public BinaryExceptionClassifier(Collection<Class<? extends Throwable>> exceptionClasses, boolean value) {
public BinaryExceptionClassifier(
Collection<Class<? extends Throwable>> exceptionClasses, boolean value) {
this(!value);
if (exceptionClasses != null) {
Map<Class<? extends Throwable>, Boolean> map = new HashMap<Class<? extends Throwable>, Boolean>();
@@ -78,18 +75,18 @@ public class BinaryExceptionClassifier extends SubclassClassifier<Throwable, Boo
}
/**
* Create a binary exception classifier with the default value false and
* value mapping true for the provided classes and their subclasses.
*
* Create a binary exception classifier with the default value false and value mapping
* true for the provided classes and their subclasses.
* @param exceptionClasses the exception types to throw
*/
public BinaryExceptionClassifier(Collection<Class<? extends Throwable>> exceptionClasses) {
public BinaryExceptionClassifier(
Collection<Class<? extends Throwable>> exceptionClasses) {
this(exceptionClasses, true);
}
/**
* Create a binary exception classifier using the given classification map
* and a default classification of false.
* Create a binary exception classifier using the given classification map and a
* default classification of false.
* @param typeMap the map of types
*/
public BinaryExceptionClassifier(Map<Class<? extends Throwable>, Boolean> typeMap) {
@@ -97,25 +94,25 @@ public class BinaryExceptionClassifier extends SubclassClassifier<Throwable, Boo
}
/**
* Create a binary exception classifier using the given classification map
* and the given value for default class.
*
* Create a binary exception classifier using the given classification map and the
* given value for default class.
* @param defaultValue the default value to use
* @param typeMap the map of types to classify
*/
public BinaryExceptionClassifier(Map<Class<? extends Throwable>, Boolean> typeMap, boolean defaultValue) {
public BinaryExceptionClassifier(Map<Class<? extends Throwable>, Boolean> typeMap,
boolean defaultValue) {
super(typeMap, defaultValue);
}
/**
* Create a binary exception classifier.
*
* @param defaultValue the default value to use
* @param typeMap the map of types to classify
* @param traverseCauses if true, throwable's causes will be inspected to find non-default class
* @param traverseCauses if true, throwable's causes will be inspected to find
* non-default class
*/
public BinaryExceptionClassifier(Map<Class<? extends Throwable>, Boolean> typeMap, boolean defaultValue,
boolean traverseCauses) {
public BinaryExceptionClassifier(Map<Class<? extends Throwable>, Boolean> typeMap,
boolean defaultValue, boolean traverseCauses) {
super(typeMap, defaultValue);
this.traverseCauses = traverseCauses;
}
@@ -132,8 +129,8 @@ public class BinaryExceptionClassifier extends SubclassClassifier<Throwable, Boo
}
/*
* If the result is the default, we need to find out if it was by default
* or so configured; if default, try the cause(es).
* If the result is the default, we need to find out if it was by default or so
* configured; if default, try the cause(es).
*/
if (classified.equals(this.getDefault())) {
Throwable cause = classifiable;

View File

@@ -24,15 +24,12 @@ import org.springframework.util.Assert;
/**
* Fluent API for BinaryExceptionClassifier configuration.
* <p>
* Can be used in while list style:
* <pre>{@code
* Can be used in while list style: <pre>{@code
* BinaryExceptionClassifier.newBuilder()
* .retryOn(IOException.class)
* .retryOn(IllegalArgumentException.class)
* .build();
* } </pre>
* or in black list style:
* <pre>{@code
* } </pre> or in black list style: <pre>{@code
* BinaryExceptionClassifier.newBuilder()
* .notRetryOn(Error.class)
* .build();
@@ -40,18 +37,16 @@ import org.springframework.util.Assert;
* <p>
* Provides traverseCauses=false by default, and no default rules for exceptions.
* <p>
* Not thread safe. Building should be performed in a single thread, publishing of
* newly created instance should be safe.
* Not thread safe. Building should be performed in a single thread, publishing of newly
* created instance should be safe.
*
* @author Aleksandr Shamukov
*/
public class BinaryExceptionClassifierBuilder {
/**
* Building notation type (white list or black list)
* - null: has not selected yet
* - true: white list
* - false: black list
* Building notation type (white list or black list) - null: has not selected yet -
* true: white list - false: black list
*/
private Boolean isWhiteList = null;
@@ -59,7 +54,8 @@ public class BinaryExceptionClassifierBuilder {
private List<Class<? extends Throwable>> exceptionClasses = new ArrayList<Class<? extends Throwable>>();
public BinaryExceptionClassifierBuilder retryOn(Class<? extends Throwable> throwable) {
public BinaryExceptionClassifierBuilder retryOn(
Class<? extends Throwable> throwable) {
Assert.isTrue(isWhiteList == null || isWhiteList,
"Please use only retryOn() or only notRetryOn()");
Assert.notNull(throwable, "Exception class can not be null");
@@ -69,7 +65,8 @@ public class BinaryExceptionClassifierBuilder {
}
public BinaryExceptionClassifierBuilder notRetryOn(Class<? extends Throwable> throwable) {
public BinaryExceptionClassifierBuilder notRetryOn(
Class<? extends Throwable> throwable) {
Assert.isTrue(isWhiteList == null || !isWhiteList,
"Please use only retryOn() or only notRetryOn()");
Assert.notNull(throwable, "Exception class can not be null");
@@ -88,10 +85,12 @@ public class BinaryExceptionClassifierBuilder {
"Attempt to build classifier with empty rules. To build always true, or always false "
+ "instance, please use explicit rule for Throwable");
BinaryExceptionClassifier classifier = new BinaryExceptionClassifier(
exceptionClasses,
isWhiteList // using white list means classifying provided classes as "true" (is retryable)
exceptionClasses, isWhiteList // using white list means classifying
// provided classes as "true" (is
// retryable)
);
classifier.setTraverseCauses(traverseCauses);
return classifier;
}
}

View File

@@ -26,14 +26,13 @@ import java.io.Serializable;
* themselves serializable.
*
* @author Dave Syer
*
*
*/
public interface Classifier<C, T> extends Serializable {
/**
* Classify the given object and return an object of a different type, possibly an
* enumerated type.
*
* @param classifiable the input object. Can be null.
* @return an object. Can be null, but implementations should declare if this is the
* case.

View File

@@ -21,9 +21,9 @@ import org.springframework.util.Assert;
/**
* Wrapper for an object to adapt it to the {@link Classifier} interface.
*
*
* @author Dave Syer
*
*
*/
@SuppressWarnings("serial")
public class ClassifierAdapter<C, T> implements Classifier<C, T> {
@@ -40,9 +40,8 @@ public class ClassifierAdapter<C, T> implements Classifier<C, T> {
}
/**
* Create a new {@link Classifier} from the delegate provided. Use the
* constructor as an alternative to the {@link #setDelegate(Object)} method.
*
* Create a new {@link Classifier} from the delegate provided. Use the constructor as
* an alternative to the {@link #setDelegate(Object)} method.
* @param delegate the delegate
*/
public ClassifierAdapter(Object delegate) {
@@ -50,10 +49,8 @@ public class ClassifierAdapter<C, T> implements Classifier<C, T> {
}
/**
* Create a new {@link Classifier} from the delegate provided. Use the
* constructor as an alternative to the {@link #setDelegate(Classifier)}
* method.
*
* Create a new {@link Classifier} from the delegate provided. Use the constructor as
* an alternative to the {@link #setDelegate(Classifier)} method.
* @param delegate the classifier to delegate to
*/
public ClassifierAdapter(Classifier<C, T> delegate) {
@@ -66,15 +63,13 @@ public class ClassifierAdapter<C, T> implements Classifier<C, T> {
}
/**
* Search for the
* {@link org.springframework.classify.annotation.Classifier
* Classifier} annotation on a method in the supplied delegate and use that
* to create a {@link Classifier} from the parameter type to the return
* type. If the annotation is not found a unique non-void method with a
* single parameter will be used, if it exists. The signature of the method
* cannot be checked here, so might be a runtime exception when the method
* is invoked if the signature doesn't match the classifier types.
*
* Search for the {@link org.springframework.classify.annotation.Classifier
* Classifier} annotation on a method in the supplied delegate and use that to create
* a {@link Classifier} from the parameter type to the return type. If the annotation
* is not found a unique non-void method with a single parameter will be used, if it
* exists. The signature of the method cannot be checked here, so might be a runtime
* exception when the method is invoked if the signature doesn't match the classifier
* types.
* @param delegate an object with an annotated method
*/
public final void setDelegate(Object delegate) {
@@ -82,7 +77,8 @@ public class ClassifierAdapter<C, T> implements Classifier<C, T> {
invoker = MethodInvokerUtils.getMethodInvokerByAnnotation(
org.springframework.classify.annotation.Classifier.class, delegate);
if (invoker == null) {
invoker = MethodInvokerUtils.<C, T> getMethodInvokerForSingleArgument(delegate);
invoker = MethodInvokerUtils
.<C, T>getMethodInvokerForSingleArgument(delegate);
}
Assert.state(invoker != null, "No single argument public method with or without "
+ "@Classifier was found in delegate of type " + delegate.getClass());

View File

@@ -17,11 +17,11 @@
package org.springframework.classify;
/**
* Base class for {@link Classifier} implementations. Provides default behaviour
* and some convenience members, like constants.
*
* Base class for {@link Classifier} implementations. Provides default behaviour and some
* convenience members, like constants.
*
* @author Dave Syer
*
*
*/
@SuppressWarnings("serial")
public class ClassifierSupport<C, T> implements Classifier<C, T> {
@@ -37,9 +37,9 @@ public class ClassifierSupport<C, T> implements Classifier<C, T> {
}
/**
* Always returns the default value. This is the main extension point for
* subclasses, so it must be able to classify null.
*
* Always returns the default value. This is the main extension point for subclasses,
* so it must be able to classify null.
*
* @see org.springframework.classify.Classifier#classify(Object)
*/
public T classify(C throwable) {

View File

@@ -31,6 +31,7 @@ import org.springframework.util.Assert;
public class PatternMatcher<S> {
private Map<String, S> map = new HashMap<String, S>();
private List<String> sorted = new ArrayList<String>();
/**
@@ -52,12 +53,10 @@ public class PatternMatcher<S> {
}
/**
* Lifted from AntPathMatcher in Spring Core. Tests whether or not a string
* matches against a pattern. The pattern may contain two special
* characters:<br>
* Lifted from AntPathMatcher in Spring Core. Tests whether or not a string matches
* against a pattern. The pattern may contain two special characters:<br>
* '*' means zero or more characters<br>
* '?' means one and only one character
*
* @param pattern pattern to match against. Must not be <code>null</code>.
* @param str string which must be matched against the pattern. Must not be
* <code>null</code>.
@@ -192,20 +191,17 @@ public class PatternMatcher<S> {
/**
* <p>
* This method takes a String key and a map from Strings to values of any
* type. During processing, the method will identify the most specific key
* in the map that matches the line. Once the correct is identified, its
* value is returned. Note that if the map contains the wildcard string "*"
* as a key, then it will serve as the "default" case, matching every line
* that does not match anything else.
*
* This method takes a String key and a map from Strings to values of any type. During
* processing, the method will identify the most specific key in the map that matches
* the line. Once the correct is identified, its value is returned. Note that if the
* map contains the wildcard string "*" as a key, then it will serve as the "default"
* case, matching every line that does not match anything else.
*
* <p>
* If no matching prefix is found, a {@link IllegalStateException} will be
* thrown.
*
* If no matching prefix is found, a {@link IllegalStateException} will be thrown.
*
* <p>
* Null keys are not allowed in the map.
*
* @param line An input string
* @return the value whose prefix matches the given line
*/
@@ -222,7 +218,8 @@ public class PatternMatcher<S> {
}
if (value == null) {
throw new IllegalStateException("Could not find a matching pattern for key=[" + line + "]");
throw new IllegalStateException(
"Could not find a matching pattern for key=[" + line + "]");
}
return value;

View File

@@ -19,13 +19,13 @@ import java.util.HashMap;
import java.util.Map;
/**
* A {@link Classifier} that maps from String patterns with wildcards to a set
* of values of a given type. An input String is matched with the most specific
* pattern possible to the corresponding value in an input map. A default value
* should be specified with a pattern key of "*".
*
* A {@link Classifier} that maps from String patterns with wildcards to a set of values
* of a given type. An input String is matched with the most specific pattern possible to
* the corresponding value in an input map. A default value should be specified with a
* pattern key of "*".
*
* @author Dave Syer
*
*
*/
@SuppressWarnings("serial")
public class PatternMatchingClassifier<T> implements Classifier<String, T> {
@@ -33,17 +33,16 @@ public class PatternMatchingClassifier<T> implements Classifier<String, T> {
private PatternMatcher<T> values;
/**
* Default constructor. Use the setter or the other constructor to create a
* sensible classifier, otherwise all inputs will cause an exception.
* Default constructor. Use the setter or the other constructor to create a sensible
* classifier, otherwise all inputs will cause an exception.
*/
public PatternMatchingClassifier() {
this(new HashMap<String, T>());
}
/**
* Create a classifier from the provided map. The keys are patterns, using
* '?' as a single character and '*' as multi-character wildcard.
*
* Create a classifier from the provided map. The keys are patterns, using '?' as a
* single character and '*' as multi-character wildcard.
* @param values the values to use in the {@link PatternMatcher}
*/
public PatternMatchingClassifier(Map<String, T> values) {
@@ -61,11 +60,9 @@ public class PatternMatchingClassifier<T> implements Classifier<String, T> {
/**
* Classify the input by matching it against the patterns provided in
* {@link #setPatternMap(Map)}. The most specific pattern that matches will
* be used to locate a value.
*
* {@link #setPatternMap(Map)}. The most specific pattern that matches will be used to
* locate a value.
* @return the value matching the most specific pattern possible
*
* @throws IllegalStateException if no matching value is found.
*/
public T classify(String classifiable) {

View File

@@ -21,12 +21,11 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* A {@link Classifier} for a parameterised object type based on a map.
* Classifies objects according to their inheritance relation with the supplied
* type map. If the object to be classified is one of the keys of the provided
* map, or is a subclass of one of the keys, then the map entry value for that
* key is returned. Otherwise returns the default value which is null by
* default.
* A {@link Classifier} for a parameterised object type based on a map. Classifies objects
* according to their inheritance relation with the supplied type map. If the object to be
* classified is one of the keys of the provided map, or is a subclass of one of the keys,
* then the map entry value for that key is returned. Otherwise returns the default value
* which is null by default.
*
* @author Dave Syer
* @author Gary Russell
@@ -49,7 +48,6 @@ public class SubclassClassifier<T, C> implements Classifier<T, C> {
/**
* Create a {@link SubclassClassifier} with supplied default value.
*
* @param defaultValue the default value
*/
public SubclassClassifier(C defaultValue) {
@@ -58,7 +56,6 @@ public class SubclassClassifier<T, C> implements Classifier<T, C> {
/**
* Create a {@link SubclassClassifier} with supplied default value.
*
* @param defaultValue the default value
* @param typeMap the map of types
*/
@@ -69,9 +66,8 @@ public class SubclassClassifier<T, C> implements Classifier<T, C> {
}
/**
* Public setter for the default value for mapping keys that are not found
* in the map (or their subclasses). Defaults to false.
*
* Public setter for the default value for mapping keys that are not found in the map
* (or their subclasses). Defaults to false.
* @param defaultValue the default value to set
*/
public void setDefaultValue(C defaultValue) {
@@ -79,10 +75,9 @@ public class SubclassClassifier<T, C> implements Classifier<T, C> {
}
/**
* Set the classifications up as a map. The keys are types and these will be
* mapped along with all their subclasses to the corresponding value. The
* most specific types will match first.
*
* Set the classifications up as a map. The keys are types and these will be mapped
* along with all their subclasses to the corresponding value. The most specific types
* will match first.
* @param map a map from type to class
*/
public void setTypeMap(Map<Class<? extends T>, C> map) {
@@ -90,9 +85,8 @@ public class SubclassClassifier<T, C> implements Classifier<T, C> {
}
/**
* Return the value from the type map whose key is the class of the given
* Throwable, or its nearest ancestor if a subclass.
*
* Return the value from the type map whose key is the class of the given Throwable,
* or its nearest ancestor if a subclass.
* @return C the classified value
* @param classifiable the classifiable thing
*/
@@ -110,11 +104,12 @@ public class SubclassClassifier<T, C> implements Classifier<T, C> {
// check for subclasses
C value = null;
for (Class<?> cls = exceptionClass; !cls.equals(Object.class) && value == null; cls = cls.getSuperclass()) {
for (Class<?> cls = exceptionClass; !cls.equals(Object.class)
&& value == null; cls = cls.getSuperclass()) {
value = classified.get(cls);
}
//ConcurrentHashMap doesn't allow nulls
// ConcurrentHashMap doesn't allow nulls
if (value != null) {
this.classified.put(exceptionClass, value);
}
@@ -128,7 +123,6 @@ public class SubclassClassifier<T, C> implements Classifier<T, C> {
/**
* Return the default value supplied in the constructor (default false).
*
* @return C the default value
*/
final public C getDefault() {
@@ -138,4 +132,5 @@ public class SubclassClassifier<T, C> implements Classifier<T, C> {
protected Map<Class<? extends T>, C> getClassified() {
return classified;
}
}

View File

@@ -23,11 +23,11 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Mark a method as capable of classifying its input to an instance of its
* output. Should only be used on non-void methods with one parameter.
*
* Mark a method as capable of classifying its input to an instance of its output. Should
* only be used on non-void methods with one parameter.
*
* @author Dave Syer
*
*
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)

View File

@@ -29,16 +29,15 @@ import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
/**
* MethodResolver implementation that finds a <em>single</em> Method on the
* given Class that contains the specified annotation type.
*
* MethodResolver implementation that finds a <em>single</em> Method on the given Class
* that contains the specified annotation type.
*
* @author Mark Fisher
*/
public class AnnotationMethodResolver implements MethodResolver {
private Class<? extends Annotation> annotationType;
/**
* Create a MethodResolver for the specified Method-level annotation type
* @param annotationType the type of the annotation
@@ -51,19 +50,14 @@ public class AnnotationMethodResolver implements MethodResolver {
this.annotationType = annotationType;
}
/**
* Find a <em>single</em> Method on the Class of the given candidate object
* that contains the annotation type for which this resolver is searching.
*
* @param candidate the instance whose Class will be checked for the
* Find a <em>single</em> Method on the Class of the given candidate object that
* contains the annotation type for which this resolver is searching.
* @param candidate the instance whose Class will be checked for the annotation
* @return a single matching Method instance or <code>null</code> if the candidate's
* Class contains no Methods with the specified annotation
* @throws IllegalArgumentException if more than one Method has the specified
* annotation
*
* @return a single matching Method instance or <code>null</code> if the
* candidate's Class contains no Methods with the specified annotation
*
* @throws IllegalArgumentException if more than one Method has the
* specified annotation
*/
public Method findMethod(Object candidate) {
Assert.notNull(candidate, "candidate object must not be null");
@@ -75,26 +69,27 @@ public class AnnotationMethodResolver implements MethodResolver {
}
/**
* Find a <em>single</em> Method on the given Class that contains the
* annotation type for which this resolver is searching.
*
* Find a <em>single</em> Method on the given Class that contains the annotation type
* for which this resolver is searching.
* @param clazz the Class instance to check for the annotation
*
* @return a single matching Method instance or <code>null</code> if the
* Class contains no Methods with the specified annotation
*
* @throws IllegalArgumentException if more than one Method has the
* specified annotation
* @return a single matching Method instance or <code>null</code> if the Class
* contains no Methods with the specified annotation
* @throws IllegalArgumentException if more than one Method has the specified
* annotation
*/
public Method findMethod(final Class<?> clazz) {
Assert.notNull(clazz, "class must not be null");
final AtomicReference<Method> annotatedMethod = new AtomicReference<Method>();
ReflectionUtils.doWithMethods(clazz, new ReflectionUtils.MethodCallback() {
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
Annotation annotation = AnnotationUtils.findAnnotation(method, annotationType);
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 ["
+ clazz + "] with the annotation type [" + annotationType + "]");
Assert.isNull(annotatedMethod.get(),
"found more than one method on target class [" + clazz
+ "] with the annotation type [" + annotationType
+ "]");
annotatedMethod.set(method);
}
}

View File

@@ -17,13 +17,12 @@
package org.springframework.classify.util;
/**
* A strategy interface for invoking a method.
* Typically used by adapters.
*
* A strategy interface for invoking a method. Typically used by adapters.
*
* @author Mark Fisher
*/
public interface MethodInvoker {
Object invokeMethod(Object ... args);
Object invokeMethod(Object... args);
}

View File

@@ -41,7 +41,6 @@ public class MethodInvokerUtils {
/**
* Create a {@link MethodInvoker} using the provided method name to search.
*
* @param object to be invoked
* @param methodName of the method to be invoked
* @param paramsRequired boolean indicating whether the parameters are required, if
@@ -71,7 +70,6 @@ public class MethodInvokerUtils {
/**
* Create a String representation of the array of parameter types.
*
* @param paramTypes the types of parameters
* @return the paramTypes as String representation
*/
@@ -89,7 +87,6 @@ public class MethodInvokerUtils {
/**
* Create a {@link MethodInvoker} using the provided interface, and method name from
* that interface.
*
* @param cls the interface to search for the method named
* @param methodName of the method to be invoked
* @param object to be invoked
@@ -111,7 +108,6 @@ public class MethodInvokerUtils {
/**
* Create a MethodInvoker from the delegate based on the annotationType. Ensure that
* the annotated method has a valid set of parameters.
*
* @param annotationType the annotation to scan for
* @param target the target object
* @param expectedParamTypes the expected parameter types for the method
@@ -163,7 +159,6 @@ public class MethodInvokerUtils {
* Create {@link MethodInvoker} for the method with the provided annotation on the
* provided object. Annotations that cannot be applied to methods (i.e. that aren't
* annotated with an element type of METHOD) will cause an exception to be thrown.
*
* @param annotationType to be searched for
* @param target to be invoked
* @return MethodInvoker for the provided annotation, null if none is found.
@@ -209,7 +204,6 @@ public class MethodInvokerUtils {
/**
* Create a {@link MethodInvoker} for the delegate from a single public method.
*
* @param target an object to search for an appropriate method
* @return a MethodInvoker that calls a method on the delegate
* @param <T> the t
@@ -241,4 +235,5 @@ public class MethodInvokerUtils {
Method method = methodHolder.get();
return new SimpleMethodInvoker(target, method);
}
}

View File

@@ -20,37 +20,29 @@ import java.lang.reflect.Method;
/**
* Strategy interface for detecting a single Method on a Class.
*
*
* @author Mark Fisher
*/
public interface MethodResolver {
/**
* Find a single Method on the provided Object that matches this resolver's
* criteria.
*
* @param candidate the candidate Object whose Class should be searched for
* a Method
*
* @return a single Method or <code>null</code> if no Method matching this
* resolver's criteria can be found.
*
* @throws IllegalArgumentException if more than one Method defined on the
* given candidate's Class matches this resolver's criteria
* Find a single Method on the provided Object that matches this resolver's criteria.
* @param candidate the candidate Object whose Class should be searched for a Method
* @return a single Method or <code>null</code> if no Method matching this resolver's
* criteria can be found.
* @throws IllegalArgumentException if more than one Method defined on the given
* candidate's Class matches this resolver's criteria
*/
Method findMethod(Object candidate) throws IllegalArgumentException;
/**
* Find a <em>single</em> Method on the given Class that matches this
* resolver's criteria.
*
* Find a <em>single</em> Method on the given Class that matches this resolver's
* criteria.
* @param clazz the Class instance on which to search for a Method
*
* @return a single Method or <code>null</code> if no Method matching this
* resolver's criteria can be found.
*
* @throws IllegalArgumentException if more than one Method defined on the
* given Class matches this resolver's criteria
* @return a single Method or <code>null</code> if no Method matching this resolver's
* criteria can be found.
* @throws IllegalArgumentException if more than one Method defined on the given Class
* matches this resolver's criteria
*/
Method findMethod(Class<?> clazz);

View File

@@ -24,10 +24,10 @@ import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* Simple implementation of the {@link MethodInvoker} interface that invokes a
* method on an object. If the method has no arguments, but arguments are
* provided, they are ignored and the method is invoked anyway. If there are
* more arguments than there are provided, then an exception is thrown.
* Simple implementation of the {@link MethodInvoker} interface that invokes a method on
* an object. If the method has no arguments, but arguments are provided, they are ignored
* and the method is invoked anyway. If there are more arguments than there are provided,
* then an exception is thrown.
*
* @author Lucas Ward
* @author Artem Bilan
@@ -54,14 +54,18 @@ public class SimpleMethodInvoker implements MethodInvoker {
public SimpleMethodInvoker(Object object, String methodName, Class<?>... paramTypes) {
Assert.notNull(object, "Object to invoke must not be null");
Method method = ClassUtils.getMethodIfAvailable(object.getClass(), methodName, paramTypes);
Method method = ClassUtils.getMethodIfAvailable(object.getClass(), methodName,
paramTypes);
if (method == null) {
// try with no params
method = ClassUtils.getMethodIfAvailable(object.getClass(), methodName, new Class[] {});
method = ClassUtils.getMethodIfAvailable(object.getClass(), methodName,
new Class[] {});
}
Assert.notNull(method, "No methods found for name: [" + methodName + "] in class: ["
+ object.getClass() + "] with arguments of type: [" + Arrays.toString(paramTypes) + "]");
Assert.notNull(method,
"No methods found for name: [" + methodName + "] in class: ["
+ object.getClass() + "] with arguments of type: ["
+ Arrays.toString(paramTypes) + "]");
this.object = object;
this.method = method;
@@ -72,14 +76,14 @@ public class SimpleMethodInvoker implements MethodInvoker {
/*
* (non-Javadoc)
*
* @see
* org.springframework.batch.core.configuration.util.MethodInvoker#invokeMethod
* @see org.springframework.batch.core.configuration.util.MethodInvoker#invokeMethod
* (java.lang.Object[])
*/
@Override
public Object invokeMethod(Object... args) {
Assert.state(this.parameterTypes.length == args.length,
"Wrong number of arguments, expected no more than: [" + this.parameterTypes.length + "]");
"Wrong number of arguments, expected no more than: ["
+ this.parameterTypes.length + "]");
try {
// Extract the target from an Advised as late as possible
@@ -88,8 +92,9 @@ public class SimpleMethodInvoker implements MethodInvoker {
return method.invoke(target, args);
}
catch (Exception e) {
throw new IllegalArgumentException("Unable to invoke method: [" + this.method + "] on object: ["
+ this.object + "] with arguments: [" + Arrays.toString(args) + "]", e);
throw new IllegalArgumentException("Unable to invoke method: [" + this.method
+ "] on object: [" + this.object + "] with arguments: ["
+ Arrays.toString(args) + "]", e);
}
}
@@ -101,7 +106,8 @@ public class SimpleMethodInvoker implements MethodInvoker {
source = ((Advised) target).getTargetSource().getTarget();
}
catch (Exception e) {
throw new IllegalStateException("Could not extract target from proxy", e);
throw new IllegalStateException("Could not extract target from proxy",
e);
}
if (source instanceof Advised) {
source = extractTarget(source, method);

View File

@@ -17,9 +17,8 @@ package org.springframework.retry;
/**
* Callback for stateful retry after all tries are exhausted.
*
*
* @author Dave Syer
*
* @since 1.1
*/
public interface RecoveryCallback<T> {

View File

@@ -22,7 +22,6 @@ package org.springframework.retry;
*
* @param <T> the type of object returned by the callback
* @param <E> the type of exception it declares may be thrown
*
* @author Rob Harrop
* @author Dave Syer
*/
@@ -30,11 +29,12 @@ public interface RetryCallback<T, E extends Throwable> {
/**
* Execute an operation with retry semantics. Operations should generally be
* idempotent, but implementations may choose to implement compensation
* semantics when an operation is retried.
* idempotent, but implementations may choose to implement compensation semantics when
* an operation is retried.
* @param context the current retry context.
* @return the result of the successful operation.
* @throws E of type E if processing fails
*/
T doWithRetry(RetryContext context) throws E;
}

View File

@@ -21,9 +21,9 @@ import org.springframework.core.AttributeAccessor;
/**
* Low-level access to ongoing retry operation. Normally not needed by clients, but can be
* used to alter the course of the retry, e.g. force an early termination.
*
*
* @author Dave Syer
*
*
*/
public interface RetryContext extends AttributeAccessor {
@@ -35,7 +35,8 @@ public interface RetryContext extends AttributeAccessor {
String NAME = "context.name";
/**
* Retry context attribute name for state key. Can be used to identify a stateful retry from its context.
* Retry context attribute name for state key. Can be used to identify a stateful
* retry from its context.
*/
String STATE_KEY = "context.state";
@@ -62,14 +63,12 @@ public interface RetryContext extends AttributeAccessor {
/**
* Public accessor for the exhausted flag {@link #setExhaustedOnly()}.
*
* @return true if the flag has been set.
*/
boolean isExhaustedOnly();
/**
* Accessor for the parent context if retry blocks are nested.
*
* @return the parent or null if there is none.
*/
RetryContext getParent();
@@ -77,14 +76,12 @@ public interface RetryContext extends AttributeAccessor {
/**
* Counts the number of retry attempts. Before the first attempt this counter is zero,
* and before the first and subsequent attempts it should increment accordingly.
*
* @return the number of retries.
*/
int getRetryCount();
/**
* Accessor for the exception object that caused the current retry.
*
* @return the last exception that caused a retry, or possibly null. It will be null
* if this is the first attempt, but also if the enclosing policy decides not to
* provide it (e.g. because of concerns about memory usage).

View File

@@ -16,53 +16,51 @@
package org.springframework.retry;
/**
* Interface for listener that can be used to add behaviour to a retry.
* Implementations of {@link RetryOperations} can chose to issue callbacks to an
* interceptor during the retry lifecycle.
*
* Interface for listener that can be used to add behaviour to a retry. Implementations of
* {@link RetryOperations} can chose to issue callbacks to an interceptor during the retry
* lifecycle.
*
* @author Dave Syer
*
*
*/
public interface RetryListener {
/**
* Called before the first attempt in a retry. For instance, implementers
* can set up state that is needed by the policies in the
* {@link RetryOperations}. The whole retry can be vetoed by returning
* false from this method, in which case a {@link TerminatedRetryException}
* will be thrown.
*
* Called before the first attempt in a retry. For instance, implementers can set up
* state that is needed by the policies in the {@link RetryOperations}. The whole
* retry can be vetoed by returning false from this method, in which case a
* {@link TerminatedRetryException} will be thrown.
* @param <T> the type of object returned by the callback
* @param <E> the type of exception it declares may be thrown
* @param context the current {@link RetryContext}.
* @param callback the current {@link RetryCallback}.
* @return true if the retry should proceed.
*/
<T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback);
<T, E extends Throwable> boolean open(RetryContext context,
RetryCallback<T, E> callback);
/**
* Called after the final attempt (successful or not). Allow the interceptor
* to clean up any resource it is holding before control returns to the
* retry caller.
*
* Called after the final attempt (successful or not). Allow the interceptor to clean
* up any resource it is holding before control returns to the retry caller.
* @param context the current {@link RetryContext}.
* @param callback the current {@link RetryCallback}.
* @param throwable the last exception that was thrown by the callback.
* @param <E> the exception type
* @param <T> the return value
*/
<T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable);
<T, E extends Throwable> void close(RetryContext context,
RetryCallback<T, E> callback, Throwable throwable);
/**
* Called after every unsuccessful attempt at a retry.
*
* @param context the current {@link RetryContext}.
* @param callback the current {@link RetryCallback}.
* @param throwable the last exception that was thrown by the callback.
* @param <T> the return value
* @param <E> the exception to throw
*/
<T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable);
<T, E extends Throwable> void onError(RetryContext context,
RetryCallback<T, E> callback, Throwable throwable);
}

View File

@@ -19,22 +19,20 @@ package org.springframework.retry;
import org.springframework.retry.support.DefaultRetryState;
/**
* Defines the basic set of operations implemented by {@link RetryOperations} to
* execute operations with configurable retry behaviour.
*
* Defines the basic set of operations implemented by {@link RetryOperations} to execute
* operations with configurable retry behaviour.
*
* @author Rob Harrop
* @author Dave Syer
*/
public interface RetryOperations {
/**
* Execute the supplied {@link RetryCallback} with the configured retry
* semantics. See implementations for configuration details.
*
* @return the value returned by the {@link RetryCallback} upon successful
* invocation.
* @throws E any {@link Exception} raised by the
* {@link RetryCallback} upon unsuccessful retry.
* Execute the supplied {@link RetryCallback} with the configured retry semantics. See
* implementations for configuration details.
* @return the value returned by the {@link RetryCallback} upon successful invocation.
* @throws E any {@link Exception} raised by the {@link RetryCallback} upon
* unsuccessful retry.
* @throws E the exception thrown
* @param <T> the return value
* @param retryCallback the {@link RetryCallback}
@@ -43,60 +41,58 @@ public interface RetryOperations {
<T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback) throws E;
/**
* Execute the supplied {@link RetryCallback} with a fallback on exhausted
* retry to the {@link RecoveryCallback}. See implementations for
* configuration details.
*
* @return the value returned by the {@link RetryCallback} upon successful
* invocation, and that returned by the {@link RecoveryCallback} otherwise.
* Execute the supplied {@link RetryCallback} with a fallback on exhausted retry to
* the {@link RecoveryCallback}. See implementations for configuration details.
* @return the value returned by the {@link RetryCallback} upon successful invocation,
* and that returned by the {@link RecoveryCallback} otherwise.
* @throws E any {@link Exception} raised by the
* @param <T> the type to return
* @param <E> the type of the exception
* @param recoveryCallback the {@link RecoveryCallback}
* @param retryCallback the {@link RetryCallback}
* {@link RecoveryCallback} upon unsuccessful retry.
* @param retryCallback the {@link RetryCallback} {@link RecoveryCallback} upon
* unsuccessful retry.
*/
<T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback, RecoveryCallback<T> recoveryCallback) throws E;
<T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback,
RecoveryCallback<T> recoveryCallback) throws E;
/**
* A simple stateful retry. Execute the supplied {@link RetryCallback} with
* a target object for the attempt identified by the {@link DefaultRetryState}.
* Exceptions thrown by the callback are always propagated immediately so
* the state is required to be able to identify the previous attempt, if
* there is one - hence the state is required. Normal patterns would see
* this method being used inside a transaction, where the callback might
* invalidate the transaction if it fails.
*
* See implementations for configuration details.
* A simple stateful retry. Execute the supplied {@link RetryCallback} with a target
* object for the attempt identified by the {@link DefaultRetryState}. Exceptions
* thrown by the callback are always propagated immediately so the state is required
* to be able to identify the previous attempt, if there is one - hence the state is
* required. Normal patterns would see this method being used inside a transaction,
* where the callback might invalidate the transaction if it fails.
*
* See implementations for configuration details.
* @param retryCallback the {@link RetryCallback}
* @param retryState the {@link RetryState}
* @return the value returned by the {@link RetryCallback} upon successful
* invocation, and that returned by the {@link RecoveryCallback} otherwise.
* @return the value returned by the {@link RetryCallback} upon successful invocation,
* and that returned by the {@link RecoveryCallback} otherwise.
* @throws E any {@link Exception} raised by the {@link RecoveryCallback}.
* @throws ExhaustedRetryException if the last attempt for this state has
* already been reached
* @throws ExhaustedRetryException if the last attempt for this state has already been
* reached
* @param <T> the type of the return value
* @param <E> the type of the exception to return
*/
<T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback, RetryState retryState) throws E, ExhaustedRetryException;
<T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback,
RetryState retryState) throws E, ExhaustedRetryException;
/**
* A stateful retry with a recovery path. Execute the supplied
* {@link RetryCallback} with a fallback on exhausted retry to the
* {@link RecoveryCallback} and a target object for the retry attempt
* identified by the {@link DefaultRetryState}.
* A stateful retry with a recovery path. Execute the supplied {@link RetryCallback}
* with a fallback on exhausted retry to the {@link RecoveryCallback} and a target
* object for the retry attempt identified by the {@link DefaultRetryState}.
* @param recoveryCallback the {@link RecoveryCallback}
* @param retryState the {@link RetryState}
* @param retryCallback the {@link RetryCallback}
* @see #execute(RetryCallback, RetryState)
* @param <T> the return value type
* @param <E> the exception type
* @return the value returned by the {@link RetryCallback} upon successful
* invocation, and that returned by the {@link RecoveryCallback} otherwise.
* @throws E any {@link Exception} raised by the {@link RecoveryCallback} upon unsuccessful retry.
* @return the value returned by the {@link RetryCallback} upon successful invocation,
* and that returned by the {@link RecoveryCallback} otherwise.
* @throws E any {@link Exception} raised by the {@link RecoveryCallback} upon
* unsuccessful retry.
*/
<T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback, RecoveryCallback<T> recoveryCallback, RetryState retryState)
throws E;
<T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback,
RecoveryCallback<T> recoveryCallback, RetryState retryState) throws E;
}

View File

@@ -19,12 +19,11 @@ package org.springframework.retry;
import java.io.Serializable;
/**
* A {@link RetryPolicy} is responsible for allocating and managing resources
* needed by {@link RetryOperations}. The {@link RetryPolicy} allows retry
* operations to be aware of their context. Context can be internal to the retry
* framework, e.g. to support nested retries. Context can also be external, and
* the {@link RetryPolicy} provides a uniform API for a range of different
* platforms for the external context.
* A {@link RetryPolicy} is responsible for allocating and managing resources needed by
* {@link RetryOperations}. The {@link RetryPolicy} allows retry operations to be aware of
* their context. Context can be internal to the retry framework, e.g. to support nested
* retries. Context can also be external, and the {@link RetryPolicy} provides a uniform
* API for a range of different platforms for the external context.
*
* @author Dave Syer
*
@@ -38,25 +37,23 @@ public interface RetryPolicy extends Serializable {
boolean canRetry(RetryContext context);
/**
* Acquire resources needed for the retry operation. The callback is passed
* in so that marker interfaces can be used and a manager can collaborate
* with the callback to set up some state in the status token.
* Acquire resources needed for the retry operation. The callback is passed in so that
* marker interfaces can be used and a manager can collaborate with the callback to
* set up some state in the status token.
* @param parent the parent context if we are in a nested retry.
*
* @return a {@link RetryContext} object specific to this policy.
*
*/
RetryContext open(RetryContext parent);
/**
* @param context a retry status created by the
* {@link #open(RetryContext)} method of this policy.
* @param context a retry status created by the {@link #open(RetryContext)} method of
* this policy.
*/
void close(RetryContext context);
/**
* Called once per retry attempt, after the callback fails.
*
* @param context the current status object.
* @param throwable the exception to throw
*/

View File

@@ -16,42 +16,39 @@
package org.springframework.retry;
/**
* Stateful retry is characterised by having to recognise the items that are
* being processed, so this interface is used primarily to provide a cache key in
* between failed attempts. It also provides a hints to the
* {@link RetryOperations} for optimisations to do with avoidable cache hits and
* switching to stateless retry if a rollback is not needed.
*
* Stateful retry is characterised by having to recognise the items that are being
* processed, so this interface is used primarily to provide a cache key in between failed
* attempts. It also provides a hints to the {@link RetryOperations} for optimisations to
* do with avoidable cache hits and switching to stateless retry if a rollback is not
* needed.
*
* @author Dave Syer
*
*/
public interface RetryState {
/**
* Key representing the state for a retry attempt. Stateful retry is
* characterised by having to recognise the items that are being processed,
* so this value is used as a cache key in between failed attempts.
*
* Key representing the state for a retry attempt. Stateful retry is characterised by
* having to recognise the items that are being processed, so this value is used as a
* cache key in between failed attempts.
* @return the key that this state represents
*/
Object getKey();
/**
* Indicate whether a cache lookup can be avoided. If the key is known ahead
* of the retry attempt to be fresh (i.e. has never been seen before) then a
* cache lookup can be avoided if this flag is true.
*
* Indicate whether a cache lookup can be avoided. If the key is known ahead of the
* retry attempt to be fresh (i.e. has never been seen before) then a cache lookup can
* be avoided if this flag is true.
* @return true if the state does not require an explicit check for the key
*/
boolean isForceRefresh();
/**
* Check whether this exception requires a rollback. The default is always
* true, which is conservative, so this method provides an optimisation for
* switching to stateless retry if there is an exception for which rollback
* is unnecessary. Example usage would be for a stateful retry to specify a
* validation exception as not for rollback.
*
* Check whether this exception requires a rollback. The default is always true, which
* is conservative, so this method provides an optimisation for switching to stateless
* retry if there is an exception for which rollback is unnecessary. Example usage
* would be for a stateful retry to specify a validation exception as not for
* rollback.
* @param exception the exception that caused a retry attempt to fail
* @return true if this exception should cause a rollback
*/

View File

@@ -17,11 +17,11 @@
package org.springframework.retry;
/**
* Interface for statistics reporting of retry attempts. Counts the number of
* retry attempts, successes, errors (including retries), and aborts.
*
* Interface for statistics reporting of retry attempts. Counts the number of retry
* attempts, successes, errors (including retries), and aborts.
*
* @author Dave Syer
*
*
*/
public interface RetryStatistics {
@@ -31,39 +31,32 @@ public interface RetryStatistics {
int getCompleteCount();
/**
* Get the number of times a retry block has been entered, irrespective of
* how many times the operation was retried.
*
* Get the number of times a retry block has been entered, irrespective of how many
* times the operation was retried.
* @return the number of retry blocks started.
*/
int getStartedCount();
/**
* Get the number of errors detected, whether or not they resulted in a
* retry.
*
* Get the number of errors detected, whether or not they resulted in a retry.
* @return the number of errors detected.
*/
int getErrorCount();
/**
* Get the number of times a block failed to complete successfully, even
* after retry.
*
* Get the number of times a block failed to complete successfully, even after retry.
* @return the number of retry attempts that failed overall.
*/
int getAbortCount();
/**
* Get the number of times a recovery callback was applied.
*
* @return the number of recovered attempts.
*/
int getRecoveryCount();
/**
* Get an identifier for the retry block for reporting purposes.
*
* @return an identifier for the block.
*/
String getName();

View File

@@ -63,8 +63,8 @@ import org.springframework.util.ReflectionUtils.MethodCallback;
import org.springframework.util.StringUtils;
/**
* Interceptor that parses the retry metadata on the method it is invoking and
* delegates to an appropriate RetryOperationsInterceptor.
* Interceptor that parses the retry metadata on the method it is invoking and delegates
* to an appropriate RetryOperationsInterceptor.
*
* @author Dave Syer
* @author Artem Bilan
@@ -72,7 +72,8 @@ import org.springframework.util.StringUtils;
* @since 1.1
*
*/
public class AnnotationAwareRetryOperationsInterceptor implements IntroductionInterceptor, BeanFactoryAware {
public class AnnotationAwareRetryOperationsInterceptor
implements IntroductionInterceptor, BeanFactoryAware {
private static final TemplateParserContext PARSER_CONTEXT = new TemplateParserContext();
@@ -80,8 +81,7 @@ public class AnnotationAwareRetryOperationsInterceptor implements IntroductionIn
private final StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
private final Map<Object, Map<Method, MethodInterceptor>> delegates =
new HashMap<Object, Map<Method, MethodInterceptor>>();
private final Map<Object, Map<Method, MethodInterceptor>> delegates = new HashMap<Object, Map<Method, MethodInterceptor>>();
private RetryContextCache retryContextCache = new MapRetryContextCache();
@@ -104,7 +104,6 @@ public class AnnotationAwareRetryOperationsInterceptor implements IntroductionIn
/**
* Public setter for the {@link RetryContextCache}.
*
* @param retryContextCache the {@link RetryContextCache} to set.
*/
public void setRetryContextCache(RetryContextCache retryContextCache) {
@@ -121,7 +120,8 @@ public class AnnotationAwareRetryOperationsInterceptor implements IntroductionIn
/**
* @param newMethodArgumentsIdentifier the {@link NewMethodArgumentsIdentifier}
*/
public void setNewItemIdentifier(NewMethodArgumentsIdentifier newMethodArgumentsIdentifier) {
public void setNewItemIdentifier(
NewMethodArgumentsIdentifier newMethodArgumentsIdentifier) {
this.newMethodArgumentsIdentifier = newMethodArgumentsIdentifier;
}
@@ -130,7 +130,8 @@ public class AnnotationAwareRetryOperationsInterceptor implements IntroductionIn
* @param globalListeners the default listeners
*/
public void setListeners(Collection<RetryListener> globalListeners) {
ArrayList<RetryListener> retryListeners = new ArrayList<RetryListener>(globalListeners);
ArrayList<RetryListener> retryListeners = new ArrayList<RetryListener>(
globalListeners);
AnnotationAwareOrderComparator.sort(retryListeners);
this.globalListeners = retryListeners.toArray(new RetryListener[0]);
}
@@ -143,12 +144,14 @@ public class AnnotationAwareRetryOperationsInterceptor implements IntroductionIn
@Override
public boolean implementsInterface(Class<?> intf) {
return org.springframework.retry.interceptor.Retryable.class.isAssignableFrom(intf);
return org.springframework.retry.interceptor.Retryable.class
.isAssignableFrom(intf);
}
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
MethodInterceptor delegate = getDelegate(invocation.getThis(), invocation.getMethod());
MethodInterceptor delegate = getDelegate(invocation.getThis(),
invocation.getMethod());
if (delegate != null) {
return delegate.invoke(invocation);
}
@@ -158,16 +161,20 @@ public class AnnotationAwareRetryOperationsInterceptor implements IntroductionIn
}
private MethodInterceptor getDelegate(Object target, Method method) {
if (!this.delegates.containsKey(target) || !this.delegates.get(target).containsKey(method)) {
if (!this.delegates.containsKey(target)
|| !this.delegates.get(target).containsKey(method)) {
synchronized (this.delegates) {
if (!this.delegates.containsKey(target)) {
this.delegates.put(target, new HashMap<Method, MethodInterceptor>());
}
Map<Method, MethodInterceptor> delegatesForTarget = this.delegates.get(target);
Map<Method, MethodInterceptor> delegatesForTarget = this.delegates
.get(target);
if (!delegatesForTarget.containsKey(method)) {
Retryable retryable = AnnotationUtils.findAnnotation(method, Retryable.class);
Retryable retryable = AnnotationUtils.findAnnotation(method,
Retryable.class);
if (retryable == null) {
retryable = AnnotationUtils.findAnnotation(method.getDeclaringClass(), Retryable.class);
retryable = AnnotationUtils.findAnnotation(
method.getDeclaringClass(), Retryable.class);
}
if (retryable == null) {
retryable = findAnnotationOnTarget(target, method);
@@ -177,7 +184,8 @@ public class AnnotationAwareRetryOperationsInterceptor implements IntroductionIn
}
MethodInterceptor delegate;
if (StringUtils.hasText(retryable.interceptor())) {
delegate = this.beanFactory.getBean(retryable.interceptor(), MethodInterceptor.class);
delegate = this.beanFactory.getBean(retryable.interceptor(),
MethodInterceptor.class);
}
else if (retryable.stateful()) {
delegate = getStatefulInterceptor(target, method, retryable);
@@ -194,10 +202,13 @@ public class AnnotationAwareRetryOperationsInterceptor implements IntroductionIn
private Retryable findAnnotationOnTarget(Object target, Method method) {
try {
Method targetMethod = target.getClass().getMethod(method.getName(), method.getParameterTypes());
Retryable retryable = AnnotationUtils.findAnnotation(targetMethod, Retryable.class);
Method targetMethod = target.getClass().getMethod(method.getName(),
method.getParameterTypes());
Retryable retryable = AnnotationUtils.findAnnotation(targetMethod,
Retryable.class);
if (retryable == null) {
retryable = AnnotationUtils.findAnnotation(targetMethod.getDeclaringClass(), Retryable.class);
retryable = AnnotationUtils.findAnnotation(
targetMethod.getDeclaringClass(), Retryable.class);
}
return retryable;
@@ -207,23 +218,23 @@ public class AnnotationAwareRetryOperationsInterceptor implements IntroductionIn
}
}
private MethodInterceptor getStatelessInterceptor(Object target, Method method, Retryable retryable) {
private MethodInterceptor getStatelessInterceptor(Object target, Method method,
Retryable retryable) {
RetryTemplate template = createTemplate(retryable.listeners());
template.setRetryPolicy(getRetryPolicy(retryable));
template.setBackOffPolicy(getBackoffPolicy(retryable.backoff()));
return RetryInterceptorBuilder.stateless()
.retryOperations(template)
.label(retryable.label())
.recoverer(getRecoverer(target, method))
.build();
return RetryInterceptorBuilder.stateless().retryOperations(template)
.label(retryable.label()).recoverer(getRecoverer(target, method)).build();
}
private MethodInterceptor getStatefulInterceptor(Object target, Method method, Retryable retryable) {
private MethodInterceptor getStatefulInterceptor(Object target, Method method,
Retryable retryable) {
RetryTemplate template = createTemplate(retryable.listeners());
template.setRetryContextCache(this.retryContextCache);
CircuitBreaker circuit = AnnotationUtils.findAnnotation(method, CircuitBreaker.class);
if (circuit!=null) {
CircuitBreaker circuit = AnnotationUtils.findAnnotation(method,
CircuitBreaker.class);
if (circuit != null) {
RetryPolicy policy = getRetryPolicy(circuit);
CircuitBreakerRetryPolicy breaker = new CircuitBreakerRetryPolicy(policy);
breaker.setOpenTimeout(getOpenTimeout(circuit));
@@ -231,15 +242,13 @@ public class AnnotationAwareRetryOperationsInterceptor implements IntroductionIn
template.setRetryPolicy(breaker);
template.setBackOffPolicy(new NoBackOffPolicy());
String label = circuit.label();
if (!StringUtils.hasText(label)) {
if (!StringUtils.hasText(label)) {
label = method.toGenericString();
}
return RetryInterceptorBuilder.circuitBreaker()
.keyGenerator(new FixedKeyGenerator("circuit"))
.retryOperations(template)
.recoverer(getRecoverer(target, method))
.label(label)
.build();
.retryOperations(template).recoverer(getRecoverer(target, method))
.label(label).build();
}
RetryPolicy policy = getRetryPolicy(retryable);
template.setRetryPolicy(policy);
@@ -248,16 +257,14 @@ public class AnnotationAwareRetryOperationsInterceptor implements IntroductionIn
return RetryInterceptorBuilder.stateful()
.keyGenerator(this.methodArgumentsKeyGenerator)
.newMethodArgumentsIdentifier(this.newMethodArgumentsIdentifier)
.retryOperations(template)
.label(label)
.recoverer(getRecoverer(target, method))
.build();
.retryOperations(template).label(label)
.recoverer(getRecoverer(target, method)).build();
}
private long getOpenTimeout(CircuitBreaker circuit) {
if (StringUtils.hasText(circuit.openTimeoutExpression())) {
Long value = PARSER.parseExpression(resolve(circuit.openTimeoutExpression()), PARSER_CONTEXT)
.getValue(Long.class);
Long value = PARSER.parseExpression(resolve(circuit.openTimeoutExpression()),
PARSER_CONTEXT).getValue(Long.class);
if (value != null) {
return value;
}
@@ -267,8 +274,8 @@ public class AnnotationAwareRetryOperationsInterceptor implements IntroductionIn
private long getResetTimeout(CircuitBreaker circuit) {
if (StringUtils.hasText(circuit.resetTimeoutExpression())) {
Long value = PARSER.parseExpression(resolve(circuit.resetTimeoutExpression()), PARSER_CONTEXT)
.getValue(Long.class);
Long value = PARSER.parseExpression(resolve(circuit.resetTimeoutExpression()),
PARSER_CONTEXT).getValue(Long.class);
if (value != null) {
return value;
}
@@ -280,7 +287,8 @@ public class AnnotationAwareRetryOperationsInterceptor implements IntroductionIn
RetryTemplate template = new RetryTemplate();
if (listenersBeanNames.length > 0) {
template.setListeners(getListenersBeans(listenersBeanNames));
} else if (globalListeners !=null) {
}
else if (globalListeners != null) {
template.setListeners(globalListeners);
}
return template;
@@ -289,7 +297,8 @@ public class AnnotationAwareRetryOperationsInterceptor implements IntroductionIn
private RetryListener[] getListenersBeans(String[] listenersBeanNames) {
RetryListener[] listeners = new RetryListener[listenersBeanNames.length];
for (int i = 0; i < listeners.length; i++) {
listeners[i] = beanFactory.getBean(listenersBeanNames[i], RetryListener.class);
listeners[i] = beanFactory.getBean(listenersBeanNames[i],
RetryListener.class);
}
return listeners;
}
@@ -301,8 +310,8 @@ public class AnnotationAwareRetryOperationsInterceptor implements IntroductionIn
final AtomicBoolean foundRecoverable = new AtomicBoolean(false);
ReflectionUtils.doWithMethods(target.getClass(), new MethodCallback() {
@Override
public void doWith(Method method) throws IllegalArgumentException,
IllegalAccessException {
public void doWith(Method method)
throws IllegalArgumentException, IllegalAccessException {
if (AnnotationUtils.findAnnotation(method, Recover.class) != null) {
foundRecoverable.set(true);
}
@@ -318,26 +327,31 @@ public class AnnotationAwareRetryOperationsInterceptor implements IntroductionIn
private RetryPolicy getRetryPolicy(Annotation retryable) {
Map<String, Object> attrs = AnnotationUtils.getAnnotationAttributes(retryable);
@SuppressWarnings("unchecked")
Class<? extends Throwable>[] includes = (Class<? extends Throwable>[]) attrs.get("value");
Class<? extends Throwable>[] includes = (Class<? extends Throwable>[]) attrs
.get("value");
String exceptionExpression = (String) attrs.get("exceptionExpression");
boolean hasExpression = StringUtils.hasText(exceptionExpression);
if (includes.length == 0) {
@SuppressWarnings("unchecked")
Class<? extends Throwable>[] value = (Class<? extends Throwable>[]) attrs.get("include");
Class<? extends Throwable>[] value = (Class<? extends Throwable>[]) attrs
.get("include");
includes = value;
}
@SuppressWarnings("unchecked")
Class<? extends Throwable>[] excludes = (Class<? extends Throwable>[]) attrs.get("exclude");
Class<? extends Throwable>[] excludes = (Class<? extends Throwable>[]) attrs
.get("exclude");
Integer maxAttempts = (Integer) attrs.get("maxAttempts");
String maxAttemptsExpression = (String) attrs.get("maxAttemptsExpression");
if (StringUtils.hasText(maxAttemptsExpression)) {
maxAttempts = PARSER.parseExpression(resolve(maxAttemptsExpression), PARSER_CONTEXT)
maxAttempts = PARSER
.parseExpression(resolve(maxAttemptsExpression), PARSER_CONTEXT)
.getValue(this.evaluationContext, Integer.class);
}
if (includes.length == 0 && excludes.length == 0) {
SimpleRetryPolicy simple = hasExpression ? new ExpressionRetryPolicy(resolve(exceptionExpression))
.withBeanFactory(this.beanFactory)
: new SimpleRetryPolicy();
SimpleRetryPolicy simple = hasExpression
? new ExpressionRetryPolicy(resolve(exceptionExpression))
.withBeanFactory(this.beanFactory)
: new SimpleRetryPolicy();
simple.setMaxAttempts(maxAttempts);
return simple;
}
@@ -350,8 +364,9 @@ public class AnnotationAwareRetryOperationsInterceptor implements IntroductionIn
}
boolean retryNotExcluded = includes.length == 0;
if (hasExpression) {
return new ExpressionRetryPolicy(maxAttempts, policyMap, true, exceptionExpression, retryNotExcluded)
.withBeanFactory(this.beanFactory);
return new ExpressionRetryPolicy(maxAttempts, policyMap, true,
exceptionExpression, retryNotExcluded)
.withBeanFactory(this.beanFactory);
}
else {
return new SimpleRetryPolicy(maxAttempts, policyMap, true, retryNotExcluded);
@@ -361,17 +376,20 @@ public class AnnotationAwareRetryOperationsInterceptor implements IntroductionIn
private BackOffPolicy getBackoffPolicy(Backoff backoff) {
long min = backoff.delay() == 0 ? backoff.value() : backoff.delay();
if (StringUtils.hasText(backoff.delayExpression())) {
min = PARSER.parseExpression(resolve(backoff.delayExpression()), PARSER_CONTEXT)
min = PARSER
.parseExpression(resolve(backoff.delayExpression()), PARSER_CONTEXT)
.getValue(this.evaluationContext, Long.class);
}
long max = backoff.maxDelay();
if (StringUtils.hasText(backoff.maxDelayExpression())) {
max = PARSER.parseExpression(resolve(backoff.maxDelayExpression()), PARSER_CONTEXT)
.getValue(this.evaluationContext, Long.class);
max = PARSER.parseExpression(resolve(backoff.maxDelayExpression()),
PARSER_CONTEXT).getValue(this.evaluationContext, Long.class);
}
double multiplier = backoff.multiplier();
if (StringUtils.hasText(backoff.multiplierExpression())) {
multiplier = PARSER.parseExpression(resolve(backoff.multiplierExpression()), PARSER_CONTEXT)
multiplier = PARSER
.parseExpression(resolve(backoff.multiplierExpression()),
PARSER_CONTEXT)
.getValue(this.evaluationContext, Double.class);
}
if (multiplier > 0) {
@@ -381,7 +399,8 @@ public class AnnotationAwareRetryOperationsInterceptor implements IntroductionIn
}
policy.setInitialInterval(min);
policy.setMultiplier(multiplier);
policy.setMaxInterval(max > min ? max : ExponentialBackOffPolicy.DEFAULT_MAX_INTERVAL);
policy.setMaxInterval(
max > min ? max : ExponentialBackOffPolicy.DEFAULT_MAX_INTERVAL);
if (this.sleeper != null) {
policy.setSleeper(this.sleeper);
}
@@ -410,8 +429,10 @@ public class AnnotationAwareRetryOperationsInterceptor implements IntroductionIn
* @see ConfigurableBeanFactory#resolveEmbeddedValue
*/
private String resolve(String value) {
if (this.beanFactory != null && this.beanFactory instanceof ConfigurableBeanFactory) {
return ((ConfigurableBeanFactory) this.beanFactory).resolveEmbeddedValue(value);
if (this.beanFactory != null
&& this.beanFactory instanceof ConfigurableBeanFactory) {
return ((ConfigurableBeanFactory) this.beanFactory)
.resolveEmbeddedValue(value);
}
return value;
}

View File

@@ -49,19 +49,17 @@ import org.springframework.retry.backoff.BackOffPolicy;
public @interface Backoff {
/**
* Synonym for {@link #delay()}. When {@link #delay()} is non-zero, value of this element
* is ignored, otherwise value of this element is taken.
*
* Synonym for {@link #delay()}. When {@link #delay()} is non-zero, value of this
* element is ignored, otherwise value of this element is taken.
* @return the delay in milliseconds (default 1000)
*/
long value() default 1000;
/**
* A canonical backoff period. Used as an initial value in the exponential case, and
* as a minimum value in the uniform case. When the value of this element is 0,
* value of element {@link #value()} istaken, otherwise value of this
* element is taken and {@link #value()} is ignored.
*
* as a minimum value in the uniform case. When the value of this element is 0, value
* of element {@link #value()} istaken, otherwise value of this element is taken and
* {@link #value()} is ignored.
* @return the initial or canonical backoff period in milliseconds (default 0)
*/
long delay() default 0;
@@ -71,14 +69,12 @@ public @interface Backoff {
* {@link #delay()} then the default of
* {@value org.springframework.retry.backoff.ExponentialBackOffPolicy#DEFAULT_MAX_INTERVAL}
* is applied.
*
* @return the maximum delay between retries (default 0 = ignored)
*/
long maxDelay() default 0;
/**
* If positive, then used as a multiplier for generating the next delay for backoff.
*
* @return a multiplier to use to calculate the next backoff delay (default 0 =
* ignored)
*/
@@ -98,7 +94,6 @@ public @interface Backoff {
* If less than the {@link #delay()} then the default of
* {@value org.springframework.retry.backoff.ExponentialBackOffPolicy#DEFAULT_MAX_INTERVAL}
* is applied. Overrides {@link #maxDelay()}
*
* @return the maximum delay between retries (default 0 = ignored)
* @since 1.2
*/
@@ -107,7 +102,6 @@ public @interface Backoff {
/**
* Evaluates to a vaule used as a multiplier for generating the next delay for
* backoff. Overrides {@link #multiplier()}.
*
* @return a multiplier expression to use to calculate the next backoff delay (default
* 0 = ignored)
* @since 1.2
@@ -118,7 +112,6 @@ public @interface Backoff {
* In the exponential case ({@link #multiplier()} &gt; 0) set this to true to have the
* backoff delays randomized, so that the maximum delay is multiplier times the
* previous delay and the distribution is uniform between the two values.
*
* @return the flag to signal randomization is required (default false)
*/
boolean random() default false;

View File

@@ -53,8 +53,8 @@ public @interface CircuitBreaker {
/**
* Exception types that are not retryable. Defaults to empty (and if includes is also
* empty all exceptions are retried).
* If includes is empty but excludes is not, all not excluded exceptions are retried
* empty all exceptions are retried). If includes is empty but excludes is not, all
* not excluded exceptions are retried
* @return exception types not to retry
*/
Class<? extends Throwable>[] exclude() default {};
@@ -74,7 +74,6 @@ public @interface CircuitBreaker {
/**
* A unique label for the circuit for reporting and state management. Defaults to the
* method signature where the annotation is declared.
*
* @return the label for the circuit
*/
String label() default "";
@@ -82,7 +81,6 @@ public @interface CircuitBreaker {
/**
* If the circuit is open for longer than this timeout then it resets on the next call
* to give the downstream component a chance to respond again.
*
* @return the timeout before an open circuit is reset in milliseconds, defaults to
* 20000
*/
@@ -92,7 +90,6 @@ public @interface CircuitBreaker {
* If the circuit is open for longer than this timeout then it resets on the next call
* to give the downstream component a chance to respond again. Overrides
* {@link #resetTimeout()}.
*
* @return the timeout before an open circuit is reset in milliseconds, no default.
* @since 1.2.3
*/
@@ -101,7 +98,6 @@ public @interface CircuitBreaker {
/**
* When {@link #maxAttempts()} failures are reached within this timeout, the circuit
* is opened automatically, preventing access to the downstream component.
*
* @return the timeout before an closed circuit is opened in milliseconds, defaults to
* 5000
*/
@@ -111,7 +107,6 @@ public @interface CircuitBreaker {
* When {@link #maxAttempts()} failures are reached within this timeout, the circuit
* is opened automatically, preventing access to the downstream component. Overrides
* {@link #openTimeout()}.
*
* @return the timeout before an closed circuit is opened in milliseconds, no default.
* @since 1.2.3
*/
@@ -133,7 +128,6 @@ public @interface CircuitBreaker {
* <pre class=code>
* {@code "@someBean.shouldRetry(#root)"}.
* </pre>
*
* @return the expression.
* @since 1.2.3
*/

View File

@@ -30,7 +30,7 @@ import org.springframework.context.annotation.Import;
* declared on any <code>@Configuration</code> in the context then beans that have
* retryable methods will be proxied and the retry handled according to the metadata in
* the annotations.
*
*
* @author Dave Syer
* @since 2.0
*
@@ -43,9 +43,8 @@ import org.springframework.context.annotation.Import;
public @interface EnableRetry {
/**
* Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
* to standard Java interface-based proxies. The default is {@code false}.
*
* Indicate whether subclass-based (CGLIB) proxies are to be created as opposed to
* standard Java interface-based proxies. The default is {@code false}.
* @return whether to proxy or not to proxy the class
*/
boolean proxyTargetClass() default false;

View File

@@ -31,7 +31,7 @@ import org.springframework.context.annotation.Import;
* The Throwable first argument is optional (but a method without it will only be called
* if no others match). Subsequent arguments are populated from the argument list of the
* failed method in order.
*
*
* @author Dave Syer
* @since 2.0
*
@@ -41,4 +41,5 @@ import org.springframework.context.annotation.Import;
@Import(RetryConfiguration.class)
@Documented
public @interface Recover {
}

View File

@@ -46,7 +46,9 @@ import org.springframework.util.ReflectionUtils.MethodCallback;
public class RecoverAnnotationRecoveryHandler<T> implements MethodInvocationRecoverer<T> {
private SubclassClassifier<Throwable, Method> classifier = new SubclassClassifier<Throwable, Method>();
private Map<Method, SimpleMetadata> methods = new HashMap<Method, SimpleMetadata>();
private Object target;
public RecoverAnnotationRecoveryHandler(Object target, Method method) {
@@ -92,8 +94,8 @@ public class RecoverAnnotationRecoveryHandler<T> 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;
}
@@ -114,14 +116,16 @@ public class RecoverAnnotationRecoveryHandler<T> 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 = args[i-1];
final Object argument = args[i - 1];
if (argument == null) {
continue;
}
@@ -140,26 +144,25 @@ public class RecoverAnnotationRecoveryHandler<T> implements MethodInvocationReco
ReflectionUtils.doWithMethods(failingMethod.getDeclaringClass(),
new MethodCallback() {
@Override
public void doWith(Method method) throws IllegalArgumentException,
IllegalAccessException {
public void doWith(Method method)
throws IllegalArgumentException, IllegalAccessException {
Recover recover = AnnotationUtils.findAnnotation(method,
Recover.class);
if (recover != null
&& method.getReturnType().isAssignableFrom(
failingMethod.getReturnType())) {
if (recover != null && method.getReturnType()
.isAssignableFrom(failingMethod.getReturnType())) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length > 0
&& Throwable.class
.isAssignableFrom(parameterTypes[0])) {
if (parameterTypes.length > 0 && Throwable.class
.isAssignableFrom(parameterTypes[0])) {
@SuppressWarnings("unchecked")
Class<? extends Throwable> type = (Class<? extends Throwable>) parameterTypes[0];
types.put(type, method);
methods.put(method, new SimpleMetadata(
parameterTypes.length, type));
} else {
methods.put(method,
new SimpleMetadata(parameterTypes.length, type));
}
else {
classifier.setDefaultValue(method);
methods.put(method, new SimpleMetadata(
parameterTypes.length, null));
methods.put(method,
new SimpleMetadata(parameterTypes.length, null));
}
}
}
@@ -181,7 +184,9 @@ public class RecoverAnnotationRecoveryHandler<T> implements MethodInvocationReco
}
private static class SimpleMetadata {
private int argCount;
private Class<? extends Throwable> type;
public SimpleMetadata(int argCount, Class<? extends Throwable> type) {
@@ -208,6 +213,7 @@ public class RecoverAnnotationRecoveryHandler<T> implements MethodInvocationReco
System.arraycopy(args, 0, result, startArgs, result.length - startArgs);
return result;
}
}
}

View File

@@ -50,10 +50,10 @@ import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.MethodCallback;
/**
* Basic configuration for <code>@Retryable</code> processing. For stateful retry, if there is a unique bean elsewhere
* in the context of type {@link RetryContextCache}, {@link MethodArgumentsKeyGenerator} or
* {@link NewMethodArgumentsIdentifier} it will be used by the corresponding retry interceptor (otherwise sensible
* defaults are adopted).
* Basic configuration for <code>@Retryable</code> processing. For stateful retry, if
* there is a unique bean elsewhere in the context of type {@link RetryContextCache},
* {@link MethodArgumentsKeyGenerator} or {@link NewMethodArgumentsIdentifier} it will be
* used by the corresponding retry interceptor (otherwise sensible defaults are adopted).
*
* @author Dave Syer
* @author Artem Bilan
@@ -62,7 +62,8 @@ import org.springframework.util.ReflectionUtils.MethodCallback;
*/
@SuppressWarnings("serial")
@Configuration
public class RetryConfiguration extends AbstractPointcutAdvisor implements IntroductionAdvisor, BeanFactoryAware {
public class RetryConfiguration extends AbstractPointcutAdvisor
implements IntroductionAdvisor, BeanFactoryAware {
private Advice advice;
@@ -87,7 +88,8 @@ public class RetryConfiguration extends AbstractPointcutAdvisor implements Intro
@PostConstruct
public void init() {
Set<Class<? extends Annotation>> retryableAnnotationTypes = new LinkedHashSet<Class<? extends Annotation>>(1);
Set<Class<? extends Annotation>> retryableAnnotationTypes = new LinkedHashSet<Class<? extends Annotation>>(
1);
retryableAnnotationTypes.add(Retryable.class);
this.pointcut = buildPointcut(retryableAnnotationTypes);
this.advice = buildAdvice();
@@ -150,11 +152,11 @@ public class RetryConfiguration extends AbstractPointcutAdvisor implements Intro
/**
* Calculate a pointcut for the given retry annotation types, if any.
*
* @param retryAnnotationTypes the retry annotation types to introspect
* @return the applicable Pointcut object, or {@code null} if none
*/
protected Pointcut buildPointcut(Set<Class<? extends Annotation>> retryAnnotationTypes) {
protected Pointcut buildPointcut(
Set<Class<? extends Annotation>> retryAnnotationTypes) {
ComposablePointcut result = null;
for (Class<? extends Annotation> retryAnnotationType : retryAnnotationTypes) {
Pointcut filter = new AnnotationClassOrMethodPointcut(retryAnnotationType);
@@ -168,7 +170,8 @@ public class RetryConfiguration extends AbstractPointcutAdvisor implements Intro
return result;
}
private final class AnnotationClassOrMethodPointcut extends StaticMethodMatcherPointcut {
private final class AnnotationClassOrMethodPointcut
extends StaticMethodMatcherPointcut {
private final MethodMatcher methodResolver;
@@ -179,9 +182,10 @@ public class RetryConfiguration extends AbstractPointcutAdvisor implements Intro
@Override
public boolean matches(Method method, Class<?> targetClass) {
return getClassFilter().matches(targetClass) || this.methodResolver.matches(method, targetClass);
return getClassFilter().matches(targetClass)
|| this.methodResolver.matches(method, targetClass);
}
@Override
public boolean equals(Object other) {
if (this == other) {
@@ -191,7 +195,8 @@ public class RetryConfiguration extends AbstractPointcutAdvisor implements Intro
return false;
}
AnnotationClassOrMethodPointcut otherAdvisor = (AnnotationClassOrMethodPointcut) other;
return ObjectUtils.nullSafeEquals(this.methodResolver, otherAdvisor.methodResolver);
return ObjectUtils.nullSafeEquals(this.methodResolver,
otherAdvisor.methodResolver);
}
}
@@ -213,31 +218,32 @@ public class RetryConfiguration extends AbstractPointcutAdvisor implements Intro
}
private static class AnnotationMethodsResolver {
private Class<? extends Annotation> annotationType;
public AnnotationMethodsResolver(Class<? extends Annotation> annotationType) {
this.annotationType = annotationType;
}
public boolean hasAnnotatedMethods(Class<?> clazz) {
final AtomicBoolean found = new AtomicBoolean(false);
ReflectionUtils.doWithMethods(clazz,
new MethodCallback() {
@Override
public void doWith(Method method) throws IllegalArgumentException,
IllegalAccessException {
if (found.get()) {
return;
}
Annotation annotation = AnnotationUtils.findAnnotation(method,
annotationType);
if (annotation != null) { found.set(true); }
}
ReflectionUtils.doWithMethods(clazz, new MethodCallback() {
@Override
public void doWith(Method method)
throws IllegalArgumentException, IllegalAccessException {
if (found.get()) {
return;
}
Annotation annotation = AnnotationUtils.findAnnotation(method,
annotationType);
if (annotation != null) {
found.set(true);
}
}
});
return found.get();
}
}
}

View File

@@ -59,8 +59,8 @@ public @interface Retryable {
/**
* Exception types that are not retryable. Defaults to empty (and if includes is also
* empty all exceptions are retried).
* If includes is empty but excludes is not, all not excluded exceptions are retried
* empty all exceptions are retried). If includes is empty but excludes is not, all
* not excluded exceptions are retried
* @return exception types not to retry
*/
Class<? extends Throwable>[] exclude() default {};
@@ -68,7 +68,6 @@ public @interface Retryable {
/**
* A unique label for statistics reporting. If not provided the caller may choose to
* ignore it, or provide a default.
*
* @return the label for the statistics
*/
String label() default "";
@@ -87,31 +86,28 @@ public @interface Retryable {
int maxAttempts() default 3;
/**
* @return an expression evaluated to the maximum number of attempts (including the first failure), defaults to 3
* Overrides {@link #maxAttempts()}.
* @return an expression evaluated to the maximum number of attempts (including the
* first failure), defaults to 3 Overrides {@link #maxAttempts()}.
* @since 1.2
*/
String maxAttemptsExpression() default "";
/**
* Specify the backoff properties for retrying this operation. The default is a
* simple {@link Backoff} specification with no properties - see it's documentation
* for defaults.
* Specify the backoff properties for retrying this operation. The default is a simple
* {@link Backoff} specification with no properties - see it's documentation for
* defaults.
* @return a backoff specification
*/
Backoff backoff() default @Backoff();
/**
* Specify an expression to be evaluated after the {@code SimpleRetryPolicy.canRetry()}
* returns true - can be used to conditionally suppress the retry. Only invoked after
* an exception is thrown. The root object for the evaluation is the last {@code Throwable}.
* Other beans in the context can be referenced.
* For example:
* <pre class=code>
* Specify an expression to be evaluated after the
* {@code SimpleRetryPolicy.canRetry()} returns true - can be used to conditionally
* suppress the retry. Only invoked after an exception is thrown. The root object for
* the evaluation is the last {@code Throwable}. Other beans in the context can be
* referenced. For example: <pre class=code>
* {@code "message.contains('you can retry this')"}.
* </pre>
* and
* <pre class=code>
* </pre> and <pre class=code>
* {@code "@someBean.shouldRetry(#root)"}.
* </pre>
* @return the expression.
@@ -120,7 +116,8 @@ public @interface Retryable {
String exceptionExpression() default "";
/**
* Bean names of retry listeners to use instead of default ones defined in Spring context
* Bean names of retry listeners to use instead of default ones defined in Spring
* context
* @return retry listeners bean names
*/
String[] listeners() default {};

View File

@@ -19,10 +19,10 @@ package org.springframework.retry.backoff;
import org.springframework.retry.RetryException;
/**
* Exception class signifiying that an attempt to back off using a
* {@link BackOffPolicy} was interrupted, most likely by an
* {@link InterruptedException} during a call to {@link Thread#sleep(long)}.
*
* Exception class signifiying that an attempt to back off using a {@link BackOffPolicy}
* was interrupted, most likely by an {@link InterruptedException} during a call to
* {@link Thread#sleep(long)}.
*
* @author Rob Harrop
* @since 2.1
*/
@@ -36,4 +36,5 @@ public class BackOffInterruptedException extends RetryException {
public BackOffInterruptedException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -22,28 +22,27 @@ import org.springframework.retry.RetryContext;
* Strategy interface to control back off between attempts in a single
* {@link org.springframework.retry.support.RetryTemplate retry operation}.
*
* Implementations are expected to be thread-safe and should be designed
* for concurrent access. Configuration for each implementation is also expected
* to be thread-safe but need not be suitable for high load concurrent access.
* Implementations are expected to be thread-safe and should be designed for concurrent
* access. Configuration for each implementation is also expected to be thread-safe but
* need not be suitable for high load concurrent access.
*
* For each block of retry operations the {@link #start} method is called
* and implementations can return an implementation-specific
* For each block of retry operations the {@link #start} method is called and
* implementations can return an implementation-specific
*
* {@link BackOffContext} that can be used to track state through subsequent
* back off invocations. Each back off process is handled via a call to {@link #backOff}.
* {@link BackOffContext} that can be used to track state through subsequent back off
* invocations. Each back off process is handled via a call to {@link #backOff}.
*
* The {@link org.springframework.retry.support.RetryTemplate} will pass in the
* corresponding {@link BackOffContext} object created by the call to {@link #start}.
*
* The {@link org.springframework.retry.support.RetryTemplate} will pass in
* the corresponding {@link BackOffContext} object created by the call to {@link #start}.
*
* @author Rob Harrop
* @author Dave Syer
*/
public interface BackOffPolicy {
/**
* Start a new block of back off operations. Implementations can choose to
* pause when this method is called, but normally it returns immediately.
*
* Start a new block of back off operations. Implementations can choose to pause when
* this method is called, but normally it returns immediately.
* @param context the {@link RetryContext} context, which might contain information
* that we can use to decide how to proceed.
* @return the implementation-specific {@link BackOffContext} or '<code>null</code>'.
@@ -52,11 +51,9 @@ public interface BackOffPolicy {
/**
* Back off/pause in an implementation-specific fashion. The passed in
* {@link BackOffContext} corresponds to the one created by the call to
* {@link #start} for a given retry operation set.
*
* @throws BackOffInterruptedException if the attempt at back off is
* interrupted.
* {@link BackOffContext} corresponds to the one created by the call to {@link #start}
* for a given retry operation set.
* @throws BackOffInterruptedException if the attempt at back off is interrupted.
* @param backOffContext the {@link BackOffContext}
*/
void backOff(BackOffContext backOffContext) throws BackOffInterruptedException;

View File

@@ -42,6 +42,7 @@ public class ExponentialBackOffPolicy
implements SleepingBackOffPolicy<ExponentialBackOffPolicy> {
protected final Log logger = LogFactory.getLog(this.getClass());
/**
* The default 'initialInterval' value - 100 millisecs. Coupled with the default
* 'multiplier' value this gives a useful initial spread of pauses for 1-5 retries.
@@ -104,7 +105,6 @@ public class ExponentialBackOffPolicy
/**
* Set the initial sleep interval value. Default is {@code 100} millisecond. Cannot be
* set to a value less than one.
*
* @param initialInterval the initial interval
*/
public void setInitialInterval(long initialInterval) {
@@ -125,7 +125,6 @@ public class ExponentialBackOffPolicy
* be reset to 1 if this method is called with a value less than 1. Set this to avoid
* infinite waits if backing off a large number of times (or if the multiplier is set
* too high).
*
* @param maxInterval in milliseconds.
*/
public void setMaxInterval(long maxInterval) {
@@ -142,7 +141,6 @@ public class ExponentialBackOffPolicy
/**
* The maximum interval to sleep for. Defaults to 30 seconds.
*
* @return the maximum interval.
*/
public long getMaxInterval() {
@@ -151,7 +149,6 @@ public class ExponentialBackOffPolicy
/**
* The multiplier to use to generate the next backoff interval from the last.
*
* @return the multiplier in use
*/
public double getMultiplier() {
@@ -226,6 +223,7 @@ public class ExponentialBackOffPolicy
public long getMaxInterval() {
return maxInterval;
}
}
public String toString() {

View File

@@ -38,11 +38,13 @@ import java.util.Random;
*
* {@link ExponentialRandomBackOffPolicy} may yield [76, 151, 304, 580, 901] or [53, 190,
* 267, 451, 815]
*
* @author Jon Travis
* @author Dave Syer
*/
@SuppressWarnings("serial")
public class ExponentialRandomBackOffPolicy extends ExponentialBackOffPolicy {
/**
* Returns a new instance of {@link org.springframework.retry.backoff.BackOffContext},
* seeded with this policies settings.
@@ -58,6 +60,7 @@ public class ExponentialRandomBackOffPolicy extends ExponentialBackOffPolicy {
static class ExponentialRandomBackOffContext
extends ExponentialBackOffPolicy.ExponentialBackOffContext {
private final Random r = new Random();
public ExponentialRandomBackOffContext(long expSeed, double multiplier,
@@ -73,4 +76,5 @@ public class ExponentialRandomBackOffPolicy extends ExponentialBackOffPolicy {
}
}
}

View File

@@ -28,8 +28,8 @@ package org.springframework.retry.backoff;
* @author Dave Syer
* @author Artem Bilan
*/
public class FixedBackOffPolicy extends StatelessBackOffPolicy implements
SleepingBackOffPolicy<FixedBackOffPolicy> {
public class FixedBackOffPolicy extends StatelessBackOffPolicy
implements SleepingBackOffPolicy<FixedBackOffPolicy> {
/**
* Default back off period - 1000ms.
@@ -90,4 +90,5 @@ public class FixedBackOffPolicy extends StatelessBackOffPolicy implements
public String toString() {
return "FixedBackOffPolicy[backOffPeriod=" + backOffPeriod + "]";
}
}

View File

@@ -16,11 +16,10 @@
package org.springframework.retry.backoff;
/**
* Implementation of {@link BackOffPolicy} that performs a no-op and as such all
* retry operation in a given set proceed one after the other with no pause.
*
* Implementation of {@link BackOffPolicy} that performs a no-op and as such all retry
* operation in a given set proceed one after the other with no pause.
*
* @author Rob Harrop
* @since 2.1
*/

View File

@@ -17,8 +17,8 @@ package org.springframework.retry.backoff;
/**
* Simple {@link Sleeper} implementation that just waits on a local Object.
* @deprecated in favor of {@link org.springframework.retry.backoff.ThreadWaitSleeper}
*
* @deprecated in favor of {@link org.springframework.retry.backoff.ThreadWaitSleeper}
* @author Dave Syer
*
*/
@@ -28,6 +28,7 @@ public class ObjectWaitSleeper implements Sleeper {
/*
* (non-Javadoc)
*
* @see org.springframework.batch.retry.backoff.Sleeper#sleep(long)
*/
public void sleep(long backOffPeriod) throws InterruptedException {

View File

@@ -19,15 +19,14 @@ import java.io.Serializable;
/**
* Strategy interface for backoff policies to delegate the pausing of execution.
*
*
* @author Dave Syer
*
*
*/
public interface Sleeper extends Serializable {
/**
* Pause for the specified period using whatever means available.
*
* @param backOffPeriod the backoff period
* @throws InterruptedException the exception when interrupted
*/

View File

@@ -20,13 +20,15 @@ package org.springframework.retry.backoff;
* A interface which can be mixed in by {@link BackOffPolicy}s indicating that they sleep
* when backing off.
*/
public interface SleepingBackOffPolicy<T extends SleepingBackOffPolicy<T>> extends BackOffPolicy {
/**
* Clone the policy and return a new policy which uses the passed sleeper.
*
* @param sleeper Target to be invoked any time the backoff policy sleeps
* @return a clone of this policy which will have all of its backoff sleeps
* routed into the passed sleeper
*/
T withSleeper(Sleeper sleeper);
public interface SleepingBackOffPolicy<T extends SleepingBackOffPolicy<T>>
extends BackOffPolicy {
/**
* Clone the policy and return a new policy which uses the passed sleeper.
* @param sleeper Target to be invoked any time the backoff policy sleeps
* @return a clone of this policy which will have all of its backoff sleeps routed
* into the passed sleeper
*/
T withSleeper(Sleeper sleeper);
}

View File

@@ -19,26 +19,26 @@ package org.springframework.retry.backoff;
import org.springframework.retry.RetryContext;
/**
* Simple base class for {@link BackOffPolicy} implementations that maintain no
* state across invocations.
*
* Simple base class for {@link BackOffPolicy} implementations that maintain no state
* across invocations.
*
* @author Rob Harrop
* @author Dave Syer
*/
public abstract class StatelessBackOffPolicy implements BackOffPolicy {
/**
* Delegates directly to the {@link #doBackOff()} method without passing on
* the {@link BackOffContext} argument which is not needed for stateless
* implementations.
* Delegates directly to the {@link #doBackOff()} method without passing on the
* {@link BackOffContext} argument which is not needed for stateless implementations.
*/
public final void backOff(BackOffContext backOffContext) throws BackOffInterruptedException {
public final void backOff(BackOffContext backOffContext)
throws BackOffInterruptedException {
doBackOff();
}
/**
* Returns '<code>null</code>'. Subclasses can add behaviour, e.g.
* initial sleep before first attempt.
* Returns '<code>null</code>'. Subclasses can add behaviour, e.g. initial sleep
* before first attempt.
*/
public BackOffContext start(RetryContext status) {
return null;
@@ -48,4 +48,5 @@ public abstract class StatelessBackOffPolicy implements BackOffPolicy {
* Sub-classes should implement this method to perform the actual back off.
*/
protected abstract void doBackOff() throws BackOffInterruptedException;
}

View File

@@ -17,7 +17,8 @@
package org.springframework.retry.backoff;
/**
* Simple {@link Sleeper} implementation that just blocks the current Thread with sleep period.
* Simple {@link Sleeper} implementation that just blocks the current Thread with sleep
* period.
*
* @author Artem Bilan
* @since 1.1

View File

@@ -18,20 +18,19 @@ package org.springframework.retry.backoff;
import java.util.Random;
/**
* Implementation of {@link BackOffPolicy} that pauses for a random period of
* time before continuing. A pause is implemented using {@link Sleeper#sleep(long)}.
* Implementation of {@link BackOffPolicy} that pauses for a random period of time before
* continuing. A pause is implemented using {@link Sleeper#sleep(long)}.
*
* {@link #setMinBackOffPeriod(long)} is thread-safe and it is safe to call
* {@link #setMaxBackOffPeriod(long)} during execution from multiple threads, however
* this may cause a single retry operation to have pauses of different
* intervals.
* {@link #setMaxBackOffPeriod(long)} during execution from multiple threads, however this
* may cause a single retry operation to have pauses of different intervals.
*
* @author Rob Harrop
* @author Dave Syer
*/
public class UniformRandomBackOffPolicy extends StatelessBackOffPolicy implements SleepingBackOffPolicy<UniformRandomBackOffPolicy> {
public class UniformRandomBackOffPolicy extends StatelessBackOffPolicy
implements SleepingBackOffPolicy<UniformRandomBackOffPolicy> {
/**
* Default min back off period - 500ms.
@@ -51,12 +50,12 @@ public class UniformRandomBackOffPolicy extends StatelessBackOffPolicy implement
private Sleeper sleeper = new ThreadWaitSleeper();
public UniformRandomBackOffPolicy withSleeper(Sleeper sleeper) {
UniformRandomBackOffPolicy res = new UniformRandomBackOffPolicy();
res.setMinBackOffPeriod(minBackOffPeriod);
res.setSleeper(sleeper);
return res;
}
public UniformRandomBackOffPolicy withSleeper(Sleeper sleeper) {
UniformRandomBackOffPolicy res = new UniformRandomBackOffPolicy();
res.setMinBackOffPeriod(minBackOffPeriod);
res.setSleeper(sleeper);
return res;
}
/**
* Public setter for the {@link Sleeper} strategy.
@@ -67,9 +66,8 @@ public class UniformRandomBackOffPolicy extends StatelessBackOffPolicy implement
}
/**
* Set the minimum back off period in milliseconds. Cannot be &lt; 1. Default value
* is 500ms.
*
* Set the minimum back off period in milliseconds. Cannot be &lt; 1. Default value is
* 500ms.
* @param backOffPeriod the backoff period
*/
public void setMinBackOffPeriod(long backOffPeriod) {
@@ -85,8 +83,8 @@ public class UniformRandomBackOffPolicy extends StatelessBackOffPolicy implement
}
/**
* Set the maximum back off period in milliseconds. Cannot be &lt; 1. Default value
* is 1500ms.
* Set the maximum back off period in milliseconds. Cannot be &lt; 1. Default value is
* 1500ms.
* @param backOffPeriod the back off period
*/
public void setMaxBackOffPeriod(long backOffPeriod) {
@@ -107,15 +105,18 @@ public class UniformRandomBackOffPolicy extends StatelessBackOffPolicy implement
*/
protected void doBackOff() throws BackOffInterruptedException {
try {
long delta = maxBackOffPeriod==minBackOffPeriod ? 0 : random.nextInt((int) (maxBackOffPeriod - minBackOffPeriod));
sleeper.sleep(minBackOffPeriod + delta );
long delta = maxBackOffPeriod == minBackOffPeriod ? 0
: random.nextInt((int) (maxBackOffPeriod - minBackOffPeriod));
sleeper.sleep(minBackOffPeriod + delta);
}
catch (InterruptedException e) {
throw new BackOffInterruptedException("Thread interrupted while sleeping", e);
}
}
public String toString() {
return "RandomBackOffPolicy[backOffPeriod=" + minBackOffPeriod + ", " + maxBackOffPeriod + "]";
}
public String toString() {
return "RandomBackOffPolicy[backOffPeriod=" + minBackOffPeriod + ", "
+ maxBackOffPeriod + "]";
}
}

View File

@@ -24,7 +24,8 @@ import org.springframework.retry.RetryPolicy;
* @author Dave Syer
*/
@SuppressWarnings("serial")
public class RetryContextSupport extends AttributeAccessorSupport implements RetryContext {
public class RetryContextSupport extends AttributeAccessorSupport
implements RetryContext {
private final RetryContext parent;
@@ -60,18 +61,16 @@ public class RetryContextSupport extends AttributeAccessorSupport implements Ret
}
/**
* Set the exception for the public interface {@link RetryContext}, and
* also increment the retry count if the throwable is non-null.
* Set the exception for the public interface {@link RetryContext}, and also increment
* the retry count if the throwable is non-null.
*
* All {@link RetryPolicy} implementations should use this method when they
* register the throwable. It should only be called once per retry attempt
* because it increments a counter.
* All {@link RetryPolicy} implementations should use this method when they register
* the throwable. It should only be called once per retry attempt because it
* increments a counter.
*
* Use of this method is not enforced by the framework - it is a service
* provider contract for authors of policies.
*
* @param throwable the exception that caused the current retry attempt to
* fail.
* Use of this method is not enforced by the framework - it is a service provider
* contract for authors of policies.
* @param throwable the exception that caused the current retry attempt to fail.
*/
public void registerThrowable(Throwable throwable) {
this.lastException = throwable;
@@ -81,7 +80,8 @@ public class RetryContextSupport extends AttributeAccessorSupport implements Ret
@Override
public String toString() {
return String.format("[RetryContext: count=%d, lastException=%s, exhausted=%b]", count, lastException, terminate);
return String.format("[RetryContext: count=%d, lastException=%s, exhausted=%b]",
count, lastException, terminate);
}
}

View File

@@ -16,8 +16,7 @@
package org.springframework.retry.interceptor;
/**
* Interface that allows method parameters to be identified and tagged by a
* unique key.
* Interface that allows method parameters to be identified and tagged by a unique key.
*
* @author Dave Syer
*
@@ -25,9 +24,8 @@ package org.springframework.retry.interceptor;
public interface MethodArgumentsKeyGenerator {
/**
* Get a unique identifier for the item that can be used to cache it between
* calls if necessary, and then identify it later.
*
* Get a unique identifier for the item that can be used to cache it between calls if
* necessary, and then identify it later.
* @param item the current method arguments (may be null if there are none).
* @return a unique identifier.
*/

View File

@@ -16,25 +16,21 @@
package org.springframework.retry.interceptor;
/**
* Strategy interface for recovery action when processing of an item fails.
*
*
* @author Dave Syer
*/
public interface MethodInvocationRecoverer<T> {
/**
* Recover gracefully from an error. Clients can call this if processing of
* the item throws an unexpected exception. Caller can use the return value
* to decide whether to try more corrective action or perhaps throw an
* exception.
*
* @param args
* the arguments for the method invocation that failed.
* @param cause
* the cause of the failure that led to this recovery.
* Recover gracefully from an error. Clients can call this if processing of the item
* throws an unexpected exception. Caller can use the return value to decide whether
* to try more corrective action or perhaps throw an exception.
* @param args the arguments for the method invocation that failed.
* @param cause the cause of the failure that led to this recovery.
* @return the value to be returned to the caller
*/
T recover(Object[] args, Throwable cause);
}

View File

@@ -17,18 +17,17 @@
package org.springframework.retry.interceptor;
/**
* Strategy interface to distinguish new arguments from ones that have been
* processed before, e.g. by examining a message flag.
*
* Strategy interface to distinguish new arguments from ones that have been processed
* before, e.g. by examining a message flag.
*
* @author Dave Syer
*
*
*/
public interface NewMethodArgumentsIdentifier {
/**
* Inspect the arguments and determine if they have never been processed
* before. The safest choice when the answer is indeterminate is 'false'.
*
* Inspect the arguments and determine if they have never been processed before. The
* safest choice when the answer is indeterminate is 'false'.
* @param args the current method arguments.
* @return true if the item is known to have never been processed before.
*/

View File

@@ -47,7 +47,6 @@ import org.springframework.util.Assert;
* @author Gary Russell
* @author Artem Bilan
* @since 1.1
*
* @param <T> The type of {@link org.aopalliance.intercept.MethodInterceptor} returned by
* the builder's {@link #build()} method.
*/
@@ -109,8 +108,8 @@ public abstract class RetryInterceptorBuilder<T extends MethodInterceptor> {
}
/**
* Apply the max attempts - a SimpleRetryPolicy will be used. Cannot be used if a custom retry operations
* or retry policy has been set.
* Apply the max attempts - a SimpleRetryPolicy will be used. Cannot be used if a
* custom retry operations or retry policy has been set.
* @param maxAttempts the max attempts (including the initial attempt).
* @return this.
*/
@@ -345,7 +344,8 @@ public abstract class RetryInterceptorBuilder<T extends MethodInterceptor> {
return this;
}
public CircuitBreakerInterceptorBuilder keyGenerator(MethodArgumentsKeyGenerator keyGenerator) {
public CircuitBreakerInterceptorBuilder keyGenerator(
MethodArgumentsKeyGenerator keyGenerator) {
this.keyGenerator = keyGenerator;
return this;
}

View File

@@ -69,7 +69,8 @@ public class RetryOperationsInterceptor implements MethodInterceptor {
String name;
if (StringUtils.hasText(label)) {
name = label;
} else {
}
else {
name = invocation.getMethod().toGenericString();
}
final String label = name;
@@ -77,7 +78,7 @@ public class RetryOperationsInterceptor implements MethodInterceptor {
RetryCallback<Object, Throwable> retryCallback = new RetryCallback<Object, Throwable>() {
public Object doWithRetry(RetryContext context) throws Exception {
context.setAttribute(RetryContext.NAME, label);
/*
@@ -88,7 +89,8 @@ public class RetryOperationsInterceptor implements MethodInterceptor {
*/
if (invocation instanceof ProxyMethodInvocation) {
try {
return ((ProxyMethodInvocation) invocation).invocableClone().proceed();
return ((ProxyMethodInvocation) invocation).invocableClone()
.proceed();
}
catch (Exception e) {
throw e;
@@ -102,8 +104,8 @@ public class RetryOperationsInterceptor implements MethodInterceptor {
}
else {
throw new IllegalStateException(
"MethodInvocation of the wrong type detected - this should not happen with Spring AOP, " +
"so please raise an issue if you see this exception");
"MethodInvocation of the wrong type detected - this should not happen with Spring AOP, "
+ "so please raise an issue if you see this exception");
}
}
@@ -132,7 +134,8 @@ public class RetryOperationsInterceptor implements MethodInterceptor {
/**
* @param args the item that failed.
*/
private ItemRecovererCallback(Object[] args, MethodInvocationRecoverer<?> recoverer) {
private ItemRecovererCallback(Object[] args,
MethodInvocationRecoverer<?> recoverer) {
this.args = Arrays.asList(args).toArray();
this.recoverer = recoverer;
}

View File

@@ -98,7 +98,6 @@ public class StatefulRetryOperationsInterceptor implements MethodInterceptor {
/**
* Rollback classifier for the retry state. Default to null (meaning rollback for
* all).
*
* @param rollbackClassifier the rollbackClassifier to set
*/
public void setRollbackClassifier(
@@ -128,8 +127,7 @@ public class StatefulRetryOperationsInterceptor implements MethodInterceptor {
/**
* Set to true to use the raw key generated by the key generator. Should only be set
* to true for cases where the key is guaranteed to be unique in all cases. When
* false, a compound key is used, including invocation metadata.
* Default: false.
* false, a compound key is used, including invocation metadata. Default: false.
* @param useRawKey the useRawKey to set.
*/
public void setUseRawKey(boolean useRawKey) {
@@ -210,6 +208,7 @@ public class StatefulRetryOperationsInterceptor implements MethodInterceptor {
implements RetryCallback<Object, Throwable> {
private final MethodInvocation invocation;
private String label;
private MethodInvocationRetryCallback(MethodInvocation invocation, String label) {
@@ -238,6 +237,7 @@ public class StatefulRetryOperationsInterceptor implements MethodInterceptor {
throw new IllegalStateException(e);
}
}
}
/**

View File

@@ -22,19 +22,22 @@ import org.springframework.retry.RetryListener;
/**
* Empty method implementation of {@link RetryListener}.
*
*
* @author Dave Syer
*
*/
public class RetryListenerSupport implements RetryListener {
public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
public <T, E extends Throwable> void close(RetryContext context,
RetryCallback<T, E> callback, Throwable throwable) {
}
public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
public <T, E extends Throwable> void onError(RetryContext context,
RetryCallback<T, E> callback, Throwable throwable) {
}
public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
public <T, E extends Throwable> boolean open(RetryContext context,
RetryCallback<T, E> callback) {
return true;
}

View File

@@ -20,18 +20,18 @@ import org.springframework.retry.RetryContext;
import org.springframework.retry.RetryPolicy;
/**
* A {@link RetryPolicy} that always permits a retry. Can also be used as a base
* class for other policies, e.g. for test purposes as a stub.
*
* A {@link RetryPolicy} that always permits a retry. Can also be used as a base class for
* other policies, e.g. for test purposes as a stub.
*
* @author Dave Syer
*
*
*/
@SuppressWarnings("serial")
public class AlwaysRetryPolicy extends NeverRetryPolicy {
/**
* Always returns true.
*
*
* @see org.springframework.retry.RetryPolicy#canRetry(org.springframework.retry.RetryContext)
*/
public boolean canRetry(RetryContext context) {

View File

@@ -20,12 +20,11 @@ import org.springframework.classify.BinaryExceptionClassifier;
import org.springframework.retry.RetryContext;
import org.springframework.retry.RetryPolicy;
import org.springframework.retry.context.RetryContextSupport;
import org.springframework.util.ClassUtils;
/**
* A policy, that is based on {@link BinaryExceptionClassifier}.
* Usually, binary classification is enough for retry purposes. If you need more flexible classification, use
* {@link ExceptionClassifierRetryPolicy}.
* A policy, that is based on {@link BinaryExceptionClassifier}. Usually, binary
* classification is enough for retry purposes. If you need more flexible classification,
* use {@link ExceptionClassifierRetryPolicy}.
*
* @author Aleksandr Shamukov
*/
@@ -34,7 +33,8 @@ public class BinaryExceptionClassifierRetryPolicy implements RetryPolicy {
private final BinaryExceptionClassifier exceptionClassifier;
public BinaryExceptionClassifierRetryPolicy(BinaryExceptionClassifier exceptionClassifier) {
public BinaryExceptionClassifierRetryPolicy(
BinaryExceptionClassifier exceptionClassifier) {
this.exceptionClassifier = exceptionClassifier;
}
@@ -62,4 +62,5 @@ public class BinaryExceptionClassifierRetryPolicy implements RetryPolicy {
public RetryContext open(RetryContext parent) {
return new RetryContextSupport(parent);
}
}

View File

@@ -39,7 +39,9 @@ public class CircuitBreakerRetryPolicy implements RetryPolicy {
private static Log logger = LogFactory.getLog(CircuitBreakerRetryPolicy.class);
private final RetryPolicy delegate;
private long resetTimeout = 20000;
private long openTimeout = 5000;
public CircuitBreakerRetryPolicy() {
@@ -53,7 +55,6 @@ public class CircuitBreakerRetryPolicy implements RetryPolicy {
/**
* Timeout for resetting circuit in milliseconds. After the circuit opens it will
* re-close after this time has elapsed and the context will be restarted.
*
* @param timeout the timeout to set in milliseconds
*/
public void setResetTimeout(long timeout) {
@@ -64,7 +65,6 @@ public class CircuitBreakerRetryPolicy implements RetryPolicy {
* Timeout for tripping the open circuit. If the delegate policy cannot retry and the
* time elapsed since the context was started is less than this window, then the
* circuit is opened.
*
* @param timeout the timeout to set in milliseconds
*/
public void setOpenTimeout(long timeout) {
@@ -104,11 +104,17 @@ public class CircuitBreakerRetryPolicy implements RetryPolicy {
}
static class CircuitBreakerRetryContext extends RetryContextSupport {
private volatile RetryContext context;
private final RetryPolicy policy;
private volatile long start = System.currentTimeMillis();
private final long timeout;
private final long openWindow;
private final AtomicInteger shortCircuitCount = new AtomicInteger();
public CircuitBreakerRetryContext(RetryContext parent, RetryPolicy policy,

View File

@@ -25,8 +25,8 @@ import org.springframework.retry.RetryPolicy;
import org.springframework.retry.context.RetryContextSupport;
/**
* A {@link RetryPolicy} that composes a list of other policies and delegates
* calls to them in order.
* A {@link RetryPolicy} that composes a list of other policies and delegates calls to
* them in order.
*
* @author Dave Syer
* @author Michael Minella
@@ -36,11 +36,11 @@ import org.springframework.retry.context.RetryContextSupport;
public class CompositeRetryPolicy implements RetryPolicy {
RetryPolicy[] policies = new RetryPolicy[0];
private boolean optimistic = false;
/**
* Setter for optimistic.
*
* @param optimistic should this retry policy be optimistic
*/
public void setOptimistic(boolean optimistic) {
@@ -49,18 +49,15 @@ public class CompositeRetryPolicy implements RetryPolicy {
/**
* Setter for policies.
*
* @param policies the {@link RetryPolicy} policies
* @param policies the {@link RetryPolicy} policies
*/
public void setPolicies(RetryPolicy[] policies) {
this.policies = Arrays.asList(policies).toArray(new RetryPolicy[policies.length]);
}
/**
* Delegate to the policies that were in operation when the context was
* created. If any of them cannot retry then return false, otherwise return
* true.
*
* Delegate to the policies that were in operation when the context was created. If
* any of them cannot retry then return false, otherwise return true.
* @param context the {@link RetryContext}
* @see org.springframework.retry.RetryPolicy#canRetry(org.springframework.retry.RetryContext)
*/
@@ -71,7 +68,7 @@ public class CompositeRetryPolicy implements RetryPolicy {
boolean retryable = true;
if(this.optimistic) {
if (this.optimistic) {
retryable = false;
for (int i = 0; i < contexts.length; i++) {
if (policies[i].canRetry(contexts[i])) {
@@ -91,9 +88,9 @@ public class CompositeRetryPolicy implements RetryPolicy {
}
/**
* Delegate to the policies that were in operation when the context was
* created. If any of them fails to close the exception is propagated (and
* those later in the chain are closed before re-throwing).
* Delegate to the policies that were in operation when the context was created. If
* any of them fails to close the exception is propagated (and those later in the
* chain are closed before re-throwing).
*
* @see org.springframework.retry.RetryPolicy#close(org.springframework.retry.RetryContext)
* @param context the {@link RetryContext}
@@ -119,8 +116,8 @@ public class CompositeRetryPolicy implements RetryPolicy {
}
/**
* Creates a new context that copies the existing policies and keeps a list
* of the contexts from each one.
* Creates a new context that copies the existing policies and keeps a list of the
* contexts from each one.
*
* @see org.springframework.retry.RetryPolicy#open(RetryContext)
*/
@@ -134,8 +131,7 @@ public class CompositeRetryPolicy implements RetryPolicy {
}
/**
* Delegate to the policies that were in operation when the context was
* created.
* Delegate to the policies that were in operation when the context was created.
*
* @see org.springframework.retry.RetryPolicy#close(org.springframework.retry.RetryContext)
*/
@@ -150,11 +146,13 @@ public class CompositeRetryPolicy implements RetryPolicy {
}
private static class CompositeRetryContext extends RetryContextSupport {
RetryContext[] contexts;
RetryPolicy[] policies;
public CompositeRetryContext(RetryContext parent, List<RetryContext> contexts, RetryPolicy[] policies) {
public CompositeRetryContext(RetryContext parent, List<RetryContext> contexts,
RetryPolicy[] policies) {
super(parent);
this.contexts = contexts.toArray(new RetryContext[contexts.size()]);
this.policies = policies;

View File

@@ -28,8 +28,8 @@ import org.springframework.retry.context.RetryContextSupport;
import org.springframework.util.Assert;
/**
* A {@link RetryPolicy} that dynamically adapts to one of a set of injected
* policies according to the value of the latest exception.
* A {@link RetryPolicy} that dynamically adapts to one of a set of injected policies
* according to the value of the latest exception.
*
* @author Dave Syer
*
@@ -37,27 +37,28 @@ import org.springframework.util.Assert;
@SuppressWarnings("serial")
public class ExceptionClassifierRetryPolicy implements RetryPolicy {
private Classifier<Throwable, RetryPolicy> exceptionClassifier = new ClassifierSupport<Throwable, RetryPolicy>(new NeverRetryPolicy());
private Classifier<Throwable, RetryPolicy> exceptionClassifier = new ClassifierSupport<Throwable, RetryPolicy>(
new NeverRetryPolicy());
/**
* Setter for policy map used to create a classifier. Either this property
* or the exception classifier directly should be set, but not both.
*
* @param policyMap a map of Throwable class to {@link RetryPolicy} that
* will be used to create a {@link Classifier} to locate a policy.
* Setter for policy map used to create a classifier. Either this property or the
* exception classifier directly should be set, but not both.
* @param policyMap a map of Throwable class to {@link RetryPolicy} that will be used
* to create a {@link Classifier} to locate a policy.
*/
public void setPolicyMap(Map<Class<? extends Throwable>, RetryPolicy> policyMap) {
this.exceptionClassifier = new SubclassClassifier<Throwable, RetryPolicy>(policyMap, new NeverRetryPolicy());
this.exceptionClassifier = new SubclassClassifier<Throwable, RetryPolicy>(
policyMap, new NeverRetryPolicy());
}
/**
* Setter for an exception classifier. The classifier is responsible for
* translating exceptions to concrete retry policies. Either this property
* or the policy map should be used, but not both.
*
* Setter for an exception classifier. The classifier is responsible for translating
* exceptions to concrete retry policies. Either this property or the policy map
* should be used, but not both.
* @param exceptionClassifier ExceptionClassifier to use
*/
public void setExceptionClassifier(Classifier<Throwable, RetryPolicy> exceptionClassifier) {
public void setExceptionClassifier(
Classifier<Throwable, RetryPolicy> exceptionClassifier) {
this.exceptionClassifier = exceptionClassifier;
}
@@ -82,13 +83,14 @@ public class ExceptionClassifierRetryPolicy implements RetryPolicy {
}
/**
* Create an active context that proxies a retry policy by choosing a target
* from the policy map.
* Create an active context that proxies a retry policy by choosing a target from the
* policy map.
*
* @see org.springframework.retry.RetryPolicy#open(RetryContext)
*/
public RetryContext open(RetryContext parent) {
return new ExceptionClassifierRetryContext(parent, exceptionClassifier).open(parent);
return new ExceptionClassifierRetryContext(parent, exceptionClassifier)
.open(parent);
}
/**
@@ -103,7 +105,8 @@ public class ExceptionClassifierRetryPolicy implements RetryPolicy {
((RetryContextSupport) context).registerThrowable(throwable);
}
private static class ExceptionClassifierRetryContext extends RetryContextSupport implements RetryPolicy {
private static class ExceptionClassifierRetryContext extends RetryContextSupport
implements RetryPolicy {
final private Classifier<Throwable, RetryPolicy> exceptionClassifier;
@@ -138,7 +141,8 @@ public class ExceptionClassifierRetryPolicy implements RetryPolicy {
public void registerThrowable(RetryContext context, Throwable throwable) {
policy = exceptionClassifier.classify(throwable);
Assert.notNull(policy, "Could not locate policy for exception=[" + throwable + "].");
Assert.notNull(policy,
"Could not locate policy for exception=[" + throwable + "].");
this.context = getContext(policy, context.getParent());
policy.registerThrowable(this.context, throwable);
}

View File

@@ -29,8 +29,8 @@ import org.springframework.retry.RetryContext;
import org.springframework.util.Assert;
/**
* Subclass of {@link SimpleRetryPolicy} that delegates to super.canRetry() and,
* if true, further evaluates an expression against the last thrown exception.
* Subclass of {@link SimpleRetryPolicy} that delegates to super.canRetry() and, if true,
* further evaluates an expression against the last thrown exception.
*
* @author Gary Russell
* @since 1.2
@@ -60,7 +60,8 @@ public class ExpressionRetryPolicy extends SimpleRetryPolicy implements BeanFact
*/
public ExpressionRetryPolicy(String expressionString) {
Assert.notNull(expressionString, "'expressionString' cannot be null");
this.expression = new SpelExpressionParser().parseExpression(expressionString, PARSER_CONTEXT);
this.expression = new SpelExpressionParser().parseExpression(expressionString,
PARSER_CONTEXT);
}
/**
@@ -70,7 +71,8 @@ public class ExpressionRetryPolicy extends SimpleRetryPolicy implements BeanFact
* @param traverseCauses true to examine causes
* @param expression the expression
*/
public ExpressionRetryPolicy(int maxAttempts, Map<Class<? extends Throwable>, Boolean> retryableExceptions,
public ExpressionRetryPolicy(int maxAttempts,
Map<Class<? extends Throwable>, Boolean> retryableExceptions,
boolean traverseCauses, Expression expression) {
super(maxAttempts, retryableExceptions, traverseCauses);
Assert.notNull(expression, "'expression' cannot be null");
@@ -85,11 +87,13 @@ public class ExpressionRetryPolicy extends SimpleRetryPolicy implements BeanFact
* @param expressionString the expression.
* @param defaultValue the default action
*/
public ExpressionRetryPolicy(int maxAttempts, Map<Class<? extends Throwable>, Boolean> retryableExceptions,
public ExpressionRetryPolicy(int maxAttempts,
Map<Class<? extends Throwable>, Boolean> retryableExceptions,
boolean traverseCauses, String expressionString, boolean defaultValue) {
super(maxAttempts, retryableExceptions, traverseCauses, defaultValue);
Assert.notNull(expressionString, "'expressionString' cannot be null");
this.expression = new SpelExpressionParser().parseExpression(expressionString, PARSER_CONTEXT);
this.expression = new SpelExpressionParser().parseExpression(expressionString,
PARSER_CONTEXT);
}
@Override
@@ -109,8 +113,8 @@ public class ExpressionRetryPolicy extends SimpleRetryPolicy implements BeanFact
return super.canRetry(context);
}
else {
return super.canRetry(context)
&& this.expression.getValue(this.evaluationContext, lastThrowable, Boolean.class);
return super.canRetry(context) && this.expression
.getValue(this.evaluationContext, lastThrowable, Boolean.class);
}
}

View File

@@ -22,21 +22,22 @@ import java.util.Map;
import org.springframework.retry.RetryContext;
/**
* Map-based implementation of {@link RetryContextCache}. The map backing the
* cache of contexts is synchronized.
*
* Map-based implementation of {@link RetryContextCache}. The map backing the cache of
* contexts is synchronized.
*
* @author Dave Syer
*/
public class MapRetryContextCache implements RetryContextCache {
/**
* Default value for maximum capacity of the cache. This is set to a
* reasonably low value (4096) to avoid users inadvertently filling the
* cache with item keys that are inconsistent.
* Default value for maximum capacity of the cache. This is set to a reasonably low
* value (4096) to avoid users inadvertently filling the cache with item keys that are
* inconsistent.
*/
public static final int DEFAULT_CAPACITY = 4096;
private Map<Object, RetryContext> map = Collections.synchronizedMap(new HashMap<Object, RetryContext>());
private Map<Object, RetryContext> map = Collections
.synchronizedMap(new HashMap<Object, RetryContext>());
private int capacity;
@@ -56,12 +57,10 @@ public class MapRetryContextCache implements RetryContextCache {
}
/**
* Public setter for the capacity. Prevents the cache from growing
* unboundedly if items that fail are misidentified and two references to an
* identical item actually do not have the same key. This can happen when
* users implement equals and hashCode based on mutable fields, for
* instance.
*
* Public setter for the capacity. Prevents the cache from growing unboundedly if
* items that fail are misidentified and two references to an identical item actually
* do not have the same key. This can happen when users implement equals and hashCode
* based on mutable fields, for instance.
* @param capacity the capacity to set
*/
public void setCapacity(int capacity) {
@@ -78,9 +77,10 @@ public class MapRetryContextCache implements RetryContextCache {
public void put(Object key, RetryContext context) {
if (map.size() >= capacity) {
throw new RetryCacheCapacityExceededException("Retry cache capacity limit breached. "
+ "Do you need to re-consider the implementation of the key generator, "
+ "or the equals and hashCode of the items that failed?");
throw new RetryCacheCapacityExceededException(
"Retry cache capacity limit breached. "
+ "Do you need to re-consider the implementation of the key generator, "
+ "or the equals and hashCode of the items that failed?");
}
map.put(key, context);
}
@@ -88,4 +88,5 @@ public class MapRetryContextCache implements RetryContextCache {
public void remove(Object key) {
map.remove(key);
}
}

View File

@@ -22,16 +22,16 @@ import org.springframework.retry.context.RetryContextSupport;
import org.springframework.retry.support.RetryTemplate;
/**
* Simple retry policy that is aware only about attempt count and retries a fixed number of times.
* The number of attempts includes the initial try.
* Simple retry policy that is aware only about attempt count and retries a fixed number
* of times. The number of attempts includes the initial try.
* <p>
* It is not recommended to use it directly, because usually exception classification is strongly
* recommended (to not retry on OutOfMemoryError, for example).
* It is not recommended to use it directly, because usually exception classification is
* strongly recommended (to not retry on OutOfMemoryError, for example).
* <p>
* For daily usage see {@link RetryTemplate#newBuilder()}
* <p>
* Volatility of maxAttempts allows concurrent modification and does not require safe publication
* of new instance after construction.
* Volatility of maxAttempts allows concurrent modification and does not require safe
* publication of new instance after construction.
*/
@SuppressWarnings("serial")
public class MaxAttemptsRetryPolicy implements RetryPolicy {
@@ -44,8 +44,8 @@ public class MaxAttemptsRetryPolicy implements RetryPolicy {
private volatile int maxAttempts;
/**
* Create a {@link MaxAttemptsRetryPolicy} with the default number of retry
* attempts, retrying all throwables.
* Create a {@link MaxAttemptsRetryPolicy} with the default number of retry attempts,
* retrying all throwables.
*/
public MaxAttemptsRetryPolicy() {
this.maxAttempts = DEFAULT_MAX_ATTEMPTS;
@@ -63,7 +63,6 @@ public class MaxAttemptsRetryPolicy implements RetryPolicy {
* Set the number of attempts before retries are exhausted. Includes the initial
* attempt before the retries begin so, generally, will be {@code >= 1}. For example
* setting this property to 3 means 3 attempts total (initial + 2 retries).
*
* @param maxAttempts the maximum number of attempts including the initial attempt.
*/
public void setMaxAttempts(int maxAttempts) {
@@ -72,7 +71,6 @@ public class MaxAttemptsRetryPolicy implements RetryPolicy {
/**
* The maximum number of attempts before failure.
*
* @return the maximum number of attempts
*/
public int getMaxAttempts() {
@@ -83,9 +81,8 @@ public class MaxAttemptsRetryPolicy implements RetryPolicy {
* Test for retryable operation based on the status.
*
* @see RetryPolicy#canRetry(RetryContext)
*
* @return true if the last exception was retryable and the number of
* attempts so far is less than the limit.
* @return true if the last exception was retryable and the number of attempts so far
* is less than the limit.
*/
@Override
public boolean canRetry(RetryContext context) {
@@ -107,9 +104,8 @@ public class MaxAttemptsRetryPolicy implements RetryPolicy {
}
/**
* Get a status object that can be used to track the current operation
* according to this policy. Has to be aware of the latest exception and the
* number of attempts.
* Get a status object that can be used to track the current operation according to
* this policy. Has to be aware of the latest exception and the number of attempts.
*
* @see RetryPolicy#open(RetryContext)
*/
@@ -117,4 +113,5 @@ public class MaxAttemptsRetryPolicy implements RetryPolicy {
public RetryContext open(RetryContext parent) {
return new RetryContextSupport(parent);
}
}

View File

@@ -21,20 +21,19 @@ import org.springframework.retry.RetryPolicy;
import org.springframework.retry.context.RetryContextSupport;
/**
* A {@link RetryPolicy} that allows the first attempt but never permits a
* retry. Also be used as a base class for other policies, e.g. for test
* purposes as a stub.
*
* A {@link RetryPolicy} that allows the first attempt but never permits a retry. Also be
* used as a base class for other policies, e.g. for test purposes as a stub.
*
* @author Dave Syer
*
*
*/
@SuppressWarnings("serial")
public class NeverRetryPolicy implements RetryPolicy {
/**
* Returns false after the first exception. So there is always one try, and
* then the retry is prevented.
*
* Returns false after the first exception. So there is always one try, and then the
* retry is prevented.
*
* @see org.springframework.retry.RetryPolicy#canRetry(org.springframework.retry.RetryContext)
*/
public boolean canRetry(RetryContext context) {
@@ -43,7 +42,7 @@ public class NeverRetryPolicy implements RetryPolicy {
/**
* Do nothing.
*
*
* @see org.springframework.retry.RetryPolicy#close(org.springframework.retry.RetryContext)
*/
public void close(RetryContext context) {
@@ -51,9 +50,9 @@ public class NeverRetryPolicy implements RetryPolicy {
}
/**
* Return a context that can respond to early termination requests, but does
* nothing else.
*
* Return a context that can respond to early termination requests, but does nothing
* else.
*
* @see org.springframework.retry.RetryPolicy#open(RetryContext)
*/
public RetryContext open(RetryContext parent) {
@@ -71,17 +70,17 @@ public class NeverRetryPolicy implements RetryPolicy {
}
/**
* Special context object for {@link NeverRetryPolicy}. Implements a flag
* with a similar function to {@link RetryContext#isExhaustedOnly()}, but
* kept separate so that if subclasses of {@link NeverRetryPolicy} need to
* they can modify the behaviour of
* {@link NeverRetryPolicy#canRetry(RetryContext)} without affecting
* Special context object for {@link NeverRetryPolicy}. Implements a flag with a
* similar function to {@link RetryContext#isExhaustedOnly()}, but kept separate so
* that if subclasses of {@link NeverRetryPolicy} need to they can modify the
* behaviour of {@link NeverRetryPolicy#canRetry(RetryContext)} without affecting
* {@link RetryContext#isExhaustedOnly()}.
*
*
* @author Dave Syer
*
*
*/
private static class NeverRetryContext extends RetryContextSupport {
private boolean finished = false;
public NeverRetryContext(RetryContext parent) {
@@ -95,6 +94,7 @@ public class NeverRetryPolicy implements RetryPolicy {
public void setFinished() {
this.finished = true;
}
}
}

View File

@@ -18,22 +18,20 @@ package org.springframework.retry.policy;
import org.springframework.retry.RetryException;
/**
* Exception that indicates that a cache limit was exceeded. This is often a
* sign of badly or inconsistently implemented hashCode, equals in failed items.
* Items can then fail repeatedly and appear different to the cache, so they get
* added over and over again until a limit is reached and this exception is
* thrown. Consult the documentation of the {@link RetryContextCache} in use to
* determine how to increase the limit if appropriate.
*
* Exception that indicates that a cache limit was exceeded. This is often a sign of badly
* or inconsistently implemented hashCode, equals in failed items. Items can then fail
* repeatedly and appear different to the cache, so they get added over and over again
* until a limit is reached and this exception is thrown. Consult the documentation of the
* {@link RetryContextCache} in use to determine how to increase the limit if appropriate.
*
* @author Dave Syer
*
*
*/
@SuppressWarnings("serial")
public class RetryCacheCapacityExceededException extends RetryException {
/**
* Constructs a new instance with a message.
*
* @param message the message sent when creating the exception
*/
public RetryCacheCapacityExceededException(String message) {
@@ -42,7 +40,6 @@ public class RetryCacheCapacityExceededException extends RetryException {
/**
* Constructs a new instance with a message and nested exception.
*
* @param msg the exception message.
* @param nested the nested exception
*/

View File

@@ -23,11 +23,10 @@ import org.springframework.retry.RetryContext;
* retrieving {@link RetryContext} instances. A null key should never be passed in by the
* caller, but if it is then implementations are free to discard the context instead of
* saving it (null key means "no information").
*
*
* @author Dave Syer
*
* @see MapRetryContextCache
*
*
*/
public interface RetryContextCache {

View File

@@ -25,9 +25,8 @@ import org.springframework.retry.context.RetryContextSupport;
import org.springframework.util.ClassUtils;
/**
* Simple retry policy that retries a fixed number of times for a set of named
* exceptions (and subclasses). The number of attempts includes the initial try,
* so e.g.
* Simple retry policy that retries a fixed number of times for a set of named exceptions
* (and subclasses). The number of attempts includes the initial try, so e.g.
*
* <pre>
* retryTemplate = new RetryTemplate(new SimpleRetryPolicy(3));
@@ -36,16 +35,16 @@ import org.springframework.util.ClassUtils;
*
* will execute the callback at least once, and as many as 3 times.
*
* Since version 1.3 it is not necessary to use this class. The same behaviour
* can be achieved by constructing a {@link CompositeRetryPolicy} with {@link MaxAttemptsRetryPolicy}
* and {@link BinaryExceptionClassifierRetryPolicy} inside, that is actually performed by:
* <pre> {@code:
* Since version 1.3 it is not necessary to use this class. The same behaviour can be
* achieved by constructing a {@link CompositeRetryPolicy} with
* {@link MaxAttemptsRetryPolicy} and {@link BinaryExceptionClassifierRetryPolicy} inside,
* that is actually performed by: <pre> {@code:
* RetryTemplate.newBuilder()
* .maxAttempts(3)
* .retryOn(Exception.class)
* .build();
* }</pre>
* or by {@link org.springframework.retry.support.RetryTemplate#newDefaultInstance()}
* }</pre> or by
* {@link org.springframework.retry.support.RetryTemplate#newDefaultInstance()}
*
* @author Dave Syer
* @author Rob Harrop
@@ -62,74 +61,74 @@ public class SimpleRetryPolicy implements RetryPolicy {
private volatile int maxAttempts;
private BinaryExceptionClassifier retryableClassifier = new BinaryExceptionClassifier(false);
private BinaryExceptionClassifier retryableClassifier = new BinaryExceptionClassifier(
false);
/**
* Create a {@link SimpleRetryPolicy} with the default number of retry
* attempts, retrying all exceptions.
* Create a {@link SimpleRetryPolicy} with the default number of retry attempts,
* retrying all exceptions.
*/
public SimpleRetryPolicy() {
this(DEFAULT_MAX_ATTEMPTS, BinaryExceptionClassifier.newDefaultClassifier());
}
/**
* Create a {@link SimpleRetryPolicy} with the specified number of retry
* attempts, retrying all exceptions.
* Create a {@link SimpleRetryPolicy} with the specified number of retry attempts,
* retrying all exceptions.
*/
public SimpleRetryPolicy(int maxAttempts) {
this(maxAttempts, BinaryExceptionClassifier.newDefaultClassifier());
}
/**
* Create a {@link SimpleRetryPolicy} with the specified number of retry
* attempts.
*
* Create a {@link SimpleRetryPolicy} with the specified number of retry attempts.
* @param maxAttempts the maximum number of attempts
* @param retryableExceptions the map of exceptions that are retryable
*/
public SimpleRetryPolicy(int maxAttempts, Map<Class<? extends Throwable>, Boolean> retryableExceptions) {
public SimpleRetryPolicy(int maxAttempts,
Map<Class<? extends Throwable>, Boolean> retryableExceptions) {
this(maxAttempts, retryableExceptions, false);
}
/**
* Create a {@link SimpleRetryPolicy} with the specified number of retry
* attempts. If traverseCauses is true, the exception causes will be traversed until
* a match is found.
*
* Create a {@link SimpleRetryPolicy} with the specified number of retry attempts. If
* traverseCauses is true, the exception causes will be traversed until a match is
* found.
* @param maxAttempts the maximum number of attempts
* @param retryableExceptions the map of exceptions that are retryable based on the
* map value (true/false).
* @param traverseCauses is this clause traversable
*/
public SimpleRetryPolicy(int maxAttempts, Map<Class<? extends Throwable>, Boolean> retryableExceptions,
public SimpleRetryPolicy(int maxAttempts,
Map<Class<? extends Throwable>, Boolean> retryableExceptions,
boolean traverseCauses) {
this(maxAttempts, retryableExceptions, traverseCauses, false);
}
/**
* Create a {@link SimpleRetryPolicy} with the specified number of retry
* attempts. If traverseCauses is true, the exception causes will be traversed until
* a match is found. The default value indicates whether to retry or not for exceptions
* (or super classes) are not found in the map.
*
* Create a {@link SimpleRetryPolicy} with the specified number of retry attempts. If
* traverseCauses is true, the exception causes will be traversed until a match is
* found. The default value indicates whether to retry or not for exceptions (or super
* classes) are not found in the map.
* @param maxAttempts the maximum number of attempts
* @param retryableExceptions the map of exceptions that are retryable based on the
* map value (true/false).
* @param traverseCauses is this clause traversable
* @param defaultValue the default action.
*/
public SimpleRetryPolicy(int maxAttempts, Map<Class<? extends Throwable>, Boolean> retryableExceptions,
public SimpleRetryPolicy(int maxAttempts,
Map<Class<? extends Throwable>, Boolean> retryableExceptions,
boolean traverseCauses, boolean defaultValue) {
super();
this.maxAttempts = maxAttempts;
this.retryableClassifier = new BinaryExceptionClassifier(retryableExceptions, defaultValue);
this.retryableClassifier = new BinaryExceptionClassifier(retryableExceptions,
defaultValue);
this.retryableClassifier.setTraverseCauses(traverseCauses);
}
/**
* Create a {@link SimpleRetryPolicy} with the specified number of retry
* attempts and provided exception classifier.
*
* Create a {@link SimpleRetryPolicy} with the specified number of retry attempts and
* provided exception classifier.
* @param maxAttempts the maximum number of attempts
* @param classifier custom exception classifier
*/
@@ -143,7 +142,6 @@ public class SimpleRetryPolicy implements RetryPolicy {
* Set the number of attempts before retries are exhausted. Includes the initial
* attempt before the retries begin so, generally, will be {@code >= 1}. For example
* setting this property to 3 means 3 attempts total (initial + 2 retries).
*
* @param maxAttempts the maximum number of attempts including the initial attempt.
*/
public void setMaxAttempts(int maxAttempts) {
@@ -152,7 +150,6 @@ public class SimpleRetryPolicy implements RetryPolicy {
/**
* The maximum number of attempts before failure.
*
* @return the maximum number of attempts
*/
public int getMaxAttempts() {
@@ -163,14 +160,14 @@ public class SimpleRetryPolicy implements RetryPolicy {
* Test for retryable operation based on the status.
*
* @see org.springframework.retry.RetryPolicy#canRetry(org.springframework.retry.RetryContext)
*
* @return true if the last exception was retryable and the number of
* attempts so far is less than the limit.
* @return true if the last exception was retryable and the number of attempts so far
* is less than the limit.
*/
@Override
public boolean canRetry(RetryContext context) {
Throwable t = context.getLastThrowable();
return (t == null || retryForException(t)) && context.getRetryCount() < maxAttempts;
return (t == null || retryForException(t))
&& context.getRetryCount() < maxAttempts;
}
/**
@@ -192,9 +189,8 @@ public class SimpleRetryPolicy implements RetryPolicy {
}
/**
* Get a status object that can be used to track the current operation
* according to this policy. Has to be aware of the latest exception and the
* number of attempts.
* Get a status object that can be used to track the current operation according to
* this policy. Has to be aware of the latest exception and the number of attempts.
*
* @see org.springframework.retry.RetryPolicy#open(RetryContext)
*/
@@ -204,17 +200,17 @@ public class SimpleRetryPolicy implements RetryPolicy {
}
private static class SimpleRetryContext extends RetryContextSupport {
public SimpleRetryContext(RetryContext parent) {
super(parent);
}
}
/**
* Delegates to an exception classifier.
*
* @param ex
* @return true if this exception or its ancestors have been registered as
* retryable.
* @return true if this exception or its ancestors have been registered as retryable.
*/
private boolean retryForException(Throwable ex) {
return retryableClassifier.classify(ex);
@@ -224,4 +220,5 @@ public class SimpleRetryPolicy implements RetryPolicy {
public String toString() {
return ClassUtils.getShortName(getClass()) + "[maxAttempts=" + maxAttempts + "]";
}
}

View File

@@ -23,20 +23,19 @@ import java.util.Map;
import org.springframework.retry.RetryContext;
/**
* Map-based implementation of {@link RetryContextCache}. The map backing the
* cache of contexts is synchronized and its entries are soft-referenced, so may
* be garbage collected under pressure.
*
* Map-based implementation of {@link RetryContextCache}. The map backing the cache of
* contexts is synchronized and its entries are soft-referenced, so may be garbage
* collected under pressure.
*
* @see MapRetryContextCache for non-soft referenced version
*
* @author Dave Syer
*/
public class SoftReferenceMapRetryContextCache implements RetryContextCache {
/**
* Default value for maximum capacity of the cache. This is set to a
* reasonably low value (4096) to avoid users inadvertently filling the
* cache with item keys that are inconsistent.
* Default value for maximum capacity of the cache. This is set to a reasonably low
* value (4096) to avoid users inadvertently filling the cache with item keys that are
* inconsistent.
*/
public static final int DEFAULT_CAPACITY = 4096;
@@ -61,12 +60,10 @@ public class SoftReferenceMapRetryContextCache implements RetryContextCache {
}
/**
* Public setter for the capacity. Prevents the cache from growing
* unboundedly if items that fail are misidentified and two references to an
* identical item actually do not have the same key. This can happen when
* users implement equals and hashCode based on mutable fields, for
* instance.
*
* Public setter for the capacity. Prevents the cache from growing unboundedly if
* items that fail are misidentified and two references to an identical item actually
* do not have the same key. This can happen when users implement equals and hashCode
* based on mutable fields, for instance.
* @param capacity the capacity to set
*/
public void setCapacity(int capacity) {
@@ -90,9 +87,10 @@ public class SoftReferenceMapRetryContextCache implements RetryContextCache {
public void put(Object key, RetryContext context) {
if (map.size() >= capacity) {
throw new RetryCacheCapacityExceededException("Retry cache capacity limit breached. "
+ "Do you need to re-consider the implementation of the key generator, "
+ "or the equals and hashCode of the items that failed?");
throw new RetryCacheCapacityExceededException(
"Retry cache capacity limit breached. "
+ "Do you need to re-consider the implementation of the key generator, "
+ "or the equals and hashCode of the items that failed?");
}
map.put(key, new SoftReference<RetryContext>(context));
}
@@ -100,4 +98,5 @@ public class SoftReferenceMapRetryContextCache implements RetryContextCache {
public void remove(Object key) {
map.remove(key);
}
}

View File

@@ -21,11 +21,11 @@ import org.springframework.retry.RetryPolicy;
import org.springframework.retry.context.RetryContextSupport;
/**
* A {@link RetryPolicy} that allows a retry only if it hasn't timed out. The
* clock is started on a call to {@link #open(RetryContext)}.
*
* A {@link RetryPolicy} that allows a retry only if it hasn't timed out. The clock is
* started on a call to {@link #open(RetryContext)}.
*
* @author Dave Syer
*
*
*/
@SuppressWarnings("serial")
public class TimeoutRetryPolicy implements RetryPolicy {
@@ -44,10 +44,9 @@ public class TimeoutRetryPolicy implements RetryPolicy {
public void setTimeout(long timeout) {
this.timeout = timeout;
}
/**
* The value of the timeout.
*
* @return the timeout in milliseconds
*/
public long getTimeout() {
@@ -55,9 +54,9 @@ public class TimeoutRetryPolicy implements RetryPolicy {
}
/**
* Only permits a retry if the timeout has not expired. Does not check the
* exception at all.
*
* Only permits a retry if the timeout has not expired. Does not check the exception
* at all.
*
* @see org.springframework.retry.RetryPolicy#canRetry(org.springframework.retry.RetryContext)
*/
public boolean canRetry(RetryContext context) {
@@ -77,6 +76,7 @@ public class TimeoutRetryPolicy implements RetryPolicy {
}
private static class TimeoutRetryContext extends RetryContextSupport {
private long timeout;
private long start;
@@ -90,6 +90,7 @@ public class TimeoutRetryPolicy implements RetryPolicy {
public boolean isAlive() {
return (System.currentTimeMillis() - start) <= timeout;
}
}
}

View File

@@ -26,13 +26,19 @@ import org.springframework.retry.RetryStatistics;
*
*/
@SuppressWarnings("serial")
public class DefaultRetryStatistics extends AttributeAccessorSupport implements RetryStatistics, MutableRetryStatistics {
public class DefaultRetryStatistics extends AttributeAccessorSupport
implements RetryStatistics, MutableRetryStatistics {
private String name;
private AtomicInteger startedCount = new AtomicInteger();
private AtomicInteger completeCount = new AtomicInteger();
private AtomicInteger recoveryCount = new AtomicInteger();
private AtomicInteger errorCount = new AtomicInteger();
private AtomicInteger abortCount = new AtomicInteger();
DefaultRetryStatistics() {

View File

@@ -23,7 +23,7 @@ package org.springframework.retry.stats;
public class DefaultRetryStatisticsFactory implements RetryStatisticsFactory {
private long window = 15000;
/**
* Window in milliseconds for exponential decay factor in rolling averages.
* @param window the window to set
@@ -34,7 +34,8 @@ public class DefaultRetryStatisticsFactory implements RetryStatisticsFactory {
@Override
public MutableRetryStatistics create(String name) {
ExponentialAverageRetryStatistics stats = new ExponentialAverageRetryStatistics(name);
ExponentialAverageRetryStatistics stats = new ExponentialAverageRetryStatistics(
name);
stats.setWindow(window);
return stats;
}

View File

@@ -29,6 +29,7 @@ import org.springframework.retry.RetryStatistics;
public class DefaultStatisticsRepository implements StatisticsRepository {
private ConcurrentMap<String, MutableRetryStatistics> map = new ConcurrentHashMap<String, MutableRetryStatistics>();
private RetryStatisticsFactory factory = new DefaultRetryStatisticsFactory();
public void setRetryStatisticsFactory(RetryStatisticsFactory factory) {

View File

@@ -26,15 +26,15 @@ public class ExponentialAverageRetryStatistics extends DefaultRetryStatistics {
private long window = 15000;
private ExponentialAverage started;
private ExponentialAverage error;
private ExponentialAverage complete;
private ExponentialAverage recovery;
private ExponentialAverage abort;
public ExponentialAverageRetryStatistics(String name) {
super(name);
init();
@@ -50,7 +50,6 @@ public class ExponentialAverageRetryStatistics extends DefaultRetryStatistics {
/**
* Window in milliseconds for exponential decay factor in rolling average.
*
* @param window the window to set
*/
public void setWindow(long window) {
@@ -65,32 +64,32 @@ public class ExponentialAverageRetryStatistics extends DefaultRetryStatistics {
public int getRollingErrorCount() {
return (int) Math.round(error.getValue());
}
public int getRollingAbortCount() {
return (int) Math.round(abort.getValue());
}
public int getRollingRecoveryCount() {
return (int) Math.round(recovery.getValue());
}
public int getRollingCompleteCount() {
return (int) Math.round(complete.getValue());
}
public double getRollingErrorRate() {
if (Math.round(started.getValue())==0) {
if (Math.round(started.getValue()) == 0) {
return 0.;
}
return (abort.getValue() + recovery.getValue()) / started.getValue();
}
@Override
public void incrementStartedCount() {
super.incrementStartedCount();
started.increment();
}
@Override
public void incrementCompleteCount() {
super.incrementCompleteCount();
@@ -118,9 +117,9 @@ public class ExponentialAverageRetryStatistics extends DefaultRetryStatistics {
private class ExponentialAverage {
private final double alpha;
private volatile long lastTime = System.currentTimeMillis();
private volatile double value = 0;
public ExponentialAverage(long window) {
@@ -129,13 +128,13 @@ public class ExponentialAverageRetryStatistics extends DefaultRetryStatistics {
public synchronized void increment() {
long time = System.currentTimeMillis();
value = value * Math.exp(-alpha*(time - lastTime)) + 1;
value = value * Math.exp(-alpha * (time - lastTime)) + 1;
lastTime = time;
}
public double getValue() {
long time = System.currentTimeMillis();
return value * Math.exp(-alpha*(time - lastTime));
return value * Math.exp(-alpha * (time - lastTime));
}
}

View File

@@ -21,7 +21,7 @@ package org.springframework.retry.stats;
*
*/
public interface RetryStatisticsFactory {
MutableRetryStatistics create(String name);
}

View File

@@ -105,4 +105,5 @@ public class StatisticsListener extends RetryListenerSupport {
private String getName(RetryContext context) {
return (String) context.getAttribute(RetryContext.NAME);
}
}

View File

@@ -25,11 +25,11 @@ import org.springframework.retry.RetryStatistics;
public interface StatisticsRepository {
RetryStatistics findOne(String name);
Iterable<RetryStatistics> findAll();
void addStarted(String name);
void addError(String name);
void addRecovery(String name);

View File

@@ -22,9 +22,8 @@ import org.springframework.retry.RetryOperations;
import org.springframework.retry.RetryState;
/**
*
* @author Dave Syer
*
*
*/
public class DefaultRetryState implements RetryState {
@@ -35,21 +34,18 @@ public class DefaultRetryState implements RetryState {
final private Classifier<? super Throwable, Boolean> rollbackClassifier;
/**
* Create a {@link DefaultRetryState} representing the state for a new retry
* attempt.
*
* Create a {@link DefaultRetryState} representing the state for a new retry attempt.
*
* @see RetryOperations#execute(RetryCallback, RetryState)
* @see RetryOperations#execute(RetryCallback, RecoveryCallback, RetryState)
*
* @param key the key for the state to allow this retry attempt to be
* recognised
* @param forceRefresh true if the attempt is known to be a brand new state
* (could not have previously failed)
* @param rollbackClassifier the rollback classifier to set. The rollback
* classifier answers true if the exception provided should cause a
* rollback.
* @param key the key for the state to allow this retry attempt to be recognised
* @param forceRefresh true if the attempt is known to be a brand new state (could not
* have previously failed)
* @param rollbackClassifier the rollback classifier to set. The rollback classifier
* answers true if the exception provided should cause a rollback.
*/
public DefaultRetryState(Object key, boolean forceRefresh, Classifier<? super Throwable, Boolean> rollbackClassifier) {
public DefaultRetryState(Object key, boolean forceRefresh,
Classifier<? super Throwable, Boolean> rollbackClassifier) {
this.key = key;
this.forceRefresh = forceRefresh;
this.rollbackClassifier = rollbackClassifier;
@@ -61,7 +57,8 @@ public class DefaultRetryState implements RetryState {
* @param key the key
* @param rollbackClassifier the rollback {@link Classifier}
*/
public DefaultRetryState(Object key, Classifier<? super Throwable, Boolean> rollbackClassifier) {
public DefaultRetryState(Object key,
Classifier<? super Throwable, Boolean> rollbackClassifier) {
this(key, false, rollbackClassifier);
}
@@ -76,9 +73,7 @@ public class DefaultRetryState implements RetryState {
}
/**
* Defaults the force refresh flag (to false) and the rollback classifier
* (to null).
*
* Defaults the force refresh flag (to false) and the rollback classifier (to null).
* @param key the key to use
* @see DefaultRetryState#DefaultRetryState(Object, boolean, Classifier)
*/
@@ -88,7 +83,7 @@ public class DefaultRetryState implements RetryState {
/*
* (non-Javadoc)
*
*
* @see org.springframework.batch.retry.IRetryState#getKey()
*/
public Object getKey() {
@@ -97,7 +92,7 @@ public class DefaultRetryState implements RetryState {
/*
* (non-Javadoc)
*
*
* @see org.springframework.batch.retry.IRetryState#isForceRefresh()
*/
public boolean isForceRefresh() {
@@ -106,10 +101,8 @@ public class DefaultRetryState implements RetryState {
/*
* (non-Javadoc)
*
* @see
* org.springframework.batch.retry.RetryState#rollbackFor(java.lang.Throwable
* )
*
* @see org.springframework.batch.retry.RetryState#rollbackFor(java.lang.Throwable )
*/
public boolean rollbackFor(Throwable exception) {
if (rollbackClassifier == null) {
@@ -120,6 +113,8 @@ public class DefaultRetryState implements RetryState {
@Override
public String toString() {
return String.format("[%s: key=%s, forceRefresh=%b]", getClass().getSimpleName(), key, forceRefresh);
return String.format("[%s: key=%s, forceRefresh=%b]", getClass().getSimpleName(),
key, forceRefresh);
}
}

View File

@@ -42,7 +42,8 @@ public class RetrySimulation {
}
/**
* @return Returns a list of all the unique sleep values which were executed within all simulations.
* @return Returns a list of all the unique sleep values which were executed within
* all simulations.
*/
public List<Double> getPercentiles() {
List<Double> res = new ArrayList<Double>();
@@ -77,6 +78,7 @@ public class RetrySimulation {
}
public static class SleepSequence {
private final List<Long> sleeps;
private final long longestSleep;
@@ -111,5 +113,7 @@ public class RetrySimulation {
public String toString() {
return "totalSleep=" + totalSleep + ": " + sleeps.toString();
}
}
}

View File

@@ -28,94 +28,102 @@ import org.springframework.retry.backoff.SleepingBackOffPolicy;
/**
* A {@link RetrySimulator} is a tool for exercising retry + backoff operations.
*
* When calibrating a set of retry + backoff pairs, it is useful to know the behaviour
* of the retry for various scenarios.
* When calibrating a set of retry + backoff pairs, it is useful to know the behaviour of
* the retry for various scenarios.
*
* Things you may want to know:
* - Does a 'maxInterval' of 5000 ms in my backoff even matter?
* (This is often the case when retry counts are low -- so why set the max interval
* at something that cannot be achieved?)
* - What are the typical sleep durations for threads in a retry
* - What was the longest sleep duration for any retry sequence
* Things you may want to know: - Does a 'maxInterval' of 5000 ms in my backoff even
* matter? (This is often the case when retry counts are low -- so why set the max
* interval at something that cannot be achieved?) - What are the typical sleep durations
* for threads in a retry - What was the longest sleep duration for any retry sequence
*
* The simulator provides this information by executing a retry + backoff pair until failure
* (that is all retries are exhausted). The information about each retry is provided
* as part of the {@link RetrySimulation}.
* The simulator provides this information by executing a retry + backoff pair until
* failure (that is all retries are exhausted). The information about each retry is
* provided as part of the {@link RetrySimulation}.
*
* Note that the impetus for this class was to expose the timings which are possible with
* {@link org.springframework.retry.backoff.ExponentialRandomBackOffPolicy}, which provides
* random values and must be looked at over a series of trials.
* {@link org.springframework.retry.backoff.ExponentialRandomBackOffPolicy}, which
* provides random values and must be looked at over a series of trials.
*
* @author Jon Travis
*/
public class RetrySimulator {
private final SleepingBackOffPolicy<?> backOffPolicy;
private final RetryPolicy retryPolicy;
public RetrySimulator(SleepingBackOffPolicy<?> backOffPolicy, RetryPolicy retryPolicy) {
this.backOffPolicy = backOffPolicy;
this.retryPolicy = retryPolicy;
}
private final RetryPolicy retryPolicy;
/**
* Execute the simulator for a give # of iterations.
*
* @param numSimulations Number of simulations to run
* @return the outcome of all simulations
*/
public RetrySimulation executeSimulation(int numSimulations) {
RetrySimulation simulation = new RetrySimulation();
public RetrySimulator(SleepingBackOffPolicy<?> backOffPolicy,
RetryPolicy retryPolicy) {
this.backOffPolicy = backOffPolicy;
this.retryPolicy = retryPolicy;
}
for (int i=0; i<numSimulations; i++) {
simulation.addSequence(executeSingleSimulation());
}
return simulation;
}
/**
* Execute the simulator for a give # of iterations.
* @param numSimulations Number of simulations to run
* @return the outcome of all simulations
*/
public RetrySimulation executeSimulation(int numSimulations) {
RetrySimulation simulation = new RetrySimulation();
/**
* Execute a single simulation
* @return The sleeps which occurred within the single simulation.
*/
public List<Long> executeSingleSimulation() {
StealingSleeper stealingSleeper = new StealingSleeper();
SleepingBackOffPolicy<?> stealingBackoff = backOffPolicy.withSleeper(stealingSleeper);
for (int i = 0; i < numSimulations; i++) {
simulation.addSequence(executeSingleSimulation());
}
return simulation;
}
RetryTemplate template = new RetryTemplate();
template.setBackOffPolicy(stealingBackoff);
template.setRetryPolicy(retryPolicy);
/**
* Execute a single simulation
* @return The sleeps which occurred within the single simulation.
*/
public List<Long> executeSingleSimulation() {
StealingSleeper stealingSleeper = new StealingSleeper();
SleepingBackOffPolicy<?> stealingBackoff = backOffPolicy
.withSleeper(stealingSleeper);
try {
template.execute(new FailingRetryCallback());
} catch(FailingRetryException e) {
RetryTemplate template = new RetryTemplate();
template.setBackOffPolicy(stealingBackoff);
template.setRetryPolicy(retryPolicy);
} catch(Throwable e) {
throw new RuntimeException("Unexpected exception", e);
}
try {
template.execute(new FailingRetryCallback());
}
catch (FailingRetryException e) {
return stealingSleeper.getSleeps();
}
}
catch (Throwable e) {
throw new RuntimeException("Unexpected exception", e);
}
static class FailingRetryCallback implements RetryCallback<Object, Exception> {
public Object doWithRetry(RetryContext context) throws Exception {
throw new FailingRetryException();
}
}
return stealingSleeper.getSleeps();
}
@SuppressWarnings("serial")
static class FailingRetryCallback implements RetryCallback<Object, Exception> {
public Object doWithRetry(RetryContext context) throws Exception {
throw new FailingRetryException();
}
}
@SuppressWarnings("serial")
static class FailingRetryException extends Exception {
}
@SuppressWarnings("serial")
}
@SuppressWarnings("serial")
static class StealingSleeper implements Sleeper {
private final List<Long> sleeps = new ArrayList<Long>();
public void sleep(long backOffPeriod) throws InterruptedException {
sleeps.add(backOffPeriod);
}
private final List<Long> sleeps = new ArrayList<Long>();
public void sleep(long backOffPeriod) throws InterruptedException {
sleeps.add(backOffPeriod);
}
public List<Long> getSleeps() {
return sleeps;
}
}
public List<Long> getSleeps() {
return sleeps;
}
}
}

View File

@@ -21,27 +21,26 @@ import org.springframework.retry.RetryContext;
import org.springframework.retry.RetryOperations;
/**
* Global variable support for retry clients. Normally it is not necessary for
* clients to be aware of the surrounding environment because a
* {@link RetryCallback} can always use the context it is passed by the
* enclosing {@link RetryOperations}. But occasionally it might be helpful to
* have lower level access to the ongoing {@link RetryContext} so we provide a
* global accessor here. The mutator methods ({@link #clear()} and
* Global variable support for retry clients. Normally it is not necessary for clients to
* be aware of the surrounding environment because a {@link RetryCallback} can always use
* the context it is passed by the enclosing {@link RetryOperations}. But occasionally it
* might be helpful to have lower level access to the ongoing {@link RetryContext} so we
* provide a global accessor here. The mutator methods ({@link #clear()} and
* {@link #register(RetryContext)} should not be used except internally by
* {@link RetryOperations} implementations.
*
*
* @author Dave Syer
*
*
*/
public final class RetrySynchronizationManager {
private RetrySynchronizationManager() {}
private RetrySynchronizationManager() {
}
private static final ThreadLocal<RetryContext> context = new ThreadLocal<RetryContext>();
/**
* Public accessor for the locally enclosing {@link RetryContext}.
*
* @return the current retry context, or null if there isn't one
*/
public static RetryContext getContext() {
@@ -50,10 +49,9 @@ public final class RetrySynchronizationManager {
}
/**
* Method for registering a context - should only be used by
* {@link RetryOperations} implementations to ensure that
* {@link #getContext()} always returns the correct value.
*
* Method for registering a context - should only be used by {@link RetryOperations}
* implementations to ensure that {@link #getContext()} always returns the correct
* value.
* @param context the new context to register
* @return the old context if there was one
*/
@@ -66,7 +64,6 @@ public final class RetrySynchronizationManager {
/**
* Clear the current context at the end of a batch - should only be used by
* {@link RetryOperations} implementations.
*
* @return the old value if there was one.
*/
public static RetryContext clear() {

View File

@@ -57,14 +57,12 @@ import org.springframework.retry.policy.SimpleRetryPolicy;
* properties. The {@link org.springframework.retry.backoff.BackOffPolicy} controls how
* long the pause is between each individual retry attempt.
* <p>
* A new instance can be fluently configured via {@link #newBuilder}, e.g:
* <pre> {@code
* A new instance can be fluently configured via {@link #newBuilder}, e.g: <pre> {@code
* RetryTemplate.newBuilder()
* .maxAttempts(10)
* .fixedBackoff(1000)
* .build();
* }</pre>
* See {@link RetryTemplateBuilder} for more examples and details.
* }</pre> See {@link RetryTemplateBuilder} for more examples and details.
* <p>
* This class is thread-safe and suitable for concurrent access when executing operations
* and when performing configuration changes. As such, it is possible to change the number
@@ -99,11 +97,10 @@ public class RetryTemplate implements RetryOperations {
private boolean throwLastExceptionOnExhausted;
/**
* Main entry point to configure RetryTemplate using fluent API.
* See {@link RetryTemplateBuilder} for usage examples and details.
*
* @return a new instance of RetryTemplateBuilder with preset default behaviour, that can be overwritten during
* manual configuration
* Main entry point to configure RetryTemplate using fluent API. See
* {@link RetryTemplateBuilder} for usage examples and details.
* @return a new instance of RetryTemplateBuilder with preset default behaviour, that
* can be overwritten during manual configuration
* @since 1.3
*/
public static RetryTemplateBuilder newBuilder() {
@@ -111,9 +108,8 @@ public class RetryTemplate implements RetryOperations {
}
/**
* Creates a new default instance. The properties of default instance are described in {@link RetryTemplateBuilder}
* documentation.
*
* Creates a new default instance. The properties of default instance are described in
* {@link RetryTemplateBuilder} documentation.
* @return a new instance of RetryTemplate with default behaviour
* @since 1.3
*/
@@ -130,7 +126,6 @@ public class RetryTemplate implements RetryOperations {
/**
* Public setter for the {@link RetryContextCache}.
*
* @param retryContextCache the {@link RetryContextCache} to set.
*/
public void setRetryContextCache(RetryContextCache retryContextCache) {
@@ -140,7 +135,6 @@ public class RetryTemplate implements RetryOperations {
/**
* Setter for listeners. The listeners are executed before and after a retry block
* (i.e. before and after all the attempts), and on an error (every attempt).
*
* @param listeners the {@link RetryListener}s
* @see RetryListener
*/
@@ -151,7 +145,6 @@ public class RetryTemplate implements RetryOperations {
/**
* Register an additional listener.
*
* @param listener the {@link RetryListener}
* @see #setListeners(RetryListener[])
*/
@@ -164,7 +157,6 @@ public class RetryTemplate implements RetryOperations {
/**
* Setter for {@link BackOffPolicy}.
*
* @param backOffPolicy the {@link BackOffPolicy}
*/
public void setBackOffPolicy(BackOffPolicy backOffPolicy) {
@@ -173,7 +165,6 @@ public class RetryTemplate implements RetryOperations {
/**
* Setter for {@link RetryPolicy}.
*
* @param retryPolicy the {@link RetryPolicy}
*/
public void setRetryPolicy(RetryPolicy retryPolicy) {
@@ -187,7 +178,6 @@ public class RetryTemplate implements RetryOperations {
*
* @see RetryOperations#execute(RetryCallback)
* @param retryCallback the {@link RetryCallback}
*
* @throws TerminatedRetryException if the retry has been manually terminated by a
* listener.
*/
@@ -399,7 +389,6 @@ public class RetryTemplate implements RetryOperations {
* Decide whether to proceed with the ongoing retry attempt. This method is called
* before the {@link RetryCallback} is executed, but after the backoff and open
* interceptors.
*
* @param retryPolicy the policy to apply
* @param context the current retry context
* @return true if we can continue with the attempt
@@ -411,7 +400,6 @@ public class RetryTemplate implements RetryOperations {
/**
* Clean up the cache if necessary and close the context provided (if the flag
* indicates that processing was successful).
*
* @param retryPolicy the {@link RetryPolicy}
* @param context the {@link RetryContext}
* @param state the {@link RetryState}
@@ -459,10 +447,8 @@ public class RetryTemplate implements RetryOperations {
/**
* Delegate to the {@link RetryPolicy} having checked in the cache for an existing
* value if the state is not null.
*
* @param state a {@link RetryState}
* @param retryPolicy a {@link RetryPolicy} to delegate the context creation
*
* @return a retry context, either a new one or the one used last time the same state
* was encountered
*/
@@ -524,7 +510,6 @@ public class RetryTemplate implements RetryOperations {
* Actions to take after final attempt has failed. If there is state clean up the
* cache. If there is a recovery callback, execute that and return its result.
* Otherwise throw an exception.
*
* @param recoveryCallback the callback for recovery (might be null)
* @param context the current retry context
* @param state the {@link RetryState}
@@ -570,7 +555,6 @@ public class RetryTemplate implements RetryOperations {
* Extension point for subclasses to decide on behaviour after catching an exception
* in a {@link RetryCallback}. Normal stateless behaviour is not to rethrow, and if
* there is state we rethrow.
*
* @param retryPolicy the retry policy
* @param context the current context
* @param state the current retryState

View File

@@ -1,6 +1,5 @@
package org.springframework.retry.support;
import java.util.ArrayList;
import java.util.List;
@@ -22,12 +21,11 @@ import org.springframework.retry.policy.TimeoutRetryPolicy;
import org.springframework.util.Assert;
/**
* Fluent API to configure new instance of RetryTemplate.
* For detailed description of each builder method - see it's doc.
* Fluent API to configure new instance of RetryTemplate. For detailed description of each
* builder method - see it's doc.
*
* <p>
* Examples:
* <pre>{@code
* Examples: <pre>{@code
* RetryTemplate.newBuilder()
* .maxAttempts(10)
* .exponentialBackoff(100, 2, 10000)
@@ -50,15 +48,15 @@ import org.springframework.util.Assert;
* <p>
* The builder provides the following defaults:
* <ul>
* <li> retry policy: max attempts = 3 (initial + 2 retries) </li>
* <li> backoff policy: no backoff (retry immediately) </li>
* <li> exception classification: retry only on {@link Exception} and it's subclasses,
* without traversing of causes </li>
* <li>retry policy: max attempts = 3 (initial + 2 retries)</li>
* <li>backoff policy: no backoff (retry immediately)</li>
* <li>exception classification: retry only on {@link Exception} and it's subclasses,
* without traversing of causes</li>
* </ul>
*
* <p>
* The builder supports only widely used properties of {@link RetryTemplate}. More specific
* properties can be configured directly (after building).
* The builder supports only widely used properties of {@link RetryTemplate}. More
* specific properties can be configured directly (after building).
*
* <p>
* Not thread safe. Building should be performed in a single thread. Also, there is no
@@ -72,353 +70,329 @@ import org.springframework.util.Assert;
*/
public class RetryTemplateBuilder {
private RetryPolicy baseRetryPolicy;
private BackOffPolicy backOffPolicy;
private List<RetryListener> listeners;
private BinaryExceptionClassifierBuilder classifierBuilder;
private RetryPolicy baseRetryPolicy;
/* ---------------- Configure retry policy -------------- */
private BackOffPolicy backOffPolicy;
/**
* Limits maximum number of attempts to provided value.
* <p>
* Invocation of this method does not discard default exception classification rule,
* that is "retry only on {@link Exception} and it's subclasses".
*
* @param maxAttempts includes initial attempt and all retries. E.g: maxAttempts = 3 means
* one initial attempt and two retries.
* @see MaxAttemptsRetryPolicy
*/
public RetryTemplateBuilder maxAttempts(int maxAttempts) {
Assert.isTrue( maxAttempts > 0, "Number of attempts should be positive");
Assert.isNull(baseRetryPolicy, "You have already selected another retry policy");
baseRetryPolicy = new MaxAttemptsRetryPolicy(maxAttempts);
return this;
}
private List<RetryListener> listeners;
/**
* Allows retry if there is no more than {@code timeout} millis since first attempt.
* <p>
* Invocation of this method does not discard default exception classification rule,
* that is "retry only on {@link Exception} and it's subclasses".
*
* @param timeout whole execution timeout in milliseconds
* @see TimeoutRetryPolicy
*/
public RetryTemplateBuilder withinMillis(long timeout) {
Assert.isTrue(timeout > 0, "Timeout should be positive");
Assert.isNull(baseRetryPolicy, "You have already selected another retry policy");
TimeoutRetryPolicy timeoutRetryPolicy = new TimeoutRetryPolicy();
timeoutRetryPolicy.setTimeout(timeout);
this.baseRetryPolicy = timeoutRetryPolicy;
return this;
}
private BinaryExceptionClassifierBuilder classifierBuilder;
/**
* Allows infinite retry, do not limit attempts by number or time.
* <p>
* Invocation of this method does not discard default exception classification rule,
* that is "retry only on {@link Exception} and it's subclasses".
*
* @see TimeoutRetryPolicy
*/
public RetryTemplateBuilder infiniteRetry() {
Assert.isNull(baseRetryPolicy, "You have already selected another retry policy");
baseRetryPolicy = new AlwaysRetryPolicy();
return this;
}
/* ---------------- Configure retry policy -------------- */
/**
* If flexibility of this builder is not enough for you, you can provide your own
* {@link RetryPolicy} via this method.
* <p>
* Invocation of this method does not discard default exception classification rule,
* that is "retry only on {@link Exception} and it's subclasses".
*
* @param policy will be directly set to resulting {@link RetryTemplate}
*/
public RetryTemplateBuilder customPolicy(RetryPolicy policy) {
Assert.notNull(policy, "Policy should not be null");
Assert.isNull(baseRetryPolicy, "You have already selected another retry policy");
baseRetryPolicy = policy;
return this;
}
/**
* Limits maximum number of attempts to provided value.
* <p>
* Invocation of this method does not discard default exception classification rule,
* that is "retry only on {@link Exception} and it's subclasses".
* @param maxAttempts includes initial attempt and all retries. E.g: maxAttempts = 3
* means one initial attempt and two retries.
* @see MaxAttemptsRetryPolicy
*/
public RetryTemplateBuilder maxAttempts(int maxAttempts) {
Assert.isTrue(maxAttempts > 0, "Number of attempts should be positive");
Assert.isNull(baseRetryPolicy, "You have already selected another retry policy");
baseRetryPolicy = new MaxAttemptsRetryPolicy(maxAttempts);
return this;
}
/**
* Allows retry if there is no more than {@code timeout} millis since first attempt.
* <p>
* Invocation of this method does not discard default exception classification rule,
* that is "retry only on {@link Exception} and it's subclasses".
* @param timeout whole execution timeout in milliseconds
* @see TimeoutRetryPolicy
*/
public RetryTemplateBuilder withinMillis(long timeout) {
Assert.isTrue(timeout > 0, "Timeout should be positive");
Assert.isNull(baseRetryPolicy, "You have already selected another retry policy");
TimeoutRetryPolicy timeoutRetryPolicy = new TimeoutRetryPolicy();
timeoutRetryPolicy.setTimeout(timeout);
this.baseRetryPolicy = timeoutRetryPolicy;
return this;
}
/* ---------------- Configure backoff policy -------------- */
/**
* Allows infinite retry, do not limit attempts by number or time.
* <p>
* Invocation of this method does not discard default exception classification rule,
* that is "retry only on {@link Exception} and it's subclasses".
*
* @see TimeoutRetryPolicy
*/
public RetryTemplateBuilder infiniteRetry() {
Assert.isNull(baseRetryPolicy, "You have already selected another retry policy");
baseRetryPolicy = new AlwaysRetryPolicy();
return this;
}
/**
* Use exponential backoff policy.
* The formula of backoff period:
* <p>
* {@code currentInterval = Math.min(initialInterval * Math.pow(multiplier, retryNum), maxInterval)}
* <p>
* (for first attempt retryNum = 0)
*
* @param initialInterval in milliseconds
* @param multiplier see the formula above
* @param maxInterval in milliseconds
* @see ExponentialBackOffPolicy
*/
public RetryTemplateBuilder exponentialBackoff(long initialInterval, double multiplier, long maxInterval) {
return exponentialBackoff(initialInterval, multiplier, maxInterval, false);
}
/**
* If flexibility of this builder is not enough for you, you can provide your own
* {@link RetryPolicy} via this method.
* <p>
* Invocation of this method does not discard default exception classification rule,
* that is "retry only on {@link Exception} and it's subclasses".
* @param policy will be directly set to resulting {@link RetryTemplate}
*/
public RetryTemplateBuilder customPolicy(RetryPolicy policy) {
Assert.notNull(policy, "Policy should not be null");
Assert.isNull(baseRetryPolicy, "You have already selected another retry policy");
baseRetryPolicy = policy;
return this;
}
/**
* Use exponential backoff policy.
* The formula of backoff period (without randomness):
* <p>
* {@code currentInterval = Math.min(initialInterval * Math.pow(multiplier, retryNum), maxInterval)}
* <p>
* (for first attempt retryNum = 0)
*
* @param initialInterval in milliseconds
* @param multiplier see the formula above
* @param maxInterval in milliseconds
* @param withRandom adds some randomness to backoff intervals. For details, see
* {@link ExponentialRandomBackOffPolicy}
* @see ExponentialBackOffPolicy
* @see ExponentialRandomBackOffPolicy
*/
public RetryTemplateBuilder exponentialBackoff(long initialInterval, double multiplier, long maxInterval,
boolean withRandom) {
Assert.isNull(backOffPolicy, "You have already selected backoff policy");
Assert.isTrue(initialInterval >= 1, "Initial interval should be >= 1");
Assert.isTrue(multiplier > 1, "Multiplier should be > 1");
Assert.isTrue(maxInterval > initialInterval,
"Max interval should be > than initial interval");
ExponentialBackOffPolicy policy = withRandom
? new ExponentialRandomBackOffPolicy()
: new ExponentialBackOffPolicy();
policy.setInitialInterval(initialInterval);
policy.setMultiplier(multiplier);
policy.setMaxInterval(maxInterval);
backOffPolicy = policy;
return this;
}
/* ---------------- Configure backoff policy -------------- */
/**
* Perform each retry after fixed amount of time.
*
* @param interval fixed interval in milliseconds
* @see FixedBackOffPolicy
*/
public RetryTemplateBuilder fixedBackoff(long interval) {
Assert.isNull(backOffPolicy, "You have already selected backoff policy");
Assert.isTrue(interval >= 1, "Interval should be >= 1");
FixedBackOffPolicy policy = new FixedBackOffPolicy();
policy.setBackOffPeriod(interval);
backOffPolicy = policy;
return this;
}
/**
* Use exponential backoff policy. The formula of backoff period:
* <p>
* {@code currentInterval = Math.min(initialInterval * Math.pow(multiplier, retryNum), maxInterval)}
* <p>
* (for first attempt retryNum = 0)
* @param initialInterval in milliseconds
* @param multiplier see the formula above
* @param maxInterval in milliseconds
* @see ExponentialBackOffPolicy
*/
public RetryTemplateBuilder exponentialBackoff(long initialInterval,
double multiplier, long maxInterval) {
return exponentialBackoff(initialInterval, multiplier, maxInterval, false);
}
/**
* Use {@link UniformRandomBackOffPolicy}, see it's doc for details.
*
* @param minInterval in milliseconds
* @param maxInterval in milliseconds
* @see UniformRandomBackOffPolicy
*/
public RetryTemplateBuilder uniformRandomBackoff(long minInterval, long maxInterval) {
Assert.isNull(backOffPolicy, "You have already selected backoff policy");
Assert.isTrue(minInterval >= 1, "Min interval should be >= 1");
Assert.isTrue(maxInterval >= 1, "Max interval should be >= 1");
Assert.isTrue(maxInterval > minInterval,
"Max interval should be > than min interval");
UniformRandomBackOffPolicy policy = new UniformRandomBackOffPolicy();
policy.setMinBackOffPeriod(minInterval);
policy.setMaxBackOffPeriod(maxInterval);
backOffPolicy = policy;
return this;
}
/**
* Use exponential backoff policy. The formula of backoff period (without randomness):
* <p>
* {@code currentInterval = Math.min(initialInterval * Math.pow(multiplier, retryNum), maxInterval)}
* <p>
* (for first attempt retryNum = 0)
* @param initialInterval in milliseconds
* @param multiplier see the formula above
* @param maxInterval in milliseconds
* @param withRandom adds some randomness to backoff intervals. For details, see
* {@link ExponentialRandomBackOffPolicy}
* @see ExponentialBackOffPolicy
* @see ExponentialRandomBackOffPolicy
*/
public RetryTemplateBuilder exponentialBackoff(long initialInterval,
double multiplier, long maxInterval, boolean withRandom) {
Assert.isNull(backOffPolicy, "You have already selected backoff policy");
Assert.isTrue(initialInterval >= 1, "Initial interval should be >= 1");
Assert.isTrue(multiplier > 1, "Multiplier should be > 1");
Assert.isTrue(maxInterval > initialInterval,
"Max interval should be > than initial interval");
ExponentialBackOffPolicy policy = withRandom
? new ExponentialRandomBackOffPolicy() : new ExponentialBackOffPolicy();
policy.setInitialInterval(initialInterval);
policy.setMultiplier(multiplier);
policy.setMaxInterval(maxInterval);
backOffPolicy = policy;
return this;
}
/**
* Do not pause between attempts, retry immediately.
*
* @see NoBackOffPolicy
*/
public RetryTemplateBuilder noBackoff() {
Assert.isNull(backOffPolicy, "You have already selected backoff policy");
backOffPolicy = new NoBackOffPolicy();
return this;
}
/**
* Perform each retry after fixed amount of time.
* @param interval fixed interval in milliseconds
* @see FixedBackOffPolicy
*/
public RetryTemplateBuilder fixedBackoff(long interval) {
Assert.isNull(backOffPolicy, "You have already selected backoff policy");
Assert.isTrue(interval >= 1, "Interval should be >= 1");
FixedBackOffPolicy policy = new FixedBackOffPolicy();
policy.setBackOffPeriod(interval);
backOffPolicy = policy;
return this;
}
/**
* You can provide your own {@link BackOffPolicy} via this method.
*
* @param backOffPolicy will be directly set to resulting {@link RetryTemplate}
*/
public RetryTemplateBuilder customBackoff(BackOffPolicy backOffPolicy) {
Assert.isNull(this.backOffPolicy, "You have already selected backoff policy");
Assert.notNull(backOffPolicy, "You should provide non null custom policy");
this.backOffPolicy = backOffPolicy;
return this;
}
/**
* Use {@link UniformRandomBackOffPolicy}, see it's doc for details.
* @param minInterval in milliseconds
* @param maxInterval in milliseconds
* @see UniformRandomBackOffPolicy
*/
public RetryTemplateBuilder uniformRandomBackoff(long minInterval, long maxInterval) {
Assert.isNull(backOffPolicy, "You have already selected backoff policy");
Assert.isTrue(minInterval >= 1, "Min interval should be >= 1");
Assert.isTrue(maxInterval >= 1, "Max interval should be >= 1");
Assert.isTrue(maxInterval > minInterval,
"Max interval should be > than min interval");
UniformRandomBackOffPolicy policy = new UniformRandomBackOffPolicy();
policy.setMinBackOffPeriod(minInterval);
policy.setMaxBackOffPeriod(maxInterval);
backOffPolicy = policy;
return this;
}
/**
* Do not pause between attempts, retry immediately.
*
* @see NoBackOffPolicy
*/
public RetryTemplateBuilder noBackoff() {
Assert.isNull(backOffPolicy, "You have already selected backoff policy");
backOffPolicy = new NoBackOffPolicy();
return this;
}
/* ---------------- Configure exception classifier -------------- */
/**
* You can provide your own {@link BackOffPolicy} via this method.
* @param backOffPolicy will be directly set to resulting {@link RetryTemplate}
*/
public RetryTemplateBuilder customBackoff(BackOffPolicy backOffPolicy) {
Assert.isNull(this.backOffPolicy, "You have already selected backoff policy");
Assert.notNull(backOffPolicy, "You should provide non null custom policy");
this.backOffPolicy = backOffPolicy;
return this;
}
/**
* Add a throwable to the while list of retryable exceptions.
* <p>
* Warn: touching this method drops default {@code retryOn(Exception.class)}
* and you should configure whole classifier from scratch.
* <p>
* You should select the way you want to configure exception classifier:
* white list or black list. If you choose white list - use this method,
* if black - use {@link #notRetryOn(Class)}
*
* @param throwable to be retryable (with it's subclasses)
* @see BinaryExceptionClassifierBuilder#retryOn
* @see BinaryExceptionClassifier
*/
public RetryTemplateBuilder retryOn(Class<? extends Throwable> throwable) {
classifierBuilder().retryOn(throwable);
return this;
}
/* ---------------- Configure exception classifier -------------- */
/**
* Add a throwable to the while list of retryable exceptions.
* <p>
* Warn: touching this method drops default {@code retryOn(Exception.class)} and you
* should configure whole classifier from scratch.
* <p>
* You should select the way you want to configure exception classifier: white list or
* black list. If you choose white list - use this method, if black - use
* {@link #notRetryOn(Class)}
* @param throwable to be retryable (with it's subclasses)
* @see BinaryExceptionClassifierBuilder#retryOn
* @see BinaryExceptionClassifier
*/
public RetryTemplateBuilder retryOn(Class<? extends Throwable> throwable) {
classifierBuilder().retryOn(throwable);
return this;
}
/**
* Add a throwable to the black list of retryable exceptions.
* <p>
* Warn: touching this method drops default {@code retryOn(Exception.class)}
* and you should configure whole classifier from scratch.
* <p>
* You should select the way you want to configure exception classifier:
* white list or black list. If you choose black list - use this method,
* if white - use {@link #retryOn(Class)}
*
* @param throwable to be not retryable (with it's subclasses)
* @see BinaryExceptionClassifierBuilder#notRetryOn
* @see BinaryExceptionClassifier
*/
public RetryTemplateBuilder notRetryOn(Class<? extends Throwable> throwable) {
classifierBuilder().notRetryOn(throwable);
return this;
}
/**
* Add a throwable to the black list of retryable exceptions.
* <p>
* Warn: touching this method drops default {@code retryOn(Exception.class)} and you
* should configure whole classifier from scratch.
* <p>
* You should select the way you want to configure exception classifier: white list or
* black list. If you choose black list - use this method, if white - use
* {@link #retryOn(Class)}
* @param throwable to be not retryable (with it's subclasses)
* @see BinaryExceptionClassifierBuilder#notRetryOn
* @see BinaryExceptionClassifier
*/
public RetryTemplateBuilder notRetryOn(Class<? extends Throwable> throwable) {
classifierBuilder().notRetryOn(throwable);
return this;
}
/**
* Suppose throwing a {@code new MyLogicException(new IOException())}.
* This template will not retry on it:
* <pre>{@code
* RetryTemplate.newBuilder()
* .retryOn(IOException.class)
* .build()
* }</pre>
* but this will retry:
* <pre>{@code
* RetryTemplate.newBuilder()
* .retryOn(IOException.class)
* .traversingCauses()
* .build()
* }</pre>
*
* @see BinaryExceptionClassifier
*/
public RetryTemplateBuilder traversingCauses() {
classifierBuilder().traversingCauses();
return this;
}
/**
* Suppose throwing a {@code new MyLogicException(new IOException())}. This template
* will not retry on it: <pre>{@code
* RetryTemplate.newBuilder()
* .retryOn(IOException.class)
* .build()
* }</pre> but this will retry: <pre>{@code
* RetryTemplate.newBuilder()
* .retryOn(IOException.class)
* .traversingCauses()
* .build()
* }</pre>
*
* @see BinaryExceptionClassifier
*/
public RetryTemplateBuilder traversingCauses() {
classifierBuilder().traversingCauses();
return this;
}
/* ---------------- Add listeners -------------- */
/* ---------------- Add listeners -------------- */
/**
* Appends provided {@code listener} to {@link RetryTemplate}'s listener list.
* @param listener to be appended
* @see RetryTemplate
* @see RetryListener
*/
public RetryTemplateBuilder withListener(RetryListener listener) {
Assert.notNull(listener, "Listener should not be null");
listenersList().add(listener);
return this;
}
/**
* Appends provided {@code listener} to {@link RetryTemplate}'s listener list.
*
* @param listener to be appended
* @see RetryTemplate
* @see RetryListener
*/
public RetryTemplateBuilder withListener(RetryListener listener) {
Assert.notNull(listener, "Listener should not be null");
listenersList().add(listener);
return this;
}
/**
* Appends all provided {@code listeners} to {@link RetryTemplate}'s listener list.
* @param listeners to be appended
* @see RetryTemplate
* @see RetryListener
*/
public RetryTemplateBuilder withListeners(List<RetryListener> listeners) {
for (final RetryListener listener : listeners) {
Assert.notNull(listener, "Listener should not be null");
}
listenersList().addAll(listeners);
return this;
}
/**
* Appends all provided {@code listeners} to {@link RetryTemplate}'s listener list.
*
* @param listeners to be appended
* @see RetryTemplate
* @see RetryListener
*/
public RetryTemplateBuilder withListeners(List<RetryListener> listeners) {
for (final RetryListener listener : listeners) {
Assert.notNull(listener, "Listener should not be null");
}
listenersList().addAll(listeners);
return this;
}
/* ---------------- Building -------------- */
/**
* Finish configuration and build resulting {@link RetryTemplate}. For default
* behaviour and concurrency note see class-level doc of {@link RetryTemplateBuilder}.
*
* @implNote The {@code retryPolicy} of the returned {@link RetryTemplate} is always
* an instance of {@link CompositeRetryPolicy}, that consists of one base policy, and
* of {@link BinaryExceptionClassifierRetryPolicy}. The motivation is: whatever base
* policy we use, exception classification is extremely recommended.
* @return new instance of {@link RetryTemplate}
*/
public RetryTemplate build() {
RetryTemplate retryTemplate = new RetryTemplate();
/* ---------------- Building -------------- */
// Exception classifier
/**
* Finish configuration and build resulting {@link RetryTemplate}.
* For default behaviour and concurrency note see class-level doc of {@link RetryTemplateBuilder}.
*
* @implNote
* The {@code retryPolicy} of the returned {@link RetryTemplate} is always
* an instance of {@link CompositeRetryPolicy}, that consists of one base
* policy, and of {@link BinaryExceptionClassifierRetryPolicy}.
* The motivation is: whatever base policy we use, exception classification
* is extremely recommended.
*
* @return new instance of {@link RetryTemplate}
*/
public RetryTemplate build() {
RetryTemplate retryTemplate = new RetryTemplate();
BinaryExceptionClassifier exceptionClassifier = classifierBuilder != null
? classifierBuilder.build()
: BinaryExceptionClassifier.newDefaultClassifier();
// Exception classifier
// Retry policy
BinaryExceptionClassifier exceptionClassifier = classifierBuilder != null
? classifierBuilder.build()
: BinaryExceptionClassifier.newDefaultClassifier();
if (baseRetryPolicy == null) {
baseRetryPolicy = new MaxAttemptsRetryPolicy();
}
// Retry policy
CompositeRetryPolicy finalPolicy = new CompositeRetryPolicy();
finalPolicy.setPolicies(new RetryPolicy[] { baseRetryPolicy,
new BinaryExceptionClassifierRetryPolicy(exceptionClassifier) });
retryTemplate.setRetryPolicy(finalPolicy);
if (baseRetryPolicy == null) {
baseRetryPolicy = new MaxAttemptsRetryPolicy();
}
// Backoff policy
CompositeRetryPolicy finalPolicy = new CompositeRetryPolicy();
finalPolicy.setPolicies(new RetryPolicy[] {
baseRetryPolicy,
new BinaryExceptionClassifierRetryPolicy(exceptionClassifier)
});
retryTemplate.setRetryPolicy(finalPolicy);
if (backOffPolicy == null) {
backOffPolicy = new NoBackOffPolicy();
}
retryTemplate.setBackOffPolicy(backOffPolicy);
// Backoff policy
// Listeners
if (backOffPolicy == null) {
backOffPolicy = new NoBackOffPolicy();
}
retryTemplate.setBackOffPolicy(backOffPolicy);
if (listeners != null) {
retryTemplate.setListeners(listeners.toArray(new RetryListener[0]));
}
// Listeners
return retryTemplate;
}
if (listeners != null) {
retryTemplate.setListeners(listeners.toArray(new RetryListener[0]));
}
/* ---------------- Private utils -------------- */
return retryTemplate;
}
private BinaryExceptionClassifierBuilder classifierBuilder() {
if (classifierBuilder == null) {
classifierBuilder = new BinaryExceptionClassifierBuilder();
}
return classifierBuilder;
}
private List<RetryListener> listenersList() {
if (listeners == null) {
listeners = new ArrayList<RetryListener>();
}
return listeners;
}
/* ---------------- Private utils -------------- */
private BinaryExceptionClassifierBuilder classifierBuilder() {
if (classifierBuilder == null) {
classifierBuilder = new BinaryExceptionClassifierBuilder();
}
return classifierBuilder;
}
private List<RetryListener> listenersList() {
if (listeners == null) {
listeners = new ArrayList<RetryListener>();
}
return listeners;
}
}

View File

@@ -26,67 +26,59 @@ import org.junit.Test;
import org.springframework.retry.support.RetryTemplate;
/**
* @author Aleksandr Shamukov
* @author Aleksandr Shamukov
*/
public class BinaryExceptionClassifierBuilderTest {
@Test
public void testWhiteList() {
RetryTemplate.newBuilder()
.infiniteRetry()
.retryOn(IOException.class)
.uniformRandomBackoff(1000, 3000)
.build();
@Test
public void testWhiteList() {
RetryTemplate.newBuilder().infiniteRetry().retryOn(IOException.class)
.uniformRandomBackoff(1000, 3000).build();
BinaryExceptionClassifier classifier = BinaryExceptionClassifier.newBuilder()
.retryOn(IOException.class)
.retryOn(TimeoutException.class)
.build();
BinaryExceptionClassifier classifier = BinaryExceptionClassifier.newBuilder()
.retryOn(IOException.class).retryOn(TimeoutException.class).build();
Assert.assertTrue(classifier.classify(new IOException()));
// should not retry due to traverseCauses=fasle
Assert.assertFalse(classifier.classify(new RuntimeException(new IOException())));
Assert.assertTrue(classifier.classify(new StreamCorruptedException()));
Assert.assertFalse(classifier.classify(new OutOfMemoryError()));
}
Assert.assertTrue(classifier.classify(new IOException()));
// should not retry due to traverseCauses=fasle
Assert.assertFalse(classifier.classify(new RuntimeException(new IOException())));
Assert.assertTrue(classifier.classify(new StreamCorruptedException()));
Assert.assertFalse(classifier.classify(new OutOfMemoryError()));
}
@Test
public void testWhiteListWithTraverseCauses() {
BinaryExceptionClassifier classifier = BinaryExceptionClassifier.newBuilder()
.retryOn(IOException.class)
.retryOn(TimeoutException.class)
.traversingCauses()
.build();
@Test
public void testWhiteListWithTraverseCauses() {
BinaryExceptionClassifier classifier = BinaryExceptionClassifier.newBuilder()
.retryOn(IOException.class).retryOn(TimeoutException.class)
.traversingCauses().build();
Assert.assertTrue(classifier.classify(new IOException()));
// should retry due to traverseCauses=true
Assert.assertTrue(classifier.classify(new RuntimeException(new IOException())));
Assert.assertTrue(classifier.classify(new StreamCorruptedException()));
// should retry due to FileNotFoundException is a subclass of TimeoutException
Assert.assertTrue(classifier.classify(new FileNotFoundException()));
Assert.assertFalse(classifier.classify(new RuntimeException()));
}
Assert.assertTrue(classifier.classify(new IOException()));
// should retry due to traverseCauses=true
Assert.assertTrue(classifier.classify(new RuntimeException(new IOException())));
Assert.assertTrue(classifier.classify(new StreamCorruptedException()));
// should retry due to FileNotFoundException is a subclass of TimeoutException
Assert.assertTrue(classifier.classify(new FileNotFoundException()));
Assert.assertFalse(classifier.classify(new RuntimeException()));
}
@Test
public void testBlackList() {
BinaryExceptionClassifier classifier = BinaryExceptionClassifier.newBuilder()
.notRetryOn(Error.class)
.notRetryOn(InterruptedException.class)
.traversingCauses()
.build();
@Test
public void testBlackList() {
BinaryExceptionClassifier classifier = BinaryExceptionClassifier.newBuilder()
.notRetryOn(Error.class).notRetryOn(InterruptedException.class)
.traversingCauses().build();
// should not retry due to OutOfMemoryError is a subclass of Error
Assert.assertFalse(classifier.classify(new OutOfMemoryError()));
Assert.assertFalse(classifier.classify(new InterruptedException()));
Assert.assertTrue(classifier.classify(new Throwable()));
// should retry due to traverseCauses=true
Assert.assertFalse(classifier.classify(new RuntimeException(new InterruptedException())));
}
// should not retry due to OutOfMemoryError is a subclass of Error
Assert.assertFalse(classifier.classify(new OutOfMemoryError()));
Assert.assertFalse(classifier.classify(new InterruptedException()));
Assert.assertTrue(classifier.classify(new Throwable()));
// should retry due to traverseCauses=true
Assert.assertFalse(
classifier.classify(new RuntimeException(new InterruptedException())));
}
@Test(expected = IllegalArgumentException.class)
public void testFailOnNotationMix() {
BinaryExceptionClassifier.newBuilder().retryOn(IOException.class)
.notRetryOn(OutOfMemoryError.class);
}
@Test(expected = IllegalArgumentException.class)
public void testFailOnNotationMix() {
BinaryExceptionClassifier.newBuilder()
.retryOn(IOException.class)
.notRetryOn(OutOfMemoryError.class);
}
}

View File

@@ -56,26 +56,31 @@ public class BinaryExceptionClassifierTests {
@Test
public void testClassifyExactMatch() {
Collection<Class<? extends Throwable>> set = Collections
.<Class<? extends Throwable>> singleton(IllegalStateException.class);
assertTrue(new BinaryExceptionClassifier(set).classify(new IllegalStateException("Foo")));
.<Class<? extends Throwable>>singleton(IllegalStateException.class);
assertTrue(new BinaryExceptionClassifier(set)
.classify(new IllegalStateException("Foo")));
}
@Test
public void testClassifyExactMatchInCause() {
Collection<Class<? extends Throwable>> set = Collections
.<Class<? extends Throwable>> singleton(IllegalStateException.class);
BinaryExceptionClassifier binaryExceptionClassifier = new BinaryExceptionClassifier(set);
.<Class<? extends Throwable>>singleton(IllegalStateException.class);
BinaryExceptionClassifier binaryExceptionClassifier = new BinaryExceptionClassifier(
set);
binaryExceptionClassifier.setTraverseCauses(true);
assertTrue(binaryExceptionClassifier.classify(new RuntimeException(new IllegalStateException("Foo"))));
assertTrue(binaryExceptionClassifier
.classify(new RuntimeException(new IllegalStateException("Foo"))));
}
@Test
public void testClassifySubclassMatchInCause() {
Collection<Class<? extends Throwable>> set = Collections
.<Class<? extends Throwable>> singleton(IllegalStateException.class);
BinaryExceptionClassifier binaryExceptionClassifier = new BinaryExceptionClassifier(set);
.<Class<? extends Throwable>>singleton(IllegalStateException.class);
BinaryExceptionClassifier binaryExceptionClassifier = new BinaryExceptionClassifier(
set);
binaryExceptionClassifier.setTraverseCauses(true);
assertTrue(binaryExceptionClassifier.classify(new RuntimeException(new FooException("Foo"))));
assertTrue(binaryExceptionClassifier
.classify(new RuntimeException(new FooException("Foo"))));
}
@Test
@@ -83,33 +88,38 @@ public class BinaryExceptionClassifierTests {
Map<Class<? extends Throwable>, Boolean> map = new HashMap<Class<? extends Throwable>, Boolean>();
map.put(IllegalStateException.class, true);
map.put(BarException.class, false);
BinaryExceptionClassifier binaryExceptionClassifier = new BinaryExceptionClassifier(map, true);
BinaryExceptionClassifier binaryExceptionClassifier = new BinaryExceptionClassifier(
map, true);
binaryExceptionClassifier.setTraverseCauses(true);
assertTrue(binaryExceptionClassifier.classify(new RuntimeException(new FooException("Foo", new BarException()))));
assertTrue(((Map<?, ?>) new DirectFieldAccessor(binaryExceptionClassifier).getPropertyValue("classified"))
.containsKey(FooException.class));
assertTrue(binaryExceptionClassifier.classify(
new RuntimeException(new FooException("Foo", new BarException()))));
assertTrue(((Map<?, ?>) new DirectFieldAccessor(binaryExceptionClassifier)
.getPropertyValue("classified")).containsKey(FooException.class));
}
@Test
public void testTypesProvidedInConstructor() {
classifier = new BinaryExceptionClassifier(Collections
.<Class<? extends Throwable>> singleton(IllegalStateException.class));
.<Class<? extends Throwable>>singleton(IllegalStateException.class));
assertTrue(classifier.classify(new IllegalStateException("Foo")));
}
@Test
public void testTypesProvidedInConstructorWithNonDefault() {
classifier = new BinaryExceptionClassifier(Collections
.<Class<? extends Throwable>> singleton(IllegalStateException.class), false);
.<Class<? extends Throwable>>singleton(IllegalStateException.class),
false);
assertFalse(classifier.classify(new IllegalStateException("Foo")));
}
@Test
public void testTypesProvidedInConstructorWithNonDefaultInCause() {
classifier = new BinaryExceptionClassifier(Collections
.<Class<? extends Throwable>> singleton(IllegalStateException.class), false);
.<Class<? extends Throwable>>singleton(IllegalStateException.class),
false);
classifier.setTraverseCauses(true);
assertFalse(classifier.classify(new RuntimeException(new RuntimeException(new IllegalStateException("Foo")))));
assertFalse(classifier.classify(new RuntimeException(
new RuntimeException(new IllegalStateException("Foo")))));
}
@SuppressWarnings("serial")
@@ -133,4 +143,5 @@ public class BinaryExceptionClassifierTests {
}
}
}

View File

@@ -23,7 +23,7 @@ import static org.junit.Assert.assertEquals;
/**
* @author Dave Syer
*
*
*/
public class ClassifierAdapterTests {
@@ -68,9 +68,11 @@ public class ClassifierAdapterTests {
public Integer getValue(String key) {
return Integer.parseInt(key);
}
@SuppressWarnings("unused")
public void doNothing(String key) {
}
@SuppressWarnings("unused")
public String doNothing(String key, int value) {
return "foo";
@@ -102,7 +104,7 @@ public class ClassifierAdapterTests {
assertEquals(23, adapter.classify("23").intValue());
}
@Test(expected=IllegalArgumentException.class)
@Test(expected = IllegalArgumentException.class)
public void testClassifyWithWrongType() {
adapter.setDelegate(new Object() {
@Classifier
@@ -116,11 +118,12 @@ public class ClassifierAdapterTests {
@SuppressWarnings("serial")
@Test
public void testClassifyWithClassifier() {
adapter.setDelegate(new org.springframework.classify.Classifier<String, Integer>() {
public Integer classify(String classifiable) {
return Integer.valueOf(classifiable);
}
});
adapter.setDelegate(
new org.springframework.classify.Classifier<String, Integer>() {
public Integer classify(String classifiable) {
return Integer.valueOf(classifiable);
}
});
assertEquals(23, adapter.classify("23").intValue());
}

View File

@@ -24,14 +24,17 @@ public class ClassifierSupportTests {
@Test
public void testClassifyNullIsDefault() {
ClassifierSupport<String,String> classifier = new ClassifierSupport<String,String>("foo");
ClassifierSupport<String, String> classifier = new ClassifierSupport<String, String>(
"foo");
assertEquals(classifier.classify(null), "foo");
}
@Test
public void testClassifyRandomException() {
ClassifierSupport<Throwable,String> classifier = new ClassifierSupport<Throwable,String>("foo");
assertEquals(classifier.classify(new IllegalStateException("Foo")), classifier.classify(null));
ClassifierSupport<Throwable, String> classifier = new ClassifierSupport<Throwable, String>(
"foo");
assertEquals(classifier.classify(new IllegalStateException("Foo")),
classifier.classify(null));
}
}

View File

@@ -26,7 +26,7 @@ import org.springframework.classify.PatternMatchingClassifier;
/**
* @author Dave Syer
*
*
*/
public class PatternMatchingClassifierTests {
@@ -52,7 +52,7 @@ public class PatternMatchingClassifierTests {
public void testCreateFromMap() {
classifier = new PatternMatchingClassifier<String>(map);
assertEquals("bar", classifier.classify("foo"));
assertEquals("spam", classifier.classify("bucket"));
assertEquals("spam", classifier.classify("bucket"));
}
}

View File

@@ -55,23 +55,27 @@ public class SubclassExceptionClassifierTests {
@Test
public void testClassifyExactMatch() {
classifier.setTypeMap(Collections.<Class<? extends Throwable>, String> singletonMap(
IllegalStateException.class, "foo"));
classifier
.setTypeMap(Collections.<Class<? extends Throwable>, String>singletonMap(
IllegalStateException.class, "foo"));
assertEquals("foo", classifier.classify(new IllegalStateException("Foo")));
}
@Test
public void testClassifySubclassMatch() {
classifier.setTypeMap(Collections.<Class<? extends Throwable>, String> singletonMap(RuntimeException.class,
"foo"));
classifier
.setTypeMap(Collections.<Class<? extends Throwable>, String>singletonMap(
RuntimeException.class, "foo"));
assertEquals("foo", classifier.classify(new IllegalStateException("Foo")));
}
@Test
public void testClassifySuperclassDoesNotMatch() {
classifier.setTypeMap(Collections.<Class<? extends Throwable>, String> singletonMap(
IllegalStateException.class, "foo"));
assertEquals(classifier.getDefault(), classifier.classify(new RuntimeException("Foo")));
classifier
.setTypeMap(Collections.<Class<? extends Throwable>, String>singletonMap(
IllegalStateException.class, "foo"));
assertEquals(classifier.getDefault(),
classifier.classify(new RuntimeException("Foo")));
}
@SuppressWarnings("serial")
@@ -108,4 +112,5 @@ public class SubclassExceptionClassifierTests {
public static class SubConnectException extends ConnectException {
}
}

View File

@@ -37,4 +37,5 @@ public abstract class AbstractExceptionTests {
public abstract Exception getException(String msg) throws Exception;
public abstract Exception getException(String msg, Throwable t) throws Exception;
}

View File

@@ -25,10 +25,10 @@ import org.junit.rules.ExpectedException;
*
*/
public class AnyThrowTests {
@Rule
public ExpectedException expected = ExpectedException.none();
@Test
public void testRuntimeException() throws Throwable {
expected.expect(RuntimeException.class);
@@ -46,7 +46,7 @@ public class AnyThrowTests {
expected.expect(Exception.class);
AnyThrow.throwAny(new Exception("planned"));
}
private static class AnyThrow {
private static void throwUnchecked(Throwable e) {
@@ -55,8 +55,9 @@ public class AnyThrowTests {
@SuppressWarnings("unchecked")
private static <E extends Throwable> void throwAny(Throwable e) throws E {
throw (E)e;
throw (E) e;
}
}
}

View File

@@ -31,4 +31,5 @@ public class BackOffInterruptedExceptionTests extends AbstractExceptionTests {
public void testNothing() throws Exception {
// fool coverage tools...
}
}

View File

@@ -31,4 +31,5 @@ public class ExhaustedRetryExceptionTests extends AbstractExceptionTests {
public void testNothing() throws Exception {
// fool coverage tools...
}
}

View File

@@ -11,12 +11,14 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
@SuppressWarnings("serial")
public class ResourcelessTransactionManager extends AbstractPlatformTransactionManager {
protected void doBegin(Object transaction, TransactionDefinition definition) throws TransactionException {
protected void doBegin(Object transaction, TransactionDefinition definition)
throws TransactionException {
((ResourcelessTransaction) transaction).begin();
}
protected void doCommit(DefaultTransactionStatus status) throws TransactionException {
logger.debug("Committing resourceless transaction on [" + status.getTransaction() + "]");
logger.debug("Committing resourceless transaction on [" + status.getTransaction()
+ "]");
}
protected Object doGetTransaction() throws TransactionException {
@@ -28,32 +30,39 @@ public class ResourcelessTransactionManager extends AbstractPlatformTransactionM
}
else {
@SuppressWarnings("unchecked")
Stack<Object> stack = (Stack<Object>) TransactionSynchronizationManager.getResource(this);
Stack<Object> stack = (Stack<Object>) TransactionSynchronizationManager
.getResource(this);
resources = stack;
}
resources.push(transaction);
return transaction;
}
protected void doRollback(DefaultTransactionStatus status) throws TransactionException {
logger.debug("Rolling back resourceless transaction on [" + status.getTransaction() + "]");
protected void doRollback(DefaultTransactionStatus status)
throws TransactionException {
logger.debug("Rolling back resourceless transaction on ["
+ status.getTransaction() + "]");
}
protected boolean isExistingTransaction(Object transaction) throws TransactionException {
protected boolean isExistingTransaction(Object transaction)
throws TransactionException {
if (TransactionSynchronizationManager.hasResource(this)) {
@SuppressWarnings("unchecked")
Stack<Object> stack = (Stack<Object>) TransactionSynchronizationManager.getResource(this);
Stack<Object> stack = (Stack<Object>) TransactionSynchronizationManager
.getResource(this);
return stack.size() > 1;
}
return ((ResourcelessTransaction) transaction).isActive();
}
protected void doSetRollbackOnly(DefaultTransactionStatus status) throws TransactionException {
protected void doSetRollbackOnly(DefaultTransactionStatus status)
throws TransactionException {
}
protected void doCleanupAfterCompletion(Object transaction) {
@SuppressWarnings("unchecked")
Stack<Object> list = (Stack<Object>) TransactionSynchronizationManager.getResource(this);
Stack<Object> list = (Stack<Object>) TransactionSynchronizationManager
.getResource(this);
Stack<Object> resources = list;
resources.clear();
TransactionSynchronizationManager.unbindResource(this);

View File

@@ -31,4 +31,5 @@ public class RetryExceptionTests extends AbstractExceptionTests {
public void testNothing() throws Exception {
// fool coverage tools...
}
}

View File

@@ -31,4 +31,5 @@ public class TerminatedRetryExceptionTests extends AbstractExceptionTests {
public void testNothing() throws Exception {
// fool coverage tools...
}
}

View File

@@ -56,21 +56,24 @@ public class CircuitBreakerTests {
}
catch (Exception e) {
}
assertFalse((Boolean)service.getContext().getAttribute(CircuitBreakerRetryPolicy.CIRCUIT_OPEN));
assertFalse((Boolean) service.getContext()
.getAttribute(CircuitBreakerRetryPolicy.CIRCUIT_OPEN));
try {
service.service();
fail("Expected exception");
}
catch (Exception e) {
}
assertFalse((Boolean)service.getContext().getAttribute(CircuitBreakerRetryPolicy.CIRCUIT_OPEN));
assertFalse((Boolean) service.getContext()
.getAttribute(CircuitBreakerRetryPolicy.CIRCUIT_OPEN));
try {
service.service();
fail("Expected exception");
}
catch (Exception e) {
}
assertTrue((Boolean)service.getContext().getAttribute(CircuitBreakerRetryPolicy.CIRCUIT_OPEN));
assertTrue((Boolean) service.getContext()
.getAttribute(CircuitBreakerRetryPolicy.CIRCUIT_OPEN));
assertEquals(3, service.getCount());
try {
service.service();
@@ -84,17 +87,21 @@ public class CircuitBreakerTests {
assertEquals(4, service.getCount());
Advised advised = (Advised) service;
Advisor advisor = advised.getAdvisors()[0];
Map<?, ?> delegates = (Map<?, ?>) new DirectFieldAccessor(advisor).getPropertyValue("advice.delegates");
Map<?, ?> delegates = (Map<?, ?>) new DirectFieldAccessor(advisor)
.getPropertyValue("advice.delegates");
assertTrue(delegates.size() == 1);
Map<?, ?> methodMap = (Map<?, ?>) delegates.values().iterator().next();
MethodInterceptor interceptor = (MethodInterceptor) methodMap
.get(Service.class.getDeclaredMethod("expressionService"));
DirectFieldAccessor accessor = new DirectFieldAccessor(interceptor);
assertEquals(8, accessor.getPropertyValue("retryOperations.retryPolicy.delegate.maxAttempts")) ;
assertEquals(19000L, accessor.getPropertyValue("retryOperations.retryPolicy.openTimeout")) ;
assertEquals(20000L, accessor.getPropertyValue("retryOperations.retryPolicy.resetTimeout")) ;
assertEquals("#root instanceof RuntimeExpression",
accessor.getPropertyValue("retryOperations.retryPolicy.delegate.expression.expression"));
assertEquals(8, accessor
.getPropertyValue("retryOperations.retryPolicy.delegate.maxAttempts"));
assertEquals(19000L,
accessor.getPropertyValue("retryOperations.retryPolicy.openTimeout"));
assertEquals(20000L,
accessor.getPropertyValue("retryOperations.retryPolicy.resetTimeout"));
assertEquals("#root instanceof RuntimeExpression", accessor.getPropertyValue(
"retryOperations.retryPolicy.delegate.expression.expression"));
context.close();
}
@@ -123,15 +130,11 @@ public class CircuitBreakerTests {
}
}
@CircuitBreaker(maxAttemptsExpression = "#{2 * ${foo:4}}",
openTimeoutExpression = "#{${bar:19}000}",
resetTimeoutExpression = "#{${baz:20}000}",
exceptionExpression = "#{#root instanceof RuntimeExpression}")
@CircuitBreaker(maxAttemptsExpression = "#{2 * ${foo:4}}", openTimeoutExpression = "#{${bar:19}000}", resetTimeoutExpression = "#{${baz:20}000}", exceptionExpression = "#{#root instanceof RuntimeExpression}")
public void expressionService() {
this.count++;
}
public RetryContext getContext() {
return this.context;
}

View File

@@ -190,7 +190,8 @@ public class EnableRetryTests {
@Test
public void testImplementation() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TestConfiguration.class);
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
TestConfiguration.class);
NotAnnotatedInterface service = context.getBean(NotAnnotatedInterface.class);
service.service1();
service.service2();
@@ -198,7 +199,6 @@ public class EnableRetryTests {
context.close();
}
@Test
public void testExpression() throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
@@ -243,7 +243,7 @@ public class EnableRetryTests {
return target;
}
try {
return target(((Advised)target).getTargetSource().getTarget());
return target(((Advised) target).getTargetSource().getTarget());
}
catch (Exception e) {
throw new IllegalStateException(e);
@@ -469,6 +469,7 @@ public class EnableRetryTests {
protected static class ExcludesOnlyService {
private int count = 0;
private RuntimeException exceptionToThrow;
@Retryable(exclude = IllegalStateException.class)
@@ -485,6 +486,7 @@ public class EnableRetryTests {
public void setExceptionToThrow(RuntimeException exceptionToThrow) {
this.exceptionToThrow = exceptionToThrow;
}
}
protected static class StatefulService {
@@ -636,5 +638,4 @@ public class EnableRetryTests {
}
}

View File

@@ -42,8 +42,8 @@ public class EnableRetryWithBackoffTests {
TestConfiguration.class);
Service service = context.getBean(Service.class);
service.service();
assertEquals("[1000, 1000]", context.getBean(PeriodSleeper.class)
.getPeriods().toString());
assertEquals("[1000, 1000]",
context.getBean(PeriodSleeper.class).getPeriods().toString());
assertEquals(3, service.getCount());
context.close();
}
@@ -67,8 +67,8 @@ public class EnableRetryWithBackoffTests {
ExponentialService service = context.getBean(ExponentialService.class);
service.service();
assertEquals(3, service.getCount());
assertEquals("[1000, 1100]", context.getBean(PeriodSleeper.class)
.getPeriods().toString());
assertEquals("[1000, 1100]",
context.getBean(PeriodSleeper.class).getPeriods().toString());
context.close();
}
@@ -81,11 +81,11 @@ public class EnableRetryWithBackoffTests {
service.service(1);
assertEquals(3, service.getCount());
List<Long> periods = context.getBean(PeriodSleeper.class).getPeriods();
assertNotEquals("[1000, 1100]", context.getBean(PeriodSleeper.class)
.getPeriods().toString());
assertNotEquals("[1000, 1100]",
context.getBean(PeriodSleeper.class).getPeriods().toString());
assertTrue("Wrong periods: " + periods, periods.get(0) > 1000);
assertTrue("Wrong periods: " + periods, periods.get(1) > 1100
&& periods.get(1) < 1210);
assertTrue("Wrong periods: " + periods,
periods.get(1) > 1100 && periods.get(1) < 1210);
context.close();
}
@@ -204,4 +204,5 @@ public class EnableRetryWithBackoffTests {
}
}
}

View File

@@ -47,7 +47,8 @@ public class EnableRetryWithListenersTests {
public void overrideListener() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
TestConfigurationMultipleListeners.class);
ServiceWithOverriddenListener service = context.getBean(ServiceWithOverriddenListener.class);
ServiceWithOverriddenListener service = context
.getBean(ServiceWithOverriddenListener.class);
service.service();
assertEquals(1, context.getBean(TestConfigurationMultipleListeners.class).count1);
assertEquals(0, context.getBean(TestConfigurationMultipleListeners.class).count2);
@@ -83,6 +84,7 @@ public class EnableRetryWithListenersTests {
protected static class TestConfigurationMultipleListeners {
private int count1 = 0;
private int count2 = 0;
@Bean
@@ -95,7 +97,7 @@ public class EnableRetryWithListenersTests {
return new RetryListenerSupport() {
@Override
public <T, E extends Throwable> void close(RetryContext context,
RetryCallback<T, E> callback, Throwable throwable) {
RetryCallback<T, E> callback, Throwable throwable) {
count1++;
}
};
@@ -106,7 +108,7 @@ public class EnableRetryWithListenersTests {
return new RetryListenerSupport() {
@Override
public <T, E extends Throwable> void close(RetryContext context,
RetryCallback<T, E> callback, Throwable throwable) {
RetryCallback<T, E> callback, Throwable throwable) {
count2++;
}
};

View File

@@ -39,10 +39,10 @@ public class RecoverAnnotationRecoveryHandlerTests {
@Test
public void defaultRecoverMethod() {
RecoverAnnotationRecoveryHandler<?> handler = new RecoverAnnotationRecoveryHandler<Integer>(
new DefaultRecover(), ReflectionUtils.findMethod(DefaultRecover.class,
"foo", String.class));
assertEquals(1,
handler.recover(new Object[] { "Dave" }, new RuntimeException("Planned")));
new DefaultRecover(),
ReflectionUtils.findMethod(DefaultRecover.class, "foo", String.class));
assertEquals(1, handler.recover(new Object[] { "Dave" },
new RuntimeException("Planned")));
}
@Test
@@ -50,8 +50,8 @@ public class RecoverAnnotationRecoveryHandlerTests {
RecoverAnnotationRecoveryHandler<?> handler = new RecoverAnnotationRecoveryHandler<Integer>(
new FewerArgs(), ReflectionUtils.findMethod(FewerArgs.class, "foo",
String.class, int.class));
assertEquals(1,
handler.recover(new Object[] { "Dave" }, new RuntimeException("Planned")));
assertEquals(1, handler.recover(new Object[] { "Dave" },
new RuntimeException("Planned")));
}
@Test
@@ -66,8 +66,8 @@ public class RecoverAnnotationRecoveryHandlerTests {
@Test
public void noMatch() {
RecoverAnnotationRecoveryHandler<?> handler = new RecoverAnnotationRecoveryHandler<Integer>(
new SpecificException(), ReflectionUtils.findMethod(
SpecificException.class, "foo", String.class));
new SpecificException(),
ReflectionUtils.findMethod(SpecificException.class, "foo", String.class));
expected.expect(ExhaustedRetryException.class);
handler.recover(new Object[] { "Dave" }, new Error("Planned"));
}
@@ -75,60 +75,60 @@ public class RecoverAnnotationRecoveryHandlerTests {
@Test
public void specificRecoverMethod() {
RecoverAnnotationRecoveryHandler<?> handler = new RecoverAnnotationRecoveryHandler<Integer>(
new SpecificRecover(), ReflectionUtils.findMethod(SpecificRecover.class,
"foo", String.class));
assertEquals(2,
handler.recover(new Object[] { "Dave" }, new RuntimeException("Planned")));
new SpecificRecover(),
ReflectionUtils.findMethod(SpecificRecover.class, "foo", String.class));
assertEquals(2, handler.recover(new Object[] { "Dave" },
new RuntimeException("Planned")));
}
@Test
public void inAccessibleRecoverMethods (){
Method foo = ReflectionUtils.findMethod(InAccessibleRecover.class,
"foo", String.class);
public void inAccessibleRecoverMethods() {
Method foo = ReflectionUtils.findMethod(InAccessibleRecover.class, "foo",
String.class);
RecoverAnnotationRecoveryHandler<?> handler = new RecoverAnnotationRecoveryHandler<Integer>(
new InAccessibleRecover(), foo);
assertEquals(1,
handler.recover(new Object[] { "Dave" }, new RuntimeException("Planned")));
assertEquals(1, handler.recover(new Object[] { "Dave" },
new RuntimeException("Planned")));
}
@Test
public void specificReturnTypeRecoverMethod() {
RecoverAnnotationRecoveryHandler<?> fooHandler = new RecoverAnnotationRecoveryHandler<Integer>(
new InheritanceReturnTypeRecover(), ReflectionUtils.findMethod(InheritanceReturnTypeRecover.class,
"foo", String.class));
assertEquals(1,
fooHandler.recover(new Object[] { "Aldo" }, new RuntimeException("Planned")));
assertEquals(2,
fooHandler.recover(new Object[] { "Aldo" }, new IllegalStateException("Planned")));
new InheritanceReturnTypeRecover(), ReflectionUtils.findMethod(
InheritanceReturnTypeRecover.class, "foo", String.class));
assertEquals(1, fooHandler.recover(new Object[] { "Aldo" },
new RuntimeException("Planned")));
assertEquals(2, fooHandler.recover(new Object[] { "Aldo" },
new IllegalStateException("Planned")));
}
@Test
public void parentReturnTypeRecoverMethod() {
RecoverAnnotationRecoveryHandler<?> barHandler = new RecoverAnnotationRecoveryHandler<Double>(
new InheritanceReturnTypeRecover(), ReflectionUtils.findMethod(InheritanceReturnTypeRecover.class,
"bar", String.class));
assertEquals(3,
barHandler.recover(new Object[] { "Aldo" }, new RuntimeException("Planned")));
new InheritanceReturnTypeRecover(), ReflectionUtils.findMethod(
InheritanceReturnTypeRecover.class, "bar", String.class));
assertEquals(3, barHandler.recover(new Object[] { "Aldo" },
new RuntimeException("Planned")));
}
@Test
public void multipleQualifyingRecoverMethods(){
Method foo = ReflectionUtils.findMethod(MultipleQualifyingRecovers.class,
"foo", String.class);
public void multipleQualifyingRecoverMethods() {
Method foo = ReflectionUtils.findMethod(MultipleQualifyingRecovers.class, "foo",
String.class);
RecoverAnnotationRecoveryHandler<?> handler = new RecoverAnnotationRecoveryHandler<Integer>(
new MultipleQualifyingRecovers(), foo);
assertEquals(1,
handler.recover(new Object[] { "Randell" }, new RuntimeException("Planned")));
assertEquals(1, handler.recover(new Object[] { "Randell" },
new RuntimeException("Planned")));
}
@Test
public void multipleQualifyingRecoverMethodsWithNull(){
Method foo = ReflectionUtils.findMethod(MultipleQualifyingRecovers.class,
"foo", String.class);
public void multipleQualifyingRecoverMethodsWithNull() {
Method foo = ReflectionUtils.findMethod(MultipleQualifyingRecovers.class, "foo",
String.class);
RecoverAnnotationRecoveryHandler<?> handler = new RecoverAnnotationRecoveryHandler<Integer>(
new MultipleQualifyingRecovers(), foo);
assertEquals(1,
@@ -137,40 +137,41 @@ public class RecoverAnnotationRecoveryHandlerTests {
}
@Test
public void multipleQualifyingRecoverMethodsReOrdered(){
public void multipleQualifyingRecoverMethodsReOrdered() {
Method foo = ReflectionUtils.findMethod(MultipleQualifyingRecoversReOrdered.class,
"foo", String.class);
RecoverAnnotationRecoveryHandler<?> handler = new RecoverAnnotationRecoveryHandler<Integer>(
new MultipleQualifyingRecoversReOrdered(), foo);
assertEquals(3,
handler.recover(new Object[] { "Randell" }, new RuntimeException("Planned")));
assertEquals(3, handler.recover(new Object[] { "Randell" },
new RuntimeException("Planned")));
}
@Test
public void multipleQualifyingRecoverMethodsExtendsThrowable(){
Method foo = ReflectionUtils.findMethod(MultipleQualifyingRecoversExtendsThrowable.class,
"foo", String.class);
@Test
public void multipleQualifyingRecoverMethodsExtendsThrowable() {
Method foo = ReflectionUtils.findMethod(
MultipleQualifyingRecoversExtendsThrowable.class, "foo", String.class);
RecoverAnnotationRecoveryHandler<?> handler = new RecoverAnnotationRecoveryHandler<Integer>(
new MultipleQualifyingRecoversExtendsThrowable(), foo);
assertEquals(2,
handler.recover(new Object[] { "Kevin" }, new IllegalArgumentException("Planned")));
assertEquals(3,
handler.recover(new Object[] { "Kevin" }, new UnsupportedOperationException("Planned")));
assertEquals(2, handler.recover(new Object[] { "Kevin" },
new IllegalArgumentException("Planned")));
assertEquals(3, handler.recover(new Object[] { "Kevin" },
new UnsupportedOperationException("Planned")));
}
private static class InAccessibleRecover {
@Retryable
private int foo (String n) {
private int foo(String n) {
throw new RuntimeException("error trying to foo('" + n + "')");
}
@Recover
private int bar(String n){
return 1 ;
private int bar(String n) {
return 1;
}
}
protected static class DefaultRecover {
@@ -184,9 +185,11 @@ public class RecoverAnnotationRecoveryHandlerTests {
public int bar(String name) {
return 1;
}
}
protected static class NoArgs {
private Throwable cause;
@Retryable
@@ -201,9 +204,11 @@ public class RecoverAnnotationRecoveryHandlerTests {
public Throwable getCause() {
return cause;
}
}
protected static class SpecificRecover {
@Retryable
public int foo(String name) {
return 0;
@@ -222,6 +227,7 @@ public class RecoverAnnotationRecoveryHandlerTests {
}
protected static class FewerArgs {
@Retryable
public int foo(String name, int value) {
return 0;
@@ -235,6 +241,7 @@ public class RecoverAnnotationRecoveryHandlerTests {
}
protected static class SpecificException {
@Retryable
public int foo(String name) {
return 0;
@@ -323,8 +330,8 @@ public class RecoverAnnotationRecoveryHandlerTests {
}
}
protected static class MultipleQualifyingRecoversExtendsThrowable {
protected static class MultipleQualifyingRecoversExtendsThrowable {
@Retryable
public int foo(String name) {
@@ -335,8 +342,8 @@ public class RecoverAnnotationRecoveryHandlerTests {
public int fooRecover(IllegalArgumentException e, String name) {
return 1;
}
@Recover
@Recover
public int barRecover(IllegalArgumentException e, String name) {
return 2;
}

View File

@@ -21,9 +21,9 @@ import java.util.List;
/**
* Simple {@link Sleeper} implementation that just waits on a local Object.
*
*
* @author Dave Syer
*
*
*/
@SuppressWarnings("serial")
public class DummySleeper implements Sleeper {
@@ -35,21 +35,22 @@ public class DummySleeper implements Sleeper {
* @return the lastBackOff
*/
public long getLastBackOff() {
return backOffs.get(backOffs.size()-1).longValue();
return backOffs.get(backOffs.size() - 1).longValue();
}
public long[] getBackOffs() {
long[] result = new long[backOffs.size()];
int i = 0;
for (Iterator<Long> iterator = backOffs.iterator(); iterator.hasNext();) {
Long value = iterator.next();
result[i++] =value.longValue();
result[i++] = value.longValue();
}
return result ;
return result;
}
/*
* (non-Javadoc)
*
* @see org.springframework.batch.retry.backoff.Sleeper#sleep(long)
*/
public void sleep(long backOffPeriod) throws InterruptedException {

View File

@@ -63,7 +63,8 @@ public class ExponentialBackOffPolicyTests {
strategy.setSleeper(sleeper);
BackOffContext context = strategy.start(null);
strategy.backOff(context);
assertEquals(ExponentialBackOffPolicy.DEFAULT_INITIAL_INTERVAL, sleeper.getLastBackOff());
assertEquals(ExponentialBackOffPolicy.DEFAULT_INITIAL_INTERVAL,
sleeper.getLastBackOff());
}
@Test

View File

@@ -32,53 +32,60 @@ import org.springframework.retry.support.RetrySimulator;
*
*/
public class ExponentialRandomBackOffPolicyTests {
static final int NUM_TRIALS = 10000;
static final int MAX_RETRIES = 6;
private ExponentialBackOffPolicy makeBackoffPolicy() {
ExponentialBackOffPolicy policy = new ExponentialRandomBackOffPolicy();
policy.setInitialInterval(50);
policy.setMultiplier(2.0);
policy.setMaxInterval(3000);
return policy;
}
static final int NUM_TRIALS = 10000;
static final int MAX_RETRIES = 6;
private SimpleRetryPolicy makeRetryPolicy() {
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
retryPolicy.setMaxAttempts(MAX_RETRIES);
return retryPolicy;
}
private ExponentialBackOffPolicy makeBackoffPolicy() {
ExponentialBackOffPolicy policy = new ExponentialRandomBackOffPolicy();
policy.setInitialInterval(50);
policy.setMultiplier(2.0);
policy.setMaxInterval(3000);
return policy;
}
private SimpleRetryPolicy makeRetryPolicy() {
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
retryPolicy.setMaxAttempts(MAX_RETRIES);
return retryPolicy;
}
@Test
public void testSingleBackoff() throws Exception {
ExponentialBackOffPolicy backOffPolicy = makeBackoffPolicy();
RetrySimulator simulator = new RetrySimulator(backOffPolicy, makeRetryPolicy());
RetrySimulation simulation = simulator.executeSimulation(1);
public void testSingleBackoff() throws Exception {
ExponentialBackOffPolicy backOffPolicy = makeBackoffPolicy();
RetrySimulator simulator = new RetrySimulator(backOffPolicy, makeRetryPolicy());
RetrySimulation simulation = simulator.executeSimulation(1);
List<Long> sleeps = simulation.getLongestTotalSleepSequence().getSleeps();
System.out.println("Single trial of " + backOffPolicy + ": sleeps=" + sleeps);
assertEquals(MAX_RETRIES - 1, sleeps.size());
long initialInterval = backOffPolicy.getInitialInterval();
for (int i=0; i<sleeps.size(); i++) {
long expectedMaxValue = 2*(long) (initialInterval + initialInterval * Math.max(1, Math.pow(backOffPolicy.getMultiplier(), i)));
assertTrue("Found a sleep [" + sleeps.get(i) + "] which exceeds our max expected value of " + expectedMaxValue + " at interval " + i,
sleeps.get(i) < expectedMaxValue);
}
}
List<Long> sleeps = simulation.getLongestTotalSleepSequence().getSleeps();
System.out.println("Single trial of " + backOffPolicy + ": sleeps=" + sleeps);
assertEquals(MAX_RETRIES - 1, sleeps.size());
long initialInterval = backOffPolicy.getInitialInterval();
for (int i = 0; i < sleeps.size(); i++) {
long expectedMaxValue = 2 * (long) (initialInterval + initialInterval
* Math.max(1, Math.pow(backOffPolicy.getMultiplier(), i)));
assertTrue(
"Found a sleep [" + sleeps.get(i)
+ "] which exceeds our max expected value of "
+ expectedMaxValue + " at interval " + i,
sleeps.get(i) < expectedMaxValue);
}
}
@Test
public void testMultiBackOff() throws Exception {
ExponentialBackOffPolicy backOffPolicy = makeBackoffPolicy();
RetrySimulator simulator = new RetrySimulator(backOffPolicy, makeRetryPolicy());
RetrySimulation simulation = simulator.executeSimulation(NUM_TRIALS);
ExponentialBackOffPolicy backOffPolicy = makeBackoffPolicy();
RetrySimulator simulator = new RetrySimulator(backOffPolicy, makeRetryPolicy());
RetrySimulation simulation = simulator.executeSimulation(NUM_TRIALS);
System.out.println("Ran " + NUM_TRIALS + " backoff trials. Each trial retried " + MAX_RETRIES + " times");
System.out.println("Policy: " + backOffPolicy);
System.out.println("All generated backoffs:");
System.out.println(" " + simulation.getPercentiles());
System.out.println("Ran " + NUM_TRIALS + " backoff trials. Each trial retried "
+ MAX_RETRIES + " times");
System.out.println("Policy: " + backOffPolicy);
System.out.println("All generated backoffs:");
System.out.println(" " + simulation.getPercentiles());
System.out.println("Backoff frequencies:");
System.out.print(" " + simulation.getPercentiles());
System.out.println("Backoff frequencies:");
System.out.print(" " + simulation.getPercentiles());
}
}

Some files were not shown because too many files have changed in this diff Show More