summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Lenz <thomas.lenz@egiz.gv.at>2020-05-11 19:19:25 +0200
committerThomas Lenz <thomas.lenz@egiz.gv.at>2020-05-11 19:19:25 +0200
commitb5aeeac822bfe1a734835e3aa0caa65b56b3643a (patch)
tree502032aeba711a17d24aa16c67ac47e7041d8685
parentf5d3668fbc9f2e261185bbf110ddf867d7004b2a (diff)
downloadEAAF-Components-b5aeeac822bfe1a734835e3aa0caa65b56b3643a.tar.gz
EAAF-Components-b5aeeac822bfe1a734835e3aa0caa65b56b3643a.tar.bz2
EAAF-Components-b5aeeac822bfe1a734835e3aa0caa65b56b3643a.zip
update HttpClientFactory to facilitate request retrying in case of an error
-rw-r--r--eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/EaafHttpRequestRetryHandler.java33
-rw-r--r--eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientConfiguration.java8
-rw-r--r--eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientFactory.java44
-rw-r--r--eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/IHttpClientFactory.java4
-rw-r--r--eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/http/HttpClientFactoryTest.java206
-rw-r--r--eaaf_core_utils/src/test/resources/data/config1.properties6
6 files changed, 292 insertions, 9 deletions
diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/EaafHttpRequestRetryHandler.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/EaafHttpRequestRetryHandler.java
new file mode 100644
index 00000000..3aa908e8
--- /dev/null
+++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/EaafHttpRequestRetryHandler.java
@@ -0,0 +1,33 @@
+package at.gv.egiz.eaaf.core.impl.http;
+
+import java.net.UnknownHostException;
+import java.util.Arrays;
+
+import javax.net.ssl.SSLException;
+
+import org.apache.http.client.HttpRequestRetryHandler;
+import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
+
+public class EaafHttpRequestRetryHandler extends DefaultHttpRequestRetryHandler implements
+ HttpRequestRetryHandler {
+
+ /**
+ * Create the request retry handler using the following list of non-retriable.
+ * IOException classes: <br>
+ * <ul>
+ * <li>UnknownHostException</li>
+ * <li>SSLException</li>
+ * </ul>
+ *
+ * @param retryCount how many times to retry; 0 means no retries
+ * @param requestSentRetryEnabled true if it's OK to retry non-idempotent
+ * requests that have been sent
+ */
+ public EaafHttpRequestRetryHandler(final int retryCount, final boolean requestSentRetryEnabled) {
+ super(retryCount, requestSentRetryEnabled, Arrays.asList(
+ UnknownHostException.class,
+ SSLException.class));
+
+ }
+
+}
diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientConfiguration.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientConfiguration.java
index 6a66dfff..ec7d115a 100644
--- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientConfiguration.java
+++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientConfiguration.java
@@ -56,7 +56,13 @@ public class HttpClientConfiguration {
@Setter
private boolean followHttpRedirects = true;
-
+
+ @Setter
+ private int httpErrorRetryCount = 3;
+
+ @Setter
+ private boolean httpErrorRetryPost = false;
+
/**
* Get a new HTTP-client configuration object.
*
diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientFactory.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientFactory.java
index 4e811eaa..b53226ce 100644
--- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientFactory.java
+++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientFactory.java
@@ -65,7 +65,11 @@ public class HttpClientFactory implements IHttpClientFactory {
public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_CONNECTION =
"client.http.connection.timeout.connection";
public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_REQUEST =
- "client.http.connection.timeout.request";
+ "client.http.connection.timeout.request";
+ public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_RETRY_COUNT =
+ "client.http.connection.retry.count";
+ public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_RETRY_POST =
+ "client.http.connection.retry.post";
public static final String PROP_CONFIG_CLIENT_HTTP_SSL_HOSTNAMEVERIFIER_TRUSTALL =
"client.http.ssl.hostnameverifier.trustall";
@@ -91,7 +95,9 @@ public class HttpClientFactory implements IHttpClientFactory {
public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_REQUEST = "30";
public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL = "500";
public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXPERROUTE = "100";
-
+ public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_RETRY_COUNT = "3";
+ public static final String DEFAUTL_CONFIG_CLIENT_HTTP_CONNECTION_RETRY_POST = String.valueOf(false);
+
private String defaultConfigurationId = null;
private final Map<String, HttpClientBuilder> availableBuilders = new HashMap<>();
@@ -127,8 +133,11 @@ public class HttpClientFactory implements IHttpClientFactory {
config.validate();
builder = HttpClients.custom();
+
+ //inject request configuration
builder.setDefaultRequestConfig(buildDefaultRequestConfig());
-
+ injectInternalRetryHandler(builder, config);
+
//inject basic authentication infos
injectBasicAuthenticationIfRequired(builder, config);
@@ -147,6 +156,22 @@ public class HttpClientFactory implements IHttpClientFactory {
}
+ private void injectInternalRetryHandler(HttpClientBuilder builder, HttpClientConfiguration config) {
+ if (config.getHttpErrorRetryCount() > 0) {
+ log.info("Set HTTP error-retry to {} for http-client: {}",
+ config.getHttpErrorRetryCount(), config.getFriendlyName());
+ builder.setRetryHandler(new EaafHttpRequestRetryHandler(
+ config.getHttpErrorRetryCount(),
+ config.isHttpErrorRetryPost()));
+
+ } else {
+ log.info("Disable HTTP error-retry for http-client: {}", config.getFriendlyName());
+ builder.disableAutomaticRetries();
+
+ }
+
+ }
+
@PostConstruct
private void initalize() throws EaafException {
final HttpClientConfiguration defaultHttpClientConfig = buildDefaultHttpClientConfiguration();
@@ -157,7 +182,8 @@ public class HttpClientFactory implements IHttpClientFactory {
// set default request configuration
defaultHttpClientBuilder.setDefaultRequestConfig(buildDefaultRequestConfig());
-
+ injectInternalRetryHandler(defaultHttpClientBuilder, defaultHttpClientConfig);
+
//inject http basic authentication
injectBasicAuthenticationIfRequired(defaultHttpClientBuilder, defaultHttpClientConfig);
@@ -205,6 +231,14 @@ public class HttpClientFactory implements IHttpClientFactory {
config.setDisableHostnameValidation(basicConfig.getBasicConfigurationBoolean(
PROP_CONFIG_CLIENT_HTTP_SSL_HOSTNAMEVERIFIER_TRUSTALL, false));
+ config.setHttpErrorRetryCount(Integer.parseInt(basicConfig.getBasicConfiguration(
+ PROP_CONFIG_CLIENT_HTTP_CONNECTION_RETRY_COUNT,
+ DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_RETRY_COUNT)));
+ config.setHttpErrorRetryPost(Boolean.parseBoolean(basicConfig.getBasicConfiguration(
+ PROP_CONFIG_CLIENT_HTTP_CONNECTION_RETRY_POST,
+ DEFAUTL_CONFIG_CLIENT_HTTP_CONNECTION_RETRY_POST)));
+
+
// validate configuration object
config.validate();
@@ -324,7 +358,7 @@ public class HttpClientFactory implements IHttpClientFactory {
.setSocketTimeout(Integer.parseInt(
basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET,
DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET))
- * 1000)
+ * 1000)
.build();
return requestConfig;
diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/IHttpClientFactory.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/IHttpClientFactory.java
index 7ec58d46..4e8374e1 100644
--- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/IHttpClientFactory.java
+++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/IHttpClientFactory.java
@@ -2,10 +2,10 @@ package at.gv.egiz.eaaf.core.impl.http;
import javax.annotation.Nonnull;
-import at.gv.egiz.eaaf.core.exceptions.EaafException;
-
import org.apache.http.impl.client.CloseableHttpClient;
+import at.gv.egiz.eaaf.core.exceptions.EaafException;
+
public interface IHttpClientFactory {
/**
diff --git a/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/http/HttpClientFactoryTest.java b/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/http/HttpClientFactoryTest.java
index 72ec7008..63ac38a3 100644
--- a/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/http/HttpClientFactoryTest.java
+++ b/eaaf_core_utils/src/test/java/at/gv/egiz/eaaf/core/test/http/HttpClientFactoryTest.java
@@ -3,6 +3,7 @@ package at.gv.egiz.eaaf.core.test.http;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.InetAddress;
+import java.net.SocketTimeoutException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.Provider;
@@ -12,6 +13,7 @@ import org.apache.commons.lang3.RandomStringUtils;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.CloseableHttpClient;
import org.junit.After;
@@ -32,6 +34,7 @@ import okhttp3.HttpUrl;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
+import okhttp3.mockwebserver.SocketPolicy;
import okhttp3.tls.HandshakeCertificates;
import okhttp3.tls.HeldCertificate;
@@ -84,6 +87,27 @@ public class HttpClientFactoryTest {
}
@Test
+ public void defaultHttpClientRetryOneTime() throws EaafException, InterruptedException,
+ ClientProtocolException, IOException {
+ CloseableHttpClient client = httpClientFactory.getHttpClient();
+ Assert.assertNotNull("No httpClient", client);
+
+ mockWebServer = new MockWebServer();
+ mockServerUrl = mockWebServer.url("/sp/junit");
+ mockWebServer.enqueue(new MockResponse()
+ .setSocketPolicy(SocketPolicy.NO_RESPONSE)
+ .setResponseCode(HttpURLConnection.HTTP_NO_CONTENT));
+ mockWebServer.enqueue(new MockResponse().setResponseCode(200)
+ .setBody("GetData"));
+
+ //request webservice
+ final HttpUriRequest httpGet1 = new HttpGet(mockServerUrl.url().toString());
+ final CloseableHttpResponse httpResp1 = client.execute(httpGet1);
+ Assert.assertEquals("http statusCode", 200, httpResp1.getStatusLine().getStatusCode());
+
+ }
+
+ @Test
public void getCustomClientsDefault() throws EaafException {
final HttpClientConfiguration config = new HttpClientConfiguration("jUnit");
Assert.assertFalse("Wrong default config - Hostnamevalidation",
@@ -157,6 +181,187 @@ public class HttpClientFactoryTest {
}
@Test
+ public void httpPostRetryNotAllowed() throws EaafException, InterruptedException,
+ ClientProtocolException, IOException {
+ final HttpClientConfiguration config =
+ new HttpClientConfiguration("jUnit_retry_" + RandomStringUtils.randomAlphabetic(3));
+ config.setHttpErrorRetryCount(2);
+ config.setHttpErrorRetryPost(false);
+
+ CloseableHttpClient client = httpClientFactory.getHttpClient(config);
+ Assert.assertNotNull("No httpClient", client);
+
+
+ mockWebServer = new MockWebServer();
+ mockServerUrl = mockWebServer.url("/sp/junit");
+ mockWebServer.enqueue(new MockResponse()
+ .setSocketPolicy(SocketPolicy.NO_RESPONSE)
+ .setResponseCode(HttpURLConnection.HTTP_NO_CONTENT));
+ mockWebServer.enqueue(new MockResponse().setResponseCode(200)
+ .setBody("GetData"));
+
+ //request webservice
+ final HttpUriRequest httpGet1 = new HttpPost(mockServerUrl.url().toString());
+ try {
+ client.execute(httpGet1);
+ Assert.fail("HTTP POST retry not allowed");
+
+ } catch (SocketTimeoutException e) {
+ Assert.assertNotNull("No errorMsg", e.getMessage());
+
+ }
+
+ }
+
+ @Test
+ public void httpPostRetryOneTime() throws EaafException, InterruptedException,
+ ClientProtocolException, IOException {
+ final HttpClientConfiguration config =
+ new HttpClientConfiguration("jUnit_retry_" + RandomStringUtils.randomAlphabetic(3));
+ config.setHttpErrorRetryCount(2);
+ config.setHttpErrorRetryPost(true);
+
+ CloseableHttpClient client = httpClientFactory.getHttpClient(config);
+ Assert.assertNotNull("No httpClient", client);
+
+
+ mockWebServer = new MockWebServer();
+ mockServerUrl = mockWebServer.url("/sp/junit");
+ mockWebServer.enqueue(new MockResponse()
+ .setSocketPolicy(SocketPolicy.NO_RESPONSE)
+ .setResponseCode(HttpURLConnection.HTTP_NO_CONTENT));
+ mockWebServer.enqueue(new MockResponse().setResponseCode(200)
+ .setBody("GetData"));
+
+ //request webservice
+ final HttpUriRequest httpGet1 = new HttpPost(mockServerUrl.url().toString());
+ final CloseableHttpResponse httpResp1 = client.execute(httpGet1);
+ Assert.assertEquals("http statusCode", 200, httpResp1.getStatusLine().getStatusCode());
+
+ }
+
+ @Test
+ public void testHttpClientRetryOneTime() throws EaafException, InterruptedException,
+ ClientProtocolException, IOException {
+ final HttpClientConfiguration config =
+ new HttpClientConfiguration("jUnit_retry_" + RandomStringUtils.randomAlphabetic(3));
+ config.setHttpErrorRetryCount(2);
+
+ CloseableHttpClient client = httpClientFactory.getHttpClient(config);
+ Assert.assertNotNull("No httpClient", client);
+
+
+ mockWebServer = new MockWebServer();
+ mockServerUrl = mockWebServer.url("/sp/junit");
+ mockWebServer.enqueue(new MockResponse()
+ .setSocketPolicy(SocketPolicy.NO_RESPONSE)
+ .setResponseCode(HttpURLConnection.HTTP_NO_CONTENT));
+ mockWebServer.enqueue(new MockResponse().setResponseCode(200)
+ .setBody("GetData"));
+
+ //request webservice
+ final HttpUriRequest httpGet1 = new HttpGet(mockServerUrl.url().toString());
+ final CloseableHttpResponse httpResp1 = client.execute(httpGet1);
+ Assert.assertEquals("http statusCode", 200, httpResp1.getStatusLine().getStatusCode());
+
+ }
+
+ @Test
+ public void testHttpClientRetryTwoTime() throws EaafException, InterruptedException,
+ ClientProtocolException, IOException {
+ final HttpClientConfiguration config =
+ new HttpClientConfiguration("jUnit_retry_" + RandomStringUtils.randomAlphabetic(3));
+ config.setHttpErrorRetryCount(2);
+
+ CloseableHttpClient client = httpClientFactory.getHttpClient(config);
+ Assert.assertNotNull("No httpClient", client);
+
+
+ mockWebServer = new MockWebServer();
+ mockServerUrl = mockWebServer.url("/sp/junit");
+ mockWebServer.enqueue(new MockResponse()
+ .setSocketPolicy(SocketPolicy.NO_RESPONSE)
+ .setResponseCode(HttpURLConnection.HTTP_NO_CONTENT));
+ mockWebServer.enqueue(new MockResponse()
+ .setSocketPolicy(SocketPolicy.NO_RESPONSE)
+ .setResponseCode(HttpURLConnection.HTTP_NO_CONTENT));
+ mockWebServer.enqueue(new MockResponse().setResponseCode(200)
+ .setBody("GetData"));
+
+ //request webservice
+ final HttpUriRequest httpGet1 = new HttpGet(mockServerUrl.url().toString());
+ final CloseableHttpResponse httpResp1 = client.execute(httpGet1);
+ Assert.assertEquals("http statusCode", 200, httpResp1.getStatusLine().getStatusCode());
+
+ }
+
+ @Test
+ public void testHttpClientRetryMaxReached() throws EaafException, InterruptedException,
+ ClientProtocolException, IOException {
+ final HttpClientConfiguration config =
+ new HttpClientConfiguration("jUnit_retry_" + RandomStringUtils.randomAlphabetic(3));
+ config.setHttpErrorRetryCount(2);
+
+ CloseableHttpClient client = httpClientFactory.getHttpClient(config);
+ Assert.assertNotNull("No httpClient", client);
+
+ mockWebServer = new MockWebServer();
+ mockServerUrl = mockWebServer.url("/sp/junit");
+ mockWebServer.enqueue(new MockResponse()
+ .setSocketPolicy(SocketPolicy.NO_RESPONSE)
+ .setResponseCode(HttpURLConnection.HTTP_NO_CONTENT));
+ mockWebServer.enqueue(new MockResponse()
+ .setSocketPolicy(SocketPolicy.NO_RESPONSE)
+ .setResponseCode(HttpURLConnection.HTTP_NO_CONTENT));
+ mockWebServer.enqueue(new MockResponse()
+ .setSocketPolicy(SocketPolicy.NO_RESPONSE)
+ .setResponseCode(HttpURLConnection.HTTP_NO_CONTENT));
+ mockWebServer.enqueue(new MockResponse().setResponseCode(200)
+ .setBody("GetData"));
+
+ //request webservice
+ final HttpUriRequest httpGet1 = new HttpGet(mockServerUrl.url().toString());
+ try {
+ client.execute(httpGet1);
+ Assert.fail("Max retry failed");
+
+ } catch (SocketTimeoutException e) {
+ Assert.assertNotNull("No errorMsg", e.getMessage());
+
+ }
+ }
+
+ @Test
+ public void testHttpClientNoRetry() throws EaafException, InterruptedException,
+ ClientProtocolException, IOException {
+ final HttpClientConfiguration config =
+ new HttpClientConfiguration("jUnit_retry_" + RandomStringUtils.randomAlphabetic(3));
+ config.setHttpErrorRetryCount(0);
+
+ CloseableHttpClient client = httpClientFactory.getHttpClient(config);
+ Assert.assertNotNull("No httpClient", client);
+
+ mockWebServer = new MockWebServer();
+ mockServerUrl = mockWebServer.url("/sp/junit");
+ mockWebServer.enqueue(new MockResponse()
+ .setSocketPolicy(SocketPolicy.NO_RESPONSE)
+ .setResponseCode(HttpURLConnection.HTTP_NO_CONTENT));
+ mockWebServer.enqueue(new MockResponse().setResponseCode(200)
+ .setBody("GetData"));
+
+ //request webservice
+ final HttpUriRequest httpGet1 = new HttpGet(mockServerUrl.url().toString());
+ try {
+ client.execute(httpGet1);
+ Assert.fail("Max retry failed");
+
+ } catch (SocketTimeoutException e) {
+ Assert.assertNotNull("No errorMsg", e.getMessage());
+
+ }
+ }
+
+ @Test
public void getCustomClientBasicAuthNoPassword() throws EaafException {
final HttpClientConfiguration config = new HttpClientConfiguration("jUnit");
config.setAuthMode("password");
@@ -370,4 +575,5 @@ public class HttpClientFactoryTest {
Assert.assertEquals("http statusCode", 200, httpResp2.getStatusLine().getStatusCode());
}
+
}
diff --git a/eaaf_core_utils/src/test/resources/data/config1.properties b/eaaf_core_utils/src/test/resources/data/config1.properties
index 25bd201f..93729b15 100644
--- a/eaaf_core_utils/src/test/resources/data/config1.properties
+++ b/eaaf_core_utils/src/test/resources/data/config1.properties
@@ -2,4 +2,8 @@ security.hsmfacade.host=eid.a-sit.at
security.hsmfacade.port=9050
security.hsmfacade.trustedsslcert=src/test/resources/data/hsm_facade_trust_root.crt
security.hsmfacade.username=authhandler-junit
-security.hsmfacade.password=supersecret123 \ No newline at end of file
+security.hsmfacade.password=supersecret123
+
+client.http.connection.timeout.socket=1
+client.http.connection.timeout.connection=1
+client.http.connection.timeout.request=1 \ No newline at end of file