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/