Add Serializable to all retry contexts and policies
Any time they get cached in a more advanced cache than the default map-based one, the serializability is useful (probably mandatory for most distribued caches, even if that is probably overkill).
This commit is contained in:
@@ -130,7 +130,7 @@ In the simplest case, a retry is just a while loop: the `RetryTemplate` can just
|
||||
|
||||
Where the failure has caused a transactional resource to become invalid, there are some special considerations. This does not apply to a simple remote call because there is no transactional resource (usually), but it does sometimes apply to a database update, especially when using Hibernate. In this case it only makes sense to rethrow the exception that called the failure immediately so that the transaction can roll back and we can start a new valid one.
|
||||
|
||||
In these cases a stateless retry is not good enough because the re-throw and roll back necessarily involve leaving the `RetryOperations.execute()` method and potentially losing the context that was on the stack. To avoid losing it we have to introduce a storage strategy to lift it off the stack and put it (at a minimum) in heap storage. For this purpose Spring Retry provides a storage strategy `RetryContextCache` which can be injected into the `RetryTemplate`. The default implementation of the `RetryContextCache` is in memory, using a simple `Map. Advanced usage with multiple processes in a clustered environment might also consider implementing the `RetryContextCache` with a cluster cache of some sort (though, even in a clustered environment this might be overkill).
|
||||
In these cases a stateless retry is not good enough because the re-throw and roll back necessarily involve leaving the `RetryOperations.execute()` method and potentially losing the context that was on the stack. To avoid losing it we have to introduce a storage strategy to lift it off the stack and put it (at a minimum) in heap storage. For this purpose Spring Retry provides a storage strategy `RetryContextCache` which can be injected into the `RetryTemplate`. The default implementation of the `RetryContextCache` is in memory, using a simple `Map`. It has a strictly enforced maximum capacity, to avoid memory leaks, but it doesn't have any advanced cache features like time to live. You should consider injecting a `Map` that had those features if you need them. Advanced usage with multiple processes in a clustered environment might also consider implementing the `RetryContextCache` with a cluster cache of some sort (though, even in a clustered environment this might be overkill).
|
||||
|
||||
Part of the responsibility of the `RetryOperations` is to recognize the failed operations when they come back in a new execution (and usually wrapped in a new transaction). To facilitate this, Spring Retry provides the `RetryState` abstraction. This works in conjunction with a special execute methods in the `RetryOperations`.
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.classify;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -32,7 +33,8 @@ import java.util.Map;
|
||||
* @author Gary Russell
|
||||
*
|
||||
*/
|
||||
public class BinaryExceptionClassifier extends SubclassClassifier<Throwable, Boolean> {
|
||||
@SuppressWarnings("serial")
|
||||
public class BinaryExceptionClassifier extends SubclassClassifier<Throwable, Boolean> implements Serializable {
|
||||
|
||||
private boolean traverseCauses;
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
package org.springframework.classify;
|
||||
|
||||
/**
|
||||
* Interface for a classifier. At its simplest a {@link Classifier} is just a
|
||||
* map from objects of one type to objects of another type.
|
||||
* Interface for a classifier. At its simplest a {@link Classifier} is just a map from
|
||||
* objects of one type to objects of another type.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
@@ -26,12 +26,12 @@ package org.springframework.classify;
|
||||
public interface Classifier<C, T> {
|
||||
|
||||
/**
|
||||
* Classify the given object and return an object of a different type,
|
||||
* possibly an enumerated type.
|
||||
* 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.
|
||||
* @return an object. Can be null, but implementations should declare if this is the
|
||||
* case.
|
||||
*/
|
||||
T classify(C classifiable);
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.classify;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Base class for {@link Classifier} implementations. Provides default behaviour
|
||||
* and some convenience members, like constants.
|
||||
@@ -23,7 +25,8 @@ package org.springframework.classify;
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class ClassifierSupport<C, T> implements Classifier<C, T> {
|
||||
@SuppressWarnings("serial")
|
||||
public class ClassifierSupport<C, T> implements Classifier<C, T>, Serializable {
|
||||
|
||||
final private T defaultValue;
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ import java.util.concurrent.ConcurrentMap;
|
||||
* @author Gary Russell
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class SubclassClassifier<T, C> implements Classifier<T, C> {
|
||||
|
||||
private ConcurrentMap<Class<? extends T>, C> classified = new ConcurrentHashMap<Class<? extends T>, C>();
|
||||
@@ -144,7 +145,6 @@ public class SubclassClassifier<T, C> implements Classifier<T, C> {
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
private static class ClassComparator implements Comparator<Class<?>>, Serializable {
|
||||
/**
|
||||
* @return 1 if arg0 is assignable from arg1, -1 otherwise
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
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
|
||||
@@ -27,7 +29,7 @@ package org.springframework.retry;
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public interface RetryPolicy {
|
||||
public interface RetryPolicy extends Serializable {
|
||||
|
||||
/**
|
||||
* @param context the current retry status
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.springframework.retry.RetryPolicy;
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class AlwaysRetryPolicy extends NeverRetryPolicy {
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.retry.RetryContext;
|
||||
import org.springframework.retry.RetryPolicy;
|
||||
import org.springframework.retry.context.RetryContextSupport;
|
||||
@@ -28,6 +29,7 @@ import org.springframework.retry.context.RetryContextSupport;
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class CircuitBreakerRetryPolicy implements RetryPolicy {
|
||||
|
||||
public static final String CIRCUIT_OPEN = "circuit.open";
|
||||
@@ -39,6 +41,10 @@ public class CircuitBreakerRetryPolicy implements RetryPolicy {
|
||||
private final RetryPolicy delegate;
|
||||
private long resetTimeout = 20000;
|
||||
private long openTimeout = 5000;
|
||||
|
||||
public CircuitBreakerRetryPolicy() {
|
||||
this(new SimpleRetryPolicy());
|
||||
}
|
||||
|
||||
public CircuitBreakerRetryPolicy(RetryPolicy delegate) {
|
||||
this.delegate = delegate;
|
||||
@@ -97,7 +103,6 @@ public class CircuitBreakerRetryPolicy implements RetryPolicy {
|
||||
this.delegate.registerThrowable(circuit.context, throwable);
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
static class CircuitBreakerRetryContext extends RetryContextSupport {
|
||||
private volatile RetryContext context;
|
||||
private final RetryPolicy policy;
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.springframework.retry.context.RetryContextSupport;
|
||||
* @author Michael Minella
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class CompositeRetryPolicy implements RetryPolicy {
|
||||
|
||||
RetryPolicy[] policies = new RetryPolicy[0];
|
||||
@@ -129,7 +130,7 @@ public class CompositeRetryPolicy implements RetryPolicy {
|
||||
for (RetryPolicy policy : this.policies) {
|
||||
list.add(policy.open(parent));
|
||||
}
|
||||
return new CompositeRetryContext(parent, list);
|
||||
return new CompositeRetryContext(parent, list, this.policies);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -148,16 +149,15 @@ public class CompositeRetryPolicy implements RetryPolicy {
|
||||
((RetryContextSupport) context).registerThrowable(throwable);
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private class CompositeRetryContext extends RetryContextSupport {
|
||||
private static class CompositeRetryContext extends RetryContextSupport {
|
||||
RetryContext[] contexts;
|
||||
|
||||
RetryPolicy[] policies;
|
||||
|
||||
public CompositeRetryContext(RetryContext parent, List<RetryContext> contexts) {
|
||||
public CompositeRetryContext(RetryContext parent, List<RetryContext> contexts, RetryPolicy[] policies) {
|
||||
super(parent);
|
||||
this.contexts = contexts.toArray(new RetryContext[contexts.size()]);
|
||||
this.policies = CompositeRetryPolicy.this.policies;
|
||||
this.policies = policies;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.springframework.util.Assert;
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class ExceptionClassifierRetryPolicy implements RetryPolicy {
|
||||
|
||||
private Classifier<Throwable, RetryPolicy> exceptionClassifier = new ClassifierSupport<Throwable, RetryPolicy>(new NeverRetryPolicy());
|
||||
@@ -102,7 +103,6 @@ public class ExceptionClassifierRetryPolicy implements RetryPolicy {
|
||||
((RetryContextSupport) context).registerThrowable(throwable);
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static class ExceptionClassifierRetryContext extends RetryContextSupport implements RetryPolicy {
|
||||
|
||||
final private Classifier<Throwable, RetryPolicy> exceptionClassifier;
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.springframework.retry.context.RetryContextSupport;
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class NeverRetryPolicy implements RetryPolicy {
|
||||
|
||||
/**
|
||||
@@ -80,7 +81,6 @@ public class NeverRetryPolicy implements RetryPolicy {
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
private static class NeverRetryContext extends RetryContextSupport {
|
||||
private boolean finished = false;
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ import org.springframework.util.ClassUtils;
|
||||
* @author Gary Russell
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class SimpleRetryPolicy implements RetryPolicy {
|
||||
|
||||
/**
|
||||
@@ -182,7 +183,6 @@ public class SimpleRetryPolicy implements RetryPolicy {
|
||||
return new SimpleRetryContext(parent);
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static class SimpleRetryContext extends RetryContextSupport {
|
||||
public SimpleRetryContext(RetryContext parent) {
|
||||
super(parent);
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.springframework.retry.context.RetryContextSupport;
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class TimeoutRetryPolicy implements RetryPolicy {
|
||||
|
||||
/**
|
||||
@@ -75,7 +76,6 @@ public class TimeoutRetryPolicy implements RetryPolicy {
|
||||
// otherwise no-op - we only time out, otherwise retry everything...
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static class TimeoutRetryContext extends RetryContextSupport {
|
||||
private long timeout;
|
||||
|
||||
|
||||
@@ -15,11 +15,12 @@
|
||||
*/
|
||||
package org.springframework.classify;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.classify.annotation.Classifier;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
|
||||
@@ -50,6 +50,7 @@ public class CompositeRetryPolicyTests {
|
||||
assertTrue(policy.canRetry(context));
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@Test
|
||||
public void testNonTrivialPolicies() throws Exception {
|
||||
CompositeRetryPolicy policy = new CompositeRetryPolicy();
|
||||
@@ -63,6 +64,7 @@ public class CompositeRetryPolicyTests {
|
||||
assertFalse(policy.canRetry(context));
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@Test
|
||||
public void testNonTrivialPoliciesWithThrowable() throws Exception {
|
||||
CompositeRetryPolicy policy = new CompositeRetryPolicy();
|
||||
@@ -84,6 +86,7 @@ public class CompositeRetryPolicyTests {
|
||||
assertFalse("Should be still able to retry", policy.canRetry(context));
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@Test
|
||||
public void testNonTrivialPoliciesClose() throws Exception {
|
||||
final List<String> list = new ArrayList<String>();
|
||||
@@ -103,6 +106,7 @@ public class CompositeRetryPolicyTests {
|
||||
assertEquals(2, list.size());
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@Test
|
||||
public void testExceptionOnPoliciesClose() throws Exception {
|
||||
final List<String> list = new ArrayList<String>();
|
||||
@@ -150,6 +154,7 @@ public class CompositeRetryPolicyTests {
|
||||
assertSame(context, child.getParent());
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@Test
|
||||
public void testOptimistic() throws Exception {
|
||||
CompositeRetryPolicy policy = new CompositeRetryPolicy();
|
||||
|
||||
@@ -103,6 +103,7 @@ public class ExceptionClassifierRetryPolicyTests {
|
||||
|
||||
int count = 0;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@Test
|
||||
public void testClose() throws Exception {
|
||||
policy.setExceptionClassifier(new Classifier<Throwable, RetryPolicy>() {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.retry.policy;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class MockRetryPolicySupport extends AlwaysRetryPolicy {
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.retry.policy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
|
||||
import org.springframework.core.type.filter.AssignableTypeFilter;
|
||||
import org.springframework.core.type.filter.RegexPatternTypeFilter;
|
||||
import org.springframework.retry.RetryContext;
|
||||
import org.springframework.retry.RetryPolicy;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.SerializationUtils;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@RunWith(Parameterized.class)
|
||||
public class RetryContextSerializationTests {
|
||||
|
||||
private static Log logger = LogFactory.getLog(RetryContextSerializationTests.class);
|
||||
|
||||
private RetryPolicy policy;
|
||||
|
||||
@Parameters
|
||||
public static List<Object[]> policies() {
|
||||
List<Object[]> result = new ArrayList<Object[]>();
|
||||
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(true);
|
||||
scanner.addIncludeFilter(new AssignableTypeFilter(RetryPolicy.class));
|
||||
scanner.addExcludeFilter(new RegexPatternTypeFilter(Pattern.compile(".*Test.*")));
|
||||
scanner.addExcludeFilter(new RegexPatternTypeFilter(Pattern.compile(".*Mock.*")));
|
||||
Set<BeanDefinition> candidates = scanner.findCandidateComponents("org.springframework.retry.policy");
|
||||
for (BeanDefinition beanDefinition : candidates) {
|
||||
try {
|
||||
result.add(new Object[] { BeanUtils.instantiate(ClassUtils.resolveClassName(beanDefinition.getBeanClassName(), null)) });
|
||||
} catch (Exception e) {
|
||||
logger.warn("Cannot create instance of " + beanDefinition.getBeanClassName());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public RetryContextSerializationTests(RetryPolicy policy) {
|
||||
this.policy = policy;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSerializationCycleForContext() {
|
||||
assertTrue(SerializationUtils.deserialize(
|
||||
SerializationUtils.serialize(policy.open(null))) instanceof RetryContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSerializationCycleForPolicy() {
|
||||
assertTrue(SerializationUtils.deserialize(
|
||||
SerializationUtils.serialize(policy)) instanceof RetryPolicy);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -294,6 +294,7 @@ public class RetryTemplateTests {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@Test
|
||||
public void testFailedPolicy() throws Throwable {
|
||||
RetryTemplate retryTemplate = new RetryTemplate();
|
||||
|
||||
Reference in New Issue
Block a user