users@jersey.java.net

[Jersey] Re: Root resource as a resource factory (to handle @Path("/") as well)

From: cowwoc <cowwoc_at_bbs.darktech.org>
Date: Fri, 12 Oct 2012 11:52:04 -0400

     FYI, I learned the hard way that REST doesn't mix well with
polymorphism. See http://java.net/jira/browse/JERSEY-798 for a related
discussion.

Gili

On 12/10/2012 11:16 AM, Martynas Jusevičius wrote:
> Gili,
>
> I was looking at resource factory as a way to achieve polymorphism as
> I actually have 2 interfaces: Resource and ContainerResource that
> extends Resource. So my plan was to have getResource() like this:
>
> @Path("/")
> class RootResource
> {
> @Path("{path: .*}")
> public Resource getResource()
> {
> Resource resource = new ResourceBase();
> if (resource.canBeContainer()) resource = new
> ContainerResourceBase(resource);
> return resource;
> }
> }
>
> But you're right, I can have the root resource handling all cases
> without sub-resources and achieve the polymorphism inside its
> constructor:
>
> @Path("{path: .*}")
> class RootResource implements Resource
> {
> private Resource resource = null;
>
> public RootResource()
> {
> resource = new ResourceBase();
> if (resource.canBeContainer()) resource = new
> ContainerResourceBase(resource);
> }
>
> @GET
> public Response get()
> {
> // implementation reuse
> return resource.get();
> }
>
> // other Resource methods
> }
>
> I think this is less elegant, but at least it shouldn't have the issue
> with handling @Path("/") case.
>
> Martynas
> graphity.org
>
> On Fri, Oct 12, 2012 at 5:26 PM, cowwoc <cowwoc_at_bbs.darktech.org> wrote:
>> Can't you get the RootResource to handle all requests itself, as opposed
>> to having a sub-resource in the first place?
>>
>> Gili
>>
>>
>> On 12/10/2012 10:21 AM, Martynas Jusevičius wrote:
>>> Hey Gili,
>>>
>>> I've come to a similar conclusion, but I think this is a limitation
>>> however. I think root resources should be able to function simply as
>>> resource factories -- also in case of @Path("/").
>>>
>>> If RootResource has to implement Resource interface for example, which
>>> not only contains @GET get(), but also @POST post() and some more
>>> methods, your example doesn't look so elegant anymore -- probably smth
>>> like this:
>>>
>>> @Path("/")
>>> public class RootResource implements Resource
>>> {
>>> private Resource resource = null;
>>>
>>> public RootResource()
>>> {
>>> resource = getResource();
>>> }
>>>
>>> @GET
>>> public Response get()
>>> {
>>> // implementation reuse
>>> return resource.get();
>>>
>>> }
>>>
>>> @POST
>>> public Response post()
>>> {
>>> // implementation reuse
>>> return resource.post();
>>>
>>> }
>>>
>>> // some more methods wrapping this.resource
>>>
>>> @Path("{path: .*}")
>>> public Resource getResource()
>>> {
>>> return new Resource();
>>> }
>>> }
>>>
>>> But there doesn't seem to be a better way?
>>>
>>> Martynas
>>> graphity.org
>>>
>>> On Fri, Oct 12, 2012 at 4:40 PM, cowwoc <cowwoc_at_bbs.darktech.org> wrote:
>>>> Hi Martynas,
>>>>
>>>> My guess is that what you're trying to do won't work, because the
>>>> entire
>>>> point of @Path is to say "the current resource handles *this* path". Now,
>>>> if
>>>> you really want the sub-resource to handle it, and there is no such thing
>>>> as
>>>> a parent of "/" then the only way to handle this is declare
>>>> @GET/_at_POST/etc
>>>> inside RootResource and delegate to getResource(). For example:
>>>>
>>>>
>>>> @Path("/")
>>>> public class RootResource
>>>> {
>>>> @GET
>>>> public Response get()
>>>> {
>>>> // implementation reuse
>>>> return getResource().get();
>>>>
>>>> }
>>>>
>>>> @Path("{path: .*}")
>>>> public Resource getResource()
>>>> {
>>>> return new Resource();
>>>> }
>>>> }
>>>>
>>>> I've done something similar in my own code, for different reasons.
>>>> One
>>>> benefit of this approach is that type-safety remains enforced at
>>>> compile-time. If the sub-resource ever changes in a way that requires
>>>> RootResource to change you are likely to get a compiler error.
>>>>
>>>> Gili
>>>>
>>>>
>>>> On 12/10/2012 6:31 AM, Martynas Jusevičius wrote:
>>>>> Marek,
>>>>>
>>>>> sorry, this is a typo left from when I tried to split the sub-resource
>>>>> locators into 2 -- one for the base URI and one for the rest.
>>>>>
>>>>> The following code has the same effect (HTTP Status 405 - Method Not
>>>>> Allowed):
>>>>>
>>>>> @Path("/")
>>>>> public class RootResource
>>>>> {
>>>>>
>>>>> @Path("{path: .*}")
>>>>> public Resource getResource()
>>>>> {
>>>>> return new Resource();
>>>>> }
>>>>>
>>>>> }
>>>>>
>>>>> BTW, I'm using Jersey 1.9 -- maybe there have been some bugs in this
>>>>> area?
>>>>>
>>>>> Martynas
>>>>>
>>>>> On Fri, Oct 12, 2012 at 1:22 PM, Marek Potociar
>>>>> <marek.potociar_at_oracle.com> wrote:
>>>>>> On Oct 12, 2012, at 9:10 AM, Martynas Jusevičius
>>>>>> <martynas_at_graphity.org>
>>>>>> wrote:
>>>>>>
>>>>>>> Hey again,
>>>>>>>
>>>>>>> how can turn a single root resource into a resource factory that
>>>>>>> handles all requests through sub-resource locators -- *including* the
>>>>>>> base URI @Path("/")?
>>>>>>> I tried the following but I get 405 Method not allowed:
>>>>>>>
>>>>>>> @Path("/")
>>>>>>> public class RootResource
>>>>>>> {
>>>>>>>
>>>>>>> @Path("{path: .+}")
>>>>>>> public Resource getResource()
>>>>>>> {
>>>>>>> return new Resource();
>>>>>>> }
>>>>>>>
>>>>>>> }
>>>>>>>
>>>>>>> I guess Jersey expects @GET etc. on the RootResource -- but I want
>>>>>>> @Path("/") to be handled by getResource() as well.
>>>>>>> I haven't found any requirements for root resources to contain
>>>>>>> resource methods e.g. GET - but maybe @Path("/") is a special case?
>>>>>>>
>>>>>> Your regular expression seems to require at least one character. Try to
>>>>>> replace it with "{path: .*}".
>>>>>>
>>>>>> Marek
>>>>>>
>>>>>>
>>>>>>> Is this possible with JAX-RS/Jersey?
>>>>>>>
>>>>>>> I found a very similar dynamic dispatching example in the book
>>>>>>> "RESTful Java with JAX-RS" -- however not with the base @Path("/"):
>>>>>>>
>>>>>>> @Path("/customers")
>>>>>>> public class CustomerDatabaseResource {
>>>>>>> protected CustomerResource europe = new CustomerResource();
>>>>>>> protected FirstLastCustomerResource northamerica =
>>>>>>> new FirstLastCustomerResource();
>>>>>>> @Path("{database}-db")
>>>>>>> public Object getDatabase(@PathParam("database") String db) {
>>>>>>> if (db.equals("europe")) {
>>>>>>> return europe;
>>>>>>> }
>>>>>>> else if (db.equals("northamerica")) {
>>>>>>> return northamerica;
>>>>>>> }
>>>>>>> else return null;
>>>>>>> }
>>>>>>>
>>>>>>> Martynas
>>>>>>> graphity.org
>>>>