You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by du...@apache.org on 2020/02/13 15:00:33 UTC
[sling-org-apache-sling-testing-clients] 01/01: SLING-9053
Implement retries mechanism for http 5XX response codes
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
commit 3d658ce650da482056d61d131cc48d4890cbc94b
Author: Andrei Dulvac <ad...@adobe.com>
AuthorDate: Thu Feb 13 16:00:07 2020 +0100
SLING-9053 Implement retries mechanism for http 5XX response codes
---
.../apache/sling/testing/clients/Constants.java | 24 ++--
.../apache/sling/testing/clients/SlingClient.java | 13 +-
.../interceptors/FormBasedAuthInterceptor.java | 6 +-
.../LoggedServiceUnavailableRetryStrategy.java | 56 ---------
.../clients/util/ServerErrorRetryStrategy.java | 84 +++++++++++++
.../SlingClientRetryServiceUnavailableTest.java | 90 --------------
.../clients/SlingClientRetryStrategyTest.java | 132 +++++++++++++++++++++
7 files changed, 242 insertions(+), 163 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 18eafc1..43a2cf6 100644
--- a/src/main/java/org/apache/sling/testing/clients/Constants.java
+++ b/src/main/java/org/apache/sling/testing/clients/Constants.java
@@ -16,10 +16,6 @@
*/
package org.apache.sling.testing.clients;
-import org.apache.http.client.ServiceUnavailableRetryStrategy;
-import org.apache.http.impl.client.DefaultServiceUnavailableRetryStrategy;
-import org.apache.sling.testing.clients.util.LoggedServiceUnavailableRetryStrategy;
-
public class Constants {
/**
@@ -62,13 +58,21 @@ public class Constants {
public static final long HTTP_DELAY = delay;
/**
- * Custom ServiceUnavailableRetryStrategy.
- * Used by {@link org.apache.sling.testing.clients.SlingClient}
- * and {@link org.apache.sling.testing.clients.interceptors.FormBasedAuthInterceptor}
+ * Number of http call retries in case of a 5XX response code
*/
- public static final ServiceUnavailableRetryStrategy HTTP_RETRY_STRATEGY = logRetries
- ? new LoggedServiceUnavailableRetryStrategy(retries, retriesDelay)
- : new DefaultServiceUnavailableRetryStrategy(retries, retriesDelay);
+ public static final int HTTP_RETRIES = retries;
+
+ /**
+ * The delay in milliseconds between http retries
+ */
+ public static final int HTTP_RETRIES_DELAY = retriesDelay;
+
+ /**
+ * Whether to log or not http request retries
+ */
+ public static final boolean HTTP_LOG_RETRIES = logRetries;
+
+
/**
* Handle to OSGI console
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 4ecf731..15a20db 100644
--- a/src/main/java/org/apache/sling/testing/clients/SlingClient.java
+++ b/src/main/java/org/apache/sling/testing/clients/SlingClient.java
@@ -25,16 +25,16 @@ import org.apache.http.annotation.Immutable;
import org.apache.http.client.CookieStore;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.RedirectStrategy;
+import org.apache.http.client.ServiceUnavailableRetryStrategy;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.DefaultServiceUnavailableRetryStrategy;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.sling.testing.clients.interceptors.DelayRequestInterceptor;
import org.apache.sling.testing.clients.interceptors.TestDescriptionInterceptor;
-import org.apache.sling.testing.clients.util.FormEntityBuilder;
-import org.apache.sling.testing.clients.util.HttpUtils;
-import org.apache.sling.testing.clients.util.JsonUtils;
+import org.apache.sling.testing.clients.util.*;
import org.apache.sling.testing.clients.util.poller.AbstractPoller;
import org.apache.sling.testing.clients.util.poller.Polling;
import org.codehaus.jackson.JsonNode;
@@ -626,8 +626,7 @@ public class SlingClient extends AbstractSlingClient {
private final HttpClientBuilder httpClientBuilder;
protected InternalBuilder(URI url, String user, String password) {
- this.httpClientBuilder = HttpClientBuilder.create()
- .setServiceUnavailableRetryStrategy(Constants.HTTP_RETRY_STRATEGY);
+ this.httpClientBuilder = HttpClientBuilder.create();
this.configBuilder = SlingClientConfig.Builder.create().setUrl(url).setUser(user).setPassword(password);
setDefaults();
@@ -692,6 +691,10 @@ public class SlingClient extends AbstractSlingClient {
httpClientBuilder.addInterceptorLast(new TestDescriptionInterceptor());
httpClientBuilder.addInterceptorLast(new DelayRequestInterceptor(Constants.HTTP_DELAY));
+ // HTTP request strategy
+ httpClientBuilder.setServiceUnavailableRetryStrategy(new ServerErrorRetryStrategy(
+ Constants.HTTP_RETRIES, Constants.HTTP_RETRIES_DELAY, Constants.HTTP_LOG_RETRIES));
+
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 9360f4b..e7e251f 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
@@ -28,6 +28,7 @@ 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;
@@ -75,8 +76,9 @@ 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(Constants.HTTP_RETRY_STRATEGY)
+ final CloseableHttpClient client = HttpClientBuilder.create().setServiceUnavailableRetryStrategy(
+ new ServerErrorRetryStrategy(
+ Constants.HTTP_RETRIES, Constants.HTTP_RETRIES_DELAY, Constants.HTTP_LOG_RETRIES))
.disableRedirectHandling().build();
client.execute(host, loginPost, context);
diff --git a/src/main/java/org/apache/sling/testing/clients/util/LoggedServiceUnavailableRetryStrategy.java b/src/main/java/org/apache/sling/testing/clients/util/LoggedServiceUnavailableRetryStrategy.java
deleted file mode 100644
index 1303b7b..0000000
--- a/src/main/java/org/apache/sling/testing/clients/util/LoggedServiceUnavailableRetryStrategy.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with this
- * work for additional information regarding copyright ownership. The ASF
- * licenses this file to You 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.apache.sling.testing.clients.util;
-
-import org.apache.http.HttpResponse;
-import org.apache.http.annotation.Immutable;
-import org.apache.http.impl.client.DefaultServiceUnavailableRetryStrategy;
-import org.apache.http.protocol.HttpContext;
-import org.apache.http.util.EntityUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.util.Arrays;
-
-@Immutable
-public class LoggedServiceUnavailableRetryStrategy extends DefaultServiceUnavailableRetryStrategy {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(LoggedServiceUnavailableRetryStrategy.class);
-
- public LoggedServiceUnavailableRetryStrategy(final int maxRetries, final int retryInterval) {
- super(maxRetries, retryInterval);
- }
-
- @Override
- public boolean retryRequest(final HttpResponse response, final int executionCount, final HttpContext context) {
- boolean needRetry = super.retryRequest(response, executionCount, context);
- if (needRetry && LOGGER.isWarnEnabled()) {
- LOGGER.warn("Request retry needed due to service unavailable response");
- LOGGER.warn("Response headers contained:");
- Arrays.stream(response.getAllHeaders()).forEach(h -> LOGGER.warn("Header {}:{}", h.getName(), h.getValue()));
- try {
- String content = EntityUtils.toString(response.getEntity());
- LOGGER.warn("Response content: {}", content);
- } catch (IOException exc) {
- LOGGER.warn("Response as no content");
- }
- }
- return needRetry;
- }
-
-}
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
new file mode 100644
index 0000000..bcce9e4
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/clients/util/ServerErrorRetryStrategy.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You 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.apache.sling.testing.clients.util;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.ServiceUnavailableRetryStrategy;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.util.Args;
+import org.apache.http.util.EntityUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+/**
+ * {code ServiceUnavailableRetryStrategy} strategy for retrying request in case of a 5XX response code
+ */
+public class ServerErrorRetryStrategy implements ServiceUnavailableRetryStrategy {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ServerErrorRetryStrategy.class);
+
+ private final int maxRetries;
+ private final int retryInterval;
+ private final boolean logRetries;
+
+ public ServerErrorRetryStrategy(final int maxRetries, final int retryInterval, final boolean logRetries) {
+ super();
+ Args.positive(maxRetries, "Max retries");
+ Args.positive(retryInterval, "Retry interval");
+ this.maxRetries = maxRetries;
+ this.retryInterval = retryInterval;
+ this.logRetries = logRetries;
+ }
+
+ public ServerErrorRetryStrategy(int maxRetries, int retryInterval) {
+ this (1, 1000, true);
+ }
+
+ public ServerErrorRetryStrategy() {
+ this(1, 1000);
+ }
+
+ @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;
+
+ if (this.logRetries && needsRetry && LOG.isWarnEnabled()) {
+ LOG.warn("Request retry needed due to service unavailable response");
+ LOG.warn("Response headers contained:");
+ Arrays.stream(response.getAllHeaders()).forEach(h -> LOG.warn("Header {}:{}", h.getName(), h.getValue()));
+ try {
+ String content = EntityUtils.toString(response.getEntity());
+ LOG.warn("Response content: {}", content);
+ } catch (IOException exc) {
+ LOG.warn("Response as no content");
+ }
+ }
+ return needsRetry;
+ }
+
+ @Override
+ public long getRetryInterval() {
+ return retryInterval;
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/testing/clients/SlingClientRetryServiceUnavailableTest.java b/src/test/java/org/apache/sling/testing/clients/SlingClientRetryServiceUnavailableTest.java
deleted file mode 100644
index 2670983..0000000
--- a/src/test/java/org/apache/sling/testing/clients/SlingClientRetryServiceUnavailableTest.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with this
- * work for additional information regarding copyright ownership. The ASF
- * licenses this file to You 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.apache.sling.testing.clients;
-
-import org.apache.http.HttpException;
-import org.apache.http.HttpRequest;
-import org.apache.http.HttpResponse;
-import org.apache.http.entity.StringEntity;
-import org.apache.http.protocol.HttpContext;
-import org.apache.http.protocol.HttpRequestHandler;
-import org.junit.ClassRule;
-import org.junit.Test;
-
-import java.io.IOException;
-import java.util.concurrent.TimeoutException;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-public class SlingClientRetryServiceUnavailableTest {
- private static final String GET_UNAVAILABLE_PATH = "/test/unavailable/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 int requestCount = 0;
- private static int availableAtRequestCount = Integer.MAX_VALUE;
-
- static {
- System.setProperty(Constants.CONFIG_PROP_PREFIX + "http.logRetries", "true");
- }
-
- @ClassRule
- public static HttpServerRule httpServer = new HttpServerRule() {
- @Override
- protected void registerHandlers() throws IOException {
- serverBootstrap.registerHandler(GET_UNAVAILABLE_PATH, new HttpRequestHandler() {
- @Override
- public void handle(HttpRequest request, HttpResponse response, HttpContext context) throws HttpException, IOException {
- requestCount++;
- if (requestCount == availableAtRequestCount) {
- response.setEntity(new StringEntity(OK_RESPONSE));
- response.setStatusCode(200);
- } else {
- response.setEntity(new StringEntity(NOK_RESPONSE));
- response.setStatusCode(503);
- }
- }
- });
- }
- };
-
- @Test
- public void testRetryReallyUnavailable() throws Exception {
- requestCount = 0;
- availableAtRequestCount = Integer.MAX_VALUE; // never available
- SlingClient c = new SlingClient(httpServer.getURI(), "user", "pass");
- SlingHttpResponse slingHttpResponse = c.doGet(GET_UNAVAILABLE_PATH, 503, 10);
- assertEquals(MAX_RETRIES + 1, requestCount);
- assertEquals(NOK_RESPONSE, slingHttpResponse.getContent());
- }
-
- @Test
- public void testRetryEventuallyAvailable() throws Exception {
- requestCount = 0;
- availableAtRequestCount = 3;
- SlingClient c = new SlingClient(httpServer.getURI(), "user", "pass");
- SlingHttpResponse slingHttpResponse = c.doGet(GET_UNAVAILABLE_PATH, 200, 10);
- assertEquals(availableAtRequestCount, requestCount);
- assertEquals(OK_RESPONSE, slingHttpResponse.getContent());
-
- }
-
-}
diff --git a/src/test/java/org/apache/sling/testing/clients/SlingClientRetryStrategyTest.java b/src/test/java/org/apache/sling/testing/clients/SlingClientRetryStrategyTest.java
new file mode 100644
index 0000000..23f3b63
--- /dev/null
+++ b/src/test/java/org/apache/sling/testing/clients/SlingClientRetryStrategyTest.java
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You 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.apache.sling.testing.clients;
+
+import org.apache.http.entity.StringEntity;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import java.io.IOException;
+
+import static org.junit.Assert.assertEquals;
+
+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 NOK_RESPONSE = "TEST_NOK";
+ private static final String OK_RESPONSE = "TEST_OK";
+
+ private static final int MAX_RETRIES = 5;
+
+ 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");
+ }
+
+ @ClassRule
+ public static HttpServerRule httpServer = new HttpServerRule() {
+ @Override
+ protected void registerHandlers() throws IOException {
+ serverBootstrap.registerHandler(GET_UNAVAILABLE_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(503);
+ }
+ });
+
+ serverBootstrap.registerHandler(GET_INTERNAL_ERROR_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(500);
+ }
+ });
+
+ serverBootstrap.registerHandler(GET_INEXISTENT_PATH, (request, response, context) -> {
+ requestCount++;
+ response.setEntity(new StringEntity(NOK_RESPONSE));
+ response.setStatusCode(404);
+ });
+ }
+ };
+
+ @Test
+ public void testRetryReallyUnavailable() throws Exception {
+ requestCount = 0;
+ availableAtRequestCount = Integer.MAX_VALUE; // never available
+ SlingClient c = new SlingClient(httpServer.getURI(), "user", "pass");
+ SlingHttpResponse slingHttpResponse = c.doGet(GET_UNAVAILABLE_PATH, 503);
+ assertEquals(MAX_RETRIES + 1, requestCount);
+ assertEquals(NOK_RESPONSE, slingHttpResponse.getContent());
+ }
+
+ @Test
+ public void testRetryReallyInternalError() throws Exception {
+ requestCount = 0;
+ availableAtRequestCount = Integer.MAX_VALUE; // never available
+ SlingClient c = new SlingClient(httpServer.getURI(), "user", "pass");
+ SlingHttpResponse slingHttpResponse = c.doGet(GET_INTERNAL_ERROR_PATH, 500);
+ assertEquals(MAX_RETRIES + 1, requestCount);
+ assertEquals(NOK_RESPONSE, slingHttpResponse.getContent());
+ }
+
+ @Test
+ public void testRetryInexistent() throws Exception {
+ requestCount = 0;
+ availableAtRequestCount = Integer.MAX_VALUE; // never available
+ SlingClient c = new SlingClient(httpServer.getURI(), "user", "pass");
+ SlingHttpResponse slingHttpResponse = c.doGet(GET_INEXISTENT_PATH, 404);
+ // should not retry at all
+ assertEquals(1, requestCount);
+ assertEquals(NOK_RESPONSE, slingHttpResponse.getContent());
+ }
+
+ @Test
+ public void testRetryEventuallyAvailable() throws Exception {
+ requestCount = 0;
+ availableAtRequestCount = 3;
+ SlingClient c = new SlingClient(httpServer.getURI(), "user", "pass");
+ SlingHttpResponse slingHttpResponse = c.doGet(GET_UNAVAILABLE_PATH, 200);
+ assertEquals(availableAtRequestCount, requestCount);
+ assertEquals(OK_RESPONSE, slingHttpResponse.getContent());
+
+ }
+
+ @Test
+ public void testRetryEventuallyNoError() throws Exception {
+ requestCount = 0;
+ availableAtRequestCount = 3;
+ SlingClient c = new SlingClient(httpServer.getURI(), "user", "pass");
+ SlingHttpResponse slingHttpResponse = c.doGet(GET_INTERNAL_ERROR_PATH, 200);
+ assertEquals(availableAtRequestCount, requestCount);
+ assertEquals(OK_RESPONSE, slingHttpResponse.getContent());
+
+ }
+
+}