This is an automated email from the ASF dual-hosted git repository.

dulvac pushed a commit to branch master
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-testing-clients.git


The following commit(s) were added to refs/heads/master by this push:
     new a7c9b49  SLING-9329 Implement configurable retries mechanism for http 
5XX response codes
a7c9b49 is described below

commit a7c9b4987a85576c0017e385cfa4f62a5736b2c7
Author: Andrei Dulvac <[email protected]>
AuthorDate: Fri Apr 3 18:10:09 2020 +0200

    SLING-9329 Implement configurable retries mechanism for http 5XX response 
codes
---
 .../apache/sling/testing/clients/Constants.java    | 53 ++++++++++++++++++++--
 .../apache/sling/testing/clients/SlingClient.java  |  6 ++-
 .../interceptors/FormBasedAuthInterceptor.java     | 27 ++++++++---
 .../apache/sling/testing/clients/package-info.java |  2 +-
 .../clients/util/ServerErrorRetryStrategy.java     | 39 ++++++++++++++--
 .../sling/testing/clients/util/package-info.java   |  2 +-
 .../clients/SlingClientRetryStrategyTest.java      | 31 +++++++++++--
 7 files changed, 138 insertions(+), 22 deletions(-)

diff --git a/src/main/java/org/apache/sling/testing/clients/Constants.java 
b/src/main/java/org/apache/sling/testing/clients/Constants.java
index 43a2cf6..f8524d6 100644
--- a/src/main/java/org/apache/sling/testing/clients/Constants.java
+++ b/src/main/java/org/apache/sling/testing/clients/Constants.java
@@ -16,6 +16,10 @@
  */
 package org.apache.sling.testing.clients;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
 public class Constants {
 
     /**
@@ -37,12 +41,47 @@ public class Constants {
 
     // Custom log retries
     private static boolean logRetries;
+
+    // CSV list of error codes to retry requests for
+    private static String retryCodes = "";
+
+    /**
+     * System property for {@see HTTP_DELAY}
+     * Prefixed by {@see CONFIG_PROP_PREFIX}
+     */
+    public static final String HTTP_DELAY_PROP = "http.delay";
+
+    /**
+     * System property for {@see HTTP_RETRIES}
+     * Prefixed by {@see CONFIG_PROP_PREFIX}
+     */
+    public static final String HTTP_RETRIES_PROP = "http.retries";
+
+    /**
+     * System property for {@see HTTP_RETRIES_DELAY}
+     * Prefixed by {@see CONFIG_PROP_PREFIX}
+     */
+    public static final String HTTP_RETRIES_DELAY_PROP = "http.retriesDelay";
+
+    /**
+     * System property for {@see HTTP_LOG_RETRIES}
+     * Prefixed by {@see CONFIG_PROP_PREFIX}
+     */
+    public static final String HTTP_LOG_RETRIES_PROP = "http.logRetries";
+
+    /**
+     * System property for {@see HTTP_RETRIES_ERROR_CODES}
+     * Prefixed by {@see CONFIG_PROP_PREFIX}
+     */
+    public static final String HTTP_RETRIES_ERROR_CODES_PROP = 
"http.retriesErrorCodes";
+
     static {
         try {
-            Constants.delay = Long.getLong(Constants.CONFIG_PROP_PREFIX + 
"http.delay", 0);
-            Constants.retries = 
Integer.getInteger(Constants.CONFIG_PROP_PREFIX + "http.retries", 5);
-            Constants.retriesDelay = 
Integer.getInteger(Constants.CONFIG_PROP_PREFIX + "http.retriesDelay", 1000);
-            Constants.logRetries = 
Boolean.getBoolean(Constants.CONFIG_PROP_PREFIX + "http.logRetries");
+            Constants.delay = Long.getLong(Constants.CONFIG_PROP_PREFIX + 
HTTP_DELAY_PROP, 0);
+            Constants.retries = 
Integer.getInteger(Constants.CONFIG_PROP_PREFIX + HTTP_RETRIES_PROP, 10);
+            Constants.retriesDelay = 
Integer.getInteger(Constants.CONFIG_PROP_PREFIX + HTTP_RETRIES_DELAY_PROP, 
1000);
+            Constants.logRetries = 
Boolean.getBoolean(Constants.CONFIG_PROP_PREFIX + HTTP_LOG_RETRIES_PROP);
+            Constants.retryCodes = 
System.getProperty(Constants.CONFIG_PROP_PREFIX + 
HTTP_RETRIES_ERROR_CODES_PROP, "");
         } catch (NumberFormatException e) {
             Constants.delay = 0;
             Constants.retries = 5;
@@ -72,6 +111,12 @@ public class Constants {
      */
     public static final boolean HTTP_LOG_RETRIES = logRetries;
 
+    /**
+     * Comma-separated list of http response codes for which to retry the 
request
+     * If empty, all 5XX error codes will be retried
+     */
+    public static final String HTTP_RETRIES_ERROR_CODES = retryCodes;
+
 
 
     /**
diff --git a/src/main/java/org/apache/sling/testing/clients/SlingClient.java 
b/src/main/java/org/apache/sling/testing/clients/SlingClient.java
index 15a20db..fd5015c 100644
--- a/src/main/java/org/apache/sling/testing/clients/SlingClient.java
+++ b/src/main/java/org/apache/sling/testing/clients/SlingClient.java
@@ -693,7 +693,11 @@ public class SlingClient extends AbstractSlingClient {
 
             // HTTP request strategy
             httpClientBuilder.setServiceUnavailableRetryStrategy(new 
ServerErrorRetryStrategy(
-                    Constants.HTTP_RETRIES, Constants.HTTP_RETRIES_DELAY, 
Constants.HTTP_LOG_RETRIES));
+                    Constants.HTTP_RETRIES,
+                    Constants.HTTP_RETRIES_DELAY,
+                    Constants.HTTP_LOG_RETRIES,
+                    Constants.HTTP_RETRIES_ERROR_CODES)
+            );
 
             return this;
         }
diff --git 
a/src/main/java/org/apache/sling/testing/clients/interceptors/FormBasedAuthInterceptor.java
 
b/src/main/java/org/apache/sling/testing/clients/interceptors/FormBasedAuthInterceptor.java
index e7e251f..a8bba17 100644
--- 
a/src/main/java/org/apache/sling/testing/clients/interceptors/FormBasedAuthInterceptor.java
+++ 
b/src/main/java/org/apache/sling/testing/clients/interceptors/FormBasedAuthInterceptor.java
@@ -16,9 +16,15 @@
  */
 package org.apache.sling.testing.clients.interceptors;
 
-import org.apache.http.*;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.NameValuePair;
 import org.apache.http.auth.AuthScope;
 import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.ServiceUnavailableRetryStrategy;
 import org.apache.http.client.entity.UrlEncodedFormEntity;
 import org.apache.http.client.methods.HttpPost;
 import org.apache.http.client.protocol.HttpClientContext;
@@ -27,15 +33,18 @@ import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClientBuilder;
 import org.apache.http.message.BasicNameValuePair;
 import org.apache.http.protocol.HttpContext;
-import org.apache.sling.testing.clients.Constants;
 import org.apache.sling.testing.clients.util.ServerErrorRetryStrategy;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
 import java.net.URI;
+import java.util.Arrays;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.apache.sling.testing.clients.Constants.*;
 
 public class FormBasedAuthInterceptor implements HttpRequestInterceptor {
     static final Logger LOG = 
LoggerFactory.getLogger(FormBasedAuthInterceptor.class);
@@ -56,7 +65,8 @@ public class FormBasedAuthInterceptor implements 
HttpRequestInterceptor {
 
         Cookie loginCookie = getLoginCookie(context, loginTokenName);
         if (loginCookie != null) {
-            LOG.debug("Request has cookie {}={} so I'm not intercepting the 
request", loginCookie.getName(), loginCookie.getValue());
+            LOG.debug("Request has cookie {}={} so I'm not intercepting the 
request",
+                    loginCookie.getName(), loginCookie.getValue());
             return;
         }
 
@@ -76,10 +86,13 @@ public class FormBasedAuthInterceptor implements 
HttpRequestInterceptor {
 
         HttpPost loginPost = new 
HttpPost(URI.create(request.getRequestLine().getUri()).resolve(loginPath));
         loginPost.setEntity(httpEntity);
-        final CloseableHttpClient client = 
HttpClientBuilder.create().setServiceUnavailableRetryStrategy(
-                new ServerErrorRetryStrategy(
-                        Constants.HTTP_RETRIES, Constants.HTTP_RETRIES_DELAY, 
Constants.HTTP_LOG_RETRIES))
-                .disableRedirectHandling().build();
+
+        ServiceUnavailableRetryStrategy retryStrategy = new 
ServerErrorRetryStrategy(
+                HTTP_RETRIES, HTTP_RETRIES_DELAY, HTTP_LOG_RETRIES, 
HTTP_RETRIES_ERROR_CODES);
+        final CloseableHttpClient client = HttpClientBuilder.create()
+                .setServiceUnavailableRetryStrategy(retryStrategy)
+                .disableRedirectHandling()
+                .build();
 
         client.execute(host, loginPost, context);
 
diff --git a/src/main/java/org/apache/sling/testing/clients/package-info.java 
b/src/main/java/org/apache/sling/testing/clients/package-info.java
index df6de91..8ace300 100644
--- a/src/main/java/org/apache/sling/testing/clients/package-info.java
+++ b/src/main/java/org/apache/sling/testing/clients/package-info.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-@Version("1.6.0")
+@Version("1.7.0")
 package org.apache.sling.testing.clients;
 
 import org.osgi.annotation.versioning.Version;
diff --git 
a/src/main/java/org/apache/sling/testing/clients/util/ServerErrorRetryStrategy.java
 
b/src/main/java/org/apache/sling/testing/clients/util/ServerErrorRetryStrategy.java
index bcce9e4..0e3d770 100644
--- 
a/src/main/java/org/apache/sling/testing/clients/util/ServerErrorRetryStrategy.java
+++ 
b/src/main/java/org/apache/sling/testing/clients/util/ServerErrorRetryStrategy.java
@@ -27,6 +27,13 @@ import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import static org.apache.http.HttpStatus.*;
+import static 
org.apache.sling.testing.clients.Constants.HTTP_RETRIES_ERROR_CODES;
 
 /**
  * {code ServiceUnavailableRetryStrategy} strategy for retrying request in 
case of a 5XX response code
@@ -38,14 +45,31 @@ public class ServerErrorRetryStrategy implements 
ServiceUnavailableRetryStrategy
     private final int maxRetries;
     private final int retryInterval;
     private final boolean logRetries;
+    private Collection<Integer> retryErrorCodes;
 
-    public ServerErrorRetryStrategy(final int maxRetries, final int 
retryInterval, final boolean logRetries) {
+    public ServerErrorRetryStrategy(final int maxRetries, final int 
retryInterval, final boolean logRetries,
+                                    String retryErrorCodes) {
         super();
         Args.positive(maxRetries, "Max retries");
         Args.positive(retryInterval, "Retry interval");
         this.maxRetries = maxRetries;
         this.retryInterval = retryInterval;
         this.logRetries = logRetries;
+        if (retryErrorCodes == null) {
+            retryErrorCodes = "";
+        }
+        this.retryErrorCodes = Arrays.asList(retryErrorCodes.split(","))
+                .stream().map(s -> {
+                    try {
+                        return Integer.valueOf(s);
+                    } catch (NumberFormatException e) {
+                        return null;
+                    }
+                }).filter(Objects::nonNull).collect(Collectors.toList());
+    }
+
+    public ServerErrorRetryStrategy(final int maxRetries, final int 
retryInterval, final boolean logRetries) {
+       this(maxRetries, retryInterval, logRetries, "");
     }
 
     public ServerErrorRetryStrategy(int maxRetries, int retryInterval) {
@@ -58,9 +82,7 @@ public class ServerErrorRetryStrategy implements 
ServiceUnavailableRetryStrategy
 
     @Override
     public boolean retryRequest(final HttpResponse response, final int 
executionCount, final HttpContext context) {
-        boolean needsRetry = executionCount <= this.maxRetries &&
-                response.getStatusLine().getStatusCode() >= 
HttpStatus.SC_INTERNAL_SERVER_ERROR &&
-                response.getStatusLine().getStatusCode() < 
HttpStatus.SC_INTERNAL_SERVER_ERROR + 100;
+        boolean needsRetry = executionCount <= this.maxRetries && 
responseRetryCondition(response);
 
         if (this.logRetries && needsRetry && LOG.isWarnEnabled()) {
             LOG.warn("Request retry needed due to service unavailable 
response");
@@ -81,4 +103,13 @@ public class ServerErrorRetryStrategy implements 
ServiceUnavailableRetryStrategy
         return retryInterval;
     }
 
+    private boolean responseRetryCondition(final HttpResponse response) {
+        final Integer statusCode = response.getStatusLine().getStatusCode();
+        if (retryErrorCodes != null && !retryErrorCodes.isEmpty()) {
+            return retryErrorCodes.contains(statusCode);
+        } else {
+            return statusCode >= SC_INTERNAL_SERVER_ERROR &&
+                    statusCode < SC_INTERNAL_SERVER_ERROR + 100;
+        }
+    }
 }
diff --git 
a/src/main/java/org/apache/sling/testing/clients/util/package-info.java 
b/src/main/java/org/apache/sling/testing/clients/util/package-info.java
index d4c0bc1..d497a5a 100644
--- a/src/main/java/org/apache/sling/testing/clients/util/package-info.java
+++ b/src/main/java/org/apache/sling/testing/clients/util/package-info.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-@Version("1.1.0")
+@Version("1.2.0")
 package org.apache.sling.testing.clients.util;
 
 import org.osgi.annotation.versioning.Version;
diff --git 
a/src/test/java/org/apache/sling/testing/clients/SlingClientRetryStrategyTest.java
 
b/src/test/java/org/apache/sling/testing/clients/SlingClientRetryStrategyTest.java
index 23f3b63..60d782e 100644
--- 
a/src/test/java/org/apache/sling/testing/clients/SlingClientRetryStrategyTest.java
+++ 
b/src/test/java/org/apache/sling/testing/clients/SlingClientRetryStrategyTest.java
@@ -28,18 +28,20 @@ public class SlingClientRetryStrategyTest {
     private static final String GET_UNAVAILABLE_PATH = 
"/test/unavailable/resource";
     private static final String GET_INEXISTENT_PATH = 
"/test/inexistent/resource";
     private static final String GET_INTERNAL_ERROR_PATH = 
"/test/internalerror/resource";
+    private static final String GET_505_PATH = 
"/test/unsupportedversion/resource";
     private static final String NOK_RESPONSE = "TEST_NOK";
     private static final String OK_RESPONSE = "TEST_OK";
 
-    private static final int MAX_RETRIES = 5;
+    private static final int MAX_RETRIES = 4;
 
     private static int requestCount = 0;
     private static int availableAtRequestCount = Integer.MAX_VALUE;
 
     static {
-        System.setProperty(Constants.CONFIG_PROP_PREFIX + 
Constants.HTTP_LOG_RETRIES, "true");
-        System.setProperty(Constants.CONFIG_PROP_PREFIX + 
Constants.HTTP_DELAY, "50");
-        System.setProperty(Constants.CONFIG_PROP_PREFIX + 
Constants.HTTP_RETRIES, "5");
+        System.setProperty(Constants.CONFIG_PROP_PREFIX + 
Constants.HTTP_LOG_RETRIES_PROP, "true");
+        System.setProperty(Constants.CONFIG_PROP_PREFIX + 
Constants.HTTP_DELAY_PROP, "50");
+        System.setProperty(Constants.CONFIG_PROP_PREFIX + 
Constants.HTTP_RETRIES_PROP, "4");
+        System.setProperty(Constants.CONFIG_PROP_PREFIX + 
Constants.HTTP_RETRIES_ERROR_CODES_PROP, "500,503");
     }
 
     @ClassRule
@@ -73,6 +75,17 @@ public class SlingClientRetryStrategyTest {
                 response.setEntity(new StringEntity(NOK_RESPONSE));
                 response.setStatusCode(404);
             });
+
+            serverBootstrap.registerHandler(GET_505_PATH, (request, response, 
context) -> {
+                requestCount++;
+                if (requestCount == availableAtRequestCount) {
+                    response.setEntity(new StringEntity(OK_RESPONSE));
+                    response.setStatusCode(200);
+                } else {
+                    response.setEntity(new StringEntity(NOK_RESPONSE));
+                    response.setStatusCode(505);
+                }
+            });
         }
     };
 
@@ -97,6 +110,16 @@ public class SlingClientRetryStrategyTest {
     }
 
     @Test
+    public void test505ShouldNotRetry() throws Exception {
+        requestCount = 0;
+        availableAtRequestCount = Integer.MAX_VALUE; // never 200
+        SlingClient c = new SlingClient(httpServer.getURI(), "user", "pass");
+        SlingHttpResponse slingHttpResponse = c.doGet(GET_505_PATH, 505);
+        assertEquals(1, requestCount);
+        assertEquals(NOK_RESPONSE, slingHttpResponse.getContent());
+    }
+
+    @Test
     public void testRetryInexistent() throws Exception {
         requestCount = 0;
         availableAtRequestCount = Integer.MAX_VALUE; // never available

Reply via email to