users@jersey.java.net

Re: One Resource class for two URLs

From: Paul Sandoz <Paul.Sandoz_at_Sun.COM>
Date: Tue, 22 Apr 2008 17:06:52 +0200

On Apr 22, 2008, at 2:00 PM, Martin Probst wrote:

> Hi,
>
> I've written up how this all works for me here:
>
> http://www.martin-probst.com/blog/2008/04/22/wiring-up-jersey-jax-
> rs-hibernate
>
> Maybe I should add that to the FAQ? It's not actually _frequently_
> asked apparently, but it might still be a useful resource.
>

+1 to add the the Infrequent/FrequentAQ :-) reading it really helped
me understand things better. There is a lot of interesting things in
there.


>
>> 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.
>
> Yes, I noticed that it will basically take the Constructor with the
> most arguments (or at least that's what it did for me), even if
> they aren't annotated.
>
>> 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 ?
>
> Kind of. I do not use the constructor iff:
> * the constructor's declaring class has an @Entity annotation and
> * there is exactly one parameter
> * the parameter is an instanceof Serializable
>
> In that case I'll use Hibernate to instantiate the class, which in
> turn will use an empty default constructor if present, or black
> magic reflection/byte code manipulation voodoo stuff (well, I
> think ;-)) if not.
>
> In any other case I'll assume that I'm not really responsible for
> this call, so I delegate to the actual constructor. I could also
> create my own annotation to make this more explicit to the user,
> i.e. @LoadFromPersistence or something on the constructor.
>
> So this appears to be safe. Nice!
>

Now that i have a really clear idea about what you are trying to do I
am wondering if you can do what you require with a ResourceProvider.
ResourceProvider's are responsible for instantiating resource classes
and managing the life-cycle, often they defer to the component
provider. They are like factories for resources.

For example, say we have this:

   @Path("book/{id}")
   @PerRequestByKey("id")
   @Entity
   class Book {
      // Only has a default constructor
   }

   @Target({ElementType.TYPE})
   @Retention(RetentionPolicy.RUNTIME)
   @Documented
   @ResourceFactory(PerRequestKeyProvider.class)
   public @interface PerRequestByKey {}

   public class PerRequestKeyProvider implements ResourceProvider {
     ...
   }

The PerRequestKeyProvider can have the usual stuff injected on it.
The parameters of the ResourceProvider.getInstance method should give
you everything you need. First you can get the template name from the
PerRequestByKey annotation, then you can look the template value
using a method on UriInfo. Then you can defer to hibernate to get the
instance, then you can use the ComponentProvider to inject (just once).

But this will not work for the POST method you presented in the Books
class. However, you could do this:

   Book b = new Book();
   provider.inject(b); // Perhaps you don't need to inject for using
Book as an representation ?
   book.setFields(...);
   session.persist(book);
   return book;

One way to avoid this is to return the URI to the book you created.
Use Response.created(URI). For browsers you may have to do a redirect
(303 i think using Response.seeOther). The disadvantage is of course
that it requires another GET by the client.

Paul.


>>> 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?
>
> Currently I don't do anything. And you're right, the parameters
> would be problematic. Also, a ComponentProvider is probably the
> best solution for this problem, which is basically dependency
> injection.
>
>>> 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.
>
> Ok, so no trouble there.
>
> Thanks for your help,
> Martin
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>