users@jersey.java.net

Re: [Jersey] injecting _at_Context fields on sub resources...

From: James Strachan <james.strachan_at_gmail.com>
Date: Thu, 15 Jul 2010 10:05:48 +0100

BTW I've raised an issue to track this
https://jersey.dev.java.net/issues/show_bug.cgi?id=560

On 15 July 2010 10:01, James Strachan <james.strachan_at_gmail.com> wrote:
> On 2 July 2010 06:58, Paul Sandoz <Paul.Sandoz_at_sun.com> wrote:
>> On Jul 1, 2010, at 4:06 PM, James Strachan wrote:
>>> On 1 July 2010 14:32, Paul Sandoz <Paul.Sandoz_at_sun.com> wrote:
>>>>
>>>> Hi James,
>>>>
>>>> So would something like the following work for you:
>>>>
>>>>  def getElement(@<I do not know what to call this!> subResource: Element)
>>>> =
>>>> { ... }
>>>>
>>>> ?
>>>
>>> Sure!
>>>
>>>> That would be really easy to support because we can write an
>>>> InjectableProvider that defers to ResourceContext. I can even send one in
>>>> the email in addition to adding it to the workspace so that one can use
>>>> it
>>>> with a stable version.
>>>>
>>>> But as you can tell i am having a naming problem :-) any suggestions?
>>>
>>> Yeah, I wasn't sure what to call it either :)
>>>
>>> How about...
>>>
>>> def getElement(@SubResource e: Element) = ...
>>>
>>
>> I thought of that too, but may not be accurate as a sub-resource could ask
>> for a reference to the root resource using the same mechanism.
>>
>>  @ResourceRef?
>>
>> still don't like it much but...
>>
>> Would you mind logging an issue, since i will be away i am not sure i will
>> get to this today and may forget when i get back!
>>
>>
>>>
>>> As an aside - I hit another related thing today. One of the awesome
>>> things about JAXRS is its so modular; drop a bunch of jars together
>>> with jaxrs beans in them and provided your URIs don't clash, you've an
>>> extensible web app & REST API.
>>>
>>> However I've found I often want to extend an existing sub resource in
>>> a modular way so that adding a jar to a web app can extend/enhance a
>>> web app/REST API.
>>>
>>> e.g. camel-web is a web console for Camel which has a root resource &
>>> then a rather big tree of sub resources. If folks drop in the
>>> activemq-camel-web jar, it would be nice to be able to replace some of
>>> the endpoints with derived sub resource classes which offer more
>>> representations or behaviour with knowledge of activemq etc.
>>>
>>> If the application code is explicitly doing "new Element(id)" then you
>>> have to go through the whole resource chain from root to sub resource
>>> and overriding them all.
>>>
>>> If jersey is instantiating the sub resource we could let the framework
>>> detect a sub class being on the classpath (if its in the set of
>>> packages scanned by jersey) then a new DerivedElement class could be
>>> injected in the above example.
>>>
>>> e.g. in a base module...
>>>
>>> def getElement(@SubResource e: Element) = ...
>>>
>>>
>>> could return an instance of "Element".
>>>
>>> Add derived.jar to the classpath and jersey might create a
>>> "DerivedElement" instead.
>>>
>>> (There could be issues if there are multiple derivations of the class
>>> on the classpath though :)
>>>
>>
>> Do you think it might be better in this case to use Guice? A binding could
>> declare that when a reference to Element is requested it will be an
>> implementation of DerivedElement. This would avoid issues of multiple
>> derivations and also avoid the tricky bit of Jersey having to scan for
>> derived classes.
>>
>> I think that should actually work with @SubResource given the way Jersey
>> will ask Guice for a reference.
>
> Here's another thought on this.
>
> One of the great things about JAXRS is it leads to very modular
> web/REST applications. Its easy to split up
> features/resources/representations into different jars with different
> dependencies so folks can mix and match features.
>
> To give a more concrete example; the Apache Camel project as a Jersey
> based web/REST application. We want to allow sub resources to be
> customized and new resources added in different modules customers may
> use. e.g. if folks are using Apache ActiveMQ with Camel, if they drop
> the right jar onto the classpath then hey-presto they get new
> resources and representations (typically sub resources of existing
> resources) for ActiveMQ specific things which users who use, say,
> Camel with just file systems don't need.
>
> Imagine if we wish to add a new resource/representation to an existing
> resource or sub resource. Typically we'd have to create our own
> derivation of the respective root resource - then delve into each sub
> resource @Path method, override it to return a new derived class -
> walk the chain of resources until we get to the parent resource we are
> going to create a new sub resource from - then add a new method to
> return a new resource bean.
>
> This seems rather a lot of work - particularly for deeply nested
> resources. What would be a little more DRY and much simpler would be
> if we could add a sub resource to a parent resource in a more loosely
> coupled way - and let Jersey do the resource path walking and
> dependency injection to leave things loosely coupled.
>
> e.g. imagine these 2 loosely coupled resources, a root and
> sub-resource where the sub resource is optional defined in a separate
> jar...
>
> // style A
>
> // my-foo.jar module
>
> @Path("/whatver")
> class FooResource {
> }
>
>
> // my-bar.jar module
>
> @Path("bar/{id}")
> @SubResource(FooResource.class)
> class BarResource {
>   @PathParam("id") String id;
>   @Context FooResource parentResource;
>
>  @GET String getBody() {...}
> }
>
> Here we are changing the JAXRS model slightly for sub resources;
> rather than the parent resource, FooResource knowing at compile time
> all the possible sub resources available and having methods defined on
> it for each sub resource to return each new instance available, we use
> a new annotation (@SubResource) on the sub resource class to create a
> new sub resource on the FooResource class. In this case @SubResource
> specifically marks a class as being a sub resource - and also links
> the sub resource to its parent class. This decouples the parent
> resource from having to know at compile time of all the possible sub
> resources available.
>
> Then we use dependency injection to inject the path parameter in the
> sub resource - along with anything else we need - in this example I've
> used @Context to also inject the parent resource too.
>
> So this is functionally equivalent to writing something like...
>
> // style B
>
> @Path("/whatver")
> class FooResource {
>
>  @Path("bar/{id}")
>  pubilc BarResource getBar(@PathParam("id") String id) {
>    return new BarResource(id, this);
>  }
> }
>
> class BarResource {
>   String id;
>   FooResource parentResource;
>
>  public BarResource(String id, FooResource parentResource) {
>    this.id = id;
>    this.parentResource = parentResource;
>  }
>
>  @GET String getBody() {...}
> }
>
> However the former is better as
>
> * its leaner meaner code that's more declarative & DRY
> * annotations tend to stay closer to where they are really required -
> e.g. compare the two BarResource implementations; one of them is very
> clear what those fields are, one is not - you have to search for users
> of its constructor to see what the JAXRS metadata is.
> * we get to reuse the same injection model on sub resources as we do
> for resources (so we can inject anything we like rather than having to
> manually hand-craft constructors and methods-which-call-constructors)
> * sub resources are now very modular - anyone can add a sub resource
> to any resource easily in a separate jar without lots of redundant
> spagetti code to override all the methods invoked to navigate from the
> root resource to the newly created leaf resource. Plus it gets very
> complex if you want to support 2 kinds of optional sub resource in 2
> different jars using the same parent resource using the old way :)
> * it feels a bit more natural than my previous suggestion (style C
> below) to get injection on sub resources - of having methods on the
> parent resource which get injected with the sub resource - that
> typically don't do anything at all other than return what they are
> given :)
>
> e.g. if we wanted to get injection on the sub resource we could do
> something like this from my previous mails...
>
> // style C
>
> @Path("/whatver")
> class FooResource {
>
>  @Path("bar/{id}")
>  public BarResource getBar(@ResourceRef BarResource bar) {
>    return bar;
>  }
> }
>
>
> // my-bar.jar module
>
> class BarResource {
>   @PathParam("id") String id;
>   @Context FooResource parentResource;
>
>  @GET String getBody() {...}
> }
>
> Its interesting that the sub resource code in style C is pretty
> similar to style A (since we're using injection on it). All we're
> doing is removing a fairly pointless getBar() method in the
> FooResource - which also adds a tight compile time coupling between
> the resources - and move the method's 2 annotations into the sub
> resource class (which is more useful anyway when viewing the code for
> BarResource).
>
> So it seems simpler to just annotate sub resource classes to link them
> to parent resources and get dependency injection - i.e. style A wins.
> I guess if a sub resource were to be created from multiple parent
> resource classes using different @Path template URIs, then folks could
> always use style C - having the factory methods of the sub resources
> on the parent resources can be useful if you need many of them and/or
> using different @Paths. Style A is useful when you only have one
> parent resource class for a sub resource (which is very common), want
> slightly more DRY code keeping annotations closer to their relevant
> code and when you want more modular code (e.g. so sub resources can be
> in different jars).
>
> Thoughts?
>
> --
> James
> -------
> http://macstrac.blogspot.com/
>
> Open Source Integration
> http://fusesource.com/
>



-- 
James
-------
http://macstrac.blogspot.com/
Open Source Integration
http://fusesource.com/