web-dev-qa-db-ja.com

Spring Restテンプレートを使用するとEOFExceptionが発生する

Java.io.EOFExceptionは、AndroidでSpring RESTテンプレートを使用している場合です。

スタックトレースの原因は次のとおりです。

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

別の同様のスタックトレース:

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

これはすべて、Android 4.1.2、私のXoomタブレットにインストールされている)で発生しています。

問題が発生して消えます。長いリクエストによってもトリガーされません。サーバー部分はローカルネットワーク内のマシンで実行されています。 curlを介してAPI呼び出しを実行しようとすると、うまく機能します。

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;
}

Restテンプレートは次のように構成されています。

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);

ここでクラッシュした問題のAPI呼び出しは、Androidアノテーションベースのインターフェースで説明されています:

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

私が試したこと:

  • LoggingInterceptorを削除
  • CURLによってすべてのAPI呼び出しを呼び出した
  • 呼び出しBufferingClientHttpRequestFactoryを削除しました-少し助けましたが、エラーは引き続き発生します。
  • Android 2.3-でテストしましたエラーは再現できません

私はさまざまなフォーラムの投稿を読んでいますが、URLが正しくない場合、EOF例外が表示されているようです。この場合、これを再確認しました。

また、EOF例外が発生すると、呼び出しはサーバー側にも到達しません。

修正の検索を続けるにはどこが良いでしょうか?これはAndroid 4.1不便ですか?

この問題のデバッグ中に、 https://jira.springsource.org/browse/Android-102 も見つかりました。これにより、以前に実際のエラー(EOF)が表示されませんでした。


更新:見つかりました http://code.google.com/p/google-http-Java-client/issues/detail? id = 116 -関連している可能性があります。

この修正の概要は https://codereview.appspot.com/6225045/ にも記載されているため、4.1にマージされている可能性があります。

36
Sebastian Roth

これも私と同じように、Jelly Bean 4.2を実行しています。調査したところ、キープアライブが設定されているの組み合わせが標準のJ2SE HTTPクライアントを使用しているために発生しているようです。

私が確認できる2つの解決策があります。

1)キープアライブをオフにします。
私にとって、セバスチャンの回答で与えられた解決策、System.setProperty( "http.keepAlive"、 "false");はしませんでしたt動作します。私は使用しなければなりませんでした

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

これらのヘッダーをRestTemplateのHttpEntityで送信します。
前述のように、このソリューションはパフォーマンスに影響を与える可能性があります

2)HTTPクライアントを変更します。
SpringでAndroid(1.0.1.RELEASEでテスト済みですが、以前のリリースでも可能))RestTemplateインスタンスのデフォルトのHTTPクライアントは、=のバージョンによって決定されますAndroid。API 9以降はHttpURLConnectionを使用し、古いものはHTTPClientを使用します。クライアントを明示的に古いものに設定するには、

restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());

詳細については、こちらをご覧ください: http://static.springsource.org/spring-Android/docs/1.0.1.RELEASE/reference/htmlsingle/#d4e34
これがパフォーマンスにどのような影響を与えるかはわかりませんが、機能しないアプリよりもパフォーマンスが高いと思います。

とにかく、それが誰かを助けることを願っています。私はこれを一週間野生のガチョウを追いかけて無駄にしました。

59
jaseelder

http://code.google.com/p/google-http-Java-client/issues/detail?id=116 には、最新のコメントに回避策が含まれています。

これは間違いなくなんとかしてkeepAlive接続と接続されています。

私が使用する場合:System.setProperty( "http.keepAlive"、 "false");問題が消えます。

しかし、私の理解では、キープアライブ接続はパフォーマンスを大幅に向上させるので、それらを無効にしない方が良いでしょう。

古いバージョンではキープアライブを無効にする必要があることにも気付いていましたが、私のデバイスはジェリービーンです。

適用するとエラーは消えました。
Springに完全に関連しているのではなく、JBの問題のようです。

11
Sebastian Roth

最近この問題に直面し、次のコードでヘッダーを設定した後、この問題を解決できるようになります。

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