82

I'm using google-api-client-java 1.2.1-alpha to execute a POST request, and am getting the following stacktrace when I execute() the HttpRequest.

It happens immediately after I catch and ignore a 403 error from a previous POST to the same URL, and re-used the transport for the subsequent request. (It's in a loop inserting multiple entries to the same ATOM feed).

Is there something I should be doing to 'clean up' after a 403?

Exception in thread "main" java.lang.IllegalStateException: Invalid use of SingleClientConnManager: connection still allocated.
Make sure to release the connection before allocating another one.
    at org.apache.http.impl.conn.SingleClientConnManager.getConnection(SingleClientConnManager.java:199)
    at org.apache.http.impl.conn.SingleClientConnManager$1.getConnection(SingleClientConnManager.java:173)
    at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:390)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:641)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:576)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:554)
    at com.google.api.client.apache.ApacheHttpRequest.execute(ApacheHttpRequest.java:47)
    at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:207)
    at au.com.machaira.pss.gape.RedirectHandler.execute(RedirectHandler.java:38)
    at au.com.machaira.pss.gape.ss.model.records.TableEntry.executeModification(TableEntry.java:81)

Why would the code below me be trying to acquire a new connection?

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
David Bullock
  • 6,112
  • 3
  • 33
  • 43
  • This still seems to be an issue with version 1.11.0-beta :/ – sjngm Sep 13 '12 at 12:14
  • 5
    For the benefit of anyone arriving here after trying to consume the responses and still getting the warnings - I found the correct answer here: http://tech.chitgoks.com/2011/05/05/fixing-the-invalid-use-of-singleclientconnmanager-connection-still-allocated-problem/ – Steelight Nov 08 '12 at 14:33
  • @Steelight - using the [tech.chitgoks.com](http://tech.chitgoks.com/2011/05/05/fixing-the-invalid-use-of-singleclientconnmanager-connection-still-allocated-problem/) approach resolved my issue. – Cale Sweeney Oct 25 '17 at 01:31

9 Answers9

83

You need to consume the response body before you can reuse the connection for another request. You should not only read the response status, but read the response InputStream fully to the last byte whereby you just ignore the read bytes.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • 1
    That was it! In the case of `google-api-java-client`, this meant catching the `IOException` thrown by `HttpResponse.execute()`, testing/casting it to `HttpResponseException`, accessing the `response` member, and then invoking `paraseAsString()` on it. (Which turned out to be useful information anyhow :- ) – David Bullock Jan 07 '11 at 02:34
  • 5
    There is also a HttpEntity.consumeContent() method to discard the content. – Grzegorz Adam Hankiewicz Apr 13 '12 at 16:52
  • 3
    EntityUtils.consume(entity) - consumeContent is now deprecated. – David Carboni Oct 25 '12 at 18:31
  • Just to note that as of recent versions of Google Http Java Client (at least 1.16, but possibly earlier), calling `HttpRequest.execute()` will automatically clean up these resources if the method fails to return a `HttpResponse` object. Further, the JavaDoc of `HttpResponse` now recommends calling `response.disconnect()` in case of not fully reading the content of the HTTP response (in a `finally` block). – David Bullock Dec 23 '13 at 04:52
  • Same problem with RestEasy 3.0.4 which internally uses BasicClientConnectionManager of apache-httpclient 4.2.1. Thank You @BalusC. Also I never read anything about reading the stream fully, where should one look at when it comes to be aware of these pitfalls ? (documentation-wise, other than source code) – alegria Dec 04 '19 at 11:39
  • @dashboard: I've also never seen it documented anywhere. I guess that's what Stack Overflow is for. – BalusC Dec 04 '19 at 11:50
42

I was facing a similar issue when using the HttpClient with Jetty to build a test framework. I had to create multiple requests to the Servelet from my client, but It was giving the same exception when executed.

I found an alternative at http://foo.jasonhudgins.com/2010/03/http-connections-revisited.html

You can also use this following method to instantiate your client.

public static DefaultHttpClient getThreadSafeClient()  {

    DefaultHttpClient client = new DefaultHttpClient();
    ClientConnectionManager mgr = client.getConnectionManager();
    HttpParams params = client.getParams();
    client = new DefaultHttpClient(new ThreadSafeClientConnManager(params, 

            mgr.getSchemeRegistry()), params);
    return client;
}
Ujjwal Wadhawan
  • 733
  • 1
  • 8
  • 10
  • Thanks, worked pretty good. However I'm curious on the necessity to create two DefaultHttpClients – htafoya Jun 25 '12 at 21:29
  • 2
    He uses default HttpParams (gets them from client) instead of creating own from scratch. – Marcin Gil Jun 27 '12 at 12:06
  • That solved my problem, but is it ok to use this in android application. – Manish Mar 16 '14 at 20:10
  • Interestingly, that partially works for me, ends up blocking the remaining code from executing when I make several HTTP requests in a loop and not consume the responses. And no failure either, I didn't wait too long to see if a timeout error occurs though. So instead I went the route of allocating new DefaultHttpClients for each request and that worked for me, since I don't run the loop very long. – David Feb 16 '17 at 05:37
9

A similar exception message (since at least Apache Jarkata Commons HTTP Client 4.2) is:

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

This exception can happen when two or more threads interact with a single org.apache.http.impl.client.DefaultHttpClient.

How can you make a 4.2 DefaultHttpClient instance threadsafe (threadsafe in the sense that two or more threads can interact with it without getting above error message)? Provide DefaultHttpClient with a connection-pooling ClientConnectionManager in the form of org.apache.http.impl.conn.PoolingClientConnectionManager!

/* using
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.2.2</version>
    </dependency>
*/

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.PoolingClientConnectionManager;
import org.apache.http.impl.conn.SchemeRegistryFactory;
import org.apache.http.params.HttpParams;
import org.apache.http.client.methods.HttpGet;

public class MyComponent {

    private HttpClient client;

    {
        PoolingClientConnectionManager conMan = new PoolingClientConnectionManager( SchemeRegistryFactory.createDefault() );
        conMan.setMaxTotal(200);
        conMan.setDefaultMaxPerRoute(200);

        client = new DefaultHttpClient(conMan);

        //The following parameter configurations are not
        //neccessary for this example, but they show how
        //to further tweak the HttpClient
        HttpParams params = client.getParams();
        HttpConnectionParams.setConnectionTimeout(params, 20000);
        HttpConnectionParams.setSoTimeout(params, 15000);
    }


    //This method can be called concurrently by several threads
    private InputStream getResource(String uri) {
        try {
            HttpGet method = new HttpGet(uri);
            HttpResponse httpResponse = client.execute(method);
            int statusCode = httpResponse.getStatusLine().getStatusCode();
            InputStream is = null;
            if (HttpStatus.SC_OK == statusCode) {
                logger.debug("200 OK Amazon request");
                is = httpResponse.getEntity().getContent();
            } else {
                logger.debug("Something went wrong, statusCode is {}",
                        statusCode);
                 EntityUtils.consume(httpResponse.getEntity());
            }
            return is;
        } catch (Exception e) {
            logger.error("Something went terribly wrong", e);
            throw new RuntimeException(e);
        }
    }
}
tomas.tunkl
  • 141
  • 1
  • 2
  • 10
Abdull
  • 26,371
  • 26
  • 130
  • 172
8

This is an often-asked question. BalusC's response is correct. Please catch HttpReponseException, and call HttpResponseException.response.ignore(). If you need to read the error message, use response.parseAsString() if you don't know the response content type, else if you do know the content type use response.parseAs(MyType.class).

A simple code snippet from YouTubeSample.java in youtube-jsonc-sample (though usually you'll want to do something smarter in a real application):

  } catch (HttpResponseException e) {
    System.err.println(e.response.parseAsString());
  }

Full disclosure: I am an owner of the google-api-java-client project.

Yaniv Inbar
  • 1,409
  • 9
  • 10
3

I had the same issue with a jax-rs (resteasy) Response object in my unit tests. I solved this with a call to response.releaseConnection(); The releaseConnection()-Method is only on the resteasy ClientResponse object, so I had to add a cast from Response to ClientResponse.

markus
  • 602
  • 4
  • 13
  • That saved my day! In my case I had to cast to org.jboss.resteasy.client.jaxrs.internal.ClientResponse to further narrow it down. – Thomas Schütt Mar 22 '19 at 15:53
1

Try this

HttpResponse response = Client.execute(httpGet);
response.getEntity().consumeContent();
StatusLine statusLine = response.getStatusLine();
int statusCode = statusLine.getStatusCode();
if (statusCode == 200) {
        //task
    Log.i("Connection", "OK");
    }else{
     Log.i("Connection", "Down");
    }
Silambarasan Poonguti
  • 9,386
  • 4
  • 45
  • 38
0

Ok, i have similar problem, all those solution not work, i tested on some device, problem was date in device, it was 2011 instead 2013, check also this can help.

marko
  • 81
  • 1
  • 1
0

Read the InputStream like this:

if( response.getStatusLine().getStatusCode() == 200 ) {
    HttpEntity entity = response.getEntity();
    InputStream content = entity.getContent();
    try {
        sb = new StringBuilder();
        BufferedReader bufferedReader = new BufferedReader( new InputStreamReader( content ), 8 );
        String line;
        while( ( line = bufferedReader.readLine() ) != null ) {
            sb.append( line );
        }
        bufferedReader.close();
        content.close();
    } catch( Exception ex ) {
        Log.e( "statusCode", ex.getMessage() + "" );
    }
}
nhahtdh
  • 55,989
  • 15
  • 126
  • 162
lucasddaniel
  • 1,779
  • 22
  • 22
0

just consume the response like below, that will solve the issue

response.getEntity().consumeContent();
Mohammed Rafeeq
  • 2,586
  • 25
  • 26