users@jax-rs-spec.java.net

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

From: Sergey Beryozkin <sberyozkin_at_talend.com>
Date: Fri, 9 Dec 2011 10:21:59 +0000

Hi Marek, your comment to the option 3 make me wonder what option 1 is
actually about, see below please

On 07/12/11 10:26, Marek Potociar wrote:
> Adding some of the evaluation comments from my emails earlier.
>
> On 12/06/2011 02:31 PM, Sergey Beryozkin wrote:
>> 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.
> Keeps the call stack small (good because JVM does not support tail call optimization (TCO) which does not seem to
> change any time soon).
> Non-blocking, parallel or asynchronous filter execution strategies are possible and easy to add extensions. This
> enables high-performance implementations of execution chain leveraging the nature of multi-core environments.
> Bytecode-optimization-friendly design
>> 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.
> Looks like wrapping style, but is non wrapping, which is confusing. May lead to errors with users trying to do some
> post-processing logic after context.[actionMethod] is invoked.
>>
>> 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
> Simple to implement and sufficient for the current (basic) set of requirements.
>> 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.
> Returning a *response* from a *request* filter raises concerns about the design.

I'm wondering what this comment is really about, as I hope the idea of
the request filter blocking a request (meaning it does return a
response, even if it does not call explicitly 'return Response') does
not raise the concerns for you ?
Why am I'm asking...In the option 1, in the case of the response being
blocked, we have

NextAction action = filterContext.stop(Response.status(someStatus).build());
return action;

My understanding is that we have something like this in this case:

public class FilterContextImpl implements FilterContext {
    static class StopAction implements NextAction {
        private Response r;
        public StopAction(Response r) {
            this.r = r;
        }
        public Response getResponse() {
            return r;
        }
    }

    public NextAction stop(Response r) {
        return new StopAction(r);
    }
}

Is that what you have in mind or is it :

public class FilterContextImpl implements FilterContext {
    private Response r;

    static class StopAction implements NextAction {
    }

    public NextAction stop(Response r) {
        this.r = r;
        return new StopAction();
    }
}

Now, I'm typing this and I realize that having NextAction in the
response does not really enforce either of the above implementation
startegies;

Sergey