JoeJoesen
JoeJoesen

Reputation: 81

How to read payload in Jersey Client

I have a little problem here. When firing a request I want to sign the whole message with HMAC and add the resulting signature to the headers.

So I implemented

javax.ws.rs.ext.WriterInterceptorContext

In the

public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException

method I cannot access the string representation of the entity. It always returns an empty String. The cause seems to be the MessageBodyWriter which is executed AFTER the WriterInterceptor.

Basically I have the following two scenarios failing:

public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
    try {
        final ClientOutputStream stream = (ClientOutputStream) requestContext.getProperty(HTTPCLIENT_ENTITY_STREAM);
        String payload = stream.getString(Charset.forName("UTF-8")); // returns alway empty String
        String signature = doSomeSuffWithPayload(payload);

        MultivaluedMap<String, Object> headers = context.getHeaders();
        headers.add(HmacHeaderValue.X_SIGNATURE.headerName(), signature);
        context.proceed();
    } catch (IllegalArgumentException | ParseException | InvalidKeyException | NoSuchAlgorithmException ex) {
        LOGGER.error(ex.getMessage());
    } catch (UnsupportedEncodingException ex) {
        LOGGER.error(ex.getMessage());
    } catch (IOException ex) {
        LOGGER.error(ex.getMessage());
    }
}

Here the doSomeSuffWithPayload(payload) method does not work, because payload is always empty.

I thought a trick will do it, so I switched the context.proceed() call to anyother place:

public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
    try {
        context.proceed();
        final ClientOutputStream stream = (ClientOutputStream) requestContext.getProperty(HTTPCLIENT_ENTITY_STREAM);
        String payload = stream.getString(Charset.forName("UTF-8")); // returns the right string representation
        String signature = doSomeSuffWithPayload(payload);

        MultivaluedMap<String, Object> headers = context.getHeaders();
        headers.add(HmacHeaderValue.X_SIGNATURE.headerName(), signature); // doesn't add the header
    } catch (IllegalArgumentException | ParseException | InvalidKeyException | NoSuchAlgorithmException ex) {
        LOGGER.error(ex.getMessage());
    } catch (UnsupportedEncodingException ex) {
        LOGGER.error(ex.getMessage());
    } catch (IOException ex) {
        LOGGER.error(ex.getMessage());
    }
}

In this case the string representation of the entity is ok. But adding the header to the request does not work.

So atm I can either have the (wrong) signature added to the headers and an always empty entity OR the right signature with the correct entity, but the header is not added.

My question is: Does anybody know a way to get the string representation of the entity by using the WriterInterceptor?

EDITH said:

We are using version 2.25.1 of jersey client. 2.27 didn't solve the problem either.

Upvotes: 2

Views: 822

Answers (1)

JoeJoesen
JoeJoesen

Reputation: 81

After searching deep in the API I found out that the entity gets indeed written after the WriterInterceptor in the MessageBodyWriter. Beside that the headers also get added during the process in the MessageBodyWriter. That's why both approaches above don't work.

My solution atm is to geht the right MessageBodyWriter and let it serialize the entity as it would do in the MessageBodyWriter that is executed after the WriterInterceptor. It this case the WriterInterceptor is not needed anymore, implementing ClientRequestFilter will do the trick.

import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Providers;

@Context
private Providers providers;

private String getPayloadFromRequest(ClientRequestContext requestContext) throws IOException {
    Object object = requestContext.getEntity();
    if (object != null) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            // buffer into which myBean will be serialized
            Class<Object> type = (Class<Object>) requestContext
                    .getEntityClass();
            GenericType<Object> genericType = new GenericType<Object>(type) {
            };

            // get most appropriate MBW
            final MessageBodyWriter<Object> messageBodyWriter = providers
                    .getMessageBodyWriter(type, type, new Annotation[]{},
                            MediaType.APPLICATION_JSON_TYPE);

            try {
                // use the MBW to serialize myBean into baos
                messageBodyWriter.writeTo(object, object.getClass(),
                        genericType.getType(), new Annotation[]{},
                        MediaType.APPLICATION_JSON_TYPE,
                        new MultivaluedHashMap<String, Object>(), baos);
            } catch (IOException e) {
                throw new RuntimeException(
                        "Error while serializing MyBean.", e);
            }

            return baos.toString();
        } finally {
            baos.close();
        }

    } else {
        return "";
    }
}

The code is not mine, but unfortunately I lost the source.

Upvotes: 3

Related Questions