2

I have a highly concurrent server and need to implement some special logging. I need an easy way to match the request to the response. The filter() in ContainerResponseFilter has both the request and response. How ever I can not access the http post content because the stream has already been read.

Is there some way I can add an ID in the ContainerRequestFilter filter() and somehow have it automatically returned when the ContainerResponseFilter filter() is called? I can not modify the true applications.

I have another program that will need to process the log file and be able to match the request to the response.

Any other suggestions?

Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
AEDWIP
  • 888
  • 2
  • 9
  • 22

1 Answers1

0

The only solutions I can think of invloves "Cloning the InputStream" (another link). Seems like double the work, but I don't see how else it can be achieved (without having to write a bunch of MessageBodyReaders).

Then we can just use ContainerRequestContext.setProperty(String, Object) to set an arbitrary property than can be obtained throughout the request/response filter chain. In the response filter, you can use ContainerResponseContext.getProperty(String).

Here's an example, where I am expecting JSON, and using Jackson ObjectMapper to get a JSON Map and getting the id value, then setting the property in the context.

@Provider
public class RequestFilter implements ContainerRequestFilter {

    ObjectMapper mapper = new ObjectMapper();

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        if (!requestContext.getMethod().equalsIgnoreCase("POST")) {
            return;
        }

        InputStream entityStream = requestContext.getEntityStream();

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len;
        while ((len = entityStream.read(buffer)) > -1) {
            baos.write(buffer, 0, len);
        }
        baos.flush();

        String contentType = requestContext.getHeaderString(HttpHeaders.CONTENT_TYPE);
        if (MediaType.APPLICATION_JSON.equals(contentType)) {

            // User Jackson ObjectMapper to get JSON as Map
            Map<String, Object> jsonMap = mapper.readValue(
                    new ByteArrayInputStream(baos.toByteArray()),
                    TypeFactory.defaultInstance().constructMapType(
                            Map.class, String.class, Object.class));

            Object id = jsonMap.get("id");
            // Put id into context as property to be retrieved from response filter
            requestContext.setProperty("id", id);
        }

        requestContext.setEntityStream(new ByteArrayInputStream(baos.toByteArray()));
    }
}

The above example uses Jersey 2.x. If you are using Jersey 1.x, I don't see any method in the ContainerRequest to set any arbitrary property, but I guess you could just set a temporary header

containerRequest.getRequestHeaders().putSingle("X-Temp-ID", id);

Then you can just get the header in the response filter.

Community
  • 1
  • 1
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720