Android Annotations Logging Interceptor end-of-input error

280 Views Asked by At

I am using Android Annotations to make requests to a server. I created the following interceptor based on the answer to this question

public class LoggingInterceptor implements ClientHttpRequestInterceptor {

@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
    ClientHttpResponse response = execution.execute(request, body);

    //It works after removing these two lines
    String responseString = stringOf(response.getBody());
    Log.d("RESPONSE", responseString);

    return response;
}

public static String stringOf(InputStream inputStream) {
    inputStream.mark(Integer.MAX_VALUE);
    BufferedReader r = new BufferedReader(new InputStreamReader(inputStream));
    StringBuilder strBuilder = new StringBuilder();
    String line;
    try {
        while ((line = r.readLine()) != null)
            strBuilder.append(line);
    } catch (IOException ignored) {}
    try {
        inputStream.reset();
    } catch (IOException ignored) {}
    return strBuilder.toString();
 }
}

However, this is producing the following exception: org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: No content to map due to end-of-input

When I remove the following lines from the interceptor:

 String responseString = stringOf(response.getBody());
 Log.d("RESPONSE", responseString);

Everything works fine.

Here is my RestClient interface:

 @Rest(rootUrl= "http://107.206.158.62:1337/api/", converters={MappingJackson2HttpMessageConverter.class}, interceptors = { LoggingInterceptor.class })
 public interface IRestClient {

     @Post("users/register/")
     @Accept(MediaType.APPLICATION_JSON)
     User register(@Body User user);

}

User Model:

public class User implements Serializable {

    String first_name;

    String last_name;

    String email;

    String password;

    public User(){}

    public User(String first_name, String last_name, String email, String password) {
       this.first_name = first_name;
       this.last_name = last_name;
       this.email = email;
       this.password = password;
    }

    //Getters and Setters
}

RestClient call in my Activity

@Click(R.id.bRegister)
@Background
void createAccount() {
    User u = restClient.register(new User("Test Fname", "Test Lname", "[email protected]", "testpass"));
    Log.d("User last name", u.getLast_name());
 }

The server produces the following json:

{"first_name":"Test Fname","last_name":"Test Lname","email":"[email protected]"}

I'd like to be able to log the body of each response, and then return the response object. But it appears that reading the InputStream from the response first is causing some problems.

 Log.d("RESPONSE", responseString);

Is yielding the correct server response, but then I run into the above exception when the MappingJackson2HttpMessageConverter kicks in.

Any help would be greatly appreciated! Happy New Years! :)

2

There are 2 best solutions below

1
On BEST ANSWER

I think it's because when you read response you consume it and then it's empty. The interceptor acts before content parsing, so you consume your response to log it and then the parser finds it empty.

Logging response simply as reading it's content is not the best way to do this. I don't know the best technique but I keep commented response logging and then when I have a problem I stop with debug on a line into the intercept method and log request by watch panel, writing something like

Log.d("RESPONSE", stringOf(response.getBody())) 

Then you can read logged data into Android Monitor panel. Obviously this response now is empty, so it will not be parsed and processed, but when you need you can log your response without rebuilding your project

0
On

As firegloves said the problem is you consume input stream during logging. I solved this issue with wraping the original ClientHttpResponse object into custom one with buffered input stream which you can reset after logging

@Throws(IOException::class)
fun intercept(request: HttpRequest, body: ByteArray, execution: ClientHttpRequestExecution): ClientHttpResponse {
    val response = BufferedClientHttpResponse(execution.execute(request, body))

    response.body.mark(0)
    val responseString = StreamUtils.copyToString(response.body, defaultCharset())
    Log.d("RESPONSE", responseString)
    response.body.reset()

    return response
}


class BufferedClientHttpResponse(val originalHttpResponse: ClientHttpResponse) : ClientHttpResponse {
    val bufferedBody: BufferedInputStream = originalHttpResponse.body.buffered()

    override fun getHeaders(): HttpHeaders = originalHttpResponse.headers
    override fun getStatusCode(): HttpStatus = originalHttpResponse.statusCode
    override fun getRawStatusCode(): Int = originalHttpResponse.rawStatusCode
    override fun close() = originalHttpResponse.close()
    override fun getStatusText() = originalHttpResponse.statusText
    override fun getBody(): InputStream = bufferedBody
}