diff --git a/src/main/java/org/springframework/retry/backoff/ExponentialBackOffPolicy.java b/src/main/java/org/springframework/retry/backoff/ExponentialBackOffPolicy.java index 1c85a48..cdcbe49 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-2007 the original author or authors. + * Copyright 2006-2012 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. @@ -16,6 +16,8 @@ package org.springframework.retry.backoff; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.retry.RetryContext; import org.springframework.util.ClassUtils; @@ -35,9 +37,11 @@ import org.springframework.util.ClassUtils; * * @author Rob Harrop * @author Dave Syer + * @author Gary Russell */ public class ExponentialBackOffPolicy implements BackOffPolicy { + 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 @@ -151,7 +155,11 @@ public class ExponentialBackOffPolicy implements BackOffPolicy { public void backOff(BackOffContext backOffContext) throws BackOffInterruptedException { ExponentialBackOffContext context = (ExponentialBackOffContext) backOffContext; try { - sleeper.sleep(context.getSleepAndIncrement()); + long sleepTime = context.getSleepAndIncrement(); + if (logger.isDebugEnabled()) { + logger.debug("Sleeping for " + sleepTime); + } + sleeper.sleep(sleepTime); } catch (InterruptedException e) { throw new BackOffInterruptedException("Thread interrupted while sleeping", e); diff --git a/src/main/java/org/springframework/retry/support/RetryTemplate.java b/src/main/java/org/springframework/retry/support/RetryTemplate.java index e4c04f0..81cc451 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-2007 the original author or authors. + * Copyright 2006-2012 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,6 +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; @@ -67,6 +68,7 @@ import org.springframework.retry.policy.SimpleRetryPolicy; * * @author Rob Harrop * @author Dave Syer + * @author Gary Russell */ public class RetryTemplate implements RetryOperations { @@ -220,8 +222,22 @@ public class RetryTemplate implements RetryOperations { throw new TerminatedRetryException("Retry terminated abnormally by interceptor before first attempt"); } - // Start the backoff context... - BackOffContext backOffContext = backOffPolicy.start(context); + // 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; + } + } + if (backOffContext == null) { + backOffContext = backOffPolicy.start(context); + if (attributeAccessor != null && backOffContext != null) { + attributeAccessor.setAttribute("backOffContext", backOffContext); + } + } /* * We allow the whole loop to be skipped if the policy or context diff --git a/src/test/java/org/springframework/retry/policy/StatefulRetryIntegrationTests.java b/src/test/java/org/springframework/retry/policy/StatefulRetryIntegrationTests.java index 9113f92..a1aac0d 100644 --- a/src/test/java/org/springframework/retry/policy/StatefulRetryIntegrationTests.java +++ b/src/test/java/org/springframework/retry/policy/StatefulRetryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2006-2007 the original author or authors. + * Copyright 2006-2012 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,18 +22,23 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import java.util.ArrayList; import java.util.Collections; +import java.util.List; import org.junit.Test; import org.springframework.retry.ExhaustedRetryException; +import org.springframework.retry.RecoveryCallback; import org.springframework.retry.RetryCallback; import org.springframework.retry.RetryContext; import org.springframework.retry.RetryState; +import org.springframework.retry.backoff.ExponentialBackOffPolicy; import org.springframework.retry.support.DefaultRetryState; import org.springframework.retry.support.RetryTemplate; /** * @author Dave Syer + * @author Gary Russell * */ public class StatefulRetryIntegrationTests { @@ -114,6 +119,38 @@ public class StatefulRetryIntegrationTests { assertEquals("bar", result); } + @Test + public void testExponentialBackOffIsExponential() throws Exception { + ExponentialBackOffPolicy policy = new ExponentialBackOffPolicy(); + policy.setInitialInterval(100); + policy.setMultiplier(1.5); + RetryTemplate template = new RetryTemplate(); + template.setBackOffPolicy(policy); + final List times = new ArrayList(); + RetryState retryState = new DefaultRetryState("bar"); + for (int i = 0; i < 3; i++) { + try { + template.execute(new RetryCallback() { + public String doWithRetry(RetryContext context) throws Exception { + times.add(System.currentTimeMillis()); + throw new Exception("Fail"); + } + }, new RecoveryCallback() { + public String recover(RetryContext context) + throws Exception { + return null; + } + }, retryState); + } + catch (Exception e) { + assertTrue(e.getMessage().equals("Fail")); + } + } + assertEquals(3, times.size()); + assertTrue(times.get(1) - times.get(0) >= 100); + assertTrue(times.get(2) - times.get(1) >= 150); + } + /** * @author Dave Syer *