Ausnahme mit HttpRequest.execute (): Ungültige Verwendung von SingleClientConnManager: Verbindung noch zugewiesen

81

Ich verwende google-api-client-java 1.2.1-alpha, um eine POST-Anforderung auszuführen, und erhalte die folgende Stapelverfolgung, wenn ich die HttpRequest ausführe ().

Dies geschieht unmittelbar nachdem ich einen 403-Fehler von einem vorherigen POST an dieselbe URL abgefangen und ignoriert und den Transport für die nachfolgende Anforderung erneut verwendet habe. (Es befindet sich in einer Schleife, in der mehrere Einträge in denselben ATOM-Feed eingefügt werden.)

Gibt es etwas, das ich tun sollte, um nach einem 403 aufzuräumen?

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)

Warum sollte der Code unter mir versuchen, eine neue Verbindung herzustellen?

David Bullock
quelle
Dies scheint immer noch ein Problem mit Version 1.11.0-Beta zu sein: /
sjngm
5
Für alle, die hier ankommen, nachdem sie versucht haben, die Antworten zu konsumieren und trotzdem die Warnungen zu erhalten, habe ich hier die richtige Antwort gefunden: tech.chitgoks.com/2011/05/05/…
Steelight
@Steelight - Mit dem tech.chitgoks.com- Ansatz wurde mein Problem behoben.
Cale Sweeney

Antworten:

82

Sie müssen den Antworttext verwenden, bevor Sie die Verbindung für eine andere Anforderung wiederverwenden können. Sie sollten nicht nur den Antwortstatus lesen, sondern die Antwort InputStreamvollständig bis zum letzten Byte lesen, wobei Sie die gelesenen Bytes einfach ignorieren.

BalusC
quelle
1
Das war's! Im Fall von google-api-java-client, das der bedeutete Fang IOExceptiondurch geworfen HttpResponse.execute(), Prüfung / Gießen es HttpResponseException, den Zugriff auf das responseElement, und dann den Aufruf paraseAsString()an ihn. (Was sich jedenfalls als nützliche Information herausstellte: -)
David Bullock
5
Es gibt auch eine HttpEntity.consumeContent () -Methode, um den Inhalt zu verwerfen.
Grzegorz Adam Hankiewicz
3
EntityUtils.consume (Entität) - ConsumContent ist jetzt veraltet.
David Carboni
Zu beachten ist, dass ab den neuesten Versionen von Google Http Java Client (mindestens 1.16, möglicherweise jedoch früher) beim Aufrufen HttpRequest.execute()diese Ressourcen automatisch bereinigt werden, wenn die Methode kein HttpResponseObjekt zurückgibt. Darüber hinaus HttpResponseempfiehlt das JavaDoc von jetzt, aufzurufen, response.disconnect()falls der Inhalt der HTTP-Antwort (in einem finallyBlock) nicht vollständig gelesen wird .
David Bullock
Gleiches Problem mit RestEasy 3.0.4, das intern BasicClientConnectionManager von apache-httpclient 4.2.1 verwendet. Vielen Dank, dass Sie @BalusC. Außerdem habe ich nie etwas über das vollständige Lesen des Streams gelesen. Wo sollte man nachsehen, wenn man sich dieser Fallstricke bewusst wird? (dokumentationsmäßig, außer Quellcode)
Dashboard
42

Ich hatte ein ähnliches Problem, als ich den HttpClient mit Jetty zum Erstellen eines Testframeworks verwendete. Ich musste von meinem Client aus mehrere Anforderungen an das Servelet erstellen, aber es gab bei der Ausführung dieselbe Ausnahme.

Ich habe eine Alternative unter http://foo.jasonhudgins.com/2010/03/http-connections-revisited.html gefunden

Sie können diese folgende Methode auch verwenden, um Ihren Client zu instanziieren.

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
quelle
Danke, hat ziemlich gut funktioniert. Ich bin jedoch gespannt auf die Notwendigkeit, zwei DefaultHttpClients zu erstellen
htafoya
2
Er verwendet Standard-HttpParams (holt sie vom Client), anstatt eigene von Grund auf neu zu erstellen.
Marcin Gil
Das hat mein Problem gelöst, aber ist es in Ordnung, dies in Android-Anwendungen zu verwenden.
Manish
Interessanterweise funktioniert dies teilweise bei mir und blockiert die Ausführung des verbleibenden Codes, wenn ich mehrere HTTP-Anforderungen in einer Schleife stelle und die Antworten nicht verbrauche. Und auch kein Fehler, ich habe nicht zu lange gewartet, um zu sehen, ob ein Timeout-Fehler auftritt. Stattdessen habe ich für jede Anforderung neue DefaultHttpClients zugewiesen, und das hat bei mir funktioniert, da ich die Schleife nicht sehr lange laufen lasse.
David
9

Eine ähnliche Ausnahmemeldung (seit mindestens Apache Jarkata Commons HTTP Client 4.2) lautet:

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

Diese Ausnahme kann auftreten, wenn zwei oder mehr Threads mit einem einzelnen interagieren org.apache.http.impl.client.DefaultHttpClient.

Wie können Sie eine 4.2- DefaultHttpClientInstanz threadsicher machen ( threadsicher in dem Sinne, dass zwei oder mehr Threads damit interagieren können, ohne über die Fehlermeldung hinauszugehen)? Stellen Sie DefaultHttpClientein Verbindungspooling ClientConnectionManagerin Form von 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);
        }
    }
}
Abdull
quelle
8

Dies ist eine häufig gestellte Frage. Die Antwort von BalusC ist korrekt. Bitte fangen Sie HttpReponseException und rufen Sie HttpResponseException auf. Antwort . ignorieren (). Wenn Sie die Fehlermeldung lesen müssen, verwenden Sie die Antwort. parseAsString (), wenn Sie den Inhaltstyp der Antwort nicht kennen, andernfalls, wenn Sie den Inhaltstyp kennen, verwenden Sie die Antwort. parseAs (MyType.class).

Ein einfaches Code-Snippet aus YouTubeSample.java in youtube-jsonc-sample (obwohl Sie normalerweise in einer echten Anwendung etwas Klügeres tun möchten):

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

Vollständige Offenlegung: Ich bin Eigentümer des Google-API-Java-Client- Projekts.

Yaniv Inbar
quelle
15
Bedeutet die Tatsache, dass dies eine häufig gestellte Frage ist, nicht, dass Ihr Bibliotheksdesign ein Usability-Problem aufweist?
Vernunft
@ Sanity Oder vielleicht im http-Stack selbst;)
Krosenvold
3

Ich hatte das gleiche Problem mit einem jax-rs (resteasy) ResponseObjekt in meinen Unit-Tests. Ich habe dies mit einem Aufruf von response.releaseConnection(); The releaseConnection () gelöst - Die Methode befindet sich nur für das Resteasy- ClientResponseObjekt, daher musste ich eine Besetzung von Responsebis hinzufügen ClientResponse.

Markus
quelle
Das hat mir den Tag gerettet! In meinem Fall musste ich in org.jboss.resteasy.client.jaxrs.internal.ClientResponse umwandeln, um es weiter einzugrenzen.
user2081279
1

Versuche dies

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
quelle
0

Ok, ich habe ein ähnliches Problem, all diese Lösungen funktionieren nicht, ich habe auf einem Gerät getestet, das Problem war das Datum im Gerät, es war 2011 statt 2013, überprüfen Sie auch, ob dies helfen kann.

Marko
quelle
0

Lesen Sie den InputStream folgendermaßen:

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() + "" );
    }
}
lucasddaniel
quelle
0

Verbrauchen Sie einfach die Antwort wie unten, um das Problem zu lösen

response.getEntity().consumeContent();
Mohammed Rafeeq
quelle