users@jax-rs-spec.java.net

[jax-rs-spec users] [jsr339-experts] Filters: comparison of proposed options

From: Sergey Beryozkin <sberyozkin_at_talend.com>
Date: Tue, 6 Dec 2011 13:31:13 +0000

Hi,

We have discussed various options to do with the way Filter signatures
should look like. Simplicity and extensibility have been two major
topics during those discussions.

Here is a brief overview of these options.

1. Filter implementation calls on FilterChain in order to indicate what
it is willing to do with the current request: let it continue, block it
or suspend it. FilterChain returns an opaque interface instance such as
NextAction which will let the JAX-RS runtime decide what to do next.

Example:

public class MyFilter implements RequestFilter {

     public NextAction preFilter(FilterContext context) {

         if (stopProcessing) {
             ResponseBuilder rb = context.getResponseBuilder();
             return context.stop(rb.status(401).build())
         } else if (suspendProcessing) {
             return context.suspend(...);
         } else {
             RequestBuilder rb = context.getRequestBuilder();
             return context.continue(rb.setSomething().build());
         }

     }
}


Pros:
   FilterContext is effectively a read-only instance, developers need to
'return' it as opposed to saving it at the context itself.
Cons:
   The opaqueness of NextAction may cause questions like 'why it is here
if we can't do anything with it' ?

2. Filter implementation calls on FilterChain in order to indicate what
it is willing to do with the current request: let it continue, block it
or suspend it. No response type is available in the signature

Example:

public class MyFilter implements RequestFilter {

     public void preFilter(FilterContext context) {

         if (stopProcessing) {
             ResponseBuilder rb = context.getResponseBuilder();
             context.stop(rb.status(401).build())
         } else if (suspendProcessing) {
             context.suspend(...);
         } else {
             RequestBuilder rb = context.getRequestBuilder();
             context.continue(rb.setSomething().build());
         }
     }
}

This may be possibly optimized/improved.

Pros:
   Developers are familiar with filter signatures having no response
type, example servlet filters.
Cons:
   I believe there's a concern about possible bugs (someone forgets to
do final else for example) and the possibility of having few empty returns.

3. Filter implementation only calls on FilterChain to get the
information it needs in order to decide what to do with the current
request: let it continue or block it or suspend it. It returns Response
to indicate the request is blocked; this can be null meaning that the
the request is OK to continue

Example:

public class MyFilter implements RequestFilter {

     public Response preFilter(FilterContext context) {

         if (stopProcessing) {
             return context.getResponseBuilder().status(401).build();
         } else if (suspendProcessing) {
             context.suspend(...);
             // possibly 'return null'
         } else {
             RequestBuilder rb = context.getRequestBuilder();
             context.setRequest(rb.setSomething().build());
             return null;
         }
     }
}

Pros:
   FilterContext represents the current request and the filter
implementation only updates it when it has something to change on this
request; if it does not then it returns a blocking Response
Cons:
  Returning 'null' is not the best pattern and some users may dislike it.
  Concerns about the extensibility - FilterChain may offer an option to
query for specific extensions.

4. The current trunk version

public class MyFilter implements RequestFilter {

     public FilterAction preFilter(FilterContext context) {

         if (stopProcessing) {
             ResponseBuilder rb = context.getResponseBuilder();
             context.setResponse(rb);
             return FilterAction.STOP;
         } else if (suspendProcessing) {
             context.suspend(...);
             return FilterAction.SUSPEND;
         } else {
             RequestBuilder rb = context.getRequestBuilder();
             context.setRequest(rb.setSomething().build());
             return FilterAction.NEXT;
         }
     }
}


One extra consideration about the extensions. Ultimately, the job of the
filter is to block or modify the request. Suspending the request is
about getting the filter prepared for what it is supposed to do: block
or modify the request. Perhaps that may let us decide that making
suspending the requests possible from within filters should not affect
FilterContext except for introducing an optional generic extension query
handler

Thanks, Sergey