users@jersey.java.net

Proposed breaking changes for client API for efficient async support

From: Paul Sandoz <Paul.Sandoz_at_Sun.COM>
Date: Wed, 12 Aug 2009 14:45:22 +0200

Hi,

I mentioned before in a previous email about breaking changes client
API to support efficient asynchronous requests/responses such that a
response can be processed on a different thread to that of a request.

The current filter approach is stack-based using a general handler
chain where the last handler is "inflection" point that sends an HTTP
request and produces an HTTP response.

We need to split the a filter into separate request and response
filters:

   public interface ClientRequestFilter {
     ClientRequest filter(ClientRequest request);
   }

   public interface ClientResponseFilter {
     ClientResponse filter(ClientRequest request, ClientResponse
response);
   }

Then we can have an abstract class as follows:

   public abstract class ClientFilter implements ClientRequestFilter,
ClientResponseFilter {
     public ClientRequest filter(ClientRequest request) {
       return request;
     }

     public ClientResponse filter(ClientRequest request,
ClientResponse response) {
       return response;
     }
   }

and modify all existing filters supported by Jersey to extend from the
new ClientFilter. That way we can ensure that existing source that
uses the Jersey supplied filters to add instances to Client or
WebResource is still compatible (i strongly suspect binary
compatibility will be broken so a recompile would be required).

The ClientHandler interface is still relevant for implementations of
HTTP clients (HttpURLConnection, Apache HTTP clent, and in memory-
client). But we will require an AsyncClientHandler to implement if
async request/response processing is supported:

   public interface AsyncClientHandler {
     void handler(ClientRequest r, ClientResponseListener l);
   }

   public interface ClientResponseListener {
     void onError(Throwable t);

     void onResponse(ClientResponse cr);
   }



After I wrote the above I realized there is the possibility for an
alternative solution that will limit breaking changes to the async
parts of the API. Thus existing filters used with the non-async parts
will not be affected.

It should be possible to retain the stack-based approach for non-async
support and modify the existing filters to support stack and non-stack
approaches by filters extending from the following class:

   public abstract class ClientRequestResponseFilter extends
ClientFilter, ClientRequestFilter, ClientResponseFilter {
     public ClientResponse handle(ClientRequest request)
             throws ClientHandlerException {

          request = filter(request);

          response = getNext().handler(cr);

          return filter(response);
     }

     // state associated with request filter which needs to be
accessed by response filter
     // must be added as a property on the request, a thread local
cannot be used because
     // the response filter may be processed on a different thread to
the request filter.
     public ClientRequest filter(ClientRequest request) {
       return request;
     }

     public ClientResponse filter(ClientRequest request,
ClientResponse response) {
       return response;
     }
   }

However there is an issue if instances of ClientFilter (that are not
instances of ClientRequestResponseFilter) are added to the filter
chain of Client, which are then utilized by a AsyncWebResource. This
will render inoperable efficient async operation. Under such
circumstances we could easily generate a warning.

Paul.