Java SDK: Apache HttpClient connection pool exhaustion

The default Java SDK client seems to hold HTTP connections open indefinitely, causing pool exhaustion during high-volume polling of /api/v2/users/me.

ApiClient client = ApiClient.builder()
 .withBaseUri("https://api.mypurecloud.com")
 .withHttpClient(new ApacheHttpClientBuilder().build())
 .build();

Where do I inject a custom PoolingHttpClientConnectionManager with a max total of 200 and a 30s time-to-live? The builder pattern is too restrictive.

Are you running this inside a managed container or a standalone JAR? The pool exhaustion usually happens when the idle connections aren’t being evicted properly, and the default builder doesn’t always play nice with strict network timeouts.

Here is how I set up the PoolingHttpClientConnectionManager with a background eviction task. It’s a bit verbose, but it stops the hanging connections from piling up.

import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.conn.routing.HttpRoute;
import java.time.Duration;

PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
connManager.setMaxTotal(200);
connManager.setDefaultMaxPerRoute(20);

// This is the critical part for keeping the pool clean
connManager.setValidateAfterInactivity(30000); 

RequestConfig config = RequestConfig.custom()
 .setConnectTimeout(5000)
 .setSocketTimeout(10000)
 .build();

CloseableHttpClient httpClient = HttpClients.custom()
 .setConnectionManager(connManager)
 .setDefaultRequestConfig(config)
 .evictIdleConnections(Duration.ofSeconds(30)) // Evicts idle connections every 30s
 .build();

ApiClient client = ApiClient.builder()
 .withBaseUri("https://api.mypurecloud.com")
 .withHttpClient(new ApacheHttpClientBuilder(httpClient).build()) // Pass the pre-built client
 .build();

The key here is the evictIdleConnections call on the HttpClients.custom() builder. Without that, the manager just sits there holding onto sockets that the server might have already closed, leading to that dreaded ConnectionPoolTimeoutException. I also set validateAfterInactivity to 30 seconds. It forces a check before reusing a connection, which adds a tiny bit of latency but saves you from IOExceptions later.

Make sure you’re not creating a new ApiClient instance for every single poll. That’s a common mistake. Keep the client static or singleton. If you’re still seeing issues, check your maxPerRoute setting. If you’re hitting multiple endpoints concurrently, 20 might be too low.