Skip to content

Commit 42846e3

Browse files
authored
Merge pull request #127 from cloudsufi/bugfix/PLUGIN-1928
PLUGIN-1928: Set connect & read timeout to http client and retry ConnectTimeoutException & SocketException
2 parents a9a595b + c48e18b commit 42846e3

File tree

3 files changed

+71
-2
lines changed

3 files changed

+71
-2
lines changed

src/main/java/io/cdap/plugin/servicenow/apiclient/ServiceNowAPIException.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44

55
import org.apache.http.HttpResponse;
66
import org.apache.http.HttpStatus;
7+
import org.apache.http.conn.ConnectTimeoutException;
78
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
89

10+
import java.net.SocketException;
911
import java.util.Arrays;
1012
import java.util.HashSet;
1113
import java.util.Set;
@@ -65,6 +67,8 @@ public boolean isErrorRetryable() {
6567
}
6668
Throwable t = this.getCause();
6769
return t instanceof OAuthSystemException
70+
|| t instanceof ConnectTimeoutException
71+
|| t instanceof SocketException
6872
|| (this.getMessage() != null
6973
&& this.getMessage().contains(ServiceNowConstants.MAXIMUM_EXECUTION_TIME_EXCEEDED))
7074
|| RETRYABLE_CODES.contains(getStatusCode());

src/main/java/io/cdap/plugin/servicenow/restapi/RestAPIClient.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@
2424
import com.github.rholder.retry.WaitStrategies;
2525
import io.cdap.plugin.servicenow.apiclient.ServiceNowAPIException;
2626
import io.cdap.plugin.servicenow.util.ServiceNowConstants;
27+
import org.apache.http.client.config.RequestConfig;
2728
import org.apache.http.client.methods.CloseableHttpResponse;
2829
import org.apache.http.client.methods.HttpGet;
2930
import org.apache.http.client.methods.HttpPost;
31+
import org.apache.http.conn.ConnectTimeoutException;
3032
import org.apache.http.impl.client.CloseableHttpClient;
3133
import org.apache.http.impl.client.HttpClientBuilder;
3234
import org.apache.oltu.oauth2.client.OAuthClient;
@@ -41,6 +43,8 @@
4143
import org.slf4j.LoggerFactory;
4244

4345
import java.io.IOException;
46+
import java.net.SocketException;
47+
import java.util.Collections;
4448
import java.util.concurrent.Callable;
4549
import java.util.concurrent.ExecutionException;
4650
import java.util.concurrent.TimeUnit;
@@ -50,6 +54,17 @@
5054
*/
5155
public abstract class RestAPIClient {
5256
private static final Logger LOG = LoggerFactory.getLogger(RestAPIClient.class);
57+
58+
/* Connect Timout in ms for establishing the conenction with the server */
59+
private static final int DEFAULT_CONNECT_TIMEOUT_MS = 120000;
60+
61+
/* Read Timeout in ms for waiting for data after the connection is established */
62+
private static final int DEFAULT_READ_TIMEOUT_MS = 300000;
63+
64+
private static final RequestConfig requestConfig = RequestConfig.custom()
65+
.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT_MS)
66+
.setSocketTimeout(DEFAULT_READ_TIMEOUT_MS)
67+
.build();
5368

5469
/**
5570
* Executes the Rest API request and returns the response.
@@ -61,10 +76,13 @@ public RestAPIResponse executeGet(RestAPIRequest request) throws IOException {
6176
HttpGet httpGet = new HttpGet(request.getUrl());
6277
request.getHeaders().entrySet().forEach(e -> httpGet.addHeader(e.getKey(), e.getValue()));
6378

64-
try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) {
79+
try (CloseableHttpClient httpClient = HttpClientBuilder.create().setDefaultRequestConfig(requestConfig).build()) {
6580
try (CloseableHttpResponse httpResponse = httpClient.execute(httpGet)) {
6681
return RestAPIResponse.parse(httpResponse, request.getResponseHeaders());
6782
}
83+
} catch (ConnectTimeoutException | SocketException e) {
84+
ServiceNowAPIException exception = new ServiceNowAPIException(e, null);
85+
return new RestAPIResponse(Collections.emptyMap(), null, exception);
6886
}
6987
}
7088

@@ -136,7 +154,7 @@ public RestAPIResponse executePost(RestAPIRequest request) throws IOException {
136154
// We're retrying all transport exceptions while executing the HTTP POST method and the generic transport
137155
// exceptions in HttpClient are represented by the standard java.io.IOException class
138156
// https://hc.apache.org/httpclient-legacy/exception-handling.html
139-
try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) {
157+
try (CloseableHttpClient httpClient = HttpClientBuilder.create().setDefaultRequestConfig(requestConfig).build()) {
140158
try (CloseableHttpResponse httpResponse = httpClient.execute(httpPost)) {
141159
return RestAPIResponse.parse(httpResponse, request.getResponseHeaders());
142160
}

src/test/java/io/cdap/plugin/servicenow/restapi/RestAPIClientTest.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import org.apache.http.HttpStatus;
77
import org.apache.http.StatusLine;
88
import org.apache.http.client.methods.CloseableHttpResponse;
9+
import org.apache.http.conn.ConnectTimeoutException;
910
import org.apache.http.impl.client.CloseableHttpClient;
1011
import org.apache.http.impl.client.HttpClientBuilder;
1112
import org.apache.http.message.BasicStatusLine;
@@ -19,6 +20,7 @@
1920
import org.powermock.modules.junit4.PowerMockRunner;
2021

2122
import java.io.IOException;
23+
import java.net.SocketException;
2224

2325
@RunWith(PowerMockRunner.class)
2426
@PrepareForTest({
@@ -102,4 +104,49 @@ public void testExecuteGet_StatusOk() throws IOException {
102104
ServiceNowTableAPIClientImpl client = new ServiceNowTableAPIClientImpl(config, true);
103105
client.executeGet(request);
104106
}
107+
108+
@Test
109+
public void testExecuteGet_throwConnectTimeoutException_markAsRetryable() throws IOException {
110+
CloseableHttpClient httpClient = Mockito.mock(CloseableHttpClient.class);
111+
HttpClientBuilder httpClientBuilder = Mockito.mock(HttpClientBuilder.class);
112+
PowerMockito.mockStatic(HttpClientBuilder.class);
113+
PowerMockito.when(HttpClientBuilder.create()).thenReturn(httpClientBuilder);
114+
Mockito.when(httpClientBuilder.build()).thenReturn(httpClient);
115+
Mockito.when(httpClient.execute(Mockito.any()))
116+
.thenThrow(new ConnectTimeoutException("Connection timed out"));
117+
118+
ServiceNowTableAPIRequestBuilder builder = new ServiceNowTableAPIRequestBuilder("url");
119+
RestAPIRequest request = builder.build();
120+
121+
ServiceNowConnectorConfig config = Mockito.mock(ServiceNowConnectorConfig.class);
122+
ServiceNowTableAPIClientImpl client = new ServiceNowTableAPIClientImpl(config, true);
123+
RestAPIResponse actualResponse = client.executeGet(request);
124+
Assert.assertNotNull(actualResponse.getException());
125+
Assert.assertTrue(actualResponse.getException().isErrorRetryable());
126+
Throwable ex = actualResponse.getException().getCause();
127+
Assert.assertTrue("Expected ConnectTimeoutException or similar, got: " + ex,
128+
ex instanceof ConnectTimeoutException || ex.getMessage().toLowerCase().contains("Connection timed out"));
129+
}
130+
131+
@Test
132+
public void testExecuteGet_throwSocketException_markAsRetryable() throws IOException {
133+
CloseableHttpClient httpClient = Mockito.mock(CloseableHttpClient.class);
134+
HttpClientBuilder httpClientBuilder = Mockito.mock(HttpClientBuilder.class);
135+
PowerMockito.mockStatic(HttpClientBuilder.class);
136+
PowerMockito.when(HttpClientBuilder.create()).thenReturn(httpClientBuilder);
137+
Mockito.when(httpClientBuilder.build()).thenReturn(httpClient);
138+
Mockito.when(httpClient.execute(Mockito.any()))
139+
.thenThrow(new SocketException());
140+
141+
ServiceNowTableAPIRequestBuilder builder = new ServiceNowTableAPIRequestBuilder("url");
142+
RestAPIRequest request = builder.build();
143+
144+
ServiceNowConnectorConfig config = Mockito.mock(ServiceNowConnectorConfig.class);
145+
ServiceNowTableAPIClientImpl client = new ServiceNowTableAPIClientImpl(config, true);
146+
RestAPIResponse actualResponse = client.executeGet(request);
147+
Assert.assertNotNull(actualResponse.getException());
148+
Assert.assertTrue(actualResponse.getException().isErrorRetryable());
149+
Throwable ex = actualResponse.getException().getCause();
150+
Assert.assertTrue("Expected SocketException or similar, got: " + ex, ex instanceof SocketException);
151+
}
105152
}

0 commit comments

Comments
 (0)