users@jax-rs-spec.java.net

[jax-rs-spec users] [jsr339-experts] Re: Re: Re: Re: Re: Allowing two resources to be mapped to the same URI

From: Marek Potociar <marek.potociar_at_oracle.com>
Date: Wed, 23 May 2012 17:47:42 +0200

On May 23, 2012, at 3:58 PM, Bill Burke wrote:

> On 5/23/12 6:16 AM, Marek Potociar wrote:
>>> REST has nothing to do with Java type system. The same URI may have different representations. Different representations may require different Java types. Finally, entity providers aren't always the best solution to the problem.
>>
>> That's why we have support for multiple java methods processing the same HTTP method right? And everything about the same resource remains nicely encapsulated in a single class.
>>
>
> You're missing my point. Your solution to many of the problems I listed was, "use an entity provider". Using an entity provider isn't always a possibility.
>
>> Well for me the matching does not involve just matching the URI. In the matching process you are doing lots of complex processing in any of these phases:
>>
>> 1. match URI
>> 2. match HTTP method
>> 3. match consumed/produced media types
>>
>> If any of the phases requires stepping a level up, for me it is a back tracking. The point of the current matching algorithm is exactly to avoid the need for doing that. In Jersey we also use the approach that URI matching in phase 1 produces a list of methods that are further filtered in phases 2& 3. Same as in your impl, it does not matter what class the method belongs to (which is why I said in the beginning that it is not a problem for us to support the issue implementation-wise). However, if there is no method found after phase 1, 2& 3 are finished, we do not back-track and repeat the same process with another resource URI in the list. And that's also something I don't want to be forced to do.
>>
>
> FWIW:
>
> we break up @Path mappings into segments and map those segments onto a tree structure. Any expression ends the segmentation. When a URI comes in, we segment the URI and do a depth first search on this tree.
>
> 1. If there's a completely static path that matches the URI we check HTTP method and consumed/produced.
> 2. If there's none, at that tree node, we try to match any expressions. If one matches, we check HTTP method/consumed produces
> 3. Then, we look at locators. If any match, we just return this locator to be invoked on. This ends the lookup unfortunately.
> 4. If none match, we recurse back up the tree and try again at each level. So we do backtrack, but not for locators.
>

As I said, I don't care that you back-track as long as you comply to the algorithm in the spec - which seems you are not at the moment btw.

> Well, it would be very sad that for those of us that have superior matching algorithms than the specification which support more use cases would have to revert to something that's inferior.

Note that the goal of the algorithm from JAX-RS v1.0 was to be performant limiting the server resources consumption while supporting as many resource configurations as possible at the same time. Supporting all the resource configurations possible was a non-goal. (Feel free to check with Paul or Marc.)

The algorithm was specifically designed to avoid the need of back-tracking. Or, to put it differently, it was designed to avoid the need to scan and match the whole root resource space while occupying the limited server resources each and every time an invalid request comes in. Sounds like a potentially exploitable issue, esp. in larger deployments, to me. It would be sad if those that implemented spec-compliant algorithms would have to change them because someone else either accidentally or deliberately decided to pursue goals beyond spec and ended up with a non-portable matching algorithm. Also I find it quite arrogant to claim now that implementations providing spec-compliant algorithms are inferior.

> Change the algorithm to be resource-method pattern based:
>
> 1. Create list of match expressions:
>
> a) For all resource methods, create a match expression by concatenating the class's @Path expression to the resource method's @Path expression and add to list
> b) For all locators append ".*" to the expression
> c) Sort this list preceding number of literal characters as primary key(descending), number of matching groups as secondary(ascending) and locator produced expressions as the teritary key (descending)
>
> So, fully literal expressions take precendence over matching group patterns. Resource methods take precendence over resource locators. Implementations are allowed to weed out expressions that could never match to optimize matching performance.
>
> 2. Perform match
> for each expression
> a. match URI
> b. If locator is matched, goto step 1 and match based on remaining path
> c. Match HTTP method
> d. Match produces/consumes
> e. If all criteria match, then you are good to go.
>
> 3. If there is an expression that matches the URI, but not the method, do the 405 thing as defined currently in the spec
> 4. If there is an expression that matches the URI and the method, but not the Accept type, do the 415 as expression in the current spec.
>
> You could reduce the complex, hard to understand 2 page pseudo code, with something much simpler that covers more use cases. You'd be hard pressed to figure out whether my above algorithm is backward compatible considering the complexity of the original, so I doubt you'd ever consider this...

It's not difficult at all to see that the above is not compatible. The problem is the "Perform match for each expression" in step 2. Currently the algorithm only searches for the first matching expression (2.a) and then does the 2.b, 2.c, 2.d (currently only on a single resource, which should be expanded to cover all resources with the same path template). If it fails in 2.e, it does not iterate over the rest of the expressions.

> BTW, I think it would be very wrong for the TCK to test the 405/415 edge cases. Implementations should be able to support this type of matching. What's more important for the TCK is to test the case where you have multiple matches and the choice of that match.

I am not saying that implementations cannot support the back-tracking. But it should not be enabled by default IMO, if portability of the end-user applications is at least a bit important to the implementation providers.

> Hey, in any case, if you do add some ridiculous TCK test for this, I reserve the write to send you a hate email every time some user complains that old code doesn't work any more because we had to change our matching algorithm.

Perhaps you should send the emails to the engineer writing your non-compliant algorithm in the first case... Note that this is not about me proposing anything new - the algorithm has been there in the spec since the very beginning! And there is a good reason why it is written as a non-back-tracking one. Similarly, there is a good reason for ensuring that implementations claiming JAX-RS compliance provide compliant algorithms that would guarantee JAX-RS/JavaEE users code portability.

Anyway, it should not be a big deal to implement a configurable flag that would stop your algorithm after a first found match, should it? That way you would be able to switch to a JAX-RS compliant & portable mode as well as support your "superior" back-tracking algorithm mode at the same time. Personally, I would prefer you keep the flag enabled by default for the portability reasons, but that call is completely up to you.

Marek

>
>
> --
> Bill Burke
> JBoss, a division of Red Hat
> http://bill.burkecentral.com