Hi James,
AFIAK style A would enable a one-level deep relationship between a
root resource and a sub-resource. It would enable the developer to add
sub-resources to a any root resource if that was intended or otherwise
(although one could devise ways to disallow that).
In such a case it would be almost as easy to make the sub-resource in
style A a root resource with @Path("whatever/bar/{id}") if there is no
value in the parent/sub relationship. Some shared behavior could be
inherited from a common base class. The downside in this case is the
"whatever" is repeated.
Do you envisage a use-case where a sub-resource is added to a root
resource without modification to the latter? I guess that is what
makes me uncomfortable about style A.
I suppose in Guice one could could modify the bindings such that
BarResource is bound to a specialized implementation, in fact one
might qualify it in this case so BarResource is reusable in other
contexts:
@Path("/whatver")
class FooResource {
@SubOfFoo @Inject Provider<BarResource> bar;
@Path("bar/{id}")
public BarResource getBar() { // Should try and add support for 330
@Inject for parameters
return bar.get();
}
}
Thus specialization is possible without loosing the clear definition
of sub-resources, and specialization is clearly defined according to
Guice bindings. Although the above bugs me that there is a
dissociation between the path parameter, "id", and BarResource.
One way to solve this would be to allow the intermixing of injected
and passed parameters a bit like Guice's assisted inject, but i cannot
come up with an appropriately typed and easy solution to that problem
independent of Guice, and Guice still requires the creation of a
factory interface
@Path("/whatever")
class FooResource {
@Inject BarResourceFactory bar;
@Path("bar/{id}")
public BarResource getBar(@PathParam("id") String id) {
return bar.create(id);
}
}
interface BarResourceFactory {
public BarResource.create(String id);
}
class BarResource {
@Inject
BarResource(@Inject FooResource foo, @Assisted String id) { ... }
}
This is frustrating, i feel we are missing a language feature here
that could really help.
It is certainly easy to implement support for @ResourceRef (and say
@SuperResourceRef to validate a parent of a certain type) as we
already that support for that in a slightly different way.
Style A would be a little harder. Having @SubResource("bar/{id}",
BarResource.class) would be easier because we don't change the
definition of what a root resource is. I am currently not too sure
about the consequences of a sub-resource modifying the relationship to
a super resource when jar is dropped into the classpath. It could make
for harder debugging if more magic is involved. If the super resource
is unaware of the sub-resources that could be attached it may make it
harder to define application-based hypermedia representations on the
super resource (although at runtime the relationships will be
recognized by the WADL representation returned by the super resource).
I am wondering in such cases that perhaps it might be better to define
a routes class that provides a global view of the relationships?
resource(Foo.class, "blah).
subResources(resource(Bar.class, "b"), resource(Baz.class, "z"))
kind of hard to wire up graphs nicely using Java though.
What if we could wire up the sub-resource to the super-resource but
the path is defined on the former? that would at least resolve my
concerns about the "anything goes" relationship and the path would be
defined on the sub-resource.
@Path("/whatver")
@SubResource(BarResource.class)
class FooResource { }
@SubPath("bar/{id}") // Different annotation so as not to conflict
with def of root resource
class BarResource {
@PathParam("id") String id;
@ResourceRef FooResource parentResource;
@GET String getBody() {...}
}
It also means one could define sub-resources on sub-resources.
Paul.
On Jul 15, 2010, at 11:01 AM, James Strachan 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/
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>