users@jersey.java.net

Re: One Resource class for two URLs

From: Paul Sandoz <Paul.Sandoz_at_Sun.COM>
Date: Tue, 22 Apr 2008 13:23:18 +0200

On Apr 22, 2008, at 11:29 AM, Martin Probst wrote:

> Hi,
>
>> If you could have "/books" and "/books/book/{id}" it may make
>> things easier for you.
>
> This is what I previously did, but it didn't quite feel right.
>
>> @Path("books")
>> class Books {
>> @Path("book/{id}") getBook(...) { ... }
>> }
>>
>> It adds a bit of redundancy to the URI space but i still think it
>> is "cool" enough (in terms of Cool URIs).
>
> I think I found a solution that is about 17% more cool ;-)
>

Very good.


> I'm now supplying my own "HibernateComponentProvider" into the
> webapplication. I'm not sure if this qualifies as a clever solution
> or an ugly hack (feedback please!), but it works like this:
>

Although the main use-case for ComponentProvider was to hook into IoC
frameworks it can be used for anything you need to provide to Jersey
so i don't think it is a hack.


> I have @Path("books") class Books { ... } and @Path("book/{id}")
> class Book { ... }. Book has a constructor that takes a single long
> "id" @PathParam (not sure the annotation is necessary).

The annotation is required otherwise Jersey does not know how to
create an instance of that constructor parameter (and extract
information from the request URI), which is then passed to the
getInstance method and which you use to do the look up.


> Thus, the HibnernateComponentProvider#newInstance method is called
> with parameters, and I use those to read the resource instance from
> the persistence provider.
>
> I couldn't quite make out by what means Jersey picks the
> constructor to use for the query, is there any documentation on this?

Look at the 311 specification and sections 3.1.1 and 3.1.2:

3.1.1 Lifecycle and Environment 9
By default a new resource class instance is created for each request
to that resource. First the constructor (see 10
section 3.1.2) is called, then any requested dependencies are
injected (see chapter 5), then the appropriate 11
method (see section 3.2) is invoked and finally the object is made
available for garbage collection. 12
An implementation MAY offer other resource class lifecycles,
mechanisms for specifying these are outside 13
the scope of this specification. E.g. an implementation based on an
inversion-of-control framework may 14
support all of the lifecycle options provided by that framework.

3.1.2 Constructors
Root resource classes are instantiated by the JAX-RS runtime and MUST
have a public constructor for which 17
the JAX-RS runtime can provide all parameter values. Note that a zero
argument constructor is permissible 18
under this rule. 19
A public constructor MAY include parameters annotated with one of the
following: @Context, @Header- 20
Param, @CookieParam, @MatrixParam, @QueryParam or @PathParam- section
3.2.2 defines the pa- 21
rameter types permitted for each annotation. However, depending on
the resource class lifecycle and con- 22
currency, per-request information may not make sense in a
constructor. If more than one public constructor 23
can be used then an implementation MUST use the one with the most
parameters. Choosing amongst con- 24
structors with the same number of parameters is implementation
specific. 25
Non-root resource classes are instantiated by an application and do
not require the above-described public 26
constructor.

We need to update Jersey to conform fully to this, currently it is
not very smart about picking the constructor. So i think you will be
OK as long as the spec stays the same.


Is your HibnernateComponentProvider.getInstance(Scope scope,
Constructor<T> contructor, Object[] parameters) method ignoring the
"constructor" method parameter and using the default (empty)
constructor of the class ?


> I fear that my solution simply exploits the undocumented current
> behaviour and might break later on. Maybe this could be a bit
> formalized, so people can supply Factory methods for their
> Resources easily (which is essentially what I'm doing).
>

How would the developer specify what the constructor parameters (e.g.
@*Param) would be for the factory?

Are you relying on a general pattern the the same template variable
names etc?


> I still need to inject classes in the persistence layer for my
> methods that search resources (e.g. by title) using direct
> Hibernate queries, so classes might get injected by Jersey twice,
> but I think this shouldn't be a problem.
>

Jersey will ignore any non-null fields.


> Anyways, I can now have persistent classes that are directly
> available as Root resources, no navigation through a collection
> class required, thus I can have:
> /books -> Books, "collection instance", constructed directly
> /book/12 -> single Book instance, loaded from Hibernate
>
> It's pretty cool that this is possible with Jersey's implementation
> as is, no changes necessary, even though it probably is an
> unexpected use case. Great work!
>

Thanks. Design for serendipity :-) great to see new use-cases.

Paul.