dev@jsr311.java.net

Re: Inherited Path

From: Marc Hadley <Marc.Hadley_at_Sun.COM>
Date: Mon, 04 Feb 2008 11:55:29 -0500

On Feb 2, 2008, at 5:26 PM, Bill Burke wrote:

> I do think you need some inheritance rules though. For example, in
> the EJB 3 specification you may override a business method in a
> subclass and provide different metadata.
>
> I also wish that JAX-RS annotations could be extracted from an
> implemented interface. i.e.:
>
> public class StoreBean implements ShoppingStore {
>
> public Book getBook(int id) {...}
> }
>
> @Path("/shopping")
> public interface ShoppingStore {
>
> @GET @Path("bookstore/books/{id}")
> public Book getBook(@PathParam("id") int id);
> }
>
> This would be very similar to the EJB3/JAX-WS integration style.
>
I agree that we should be explicit about how inheritance is handled,
we already have issue 16 which we can use to track this:

https://jsr311.dev.java.net/issues/show_bug.cgi?id=16

I'm a little conflicted on what we should do here and much of that
conflict revolves around the relationship between a Java contract and
a HTTP one, specifically how wise it is to tie the two together. Let
say I want to define a re-usable Atom feed component. At first blush
you might define a Java interface like this:

@ProduceMime("application/atom+xml")
@ConsumeMime("application/atom+xml")
@Path("feeds/{feed_id}")
public interface AtomFeed {

   @GET
   Feed getFeed(@PathParam("feed_id") String feedId);

   @POST
   Entry addEntry(@PathParam("feed_id") String feedId, Entry newEntry);
}

The problem with the above is that you've restricted the shape of the
URI that can be used. What if a user of your component wanted: /users/
{userId}/feeds/{feedId} or /feeds?feed={feed_id}. For the first case a
user could override the @Path with one on the implementation class but
for the second, the use of @PathParam requires the feedId to be
embedded in the path so they would also have to override the
annotation on the method parameter (not simply the annotation value -
actually change @PathParam to @QueryParam). You've also fixed the type
of the feed id, if a user always uses numeric ids then you've forced
they to do the String->int conversion themselves rather than letting
the runtime do it for them.

You might think about dropping the URI related annotations from the
interface:

@ProduceMime("application/atom+xml")
@ConsumeMime("application/atom+xml")
public interface AtomFeed {

   @GET
   Feed getFeed();

   @POST
   Entry addEntry(Entry newEntry);
}

A user could now implement the interface and use a constructor to
extract whatever identifier they require:

@Path("feeds")
public class Feeds implements AtomFeed {

   public Feeds(@QueryParam("feed_id") String) {...}

   public Feed getFeed() {...}

   public Entry addEntry(Entry newEntry) {...}
}

This is better but you've still made some decisions for the end user
of the interface. E.g. they might or might not support use of a slug
header, the interface doesn't include a parameter for one so if one is
supported you've forced the user to inject HttpHeaders rather than use
@HeaderParam.

Essentially, the above use of an interface is imposing constraints on
an implementing class but not really adding much value beyond saying
what methods they have to support (which the Atom protocol spec
already does).

Marc.


> Marc Hadley wrote:
>> IIRC, @Inherited only applies to classes so, even if @Path was
>> inherited, any annotations on the abstract service class methods
>> (@GET, @ProduceMime etc) wouldn't be inherited and a subclass would
>> appear to unannotated except for the class-level annotations.
>> Marc.
>> On Feb 1, 2008, at 12:24 PM, Adam Karl wrote:
>>> Here is my situation. I would like to define an abstract Service
>>> class such as below. Now when the real service classes extend my
>>> service class, I would like them to inherit the path annotation.
>>> This allows the service classes to know nothing about the
>>> implementation of my service architecture other than the fact that
>>> they should be extending my abstract service class. Currently,
>>> the @Path annotation is not @Inherited so this doesn't work for
>>> me. Is there a reason why @Path cannot be @Inherited?
>>>
>>> @Path("{serviceClassName}")
>>> public abstract class AbstractService
>>> {
>>> public AbstractService
>>> getServiceClass(@PathParam("serviceClassName") String
>>> serviceClassName)
>>> {
>>> return this;
>>> }
>>> ...
>>> }
>>>
>>> ____________________________________________
>>> Adam Karl
>>> Nighthawk Radiology
>>> Software Engineer
>>>
>>> Phone: +1 414 220 4295 ext-8319
>>> Email: akarl_at_nighthawkrad.net
>>>
>>>
>> ---
>> Marc Hadley <marc.hadley at sun.com>
>> CTO Office, Sun Microsystems.
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: users-unsubscribe_at_jsr311.dev.java.net
>> For additional commands, e-mail: users-help_at_jsr311.dev.java.net
>
> --
> Bill Burke
> JBoss, a division of Red Hat
> http://bill.burkecentral.com
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe_at_jsr311.dev.java.net
> For additional commands, e-mail: users-help_at_jsr311.dev.java.net
>

---
Marc Hadley <marc.hadley at sun.com>
CTO Office, Sun Microsystems.