jsr339-experts@jax-rs-spec.java.net

[jsr339-experts] Re: [jax-rs-spec users] Re: Re: Re: Need clarification on Section 6.7

From: Marek Potociar <marek.potociar_at_oracle.com>
Date: Fri, 1 Feb 2013 14:06:21 +0100

On Feb 1, 2013, at 1:10 PM, Sergey Beryozkin <sberyozkin_at_talend.com> wrote:

> On 01/02/13 12:02, Marek Potociar wrote:
>>
>> On Feb 1, 2013, at 12:55 PM, Sergey Beryozkin<sberyozkin_at_talend.com> wrote:
>>
>>> On 01/02/13 11:38, Marek Potociar wrote:
>>>>
>>>> On Jan 31, 2013, at 6:29 PM, Sergey Beryozkin<sberyozkin_at_talend.com> wrote:
>>>>
>>>>> On 31/01/13 16:57, Marek Potociar wrote:
>>>>>> Hi Bill,
>>>>>> I'm forwarding my recent reply to a similar question we received from
>>>>>> TCK team:
>>>>>>
>>>>>> Global and globally name-bound filters and interceptors should be
>>>>>> invoked for all responses. Resource and method name-bound interceptors
>>>>>> should be invoked only for responses originated from the bound resource
>>>>>> or resource method.
>>>>>>
>>>>>> I.e. if we define 2 groups of filters and interceptors based on their
>>>>>> binding:
>>>>>>
>>>>>> G1: Global and globally name-bound (i.e. name-bound to the Application
>>>>>> sub-class) response filters and interceptors
>>>>>> - IMO the user intention with these is clearly to invoke them for ALL
>>>>>> responses
>>>>>>
>>>>>> G2: Resource and method name-bound response filters and interceptors
>>>>>> - IMO the user intention with these is to invoke them only for responses
>>>>>> originated from a particular resource or method
>>>>>>
>>>>>
>>>>> What are resource-bound filters, is it:
>>>>>
>>>>> @SomeName
>>>>> class MyFilter...
>>>>>
>>>>> @SomeName
>>>>> @Path("/root")
>>>>> public class RootResource {
>>>>> }
>>>>>
>>>>> Effectively MyFilter applies to all RootResource methods ?
>>>>
>>>> Yes, exactly.
>>>>
>>>>>
>>>>>> With the groups defined above, here's what I think it should work
>>>>>> (Santiago, please correct me if you think otherwise):
>>>>>>
>>>>>> UC1: Response returned from pre-matching filter:
>>>>>> - G1 gets invoked
>>>>>>
>>>>>> UC2: Response produced by an ExceptionMapper from an exception that
>>>>>> originated in a pre-matching filter:
>>>>>> - G1 gets invoked
>>>>>>
>>>>>> UC3: Response returned from post-matching filter:
>>>>>> - G2 + G1 gets invoked
>>>>>>
>>>>>> UC4: Response returned from matched resource method:
>>>>>> - G2 + G1 gets invoked
>>>>>>
>>>>>> UC5: Response produced by an ExceptionMapper from an exception that
>>>>>> originated in a matched resource method:
>>>>>> - G2 + G1 gets invoked
>>>>>>
>>>>>> UC6: Response produced by an ExceptionMapper from an exception that
>>>>>> originated in a response filter or interceptor processing a response to
>>>>>> a matched request:
>>>>>> - G2 + G1 gets invoked
>>>>>>
>>>>>> UC7: Response produced by an ExceptionMapper from an exception that
>>>>>> originated in a response filter or interceptor processing a response to
>>>>>> an unmatched request:
>>>>>> - G1 gets invoked
>>>>>>
>>>>>> Additionally, there is a condition in the spec that prevents infinite
>>>>>> loops for UC6 and UC7 (see 2nd paragraph in section 6.7 of the spec).
>>>>>> So, to sum-up, here's the conceptual algorithm I have in mind:
>>>>>>
>>>>>> start request processing
>>>>>> store G1 response filters and interceptors into the request context
>>>>>> try {
>>>>>> invoke pre-matching request filters
>>>>>> match request
>>>>>> if (request matched) {
>>>>>> store G2 response filters and interceptors into the request context
>>>>>> invoke global and bound post-matching request filters
>>>>>> invoke resource method
>>>>>> }
>>>>>> invoke all response filters stored to the request context
>>>>>> } catch (Exception ex) {
>>>>>> try {
>>>>>> map exception to response
>>>>>> invoke all response filters stored to the request context
>>>>>> } catch (Exception ex) {
>>>>>> propagate exception to the hosting container
>>>>>> }
>>>>>> }
>>>>>> exit request processing
>>>>>>
>>>>> I think it basically says that in Bill's (2) case all response filters are executed,
>>>>> What about his case 3 though, I think it is UC6, UC7 points, still not clear what if the mapped response has to run through remaining filters or not, for example,
>>>>>
>>>>> A->B, A throws the exception, is B still given a chance to handle the mapped response ?
>>>>
>>>> I thought the algorithm above is also clear on that. For a mapped exception response, ALL applicable response filters are invoked (not only "remaining ones"). So, both, A and B are invoked.
>>>>
>>>> FWIW, throwing an exception from a filter is IMO not the programming model we should optimize for. Such exceptions should mostly indicate that there is a problem with the filter implementation as such.
>>>>
>>>
>>> Hmm..., both A and B are response filters, they are processing some Response, A is first to process it and decides to throw an exception (intentionally) - what I don't quite get is whether it means we have an immediate 500, or, we try to map this A-originated exception and if the mapping was successful, then let B a chance to handle this mapped response, even though A thought it was an exception case...
>>>
>>> I'm sorry if I'm slow :-), but I'm still a bit unclear.
>>
>> If A throws an exception that gets mapped, then both A and B will be invoked on the mapped response. If A throws an exception again, then the exception is going to be propagated to the hosting container.
>>
>> Hope it's crystal clear now,
>
> Definitely is, thanks Marek. The fact that A can be re-entered twice was definitely something I was not getting.
>
> Are we sure it is correct though to allow re-entrance into A and indeed all the response filters before A if A throws the exception intentionally, effectively saying 'I've had enough with processing this current response' ? Not really sure whether it is right or not, I guess it is interesting to imagine a bit the possible pros and cons

I'm not 100% sure, but as I wrote earlier, I believe that we should not promote programming models where a filter throws an exception in a "stateful" way, i.e. in a way that it would not expect to be applied to the mapped response.

My main motivation factors for promoting this approach are:

1. it's based on simple rules and JAX-RS users will be able to grasp these rules without too much confusion
2. a response filters should be invoked for any response to which they are applicable.
3. in general it's better to give all response filters involved in the given request processing a chance to react and process a mapped exception response that to ignore them for such responses.

In general, it means that global filters should be invoked for all responses and bound filters to responses originating from a component. Note here that a response to a mapped exception (thrown anyway in the req/resp processing) should not deviate from this rule IMO. While it may not be obvious with 2 response filters, consider scenario with 3 filters:

A->B->C; B throws exception that gets mapped to a response R(ex).

IMO the A should be given a chance to process the new R(ex) response.

Cheers,
Marek

>
> Cheers, Sergey
>
>
>> Marek
>>
>>>
>>> thanks, Sergey
>>>
>>>> HTH,
>>>> Marek
>>>>
>>>>>
>>>>> Thanks, Sergey
>>>>>
>>>>>
>>>>>
>>>>>> HTH,
>>>>>> Marek
>>>>>>
>>>>>>
>>>>>> On Jan 30, 2013, at 4:29 PM, Bill Burke<bburke_at_redhat.com
>>>>>> <mailto:bburke_at_redhat.com>> wrote:
>>>>>>
>>>>>>> The section on filters and exception handling is unclear.
>>>>>>>
>>>>>>> if an exception is thrown from a resource method:
>>>>>>>
>>>>>>> 1. An exception mapper is found
>>>>>>> 2. bound and global filters are executed on the response
>>>>>>>
>>>>>>> If an exception is thrown from a request filter:
>>>>>>>
>>>>>>> 1. An exception mapper is found and executed
>>>>>>> 2. response filters are executed. BOund response filters only executed
>>>>>>> if this is not prematch
>>>>>>>
>>>>>>> If an exception is thrown from a response filter:
>>>>>>>
>>>>>>> 1. An exception maper is found and executed
>>>>>>> 2. ??? I assume no response filters are then executed?
>>>>>>> --
>>>>>>> Bill Burke
>>>>>>> JBoss, a division of Red Hat
>>>>>>> http://bill.burkecentral.com
>>>>>>
>>>>>
>>>>>
>>>>
>>>
>>>
>>
>