From a6aa75bb8d4700309b9ecdc6574b4715504f7433 Mon Sep 17 00:00:00 2001
From: pkupczyk <pkupczyk>
Date: Tue, 6 Dec 2011 13:31:06 +0000
Subject: [PATCH] LMS-2675 - InfectX: retry API methods when they fail - make
 it configurable

SVN: 23892
---
 .../cisd/common/retry/RetryCaller.java        | 74 +++++++++----------
 .../config/DefaultRetryConfiguration.java     | 53 +++++++++++++
 .../retry/config/RetryConfiguration.java      | 31 ++++++++
 .../config/StaticRetryConfiguration.java      | 62 ++++++++++++++++
 .../cisd/common/retry/RetryCallerTest.java    | 23 ++++--
 .../common/retry/RetryProxyFactoryTest.java   | 16 ++++
 6 files changed, 214 insertions(+), 45 deletions(-)
 create mode 100644 common/source/java/ch/systemsx/cisd/common/retry/config/DefaultRetryConfiguration.java
 create mode 100644 common/source/java/ch/systemsx/cisd/common/retry/config/RetryConfiguration.java
 create mode 100644 common/source/java/ch/systemsx/cisd/common/retry/config/StaticRetryConfiguration.java

diff --git a/common/source/java/ch/systemsx/cisd/common/retry/RetryCaller.java b/common/source/java/ch/systemsx/cisd/common/retry/RetryCaller.java
index 16483ac04d6..369d95c1e8e 100644
--- a/common/source/java/ch/systemsx/cisd/common/retry/RetryCaller.java
+++ b/common/source/java/ch/systemsx/cisd/common/retry/RetryCaller.java
@@ -18,19 +18,49 @@ package ch.systemsx.cisd.common.retry;
 
 import org.springframework.remoting.RemoteConnectFailureException;
 
+import ch.systemsx.cisd.common.retry.config.DefaultRetryConfiguration;
+import ch.systemsx.cisd.common.retry.config.RetryConfiguration;
+
 /**
  * @author pkupczyk
  */
 public abstract class RetryCaller<T, E extends Throwable>
 {
 
-    private int retryCounter = 1;
+    private RetryConfiguration configuration;
 
-    private int retryMaxCounter = 5;
+    private int retryCounter;
 
-    private int retryWaitingTime = 1000;
+    private int waitingTime;
 
-    private int retryWaitingTimeFactor = 2;
+    public RetryCaller()
+    {
+        this(DefaultRetryConfiguration.getInstance());
+    }
+
+    public RetryCaller(RetryConfiguration configuration)
+    {
+        if (configuration == null)
+        {
+            throw new IllegalArgumentException("Configuration was null");
+        }
+        if (configuration.getMaximumNumberOfRetries() < 0)
+        {
+            throw new IllegalArgumentException("MaximumNumberOfRetries must be >= 0");
+        }
+        if (configuration.getWaitingTimeBetweenRetries() <= 0)
+        {
+            throw new IllegalArgumentException("WaitingTimeBetweenRetries must be > 0");
+        }
+        if (configuration.getWaitingTimeBetweenRetriesIncreasingFactor() <= 0)
+        {
+            throw new IllegalArgumentException(
+                    "WaitingTimeBetweenRetriesIncreasingFactor must be > 0");
+        }
+
+        this.configuration = configuration;
+        this.waitingTime = configuration.getWaitingTimeBetweenRetries();
+    }
 
     protected abstract T call() throws E;
 
@@ -59,15 +89,15 @@ public abstract class RetryCaller<T, E extends Throwable>
 
     private boolean shouldRetry()
     {
-        return retryCounter < retryMaxCounter;
+        return retryCounter < configuration.getMaximumNumberOfRetries();
     }
 
     private void waitForRetry()
     {
         try
         {
-            Thread.sleep(retryWaitingTime);
-            retryWaitingTime *= retryWaitingTimeFactor;
+            Thread.sleep(waitingTime);
+            waitingTime *= configuration.getWaitingTimeBetweenRetriesIncreasingFactor();
             retryCounter++;
         } catch (InterruptedException e)
         {
@@ -75,34 +105,4 @@ public abstract class RetryCaller<T, E extends Throwable>
         }
     }
 
-    public int getRetryMaxCounter()
-    {
-        return retryMaxCounter;
-    }
-
-    public void setRetryMaxCounter(int retryMaxCounter)
-    {
-        this.retryMaxCounter = retryMaxCounter;
-    }
-
-    public int getRetryWaitingTime()
-    {
-        return retryWaitingTime;
-    }
-
-    public void setRetryWaitingTime(int retryWaitingTime)
-    {
-        this.retryWaitingTime = retryWaitingTime;
-    }
-
-    public int getRetryWaitingTimeFactor()
-    {
-        return retryWaitingTimeFactor;
-    }
-
-    public void setRetryWaitingTimeFactor(int retryWaitingTimeFactor)
-    {
-        this.retryWaitingTimeFactor = retryWaitingTimeFactor;
-    }
-
 }
diff --git a/common/source/java/ch/systemsx/cisd/common/retry/config/DefaultRetryConfiguration.java b/common/source/java/ch/systemsx/cisd/common/retry/config/DefaultRetryConfiguration.java
new file mode 100644
index 00000000000..7a8bdf32615
--- /dev/null
+++ b/common/source/java/ch/systemsx/cisd/common/retry/config/DefaultRetryConfiguration.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2011 ETH Zuerich, CISD
+ *
+ * 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 ch.systemsx.cisd.common.retry.config;
+
+/**
+ * @author pkupczyk
+ */
+public class DefaultRetryConfiguration extends StaticRetryConfiguration
+{
+
+    private static final int DEFAULT_NUMBER_OF_RETRIES = 5;
+
+    private static final int DEFAULT_WAITING_TIME_BETWEEN_RETRIES = 1000;
+
+    private static final float DEFAULT_WAITING_TIME_BETWEEN_RETRIES_INCREASING_FACTOR = 2;
+
+    private static final DefaultRetryConfiguration instance = new DefaultRetryConfiguration();
+    static
+    {
+        instance.reset();
+    }
+
+    private DefaultRetryConfiguration()
+    {
+    }
+
+    public void reset()
+    {
+        instance.setMaximumNumberOfRetries(DEFAULT_NUMBER_OF_RETRIES);
+        instance.setWaitingTimeBetweenRetries(DEFAULT_WAITING_TIME_BETWEEN_RETRIES);
+        instance.setWaitingTimeBetweenRetriesIncreasingFactor(DEFAULT_WAITING_TIME_BETWEEN_RETRIES_INCREASING_FACTOR);
+    }
+
+    public static final DefaultRetryConfiguration getInstance()
+    {
+        return instance;
+    }
+
+}
diff --git a/common/source/java/ch/systemsx/cisd/common/retry/config/RetryConfiguration.java b/common/source/java/ch/systemsx/cisd/common/retry/config/RetryConfiguration.java
new file mode 100644
index 00000000000..a85544daee3
--- /dev/null
+++ b/common/source/java/ch/systemsx/cisd/common/retry/config/RetryConfiguration.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2011 ETH Zuerich, CISD
+ *
+ * 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 ch.systemsx.cisd.common.retry.config;
+
+/**
+ * @author pkupczyk
+ */
+public interface RetryConfiguration
+{
+
+    public int getMaximumNumberOfRetries();
+
+    public int getWaitingTimeBetweenRetries();
+
+    public float getWaitingTimeBetweenRetriesIncreasingFactor();
+
+}
diff --git a/common/source/java/ch/systemsx/cisd/common/retry/config/StaticRetryConfiguration.java b/common/source/java/ch/systemsx/cisd/common/retry/config/StaticRetryConfiguration.java
new file mode 100644
index 00000000000..d7cd90cbaaf
--- /dev/null
+++ b/common/source/java/ch/systemsx/cisd/common/retry/config/StaticRetryConfiguration.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2011 ETH Zuerich, CISD
+ *
+ * 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 ch.systemsx.cisd.common.retry.config;
+
+/**
+ * @author pkupczyk
+ */
+public class StaticRetryConfiguration implements RetryConfiguration
+{
+
+    private int maximumNumberOfRetries;
+
+    private int waitingTimeBetweenRetries;
+
+    private float waitingTimeBetweenRetriesIncreasingFactor;
+
+    public int getMaximumNumberOfRetries()
+    {
+        return maximumNumberOfRetries;
+    }
+
+    public void setMaximumNumberOfRetries(int maximumNumberOfRetries)
+    {
+        this.maximumNumberOfRetries = maximumNumberOfRetries;
+    }
+
+    public int getWaitingTimeBetweenRetries()
+    {
+        return waitingTimeBetweenRetries;
+    }
+
+    public void setWaitingTimeBetweenRetries(int waitingTimeBetweenRetries)
+    {
+        this.waitingTimeBetweenRetries = waitingTimeBetweenRetries;
+    }
+
+    public float getWaitingTimeBetweenRetriesIncreasingFactor()
+    {
+        return waitingTimeBetweenRetriesIncreasingFactor;
+    }
+
+    public void setWaitingTimeBetweenRetriesIncreasingFactor(
+            float waitingTimeBetweenRetriesIncreasingFactor)
+    {
+        this.waitingTimeBetweenRetriesIncreasingFactor = waitingTimeBetweenRetriesIncreasingFactor;
+    }
+
+}
diff --git a/common/sourceTest/java/ch/systemsx/cisd/common/retry/RetryCallerTest.java b/common/sourceTest/java/ch/systemsx/cisd/common/retry/RetryCallerTest.java
index f99a0b1d94f..d43f8a60856 100644
--- a/common/sourceTest/java/ch/systemsx/cisd/common/retry/RetryCallerTest.java
+++ b/common/sourceTest/java/ch/systemsx/cisd/common/retry/RetryCallerTest.java
@@ -25,6 +25,8 @@ import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import ch.systemsx.cisd.common.retry.config.StaticRetryConfiguration;
+
 /**
  * @author pkupczyk
  */
@@ -35,6 +37,8 @@ public class RetryCallerTest
 
     private Runnable runnable;
 
+    private StaticRetryConfiguration configuration;
+
     private RetryCaller<Object, Throwable> caller;
 
     @BeforeMethod
@@ -42,7 +46,13 @@ public class RetryCallerTest
     {
         mockery = new Mockery();
         runnable = mockery.mock(Runnable.class);
-        caller = new RetryCaller<Object, Throwable>()
+
+        configuration = new StaticRetryConfiguration();
+        configuration.setMaximumNumberOfRetries(5);
+        configuration.setWaitingTimeBetweenRetries(100);
+        configuration.setWaitingTimeBetweenRetriesIncreasingFactor(2);
+
+        caller = new RetryCaller<Object, Throwable>(configuration)
             {
                 @Override
                 protected Object call() throws Throwable
@@ -51,9 +61,6 @@ public class RetryCallerTest
                     return null;
                 }
             };
-        caller.setRetryWaitingTime(100);
-        caller.setRetryWaitingTimeFactor(2);
-        caller.setRetryMaxCounter(5);
     }
 
     @Test
@@ -91,12 +98,11 @@ public class RetryCallerTest
             throws Throwable
     {
         final long startTime = System.currentTimeMillis();
-        final long waitingTime = caller.getRetryWaitingTime();
 
         mockery.checking(new Expectations()
             {
                 {
-                    for (int i = 0; i < caller.getRetryMaxCounter() - 1; i++)
+                    for (int i = 0; i < configuration.getMaximumNumberOfRetries(); i++)
                     {
                         final int ifinal = i;
                         oneOf(runnable).run();
@@ -105,7 +111,8 @@ public class RetryCallerTest
                                 public Object invoke(Invocation invocation) throws Throwable
                                 {
                                     Assert.assertTrue(System.currentTimeMillis() >= startTime
-                                            + (ifinal * waitingTime));
+                                            + (ifinal * configuration
+                                                    .getWaitingTimeBetweenRetries()));
                                     throw new RemoteConnectFailureException("", null);
                                 }
                             });
@@ -125,7 +132,7 @@ public class RetryCallerTest
         mockery.checking(new Expectations()
             {
                 {
-                    for (int i = 0; i < caller.getRetryMaxCounter(); i++)
+                    for (int i = 0; i < configuration.getMaximumNumberOfRetries() + 1; i++)
                     {
                         oneOf(runnable).run();
                         will(throwException(new RemoteConnectFailureException("", null)));
diff --git a/common/sourceTest/java/ch/systemsx/cisd/common/retry/RetryProxyFactoryTest.java b/common/sourceTest/java/ch/systemsx/cisd/common/retry/RetryProxyFactoryTest.java
index 1a365aecacc..5247211bb68 100644
--- a/common/sourceTest/java/ch/systemsx/cisd/common/retry/RetryProxyFactoryTest.java
+++ b/common/sourceTest/java/ch/systemsx/cisd/common/retry/RetryProxyFactoryTest.java
@@ -20,14 +20,30 @@ import java.lang.reflect.InvocationTargetException;
 
 import org.springframework.remoting.RemoteConnectFailureException;
 import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import ch.systemsx.cisd.common.retry.config.DefaultRetryConfiguration;
+
 /**
  * @author pkupczyk
  */
 public class RetryProxyFactoryTest
 {
 
+    @BeforeMethod
+    public void beforeMethod()
+    {
+        DefaultRetryConfiguration.getInstance().setWaitingTimeBetweenRetries(100);
+    }
+
+    @AfterMethod
+    public void afterMethod()
+    {
+        DefaultRetryConfiguration.getInstance().reset();
+    }
+
     @Test
     public void testCreateProxyForClassWithoutAnyInterfaceAndCallCommunicationFailingRetryMethod()
             throws Throwable
-- 
GitLab