36 votes

L'utilisation du modèle Spring Rest provoque une exception EOF

Je reçois java.io.EOFException's lors de l'utilisation de REPOS du Ressort de modèle sur Android.

La stacktrace cause se lit comme suit:

Caused by: java.io.EOFException
at libcore.io.Streams.readAsciiLine(Streams.java:203)
at libcore.net.http.HttpEngine.readResponseHeaders(HttpEngine.java:560)
at libcore.net.http.HttpEngine.readResponse(HttpEngine.java:813)
at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:274)
at libcore.net.http.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:486)
at org.springframework.http.client.SimpleClientHttpResponse.getRawStatusCode(SimpleClientHttpResponse.java:49)
at org.springframework.http.client.SimpleClientHttpResponse.getStatusCode(SimpleClientHttpResponse.java:55)
at org.springframework.http.client.BufferingClientHttpResponseWrapper.getStatusCode(BufferingClientHttpResponseWrapper.java:47)
at com.company.util.LoggingClientHttpRequestInterceptor.intercept(LoggingClientHttpRequestInterceptor.java:33)
at org.springframework.http.client.InterceptingClientHttpRequest$RequestExecution.execute(InterceptingClientHttpRequest.java:81)
at com.company.api.interceptor.AuthTokenInterceptor.intercept(AuthTokenInterceptor.java:51)
at org.springframework.http.client.InterceptingClientHttpRequest$RequestExecution.execute(InterceptingClientHttpRequest.java:81)
at org.springframework.http.client.InterceptingClientHttpRequest.executeInternal(InterceptingClientHttpRequest.java:67)
at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:46)
at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:63)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:475)
... 14 more

Un autre similaire stacktrace:

org.springframework.web.client.ResourceAccessException: I/O error: null; nested exception is java.io.EOFException
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:490)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:438)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:414)
at com.company.api.ApiClient_.logLoginAttempt(ApiClient_.java:299)
at com.company.security.CompanyAuthenticationService$2.onCreateCall(CompanyAuthenticationService.java:206)
at com.company.api.SafeApiCall.doInBackground(SafeApiCall.java:49)
at com.company.api.SafeApiCall.doInBackground(SafeApiCall.java:22)
at android.os.AsyncTask$2.call(AsyncTask.java:287)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
at java.util.concurrent.FutureTask.run(FutureTask.java:137)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
at java.lang.Thread.run(Thread.java:856)
Caused by: java.io.EOFException
at libcore.io.Streams.readAsciiLine(Streams.java:203)
at libcore.net.http.HttpEngine.readResponseHeaders(HttpEngine.java:560)
at libcore.net.http.HttpEngine.readResponse(HttpEngine.java:813)
at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:274)
at libcore.net.http.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:486)
at org.springframework.http.client.SimpleClientHttpResponse.getRawStatusCode(SimpleClientHttpResponse.java:49)
at org.springframework.http.client.SimpleClientHttpResponse.getStatusCode(SimpleClientHttpResponse.java:55)
at org.springframework.http.client.BufferingClientHttpResponseWrapper.getStatusCode(BufferingClientHttpResponseWrapper.java:47)
at org.springframework.web.client.DefaultResponseErrorHandler.hasError(DefaultResponseErrorHandler.java:46)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:476)
... 13 more

C'est tout ce qui se passe sur Android 4.1.2, installé sur ma tablette Xoom.

Le problème apparaît et disparaît. Ce n'est pas déclenchée par les demandes soit. La partie serveur est en cours d'exécution sur une machine sur le réseau local. Lorsque j'essaie de lancer les Appels d'API par le biais curl, il fonctionne très bien.

AuthTokenInterceptor:

@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] data, ClientHttpRequestExecution execution) throws IOException {
    HttpHeaders headers = request.getHeaders();
    if (!StringUtils.isEmpty(mAuthToken)) {
        headers.add((mIsOAuth ? "Authorization" : "authToken"), (mIsOAuth ? "Bearer " : "") + mAuthToken);
    }
    return execution.execute(request, data);
}

LoggingClientHttpRequestInterceptor:

/** {@inheritDoc} */
@Override
public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
    Log.d(TAG, "To     : " + httpRequest.getURI());
    Log.d(TAG, "Method : " + httpRequest.getMethod().name());
    Log.d(TAG, "Data   : " + new String(bytes));

    for (Object key : httpRequest.getHeaders().keySet()) {
        Log.d(TAG, "Header <" + key + ">: " + httpRequest.getHeaders().get(key));
    }

    final ClientHttpResponse response = clientHttpRequestExecution.execute(httpRequest, bytes);

    if (response != null) {
        Log.d(TAG, "Response: " + response.getStatusCode());
        if (response.getBody() != null) {
            Log.d(TAG, "Response: " + convertStreamToString(response.getBody()));
        }
    } else {
        Log.d(TAG, "Response: " + response);
    }

    return response;
}

Le Reste du Modèle est configuré comme ceci:

final RestTemplate template = new RestTemplate(false);
template.getMessageConverters().add(new MappingJacksonHttpMessageConverter());
template.setRequestFactory(new BufferingClientHttpRequestFactory(template.getRequestFactory()));
ApiUtils.addAuthTokenHeaderToRestTemplate(template, mAuthToken, false);
ApiUtils.addRequestLoggingToRestTemplate(template);

L'appel de l'API en question qui s'est écrasé ici est décrit dans l'Android annotations en fonction de l'interface:

@Post("/user/memberships")
@Accept(MediaType.APPLICATION_JSON)
CompanyApiResponse saveGroupMembership(UserGroupMembership membership) throws RestClientException;

Les choses que j'ai essayé:

  • Retiré LoggingInterceptor
  • Appelé tous les appels d'API par CURL
  • Retiré appel BufferingClientHttpRequestFactory - un peu Aidé mais l'erreur persiste.
  • Testé sur Android 2.3 - l'erreur ne peut pas être reproduit

J'ai lu sur différents forums postes, les expressions du FOLKLORE exception semble apparaître si les Url sont incorrectes, dont j'ai vérifié dans ce cas.

A noter également, une fois que les expressions du FOLKLORE Exception se produit, l'appel ne soit pas atteint, même du côté serveur.

Où serait un bon point pour continuer la recherche d'un correctif? Est-ce un Android 4.1 les inconvénients?

Lors du débogage de ce problème, j'ai aussi trouvé https://jira.springsource.org/browse/ANDROID-102 ce qui m'a empêché de voir l'erreur réelle (EOF) avant.


Mise à jour: Juste trouvé http://code.google.com/p/google-http-java-client/issues/detail?id=116 - il est peut-être lié.

Le correctif est également décrite dans le https://codereview.appspot.com/6225045/ - donc il pourrait avoir été fusionnés pour 4.1.

59voto

jaseelder Points 674

Ce un peu moi aussi, l'exécution de Jelly Bean 4.2. Après des recherches, il semble que ce qui se passe en raison d'une combinaison de Keep-Alive être ensemble et à l'aide de la norme J2SE Client HTTP, qui je crois est HttpURLConnection.

Il y a 2 solutions que je peux confirmer sont corrects.

1) eteignez Keep-Alive.
Pour moi, la solution donnée à Sebastian de la réponse du Système.setProperty("http".keepAlive", "false"); ne fonctionne pas. J'ai eu à utiliser

HttpHeaders headers = new HttpHeaders();
headers.set("Connection", "Close");

et d'envoyer les en-têtes dans un HttpEntity dans le RestTemplate.
Comme mentionné, cette solution pourrait avoir un impact sur les performances

2) Modifier le Client HTTP.
Au Printemps pour Android (testé sur la version 1.0.1.LIBÉRATION, mais qui pourraient l'être dans les versions antérieures trop) la valeur par défaut du Client HTTP pour un RestTemplate instance est déterminée par la version d'Android sur l'appareil. API 9 ou plus récent utilise HttpURLConnection, âgés utilise HTTPClient. Pour définir explicitement le client à l'ancien, l'utilisation

restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());

Plus d'informations peuvent être trouvées ici: http://static.springsource.org/spring-android/docs/1.0.1.RELEASE/reference/htmlsingle/#d4e34
Je ne sais pas quel impact cela aura sur la performance, mais je suppose que c'est plus performant qu'une application qui ne fonctionne pas.

De toute façon, l'espoir qui aide quelqu'un. Je viens de perdre une semaine sauvages-goose-la quête de ce une.

11voto

Sebastian Roth Points 5091

http://code.google.com/p/google-http-java-client/issues/detail?id=116 a une correction à la dans le dernier commentaire:

C'est defenetly en quelque sorte connecté avec des connexions persistantes.

Lorsque j'utilise: Système.setProperty("http".keepAlive", "false");les problèmes de disparaît.

Mais à partir de ma compréhension de la garder vivante connexions sont considérablement augmenter la performance de sorte qu'il est préférable de ne pas les désactiver.

Im aussi étaient que garder vivante doit être désactivée pour les anciennes versions, mais mon appareil est Jelly Bean.

Une fois appliquée, l'erreur a disparu.
Il semble qu'il n'est pas entièrement liée à de Printemps, mais un JB problème.

0voto

Récemment, j'ai fait face à ce problème et je pourrai le résoudre après avoir défini les en-têtes avec le morceau de code suivant:

 headers.set("Accept-Language", "en-US,en;q=0.8");
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X