Skip to main content
Version: 10.2.9

Use Apache HttpClient

The instruction contains the best practices of the Apache HttpClient usage in the ODF 2 framework scope.

note

For information on Apache HttpClient, see the official documentation. Also, refer to the set of examples that demonstrate advanced usage scenarios.

Preconfigured HTTP client

The ODF 2 framework comes with the predefined and preconfigured Apache HttpClient instance. This instance is available as a part of the OdfCommonsModule module and can be injected into a Bot Task using the org.apache.http.impl.client.CloseableHttpClient class.

A typical CloseableHttpClient usage example looks as follows:

@BotTask
@Requires(OdfCommonsModule.class)
public class ExampleBotTask implements AdHocTask {

private final CloseableHttpClient httpClient;

@Inject
public ExampleBotTask(CloseableHttpClient httpClient) {
this.httpClient = httpClient;
}

@Override
public TaskRunnerOutput run(TaskInput taskInput) {
int statusCode = performGetRequest("https://google.com");

return taskInput.asResult()
.withColumn("status_code", String.valueOf(statusCode));
}

private int performGetRequest(String uri) {
try {
return httpClient.execute(new HttpGet(uri), response -> response.getStatusLine().getStatusCode());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

}

After you add the @Requires(OdfCommonsModule.class) annotation, the instance of CloseableHttpClient is ready to be injected into a Bot Task through a constructor. The ODF 2 framework is responsible for client creation and configuration and will close the client at the end of a Bot Task's lifecycle, ensuring resources are properly released.

When no custom settings are needed (such as specific SSL and TLS parameters or socket and connect timeouts), using the default CloseableHttpClient is preferable. Mind that the CloseableHttpClient instances are usually quite expensive to create. Therefore, it's better to re-use the same instance wherever possible. In addition, the instance of CloseableHttpClient is fully thread-safe and will let you execute multiple requests concurrently if needed. At the same time, CloseableHttpClient does not create or hold any threads on its own.

The ODF 2 framework ensures that the instance of CloseableHttpClient is created only when needed, for example, injected somewhere in a Bot Task. Due to the specifics of the Bot Task's lifecycle, the ODF 2 framework has to create a new instance of CloseableHttpClient on each separate record, not having the ability to persist the client between two records. That's why the Plugin-Cache-Supported classloading option should be enabled. This option dramatically reduces time and other resources needed to instantiate CloseableHttpClient for the second and subsequent records in a Bot Task. The option is enabled by default in all ODF 2 projects created from the ODF 2 archetype.

Also, mind to ensure a release of low-level resources when handling responses. The simplest and the most convenient way to handle responses is to use the ResponseHandler interface. When using ResponseHandler, the HTTP client automatically ensures release of the connection back to the connection manager regardless of whether the request execution succeeds or causes an exception.

The following listing demonstrates the usage of the ResponseHandler interface:

private int performGetRequest(String uri) {
HttpGet request = new HttpGet(uri);
ResponseHandler<Integer> responseHandler = response -> response.getStatusLine().getStatusCode();
try {
return httpClient.execute(request, responseHandler);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

It is also recommended to apply the try-with-resources statement for each CloseableHttpResponse instance:

private int performGetRequest(String uri) {
HttpGet request = new HttpGet(uri);
try (CloseableHttpResponse response = httpClient.execute(request)) {
return response.getStatusLine().getStatusCode();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

While CloseableHttpClient should have the default configuration applicable to all message exchanges, you can use HttpContext to customize individual request execution parameters:

HttpClientContext clientContext = HttpClientContext.create();
clientContext.setCookieStore(cookieStore);
clientContext.setCredentialsProvider(credentialsProvider);
clientContext.setRequestConfig(RequestConfig.custom()
.setConnectTimeout(10000)
.setSocketTimeout(10000)
.setCookieSpec(CookieSpecs.STANDARD)
.build());

httpClient.execute(request, clientContext);

HTTP client with custom settings

The ODF 2 framework provides the instance of CloseableHttpClient with sensible defaults. Still, some specific application context might require adjusting SSL and TLS parameters, socket and connect timeouts or other settings. In this case, create a special CloseableHttpClient instance for your Bot Task.

@BotTask
public class CustomHttpClientBotTask implements AdHocTask {

private final CloseableHttpClient httpClient;

public CustomHttpClientBotTask() {
this.httpClient = HttpClients.custom()
.setSSLSocketFactory(new SSLConnectionSocketFactory(
SSLContexts.createSystemDefault(),
new String[] {"TLSv1.2"},
null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier()))
.setConnectionTimeToLive(1, TimeUnit.MINUTES)
.setDefaultSocketConfig(SocketConfig.custom()
.setSoTimeout(5000)
.build())
.setDefaultRequestConfig(RequestConfig.custom()
.setConnectTimeout(5000)
.setSocketTimeout(5000)
.setCookieSpec(CookieSpecs.STANDARD_STRICT)
.build())
.build();
}

@Override
public TaskRunnerOutput run(TaskInput taskInput) {
int statusCode = performGetRequest("https://google.com");

return taskInput.asResult()
.withColumn("status_code", String.valueOf(statusCode));
}

@Override
public void afterRunning(TaskRunnerOutput result) {
try {
httpClient.close();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

private int performGetRequest(String uri) {
try {
return httpClient.execute(new HttpGet(uri), response -> response.getStatusLine().getStatusCode());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

}

Use a custom HTTP client only when you cannot use the default client. Remember that the ODF 2 framework does not control custom CloseableHttpClient instances, and you have to close such instances explicitly when they are no longer needed. The afterRunning/insteadOfRunning methods are usually a good place to close a client. Mind that a Bot Task might have an alternate behaviour depending on your error-handling strategy.