users@jersey.java.net

Re: [Jersey] thoughts on _at_Link and Marc's blog post

From: Marc Hadley <Marc.Hadley_at_Sun.COM>
Date: Thu, 11 Mar 2010 10:43:45 -0500

On Mar 11, 2010, at 9:37 AM, James Strachan wrote:

> On 11 March 2010 13:32, Marc Hadley <Marc.Hadley_at_sun.com> wrote:
>> On Mar 11, 2010, at 5:22 AM, James Strachan wrote:
>>> This seems great for 'root resources' or top level resources; though
>>> it gets harder when you build deeply nested resources, where (say) a
>>> Shipment resource might be a child of a Widget resource - with the
>>> Shipment resource only knowing how its linked within a Widget. e.g.
>>>
>>> class Widget {
>>> @Path("shipment/{shipmentId}")
>>> Shipment getShipment(@PathParam("shipmentId") String shipmentId) {
>>> return new Shipment(this, shipmentId)
>>> }
>>> }
>>>
>>> I wonder if the @Link annotation could also be configured with the
>>> 'owner'? e.g.
>>>
>>> class Shipment {
>>> @Link("shipment/{shipmentId}", ownedBy="widget")
>>> URI link;
>>>
>>> String shipmentId;
>>> Widget widget;
>>> }
>>>
>>> Though this is not very DRY as there's a need to replicate the child
>>> URI template in both @Link and @Path.
>>>
>>> I wonder if there's a way to just specify the URI template once on
>>> Widget which can then be used both when navigating from Widget ->
>>> Shipment and when trying to reverse engineer the URI from a Shipment?
>>>
>> I've been thinking about this too. One issue I see is that subresources can be made available via more than one root resource so it might not always be ideal to fix on a particular path (though I suppose a canonical URI for accessing a resource isn't really a bad thing).
>
> Agreed. Though in the use case where you want to create a URI for a
> resource/DTO, you're typically not going to want to generate a list of
> links for every permutation of how you can navigate to the resource;
> you probably just want to pick one of the paths to be used as the
> canonical path (which with HATEOS you could change your mind later on
> if you wish). So choosing one doesn't seem a biggie to me - if its
> more complex figuring out what a link is, just do it by hand and don't
> use the @Link.
>
> If you wanted to generate 2 links for a given resource/DTO (which
> seems really unlikely) you could always use 2 different @Link
> annotations on 2 different fields using different owners and/or
> locators?
>
Agreed.

>
>> When you are happy to fix the path then we could offer something like (using your example above):
>>
>> @Link(resource=Widget.class, locator={"getShipment"})
>>
>> where the locator property provides the sub-resource locator method. Given this info the link processor can assemble the URI from the root resource and locator path templates. Things get trickier if there are multiple locators used within a single request, annotations don't offer much in the way of syntax to represent a list of class+method pairs other than parallel arrays.
>
> I was kinda thinking that you could use the owner object (the Widget
> instance) to generate its link; then keep walking up the tree using
> the same '_at_Link mechanism' until you get to a root. So that each @Link
> just specifies how to link the current object (Shipment) from its
> owner (Widget) - which could be a sub-resource of something else
>
> For example if we had /widgets/5/shipments/7, we'd need the Widget
> instance to be able to figure out the "/widgets/5" prefix of the URI.
> Note I'm considering resources not on the call stack here too (more
> later...)
>
>> When you want to reflect the actual URI used to access the subresource then I was thinking that we could offer access to UriInfo via EL like:
>>
>> @Link("${UriInfo.requestUri}")
>>
>>> e.g. if the @Link could just refer to the owner, then you could find
>>> the getShipment() method on Widget and find its URI template from the
>>> @Path annotation? e.g.
>>>
>>> class Shipment {
>>> @Link(ownedBy="widget")
>>> URI link;
>>>
>>> String shipmentId;
>>> Widget widget;
>>> }
>>>
>>> Then the URI template is only specified once - in the usual place -
>>> and it can be deduced by looking for the method in the owner with a
>>> @Path annotation which returns an object of the type Shipment? (For
>>> the @Path URI template on Widget.getShipment() to be usable with @Link
>>> we'd need to use consistent naming of the 'shipmentId' parameter).
>>>
>>> You could argue that the owner could be deduced too (by analysing all
>>> of the available resources and figuring out which resource is capable
>>> of making a Shipment) - but you could possibly have a resource which
>>> is available via multiple URIs maybe - so being explicit in this case
>>> is not such a bad thing IMHO.
>>>
>> For the URI of the resource or its subresources then I think the ${UriInfo.requestUri} approach will get us most of the way. For linking back up the call stack then UriInfo also has the getMatchedURIs method so you can, e.g., pull out the URI of each resource in the call stack.
>
> Yeah - though I was wondering if a single mechanism could work for
> creating the link of a resource/DTO - whether its in the call stack or
> not. (Hence my idea of passing the owner instance rather than relying
> on the current call stack).
>
> For example, when rendering a Widget's view you might want to include
> links to each of its Shipments, despite the fact that the only call
> stack is /widgets/5 and no Shipments are in the call stack yet.

Makes sense.

> Or you
> might want to render related Widget links other than the current
> widget in the call stack. So you might be looking up Widgets using
> some other means - outside of the Jersey call stack.
>
> BTW in addition to the @Link annotation I'm hoping we can have a
> helper method to generate the link too for times when you just want to
> grab related resources/DTOs and generate their links.
>
> Imagine some helper method somewhere...
>
> public URI linkOf(Object resource) { ... }
>
> if we wanted to do...
>
> Shipment shipment = ...
> URI linkToShipment = linkOf(shipment);
>
> then we'd maybe want to define the 'link owner' outside of the @Link
> annotation; so that it can be used with the @Link annotation or with
> the linkOf() method too.
>
> e.g.
>
> public class Shipment {
> @Owner
> Widget widget;
>
> // lets use @Link too
> @Link URI link;
> }
>
> or if you don't want the code to figure out the locator method on
> Widget which returns a Shipment you could be specific
>
> public class Shipment {
> @Owner(locator="getShipment")
> Widget widget;
> }
>
Interesting idea. So essentially the @Owner provides the link back to the "parent" resource and the linkOf method makes use of that information to traverse the sub resource locator chain to the root resource. The only thing missing is how to determine the values of intermediate template parameters from the properties of the resource classes. Easy enough if they have a @PathParam field but tricky otherwise unless we just rely on some convention or define another annotation that specifies the mapping.

Marc.