2

Below is the Interceptor code for logging request and response when doing Async Rest Call from Spring's AsyncRestTemplate. This code is working provided we can use BufferingClientHttpResponseWrapper in some other package. Here are some details about BufferingClientHttpResponseWrapper and how to add it with AsyncRestTemplate. My Question is when I am using HttpComponentsAsyncClientHttpRequestFactory in my AsyncRestTemplate. How I can get the Buffered Response. We CAN NOT use BufferingClientHttpResponseWrapper as show below in my code as it is not a public class. Is there any other way to do it? I know there are wire log available for HttpComponent AsyncHttpClient. But it will have all the logs from all the AsyncResttemplates in whole application. If we want to capture the logs only for one template then Interceptor is the only way I think. Please suggest if there is any other option available.

public class AsyncRestReqResInterceptor implements AsyncClientHttpRequestInterceptor {
    private static final XLogger REQ_RES_LOGGER = XLoggerFactory.getXLogger("myLogger");

    @Override
        public ListenableFuture<ClientHttpResponse> intercept(HttpRequest request, byte[] body, AsyncClientHttpRequestExecution execution) throws IOException {
            String requestPath = request.getURI().getPath();
            REQ_RES_LOGGER.debug(request.getMethod()+" "+requestPath);

            String requestBody = new String(body);
            REQ_RES_LOGGER.debug(requestBody);
            ListenableFuture<ClientHttpResponse> listenableFuture = execution.executeAsync(request, body);
            ListenableFutureAdapter<ClientHttpResponse,ClientHttpResponse> futureAdapter=
                    new ListenableFutureAdapter<ClientHttpResponse, ClientHttpResponse>(listenableFuture) {
                        @Override
                        protected ClientHttpResponse adapt(ClientHttpResponse adapteeResult) throws ExecutionException {
                            return logResponseBody(adapteeResult);
                        }
                    };
            return  futureAdapter;
        }

    private ClientHttpResponse logResponseBody(ClientHttpResponse response,boolean isImageInResponse){
                try {
                    BufferingClientHttpResponseWrapper responseWrapper = new BufferingClientHttpResponseWrapper(response);
                    REQ_RES_LOGGER.debug("Response Status Code :" + responseWrapper.getStatusCode().value());
                    REQ_RES_LOGGER.debug("Response Status Text :" + responseWrapper.getStatusText());
                    if (response != null && response.getBody() != null) {
                        String responseXml = IOUtils.toString(responseWrapper.getBody(), Charset.defaultCharset());
                        REQ_RES_LOGGER.debug(responseXml);
                    } else {
                        REQ_RES_LOGGER.debug("Empty Response Body");
                    }
                    return responseWrapper;
                }catch (IOException io){
                    REQ_RES_LOGGER.error("Unexpected Error ",io);
                    return response;
                }

        }
    }
Community
  • 1
  • 1
Vijendra Kumar Kulhade
  • 2,217
  • 3
  • 15
  • 25

1 Answers1

3

I finally figured out a way to use this class. As this is a final class it is meant to be used within Springframework only. There was a pull request in springframework for this change. I am not sure when this change will be merged in the framework, but below are the classes which will solve the above problem. These classes would be helpful if we want to intercept the request and response for logging.

BufferingAsyncClientHttpRequestFactory

public class BufferingAsyncClientHttpRequestFactory extends 

BufferingClientHttpRequestFactory
        implements AsyncClientHttpRequestFactory {

    private final AsyncClientHttpRequestFactory requestFactory;
    /**
     * Create a buffering wrapper for the given {@link ClientHttpRequestFactory}.
     * @param requestFactory the target request factory to wrap
     */
    public BufferingAsyncClientHttpRequestFactory(AsyncClientHttpRequestFactory requestFactory) {
        super((ClientHttpRequestFactory) requestFactory);
        this.requestFactory=requestFactory;
    }


    /**
     * Indicates whether the request/response exchange for the given URI and method
     * should be buffered in memory.
     * <p>The default implementation returns {@code true} for all URIs and methods.
     * Subclasses can override this method to change this behavior.
     * @param uri the URI
     * @param httpMethod the method
     * @return {@code true} if the exchange should be buffered; {@code false} otherwise
     */

    @Override
    public AsyncClientHttpRequest createAsyncRequest(URI uri, HttpMethod httpMethod) throws IOException {
        AsyncClientHttpRequest request = requestFactory.createAsyncRequest(uri, httpMethod);
        if (shouldBuffer(uri, httpMethod)) {
            return new BufferingAsyncClientHttpRequestWrapper(request);
        }
        else {
            return request;
        }
    }

    protected boolean shouldBuffer(URI uri, HttpMethod httpMethod) {
        return true;
    }


}

BufferingAsyncClientHttpRequestWrapper.java

public class BufferingAsyncClientHttpRequestWrapper extends AbstractBufferingAsyncClientHttpRequest {

    private final AsyncClientHttpRequest request;

    public BufferingAsyncClientHttpRequestWrapper(AsyncClientHttpRequest asyncClientHttpRequest){
        this.request=asyncClientHttpRequest;
    }

    @Override
    public HttpMethod getMethod() {
        return this.request.getMethod();
    }

    @Override
    public URI getURI() {
        return this.request.getURI();
    }

    @Override
    protected ListenableFuture<ClientHttpResponse> executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
        this.request.getHeaders().putAll(headers);
        if(bufferedOutput.length>0){
            StreamUtils.copy(bufferedOutput,this.request.getBody());
        }
        ListenableFuture<ClientHttpResponse> futureResponse = this.request.executeAsync();
        ListenableFutureAdapter<ClientHttpResponse,ClientHttpResponse> bufferedResponse =
                new ListenableFutureAdapter<ClientHttpResponse, ClientHttpResponse>(futureResponse) {
                    @Override
                    protected ClientHttpResponse adapt(ClientHttpResponse adapteeResult) throws ExecutionException {
                        return new BufferingClientHttpResponseWrapper(adapteeResult);
                    }
                };
        return bufferedResponse;
    }
}
Vijendra Kumar Kulhade
  • 2,217
  • 3
  • 15
  • 25