users@jersey.java.net

Re: [Jersey] modifying jersey client requests

From: Paul Sandoz <Paul.Sandoz_at_Sun.COM>
Date: Wed, 07 Oct 2009 17:46:26 +0200

On Oct 7, 2009, at 5:36 PM, Felipe Gaucho wrote:

> A filter?
>

Yes, see ClientFilter and the source code for the LoggingFilter (see
below because java.net is so damn slow).

You need to return an output stream in the implementation of
AbstractClientRequestAdapter .adapt that buffers the bytes then on the
close calculates the hash, sets the header and writes out the bytes of
the buffered output stream to the actual output stream.

Paul.


package com.sun.jersey.api.client.filter;

import com.sun.jersey.api.client.AbstractClientRequestAdapter;
import com.sun.jersey.api.client.ClientHandlerException;
import com.sun.jersey.api.client.ClientRequest;
import com.sun.jersey.api.client.ClientRequestAdapter;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.core.util.ReaderWriter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import javax.ws.rs.core.MultivaluedMap;

/**
  * A logging filter.
  *
  * @author Paul.Sandoz_at_Sun.Com
  */
public class LoggingFilter extends ClientFilter {

     private static final Logger LOGGER =
Logger.getLogger(LoggingFilter.class.getName());

     private static final String NOTIFICATION_PREFIX = "* ";

     private static final String REQUEST_PREFIX = "> ";

     private static final String RESPONSE_PREFIX = "< ";

     private final class Adapter extends AbstractClientRequestAdapter {
         private final StringBuilder b;

         Adapter(ClientRequestAdapter cra, StringBuilder b) {
             super(cra);
             this.b = b;
         }

         public OutputStream adapt(ClientRequest request, OutputStream
out) throws IOException {
             return new
LoggingOutputStream(getAdapter().adapt(request, out), b);
         }

     }

     private final class LoggingOutputStream extends OutputStream {
         private final OutputStream out;

         private final ByteArrayOutputStream baos = new
ByteArrayOutputStream();

         private final StringBuilder b;

         LoggingOutputStream(OutputStream out, StringBuilder b) {
             this.out = out;
             this.b = b;
         }

         @Override
         public void write(byte[] b) throws IOException {
             baos.write(b);
             out.write(b);
         }

         @Override
         public void write(byte[] b, int off, int len) throws
IOException {
             baos.write(b, off, len);
             out.write(b, off, len);
         }

         @Override
         public void write(int b) throws IOException {
             baos.write(b);
             out.write(b);
         }

         @Override
         public void close() throws IOException {
             printEntity(b, baos.toByteArray());
             log(b);
             out.close();
         }
     }

     private final PrintStream loggingStream;

     private final Logger logger;

     private long _id = 0;

     /**
      * Create a logging filter logging the request and response to
      * a default JDK logger, named as the fully qualified class name
of this
      * class.
      */
     public LoggingFilter() {
         this(LOGGER);
     }

     /**
      * Create a logging filter logging the request and response to
      * a JDK logger.
      *
      * @param logger the logger to log requests and responses.
      */
     public LoggingFilter(Logger logger) {
         this.loggingStream = null;
         this.logger = logger;
     }

     /**
      * Create a logging filter logging the request and response to
      * print stream.
      *
      * @param loggingStream the print stream to log requests and
responses.
      */
     public LoggingFilter(PrintStream loggingStream) {
         this.loggingStream = loggingStream;
         this.logger = null;
     }

     private void log(StringBuilder b) {
         if (logger != null) {
             logger.info(b.toString());
         } else {
             loggingStream.print(b);
         }
     }

     private StringBuilder prefixId(StringBuilder b, long id) {
         b.append(Long.toString(id)).append(" ");
         return b;
     }

     @Override
     public ClientResponse handle(ClientRequest request) throws
ClientHandlerException {
         long id = ++this._id;

         logRequest(id, request);

         ClientResponse response = getNext().handle(request);

         logResponse(id, response);

         return response;
     }

     private void logRequest(long id, ClientRequest request) {
         StringBuilder b = new StringBuilder();

         printRequestLine(b, id, request);
         printRequestHeaders(b, id, request.getHeaders());

         if (request.getEntity() != null) {
             request.setAdapter(new Adapter(request.getAdapter(), b));
         } else {
             log(b);
         }
     }

     private void printRequestLine(StringBuilder b, long id,
ClientRequest request) {
         prefixId(b, id).append(NOTIFICATION_PREFIX).append("Client
out-bound request").append("\n");
         prefixId(b,
id).append(REQUEST_PREFIX).append(request.getMethod()).append(" ").
                 append(request.getURI().toASCIIString()).append("\n");
     }

     private void printRequestHeaders(StringBuilder b, long id,
MultivaluedMap<String, Object> headers) {
         for (Map.Entry<String, List<Object>> e : headers.entrySet()) {
             String header = e.getKey();
             for (Object value : e.getValue()) {
                 prefixId(b,
id).append(REQUEST_PREFIX).append(header).append(": ").
                          
append(ClientRequest.getHeaderValue(value)).append("\n");
             }
         }
         prefixId(b, id).append(REQUEST_PREFIX).append("\n");
     }

     private void logResponse(long id, ClientResponse response) {
         StringBuilder b = new StringBuilder();

         printResponseLine(b, id, response);
         printResponseHeaders(b, id, response.getHeaders());

         ByteArrayOutputStream out = new ByteArrayOutputStream();
         InputStream in = response.getEntityInputStream();
         try {
             ReaderWriter.writeTo(in, out);

             byte[] requestEntity = out.toByteArray();
             printEntity(b, requestEntity);
             response.setEntityInputStream(new
ByteArrayInputStream(requestEntity));
         } catch (IOException ex) {
             throw new ClientHandlerException(ex);
         }
         log(b);
     }

     private void printResponseLine(StringBuilder b, long id,
ClientResponse response) {
         prefixId(b, id).append(NOTIFICATION_PREFIX).
                 append("Client in-bound response").append("\n");
         prefixId(b, id).append(RESPONSE_PREFIX).
                 append(Integer.toString(response.getStatus())).
                 append("\n");
     }

     private void printResponseHeaders(StringBuilder b, long id,
MultivaluedMap<String, String> headers) {
         for (Map.Entry<String, List<String>> e : headers.entrySet()) {
             String header = e.getKey();
             for (String value : e.getValue()) {
                 prefixId(b,
id).append(RESPONSE_PREFIX).append(header).append(": ").
                         append(value).append("\n");
             }
         }
         prefixId(b, id).append(RESPONSE_PREFIX).append("\n");
     }

     private void printEntity(StringBuilder b, byte[] entity) throws
IOException {
         if (entity.length == 0)
             return;
         b.append(new String(entity)).append("\n");
     }
}

> On 07.10.2009, at 17:06, Zoltan Arnold NAGY <Zoltan.Nagy_at_Sun.COM>
> wrote:
>
>> Hey,
>>
>> Is it possible to somehow attach a "post-processor" to a WebResource?
>>
>> I'd like to use add a header containing a hash based on the actual
>> content of the request.
>> (For that, I need the raw request, too, inside that processor).
>>
>> So when I use .put on the request, I'd need jersey to call this
>> postprocessor before
>> sending the http request, but after it's done with the
>> serialization work.
>>
>> Services such as Amazon requires you to calculate a hash based on
>> the request and your
>> private key, and add it to a header, that's what I need this
>> functionality for.
>>
>> Thanks,
>> Zoltan
>>
>>
>>
>> __________ Information from ESET Smart Security, version of virus
>> signature database 4487 (20091007) __________
>>
>> The message was checked by ESET Smart Security.
>>
>> http://www.eset.com
>>
>>
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
>> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>