From 1a54a3e1c2df5826dd9c4deb2380c4b9b7f11fdb Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Mon, 12 May 2014 12:24:08 +0300 Subject: [PATCH] Code Cleanup * Deprecate `ObjectWaitSleeper` and replace it with `ThreadWaitSleeper` * Improve `SimpleMethodInvoker` * Use `if (logger.isDebugEnabled())` for better performance The `RetryTemplate` continues to invoke `canRetry(retryPolicy, context)` with retry loop, because some end application may rely on that logic. Although it looks like overhead to call `canRetry()` twice a retry: it might be heavy operation, e.g. check the state of external system Fixes gh-10 --- .gitignore | 5 +- .../classify/util/MethodInvokerUtils.java | 26 +-- .../classify/util/SimpleMethodInvoker.java | 112 ++++++------- .../backoff/ExponentialBackOffPolicy.java | 17 +- .../retry/backoff/FixedBackOffPolicy.java | 11 +- .../retry/backoff/ObjectWaitSleeper.java | 6 +- .../retry/backoff/ThreadWaitSleeper.java | 32 ++++ .../backoff/UniformRandomBackOffPolicy.java | 14 +- .../retry/context/RetryContextSupport.java | 21 +-- .../RetryOperationsInterceptor.java | 39 +++-- .../retry/interceptor/Retryable.java | 5 +- .../StatefulRetryOperationsInterceptor.java | 94 +++++------ .../retry/policy/CompositeRetryPolicy.java | 16 +- .../ExceptionClassifierRetryPolicy.java | 29 ++-- .../retry/support/RetryTemplate.java | 149 +++++++++--------- ...Tests.java => ThreadWaitSleeperTests.java} | 7 +- 16 files changed, 306 insertions(+), 277 deletions(-) create mode 100644 src/main/java/org/springframework/retry/backoff/ThreadWaitSleeper.java rename src/test/java/org/springframework/retry/backoff/{ObjectWaitSleeperTests.java => ThreadWaitSleeperTests.java} (87%) diff --git a/.gitignore b/.gitignore index 33e7ef2..e0953d8 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,7 @@ target .settings .classpath .project - +*.iml +*.ipr +*.iws +.idea diff --git a/src/main/java/org/springframework/classify/util/MethodInvokerUtils.java b/src/main/java/org/springframework/classify/util/MethodInvokerUtils.java index d0b5721..1c8fbce 100644 --- a/src/main/java/org/springframework/classify/util/MethodInvokerUtils.java +++ b/src/main/java/org/springframework/classify/util/MethodInvokerUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2014 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. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.classify.util; import java.lang.annotation.Annotation; @@ -30,15 +31,16 @@ import org.springframework.util.ReflectionUtils; /** * Utility methods for create MethodInvoker instances. - * + * * @author Lucas Ward - * @since 2.0 + * @author Artem Bilan + * @since 1.1 */ 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 @@ -65,12 +67,12 @@ public class MethodInvokerUtils { /** * Create a String representation of the array of parameter types. - * - * @param paramTypes - * @return String + * + * @param paramTypes the types of parameters + * @return the paramTypes as String representation */ public static String getParamTypesString(Class... paramTypes) { - StringBuffer paramTypesList = new StringBuffer("("); + StringBuilder paramTypesList = new StringBuilder("("); for (int i = 0; i < paramTypes.length; i++) { paramTypesList.append(paramTypes[i].getSimpleName()); if (i + 1 < paramTypes.length) { @@ -83,7 +85,7 @@ 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 @@ -104,7 +106,7 @@ 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 @@ -144,7 +146,7 @@ public class MethodInvokerUtils { * 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. @@ -185,7 +187,7 @@ 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 */ diff --git a/src/main/java/org/springframework/classify/util/SimpleMethodInvoker.java b/src/main/java/org/springframework/classify/util/SimpleMethodInvoker.java index bb37a5c..db1b934 100644 --- a/src/main/java/org/springframework/classify/util/SimpleMethodInvoker.java +++ b/src/main/java/org/springframework/classify/util/SimpleMethodInvoker.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2014 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. @@ -14,21 +14,6 @@ * limitations under the License. */ -/* - * Copyright 2002-2008 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.classify.util; import java.lang.reflect.Method; @@ -43,90 +28,92 @@ import org.springframework.util.ClassUtils; * 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 - * @since 2.0 + * @author Artem Bilan + * @since 1.1 */ public class SimpleMethodInvoker implements MethodInvoker { private final Object object; - private Method method; + private final Method method; + + private final Class[] parameterTypes; + + private volatile Object target; public SimpleMethodInvoker(Object object, Method method) { Assert.notNull(object, "Object to invoke must not be null"); Assert.notNull(method, "Method to invoke must not be null"); this.method = method; + method.setAccessible(true); this.object = object; + this.parameterTypes = method.getParameterTypes(); } public SimpleMethodInvoker(Object object, String methodName, Class... paramTypes) { Assert.notNull(object, "Object to invoke must not be null"); - this.method = ClassUtils.getMethodIfAvailable(object.getClass(), methodName, paramTypes); - if (this.method == null) { + Method method = ClassUtils.getMethodIfAvailable(object.getClass(), methodName, paramTypes); + if (method == null) { // try with no params - this.method = ClassUtils.getMethodIfAvailable(object.getClass(), methodName, new Class[] {}); - } - if (this.method == null) { - throw new IllegalArgumentException("No methods found for name: [" + methodName + "] in class: [" - + object.getClass() + "] with arguments of type: [" + Arrays.toString(paramTypes) + "]"); + 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) + "]"); + this.object = object; + this.method = method; + method.setAccessible(true); + this.parameterTypes = method.getParameterTypes(); } /* * (non-Javadoc) - * + * * @see * org.springframework.batch.core.configuration.util.MethodInvoker#invokeMethod * (java.lang.Object[]) */ + @Override public Object invokeMethod(Object... args) { - - Class[] parameterTypes = method.getParameterTypes(); - Object[] invokeArgs; - if (parameterTypes.length == 0) { - invokeArgs = new Object[] {}; - } - else if (parameterTypes.length != args.length) { - throw new IllegalArgumentException("Wrong number of arguments, expected no more than: [" - + parameterTypes.length + "]"); - } - else { - invokeArgs = args; - } - - method.setAccessible(true); + Assert.state(this.parameterTypes.length == args.length, + "Wrong number of arguments, expected no more than: [" + this.parameterTypes.length + "]"); try { // Extract the target from an Advised as late as possible // in case it contains a lazy initialization - Object target = extractTarget(object, method); - return method.invoke(target, invokeArgs); + Object target = extractTarget(this.object, this.method); + return method.invoke(target, args); } catch (Exception e) { - throw new IllegalArgumentException("Unable to invoke method: [" + method + "] on object: [" + 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); } } private Object extractTarget(Object target, Method method) { - if (target instanceof Advised) { - Object source; - try { - source = ((Advised) target).getTargetSource().getTarget(); - } - catch (Exception e) { - throw new IllegalStateException("Could not extract target from proxy", e); - } - if (source instanceof Advised) { - source = extractTarget(source, method); - } - if (method.getDeclaringClass().isAssignableFrom(source.getClass())) { - target = source; + if (this.target == null) { + if (target instanceof Advised) { + Object source; + try { + source = ((Advised) target).getTargetSource().getTarget(); + } + catch (Exception e) { + throw new IllegalStateException("Could not extract target from proxy", e); + } + if (source instanceof Advised) { + source = extractTarget(source, method); + } + if (method.getDeclaringClass().isAssignableFrom(source.getClass())) { + target = source; + } } + this.target = target; + } - return target; + return this.target; } @Override @@ -145,8 +132,9 @@ public class SimpleMethodInvoker implements MethodInvoker { @Override public int hashCode() { int result = 25; - result = 31 * result + object.hashCode(); - result = 31 * result + method.hashCode(); + result = 31 * result + this.object.hashCode(); + result = 31 * result + this.method.hashCode(); return result; } + } diff --git a/src/main/java/org/springframework/retry/backoff/ExponentialBackOffPolicy.java b/src/main/java/org/springframework/retry/backoff/ExponentialBackOffPolicy.java index 21d0ffc..977b9d7 100644 --- a/src/main/java/org/springframework/retry/backoff/ExponentialBackOffPolicy.java +++ b/src/main/java/org/springframework/retry/backoff/ExponentialBackOffPolicy.java @@ -1,5 +1,5 @@ /* - * Copyright 2006-2012 the original author or authors. + * Copyright 2006-2014 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. @@ -34,10 +34,11 @@ import org.springframework.util.ClassUtils; * passed to {@link Math#exp(double)} and the {@link #setMultiplier(double)} * property controls by how much this value is increased for each subsequent * attempt. - * + * * @author Rob Harrop * @author Dave Syer * @author Gary Russell + * @author Artem Bilan */ public class ExponentialBackOffPolicy implements SleepingBackOffPolicy { @@ -74,11 +75,11 @@ public class ExponentialBackOffPolicy implements SleepingBackOffPolicy maxInterval) { - sleep = (long) maxInterval; + sleep = maxInterval; } else { this.interval = getNextInterval(); diff --git a/src/main/java/org/springframework/retry/backoff/FixedBackOffPolicy.java b/src/main/java/org/springframework/retry/backoff/FixedBackOffPolicy.java index 942b1c6..74104e1 100644 --- a/src/main/java/org/springframework/retry/backoff/FixedBackOffPolicy.java +++ b/src/main/java/org/springframework/retry/backoff/FixedBackOffPolicy.java @@ -1,5 +1,5 @@ /* - * Copyright 2006-2007 the original author or authors. + * Copyright 2006-2014 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. @@ -23,8 +23,10 @@ package org.springframework.retry.backoff; * {@link #setBackOffPeriod(long)} is thread-safe and it is safe to call * {@link #setBackOffPeriod} 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 + * @author Artem Bilan */ public class FixedBackOffPolicy extends StatelessBackOffPolicy implements SleepingBackOffPolicy { @@ -39,7 +41,7 @@ public class FixedBackOffPolicy extends StatelessBackOffPolicy implements */ private volatile long backOffPeriod = DEFAULT_BACK_OFF_PERIOD; - private Sleeper sleeper = new ObjectWaitSleeper(); + private Sleeper sleeper = new ThreadWaitSleeper(); public FixedBackOffPolicy withSleeper(Sleeper sleeper) { FixedBackOffPolicy res = new FixedBackOffPolicy(); @@ -50,7 +52,7 @@ public class FixedBackOffPolicy extends StatelessBackOffPolicy implements /** * Public setter for the {@link Sleeper} strategy. - * @param sleeper the sleeper to set defaults to {@link ObjectWaitSleeper}. + * @param sleeper the sleeper to set defaults to {@link ThreadWaitSleeper}. */ public void setSleeper(Sleeper sleeper) { this.sleeper = sleeper; @@ -78,7 +80,8 @@ public class FixedBackOffPolicy extends StatelessBackOffPolicy implements protected void doBackOff() throws BackOffInterruptedException { try { sleeper.sleep(backOffPeriod); - } catch (InterruptedException e) { + } + catch (InterruptedException e) { throw new BackOffInterruptedException("Thread interrupted while sleeping", e); } } diff --git a/src/main/java/org/springframework/retry/backoff/ObjectWaitSleeper.java b/src/main/java/org/springframework/retry/backoff/ObjectWaitSleeper.java index 2821c17..208ee05 100644 --- a/src/main/java/org/springframework/retry/backoff/ObjectWaitSleeper.java +++ b/src/main/java/org/springframework/retry/backoff/ObjectWaitSleeper.java @@ -17,10 +17,12 @@ 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} + * * @author Dave Syer - * + * */ +@Deprecated public class ObjectWaitSleeper implements Sleeper { /* diff --git a/src/main/java/org/springframework/retry/backoff/ThreadWaitSleeper.java b/src/main/java/org/springframework/retry/backoff/ThreadWaitSleeper.java new file mode 100644 index 0000000..322ab62 --- /dev/null +++ b/src/main/java/org/springframework/retry/backoff/ThreadWaitSleeper.java @@ -0,0 +1,32 @@ +/* + * Copyright 2014 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.backoff; + +/** + * Simple {@link Sleeper} implementation that just blocks the current Thread with sleep period. + * + * @author Artem Bilan + * @since 1.1 + */ +public class ThreadWaitSleeper implements Sleeper { + + @Override + public void sleep(long backOffPeriod) throws InterruptedException { + Thread.sleep(backOffPeriod); + } + +} diff --git a/src/main/java/org/springframework/retry/backoff/UniformRandomBackOffPolicy.java b/src/main/java/org/springframework/retry/backoff/UniformRandomBackOffPolicy.java index 5005ae5..6386689 100644 --- a/src/main/java/org/springframework/retry/backoff/UniformRandomBackOffPolicy.java +++ b/src/main/java/org/springframework/retry/backoff/UniformRandomBackOffPolicy.java @@ -1,5 +1,5 @@ /* - * Copyright 2006-2007 the original author or authors. + * Copyright 2006-2014 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. @@ -22,10 +22,12 @@ 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)}. - *

{@link #setMinBackOffPeriod(long)} is thread-safe and it is safe to call - * {@link #setBackOffPeriod} during execution from multiple threads, however + *

+ * {@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. + * * @author Rob Harrop * @author Dave Syer */ @@ -46,8 +48,8 @@ public class UniformRandomBackOffPolicy extends StatelessBackOffPolicy implement private volatile long maxBackOffPeriod = DEFAULT_BACK_OFF_MAX_PERIOD; private Random random = new Random(System.currentTimeMillis()); - - private Sleeper sleeper = new ObjectWaitSleeper(); + + private Sleeper sleeper = new ThreadWaitSleeper(); public UniformRandomBackOffPolicy withSleeper(Sleeper sleeper) { UniformRandomBackOffPolicy res = new UniformRandomBackOffPolicy(); @@ -58,7 +60,7 @@ public class UniformRandomBackOffPolicy extends StatelessBackOffPolicy implement /** * Public setter for the {@link Sleeper} strategy. - * @param sleeper the sleeper to set defaults to {@link ObjectWaitSleeper}. + * @param sleeper the sleeper to set defaults to {@link ThreadWaitSleeper}. */ public void setSleeper(Sleeper sleeper) { this.sleeper = sleeper; diff --git a/src/main/java/org/springframework/retry/context/RetryContextSupport.java b/src/main/java/org/springframework/retry/context/RetryContextSupport.java index 3adc46a..ebf9206 100644 --- a/src/main/java/org/springframework/retry/context/RetryContextSupport.java +++ b/src/main/java/org/springframework/retry/context/RetryContextSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2006-2007 the original author or authors. + * Copyright 2006-2014 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. @@ -20,16 +20,19 @@ import org.springframework.core.AttributeAccessorSupport; import org.springframework.retry.RetryContext; import org.springframework.retry.RetryPolicy; +/** + * @author Dave Syer + */ @SuppressWarnings("serial") public class RetryContextSupport extends AttributeAccessorSupport implements RetryContext { - private boolean terminate = false; + private final RetryContext parent; - private int count; + private volatile boolean terminate = false; - private Throwable lastException; + private volatile int count; - private RetryContext parent; + private volatile Throwable lastException; public RetryContextSupport(RetryContext parent) { super(); @@ -59,14 +62,14 @@ 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.
- * + * * 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. */ @@ -75,7 +78,7 @@ public class RetryContextSupport extends AttributeAccessorSupport implements Ret if (throwable != null) count++; } - + @Override public String toString() { return String.format("[RetryContext: count=%d, lastException=%s, exhausted=%b]", count, lastException, terminate); diff --git a/src/main/java/org/springframework/retry/interceptor/RetryOperationsInterceptor.java b/src/main/java/org/springframework/retry/interceptor/RetryOperationsInterceptor.java index cf0db0f..57faacd 100644 --- a/src/main/java/org/springframework/retry/interceptor/RetryOperationsInterceptor.java +++ b/src/main/java/org/springframework/retry/interceptor/RetryOperationsInterceptor.java @@ -1,11 +1,11 @@ /* * Copyright 2006-2007 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. @@ -17,6 +17,7 @@ import java.util.Arrays; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; + import org.springframework.aop.ProxyMethodInvocation; import org.springframework.retry.RecoveryCallback; import org.springframework.retry.RetryCallback; @@ -30,20 +31,21 @@ import org.springframework.util.Assert; * on a service if it fails. The injected {@link RetryOperations} is used to control the * number of retries. By default it will retry a fixed number of times, according to the * defaults in {@link RetryTemplate}.
- * + * * Hint about transaction boundaries. If you want to retry a failed transaction you need * to make sure that the transaction boundary is inside the retry, otherwise the * successful attempt will roll back with the whole transaction. If the method being * intercepted is also transactional, then use the ordering hints in the advice * declarations to ensure that this one is before the transaction interceptor in the * advice chain. - * + * * @author Rob Harrop * @author Dave Syer */ public class RetryOperationsInterceptor implements MethodInterceptor { private RetryOperations retryOperations = new RetryTemplate(); + private MethodInvocationRecoverer recoverer; public void setRetryOperations(RetryOperations retryTemplate) { @@ -54,7 +56,7 @@ public class RetryOperationsInterceptor implements MethodInterceptor { public void setRecoverer(MethodInvocationRecoverer recoverer) { this.recoverer = recoverer; } - + public Object invoke(final MethodInvocation invocation) throws Throwable { RetryCallback retryCallback = new RetryCallback() { @@ -69,18 +71,22 @@ public class RetryOperationsInterceptor implements MethodInterceptor { */ if (invocation instanceof ProxyMethodInvocation) { try { - return ((ProxyMethodInvocation) invocation).invocableClone() - .proceed(); - } catch (Exception e) { + return ((ProxyMethodInvocation) invocation).invocableClone().proceed(); + } + catch (Exception e) { throw e; - } catch (Error e) { + } + catch (Error e) { throw e; - } catch (Throwable e) { + } + catch (Throwable e) { throw new IllegalStateException(e); } - } else { + } + 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"); } } @@ -98,19 +104,18 @@ public class RetryOperationsInterceptor implements MethodInterceptor { /** * @author Dave Syer - * + * */ private static final class ItemRecovererCallback implements RecoveryCallback { private final Object[] args; - private final MethodInvocationRecoverer recoverer; + private final MethodInvocationRecoverer recoverer; /** * @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; } diff --git a/src/main/java/org/springframework/retry/interceptor/Retryable.java b/src/main/java/org/springframework/retry/interceptor/Retryable.java index 463b633..3dd3bfb 100644 --- a/src/main/java/org/springframework/retry/interceptor/Retryable.java +++ b/src/main/java/org/springframework/retry/interceptor/Retryable.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2014 the original author or authors. + * Copyright 2014 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. @@ -20,8 +20,9 @@ package org.springframework.retry.interceptor; * Marker interface for proxies that are providing retryable behaviour. Can be added by * proxy creators that use the {@link RetryOperationsInterceptor} and * {@link StatefulRetryOperationsInterceptor}. - * @author Dave Syer * + * @author Dave Syer + * @since 1.1 */ public interface Retryable { diff --git a/src/main/java/org/springframework/retry/interceptor/StatefulRetryOperationsInterceptor.java b/src/main/java/org/springframework/retry/interceptor/StatefulRetryOperationsInterceptor.java index 50754f8..1b271de 100644 --- a/src/main/java/org/springframework/retry/interceptor/StatefulRetryOperationsInterceptor.java +++ b/src/main/java/org/springframework/retry/interceptor/StatefulRetryOperationsInterceptor.java @@ -22,6 +22,7 @@ import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.retry.RecoveryCallback; import org.springframework.retry.RetryCallback; import org.springframework.retry.RetryContext; @@ -40,53 +41,48 @@ import org.springframework.util.ObjectUtils; * that failed is tracked by its unique key (via {@link MethodArgumentsKeyGenerator}) * until the retry is exhausted, at which point the {@link MethodInvocationRecoverer} is * called.
- * + * * The main use case for this is where the service is transactional, via a transaction * interceptor on the interceptor chain. In this case the retry (and recovery on * exhausted) always happens in a new transaction.
- * + * * The injected {@link RetryOperations} is used to control the number of retries. By * default it will retry a fixed number of times, according to the defaults in * {@link RetryTemplate}.
- * + * * @author Dave Syer */ public class StatefulRetryOperationsInterceptor implements MethodInterceptor { - private transient Log logger = LogFactory.getLog(getClass()); + private transient final Log logger = LogFactory.getLog(getClass()); private MethodArgumentsKeyGenerator keyGenerator; - private MethodInvocationRecoverer recoverer; + private MethodInvocationRecoverer recoverer; private NewMethodArgumentsIdentifier newMethodArgumentsIdentifier; private RetryOperations retryOperations; + public StatefulRetryOperationsInterceptor() { + RetryTemplate retryTemplate = new RetryTemplate(); + retryTemplate.setRetryPolicy(new NeverRetryPolicy()); + retryOperations = retryTemplate; + } + public void setRetryOperations(RetryOperations retryTemplate) { Assert.notNull(retryTemplate, "'retryOperations' cannot be null."); this.retryOperations = retryTemplate; } - /** - * - */ - public StatefulRetryOperationsInterceptor() { - super(); - RetryTemplate retryTemplate = new RetryTemplate(); - retryTemplate.setRetryPolicy(new NeverRetryPolicy()); - retryOperations = retryTemplate; - } - /** * Public setter for the {@link MethodInvocationRecoverer} to use if the retry is * exhausted. The recoverer should be able to return an object of the same type as the * target object because its return value will be used to return to the caller in the - * case of a recovery.
- * + * case of a recovery. * @param recoverer the {@link MethodInvocationRecoverer} to set */ - public void setRecoverer(MethodInvocationRecoverer recoverer) { + public void setRecoverer(MethodInvocationRecoverer recoverer) { this.recoverer = recoverer; } @@ -100,8 +96,7 @@ public class StatefulRetryOperationsInterceptor implements MethodInterceptor { * been processed before. * @param newMethodArgumentsIdentifier the {@link NewMethodArgumentsIdentifier} to set */ - public void setNewItemIdentifier( - NewMethodArgumentsIdentifier newMethodArgumentsIdentifier) { + public void setNewItemIdentifier(NewMethodArgumentsIdentifier newMethodArgumentsIdentifier) { this.newMethodArgumentsIdentifier = newMethodArgumentsIdentifier; } @@ -113,22 +108,21 @@ public class StatefulRetryOperationsInterceptor implements MethodInterceptor { * case the value returned from the method invocation will be the value returned by * the recoverer (so the return type for that should be the same as the intercepted * method). - * * @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation) * @see MethodInvocationRecoverer#recover(Object[], Throwable) - * + * */ public Object invoke(final MethodInvocation invocation) throws Throwable { - logger.debug("Executing proxied method in stateful retry: " - + invocation.getStaticPart() + "(" - + ObjectUtils.getIdentityHexString(invocation) + ")"); + if (logger.isDebugEnabled()) { + logger.debug("Executing proxied method in stateful retry: " + + invocation.getStaticPart() + "(" + + ObjectUtils.getIdentityHexString(invocation) + ")"); + } Object[] args = invocation.getArguments(); - Assert.state( - args.length > 0, - "Stateful retry applied to method that takes no arguments: " - + invocation.getStaticPart()); + Assert.state(args.length > 0, "Stateful retry applied to method that takes no arguments: " + + invocation.getStaticPart()); Object arg = args; if (args.length == 1) { arg = args[0]; @@ -137,15 +131,15 @@ public class StatefulRetryOperationsInterceptor implements MethodInterceptor { RetryState retryState = new DefaultRetryState( keyGenerator != null ? keyGenerator.getKey(args) : item, - newMethodArgumentsIdentifier != null ? newMethodArgumentsIdentifier - .isNew(args) : false); + newMethodArgumentsIdentifier != null && newMethodArgumentsIdentifier.isNew(args) + ); - Object result = retryOperations.execute(new MethodInvocationRetryCallback( - invocation), recoverer != null ? new ItemRecovererCallback(args, - recoverer) : null, retryState); + Object result = retryOperations.execute(new MethodInvocationRetryCallback(invocation), + recoverer != null ? new ItemRecovererCallback(args, recoverer) : null, retryState); - logger.debug("Exiting proxied method in stateful retry with result: (" + result - + ")"); + if (logger.isDebugEnabled()) { + logger.debug("Exiting proxied method in stateful retry with result: (" + result + ")"); + } return result; @@ -153,18 +147,12 @@ public class StatefulRetryOperationsInterceptor implements MethodInterceptor { /** * @author Dave Syer - * + * */ - private static final class MethodInvocationRetryCallback implements - RetryCallback { - /** - * - */ + private static final class MethodInvocationRetryCallback implements RetryCallback { + private final MethodInvocation invocation; - /** - * @param invocation - */ private MethodInvocationRetryCallback(MethodInvocation invocation) { this.invocation = invocation; } @@ -172,11 +160,14 @@ public class StatefulRetryOperationsInterceptor implements MethodInterceptor { public Object doWithRetry(RetryContext context) throws Exception { try { return invocation.proceed(); - } catch (Exception e) { + } + catch (Exception e) { throw e; - } catch (Error e) { + } + catch (Error e) { throw e; - } catch (Throwable e) { + } + catch (Throwable e) { throw new IllegalStateException(e); } } @@ -184,19 +175,18 @@ public class StatefulRetryOperationsInterceptor implements MethodInterceptor { /** * @author Dave Syer - * + * */ private static final class ItemRecovererCallback implements RecoveryCallback { private final Object[] args; - private final MethodInvocationRecoverer recoverer; + private final MethodInvocationRecoverer recoverer; /** * @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; } diff --git a/src/main/java/org/springframework/retry/policy/CompositeRetryPolicy.java b/src/main/java/org/springframework/retry/policy/CompositeRetryPolicy.java index 6089b4c..10494ae 100644 --- a/src/main/java/org/springframework/retry/policy/CompositeRetryPolicy.java +++ b/src/main/java/org/springframework/retry/policy/CompositeRetryPolicy.java @@ -48,7 +48,7 @@ public class CompositeRetryPolicy implements RetryPolicy { /** * Setter for policies. - * + * * @param policies */ public void setPolicies(RetryPolicy[] policies) { @@ -59,7 +59,7 @@ public class CompositeRetryPolicy implements RetryPolicy { * Delegate to the policies that were in operation when the context was * created. If any of them cannot retry then return false, oetherwise return * true. - * + * * @see org.springframework.retry.RetryPolicy#canRetry(org.springframework.retry.RetryContext) */ public boolean canRetry(RetryContext context) { @@ -91,7 +91,7 @@ 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). - * + * * @see org.springframework.retry.RetryPolicy#close(org.springframework.retry.RetryContext) */ public void close(RetryContext context) { @@ -116,13 +116,13 @@ public class CompositeRetryPolicy implements RetryPolicy { /** * 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) */ public RetryContext open(RetryContext parent) { List list = new ArrayList(); - for (int i = 0; i < policies.length; i++) { - list.add(policies[i].open(parent)); + for (RetryPolicy policy : policies) { + list.add(policy.open(parent)); } return new CompositeRetryContext(parent, list); } @@ -130,7 +130,7 @@ public class CompositeRetryPolicy implements RetryPolicy { /** * Delegate to the policies that were in operation when the context was * created. - * + * * @see org.springframework.retry.RetryPolicy#close(org.springframework.retry.RetryContext) */ public void registerThrowable(RetryContext context, Throwable throwable) { @@ -150,7 +150,7 @@ public class CompositeRetryPolicy implements RetryPolicy { public CompositeRetryContext(RetryContext parent, List contexts) { super(parent); - this.contexts = contexts.toArray(new RetryContext[0]); + this.contexts = contexts.toArray(new RetryContext[contexts.size()]); this.policies = CompositeRetryPolicy.this.policies; } diff --git a/src/main/java/org/springframework/retry/policy/ExceptionClassifierRetryPolicy.java b/src/main/java/org/springframework/retry/policy/ExceptionClassifierRetryPolicy.java index bc76774..d056f15 100644 --- a/src/main/java/org/springframework/retry/policy/ExceptionClassifierRetryPolicy.java +++ b/src/main/java/org/springframework/retry/policy/ExceptionClassifierRetryPolicy.java @@ -30,33 +30,30 @@ 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. - * + * * @author Dave Syer - * + * */ public class ExceptionClassifierRetryPolicy implements RetryPolicy { - private Classifier exceptionClassifier = new ClassifierSupport( - new NeverRetryPolicy()); + private Classifier exceptionClassifier = new ClassifierSupport(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. */ public void setPolicyMap(Map, RetryPolicy> policyMap) { - SubclassClassifier subclassClassifier = new SubclassClassifier( - policyMap, (RetryPolicy) new NeverRetryPolicy()); - this.exceptionClassifier = subclassClassifier; + this.exceptionClassifier = new SubclassClassifier(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. - * + * * @param exceptionClassifier ExceptionClassifier to use */ public void setExceptionClassifier(Classifier exceptionClassifier) { @@ -65,7 +62,7 @@ public class ExceptionClassifierRetryPolicy implements RetryPolicy { /** * Delegate to the policy currently activated in the context. - * + * * @see org.springframework.retry.RetryPolicy#canRetry(org.springframework.retry.RetryContext) */ public boolean canRetry(RetryContext context) { @@ -75,7 +72,7 @@ public class ExceptionClassifierRetryPolicy implements RetryPolicy { /** * Delegate to the policy currently activated in the context. - * + * * @see org.springframework.retry.RetryPolicy#close(org.springframework.retry.RetryContext) */ public void close(RetryContext context) { @@ -86,7 +83,7 @@ public class ExceptionClassifierRetryPolicy implements RetryPolicy { /** * 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) { @@ -95,7 +92,7 @@ public class ExceptionClassifierRetryPolicy implements RetryPolicy { /** * Delegate to the policy currently activated in the context. - * + * * @see org.springframework.retry.RetryPolicy#registerThrowable(org.springframework.retry.RetryContext, * Throwable) */ @@ -125,11 +122,7 @@ public class ExceptionClassifierRetryPolicy implements RetryPolicy { } public boolean canRetry(RetryContext context) { - if (this.context == null) { - // there was no error yet - return true; - } - return policy.canRetry(this.context); + return this.context == null || policy.canRetry(this.context); } public void close(RetryContext context) { diff --git a/src/main/java/org/springframework/retry/support/RetryTemplate.java b/src/main/java/org/springframework/retry/support/RetryTemplate.java index 5c1ee08..ed48a98 100644 --- a/src/main/java/org/springframework/retry/support/RetryTemplate.java +++ b/src/main/java/org/springframework/retry/support/RetryTemplate.java @@ -1,5 +1,5 @@ /* - * Copyright 2006-2012 the original author or authors. + * Copyright 2006-2014 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. @@ -23,7 +23,7 @@ import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.core.AttributeAccessor; + import org.springframework.retry.ExhaustedRetryException; import org.springframework.retry.RecoveryCallback; import org.springframework.retry.RetryCallback; @@ -46,25 +46,26 @@ import org.springframework.retry.policy.SimpleRetryPolicy; * Template class that simplifies the execution of operations with retry semantics.
* Retryable operations are encapsulated in implementations of the {@link RetryCallback} * interface and are executed using one of the supplied execute methods.
- * + * * By default, an operation is retried if is throws any {@link Exception} or subclass of * {@link Exception}. This behaviour can be changed by using the * {@link #setRetryPolicy(RetryPolicy)} method.
- * + * * Also by default, each operation is retried for a maximum of three attempts with no back * off in between. This behaviour can be configured using the * {@link #setRetryPolicy(RetryPolicy)} and {@link #setBackOffPolicy(BackOffPolicy)} * properties. The {@link org.springframework.retry.backoff.BackOffPolicy} controls how * long the pause is between each individual retry attempt.
- * + * * 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 * of retries on the fly, as well as the {@link BackOffPolicy} used and no in progress * retryable operations will be affected. - * + * * @author Rob Harrop * @author Dave Syer * @author Gary Russell + * @author Artem Bilan */ public class RetryTemplate implements RetryOperations { @@ -72,9 +73,8 @@ public class RetryTemplate implements RetryOperations { private volatile BackOffPolicy backOffPolicy = new NoBackOffPolicy(); - private volatile RetryPolicy retryPolicy = new SimpleRetryPolicy(3, - Collections., Boolean> singletonMap( - Exception.class, true)); + private volatile RetryPolicy retryPolicy = + new SimpleRetryPolicy(3, Collections., Boolean> singletonMap(Exception.class, true)); private volatile RetryListener[] listeners = new RetryListener[0]; @@ -91,7 +91,7 @@ public class RetryTemplate implements RetryOperations { /** * Public setter for the {@link RetryContextCache}. - * + * * @param retryContextCache the {@link RetryContextCache} to set. */ public void setRetryContextCache(RetryContextCache retryContextCache) { @@ -101,7 +101,7 @@ 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 * @see RetryListener */ @@ -112,7 +112,7 @@ public class RetryTemplate implements RetryOperations { /** * Register an additional listener. - * + * * @param listener * @see #setListeners(RetryListener[]) */ @@ -124,7 +124,7 @@ public class RetryTemplate implements RetryOperations { /** * Setter for {@link BackOffPolicy}. - * + * * @param backOffPolicy */ public void setBackOffPolicy(BackOffPolicy backOffPolicy) { @@ -133,7 +133,7 @@ public class RetryTemplate implements RetryOperations { /** * Setter for {@link RetryPolicy}. - * + * * @param retryPolicy */ public void setRetryPolicy(RetryPolicy retryPolicy) { @@ -144,9 +144,9 @@ public class RetryTemplate implements RetryOperations { * Keep executing the callback until it either succeeds or the policy dictates that we * stop, in which case the most recent exception thrown by the callback will be * rethrown. - * + * * @see RetryOperations#execute(RetryCallback) - * + * * @throws TerminatedRetryException if the retry has been manually terminated by a * listener. */ @@ -157,9 +157,9 @@ public class RetryTemplate implements RetryOperations { /** * Keep executing the callback until it either succeeds or the policy dictates that we * stop, in which case the recovery callback will be executed. - * + * * @see RetryOperations#execute(RetryCallback, RecoveryCallback) - * + * * @throws TerminatedRetryException if the retry has been manually terminated by a * listener. */ @@ -171,9 +171,9 @@ public class RetryTemplate implements RetryOperations { /** * Execute the callback once if the policy dictates that we can, re-throwing any * exception encountered so that clients can re-present the same task later. - * + * * @see RetryOperations#execute(RetryCallback, RetryState) - * + * * @throws ExhaustedRetryException if the retry has been exhausted. */ public final T execute(RetryCallback retryCallback, RetryState retryState) @@ -184,7 +184,7 @@ public class RetryTemplate implements RetryOperations { /** * Execute the callback once if the policy dictates that we can, re-throwing any * exception encountered so that clients can re-present the same task later. - * + * * @see RetryOperations#execute(RetryCallback, RetryState) */ public final T execute(RetryCallback retryCallback, @@ -196,7 +196,7 @@ public class RetryTemplate implements RetryOperations { /** * Execute the callback once if the policy dictates that we can, otherwise execute the * recovery callback. - * + * * @see RetryOperations#execute(RetryCallback, RecoveryCallback, RetryState) * @throws ExhaustedRetryException if the retry has been exhausted. finally { @@ -232,18 +232,16 @@ public class RetryTemplate implements RetryOperations { // Get or Start the backoff context... BackOffContext backOffContext = null; - AttributeAccessor attributeAccessor = null; - if (context instanceof AttributeAccessor) { - attributeAccessor = (AttributeAccessor) context; - Object resource = attributeAccessor.getAttribute("backOffContext"); - if (resource instanceof BackOffContext) { - backOffContext = (BackOffContext) resource; - } + Object resource = context.getAttribute("backOffContext"); + + if (resource instanceof BackOffContext) { + backOffContext = (BackOffContext) resource; } + if (backOffContext == null) { backOffContext = backOffPolicy.start(context); - if (attributeAccessor != null && backOffContext != null) { - attributeAccessor.setAttribute("backOffContext", backOffContext); + if (backOffContext != null) { + context.setAttribute("backOffContext", backOffContext); } } @@ -256,12 +254,15 @@ public class RetryTemplate implements RetryOperations { while (canRetry(retryPolicy, context) && !context.isExhaustedOnly()) { try { - logger.debug("Retry: count=" + context.getRetryCount()); + if (logger.isDebugEnabled()) { + logger.debug("Retry: count=" + context.getRetryCount()); + } // Reset the last exception, so if we are successful // the close interceptors will not think we failed... lastException = null; return retryCallback.doWithRetry(context); - } catch (Throwable e) { + } + catch (Throwable e) { lastException = e; @@ -269,31 +270,34 @@ public class RetryTemplate implements RetryOperations { try { registerThrowable(retryPolicy, state, context, e); - } catch (Exception ex) { - throw new TerminatedRetryException( - "Could not register throwable", ex); + } + catch (Exception ex) { + throw new TerminatedRetryException("Could not register throwable", ex); } if (canRetry(retryPolicy, context) && !context.isExhaustedOnly()) { try { backOffPolicy.backOff(backOffContext); - } catch (BackOffInterruptedException ex) { + } + catch (BackOffInterruptedException ex) { lastException = e; - // back off was prevented by another thread - fail - // the retry - logger.debug("Abort retry because interrupted: count=" - + context.getRetryCount()); + // back off was prevented by another thread - fail the retry + if (logger.isDebugEnabled()) { + logger.debug("Abort retry because interrupted: count=" + context.getRetryCount()); + } throw ex; } } - logger.debug("Checking for rethrow: count=" + context.getRetryCount()); + if (logger.isDebugEnabled()) { + logger.debug("Checking for rethrow: count=" + context.getRetryCount()); + } + if (shouldRethrow(retryPolicy, context, state)) { - logger.debug("Rethrow in retry for policy: count=" - + context.getRetryCount()); - @SuppressWarnings("unchecked") - E rethrow = (E) wrapIfNecessary(e); - throw rethrow; + if (logger.isDebugEnabled()) { + logger.debug("Rethrow in retry for policy: count=" + context.getRetryCount()); + } + throw RetryTemplate.wrapIfNecessary(e); } } @@ -305,19 +309,19 @@ public class RetryTemplate implements RetryOperations { */ } - logger.debug("Retry failed last attempt: count=" + context.getRetryCount()); + if (logger.isDebugEnabled()) { + logger.debug("Retry failed last attempt: count=" + context.getRetryCount()); + } if (context.isExhaustedOnly()) { - rethrow(context, - "Retry exhausted after last attempt with no recovery path."); + rethrow(context, "Retry exhausted after last attempt with no recovery path."); } return handleRetryExhausted(recoveryCallback, context, state); - } catch (Throwable e) { - @SuppressWarnings("unchecked") - E rethrow = (E) wrapIfNecessary(e); - throw rethrow; + } + catch (Throwable e) { + throw RetryTemplate.wrapIfNecessary(e); } finally { close(retryPolicy, context, state, lastException == null); @@ -331,7 +335,7 @@ 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 @@ -343,7 +347,7 @@ 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 context * @param state * @param succeeded @@ -355,7 +359,8 @@ public class RetryTemplate implements RetryOperations { retryContextCache.remove(state.getKey()); retryPolicy.close(context); } - } else { + } + else { retryPolicy.close(context); } } @@ -384,7 +389,7 @@ 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 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 @@ -436,7 +441,7 @@ 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 * @throws Exception if the callback does, and if there is no callback and the state @@ -464,7 +469,8 @@ public class RetryTemplate implements RetryOperations { @SuppressWarnings("unchecked") E rethrow = (E) context.getLastThrowable(); throw rethrow; - } else { + } + else { throw new ExhaustedRetryException(message, context.getLastThrowable()); } } @@ -473,27 +479,22 @@ 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 * @param context the current context - * + * * @return true if the state is not null but subclasses might choose otherwise */ - protected boolean shouldRethrow(RetryPolicy retryPolicy, RetryContext context, - RetryState state) { - if (state == null) { - return false; - } else { - return state.rollbackFor(context.getLastThrowable()); - } + protected boolean shouldRethrow(RetryPolicy retryPolicy, RetryContext context, RetryState state) { + return state != null && state.rollbackFor(context.getLastThrowable()); } private boolean doOpenInterceptors(RetryCallback callback, RetryContext context) { boolean result = true; - for (int i = 0; i < listeners.length; i++) { - result = result && listeners[i].open(context, callback); + for (RetryListener listener : listeners) { + result = result && listener.open(context, callback); } return result; @@ -521,11 +522,13 @@ public class RetryTemplate implements RetryOperations { private static E wrapIfNecessary(Throwable throwable) throws RetryException { if (throwable instanceof Error) { throw (Error) throwable; - } else if (throwable instanceof Exception) { + } + else if (throwable instanceof Exception) { @SuppressWarnings("unchecked") E rethrow = (E) throwable; return rethrow; - } else { + } + else { throw new RetryException("Exception in batch process", throwable); } } diff --git a/src/test/java/org/springframework/retry/backoff/ObjectWaitSleeperTests.java b/src/test/java/org/springframework/retry/backoff/ThreadWaitSleeperTests.java similarity index 87% rename from src/test/java/org/springframework/retry/backoff/ObjectWaitSleeperTests.java rename to src/test/java/org/springframework/retry/backoff/ThreadWaitSleeperTests.java index 13d953c..3dba38c 100644 --- a/src/test/java/org/springframework/retry/backoff/ObjectWaitSleeperTests.java +++ b/src/test/java/org/springframework/retry/backoff/ThreadWaitSleeperTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2006-2007 the original author or authors. + * Copyright 2006-2014 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. @@ -22,13 +22,14 @@ import org.junit.Test; /** * @author Dave Syer + * @author Artem Bilan */ -public class ObjectWaitSleeperTests { +public class ThreadWaitSleeperTests { @Test public void testSingleBackOff() throws Exception { long backOffPeriod = 50; - ObjectWaitSleeper strategy = new ObjectWaitSleeper(); + ThreadWaitSleeper strategy = new ThreadWaitSleeper(); long before = System.currentTimeMillis(); strategy.sleep(backOffPeriod); long after = System.currentTimeMillis();