Reusing JAX RS Client in multi-threaded environment (with resteasy)

Alexandr picture Alexandr · Oct 13, 2015 · Viewed 12k times · Source

According to the documentation,

"Clients are heavy-weight objects that manage the client-side communication infrastructure. Initialization as well as disposal of a Client instance may be a rather expensive operation. It is therefore advised to construct only a small number of Client instances in the application. "

Ok, I'm trying to cache Client itself and WebTarget instances in a static variable, the someMethod() is invoked in multi-threaded environment:

private static Client client = ClientBuilder.newClient();
private static WebTarget webTarget = client.target("someBaseUrl");
...
public static String someMethod(String arg1, String arg2)
{
    WebTarget target = entrTarget.queryParam("arg1", arg1).queryParam("arg2", arg2);
    Response response = target.request().get();
    final String result = response.readEntity(String.class);
    response.close();
    return result;
}

But sometimes (not always) I'm get an exception:

Invalid use of BasicClientConnManager: connection still allocated. Make sure to release the connection before allocating another one.

How can Client/WebTarget be reused/cached correctly? Is it possible with JAX RS Client API? Or I have to use some framework-specific features (resteasy/jersey) Could you provide some example or documentation?

Answer

Patrick picture Patrick · Jul 13, 2016

Since this issue is still open at the time of writing (version 3.0.X) RESTEASY: deprecated Apache classes cleanup

You can go deeper to use the newer, non-deprecated classes instead to create you resteasy client. You will also have more control over how you want the pool to be etc.

Here is what I did:

// This will create a threadsafe JAX-RS client using pooled connections.
// Per default this implementation will create no more than than 2
// concurrent connections per given route and no more 20 connections in
// total. (see javadoc of PoolingHttpClientConnectionManager)
PoolingHttpClientConnectionManager cm =
        new PoolingHttpClientConnectionManager();

CloseableHttpClient closeableHttpClient =
        HttpClientBuilder.create().setConnectionManager(cm).build();
ApacheHttpClient4Engine engine =
        new ApacheHttpClient4Engine(closeableHttpClient);
return new ResteasyClientBuilder().httpEngine(engine).build();

Also make sure you release the connection after making a call. Calling response.close() will do that for you so probably put that in a finally block.