users@jersey.java.net

[Jersey] Re: Specifying different sub-resources depending on the Content-Type?

From: Cameron Heavon-Jones <cmhjones_at_gmail.com>
Date: Mon, 20 Jun 2011 17:56:04 +0100

Hi Gili,

On 20/06/2011, at 3:43 PM, Gili wrote:

> Hi Cameron,
>
> Here is my specific use-case:
>
> 1. I am using JSON, so @GET on /devices should return:
>
> [
> “name”: string,
> “type”: string,
> “uri”: URI
> ]
>
> Where "name" is a user-assigned identifier. "type" is the device
> type/differentiator. I'm defining a hierarchy where all devices share the
> same id sequence. See
> http://en.wikibooks.org/wiki/Java_Persistence/Inheritance#Joined.2C_Multiple_Table_Inheritance
> for an example.
>

OK, but just because they share the same table does not mean they must share the same resource path. It is equally valid to have, for example:

/device/iphone/{id}
/device/android/{id}
/device/laptop/{id}

and you have collected the discriminator from the path.

> 2. Say /devices/5 refers to Device "A" but the user invokes @GET on
> /devices/5 with Accepts "B", the resource should return HTTP 404 because
> Device "A" with id 5 does not exist.
>

So they really are different media types? The only problem is that this requires the explicit and exact use of current media types which is ok but a more flexible solution would probably just use specific uri path. then i can still get the correct response from a browser which sends text\html accept.


> 3. @GET on /devices/5 with the right Accepts header should return the
> device's state


This can be enforced as you showed through created new methods with consume\produce annotations, but since there will always be a device with id=5, and no duplicate ids, i'm not sure what you're really accomplishing if this is just to return data?

the full data can be delivered polymorphically by jaxb (with json serialization), so you might not even require any custom GET. i see you need to POST\PUT as well, the customizations for these can be done in the main resource class with dispatch on consumer type, eg:

@GET
public Response getDevice(String id){
        Device device = load(id);
        return Response.ok(device).type(device.getMediaType()).build();
}

@POST
@Consumes(IPHONE_TYPE)
public Response postIphone(String name){
        // saveIPhone(name);
}

@POST
@Consumes(ANDROID_TYPE)
public AndroidDevice getAndroid(String name){
        // saveAndroid(name);
}


>
> 4. Now that I think about it, I don't want to have to declare @Consumes,
> @Produces twice (once on the parent resource, and one on the actual
> resource). Ideally I just want to provide Jersey with a list of possible
> sub-resources and it should select the correct one by matching the user's
> Accepts header to a sub-resource @Consumes/_at_Produces.
>
> Gili
>

How about this?

@Consumes(IPHONE_TYPE)
public IPhoneDevice getIphone(String id){
        return new IphoneDevice(id);
}

@Consumes(ANDROID_TYPE)
public AndroidDevice getAndroid(String id){
        return new AndroidDevice(id);
}

Any @Consumes annotation on the sub-resource is not used, as this has already been restricted on the locator.

I think i'd prefer using a path name but this would work ok.

Cam


> Cameron Heavon-Jones wrote:
>>
>> When you say "Content-Type" i think of the http header, which is the
>> MIME\Media type, but it seems you require alternate JAXB classes?
>>
>> Do the sub-resources provide any additional functionality or are you just
>> looking for a way to return specific JAXB types?
>>
>> If you don't need additional functions on the resources, i think you could
>> just return the correct type from the parent resource and let JAXB create
>> the full xml based on specific type and data.
>>
>> However, if the sub-resources are distinct resources with specific
>> operations you want them to have their own resource classes which seems to
>> be the case form you example.
>>
>> In that case it comes down to resource design, specifically, is there a
>> common ancestor which is required? Cat and Dog would suggest Animal as the
>> common ancestor. But if your system is not some creature taxonomy where
>> there is value in a hierarchy, but really a more real world case, you
>> might not require the additional complexity this brings.
>>
>> I don't really think you can go wrong, both are equally valid, it comes
>> down to a question of design, which is more constrained by your specific
>> case.
>>
>> To go back to the "devices", if the client is aware of multiple types of
>> devices i would be inclined to expose that information (barring business
>> cases), however if the client is not aware of, does not care, or if the
>> sub-resources are an implementation detail, i would not expose the
>> information through structure.
>>
>> helpful? :)
>>
>> if you want to discuss a specific business case, feel free to email
>> specifics privately and i will respect your business's and individual
>> confidentiality if public dissemination is not desired.
>>
>> cam
>>
>>
>> On 20/06/2011, at 1:24 PM, Gili wrote:
>>
>>> Hi Cameron,
>>>
>>> I would like to differentiate between the different devices based on
>>> the Content-Type but the following code seems a bit weird:
>>>
>>> @Consumes("Dog")
>>> @Produces("Dog")
>>> public DogResource getDog()
>>> {
>>> return new DogResource();
>>> }
>>>
>>> @Consumes("Cat")
>>> @Produces("Cat")
>>> public CatResource getCat(){
>>> return new CatResource();
>>> }
>>>
>>> [...]
>>>
>>> DogResource
>>> {
>>> @GET
>>> public String getDog() {}
>>> @PUT
>>> public void setDog(String) {}
>>> }
>>>
>>> CatResource
>>> {
>>> @GET
>>> public String getCat() {}
>>> @PUT
>>> public void setCat(String) {}
>>> }
>>>
>>> What do you think?
>>>
>>> Gili
>>>
>>> On 20/06/2011 8:04 AM, Cameron Heavon-Jones [via Jersey] wrote:
>>>>
>>>> it sounds like you want a differentiator to be able to choose between
>>>> the types of resources.
>>>>
>>>> you will either require the client to provide the differentiator as part
>>>> of their request - uri, media type, headers etc - the switch can be
>>>> handled by jersey using annotations.
>>>>
>>>> or you can store locally in your database and lookup the correct type on
>>>> request. in your case this would amount to loading the device from the
>>>> database and checking it's differentiator:
>>>>
>>>> @Path("device/{id}")
>>>> public DeviceResource getDevice(@PathParam("id") Long id){
>>>> Device d = loadDevice(id);
>>>> if (d.isSomeType()) // or instanceof with cast
>>>> return new SpecificDeviceResource(device);
>>>> else
>>>> return new GenericDeviceResource(device);
>>>> }
>>>>
>>>>
>>>> The other method, to get this managed by the client instead, you could
>>>> do:
>>>>
>>>> @Path("genericdevice/{id}")
>>>> public DeviceResource getGeneric(@PathParam("id") Long id){
>>>> return new GenericDeviceResource(id);
>>>> }
>>>>
>>>> @Path("specificdevice/{id}")
>>>> public DeviceResource getSpecific(@PathParam("id") Long id){
>>>> return new SpecificDeviceResource(id);
>>>> }
>>>>
>>>> or use some other request parameter other than the path.
>>>>
>>>> hth,
>>>> cam
>>>>
>>>> On 17/06/2011, at 3:23 PM, Gili wrote:
>>>>
>>>>> Hi,
>>>>>
>>>>> Currently when I want to specify sub-resource I do:
>>>>>
>>>>> @Path("devices")
>>>>> class Devices
>>>>> {
>>>>> [...]
>>>>> @Path("{id}")
>>>>> public getDevice(@PathParam("id") long id)
>>>>> {
>>>>> return new Device(id);
>>>>> }
>>>>> }
>>>>>
>>>>> Now I have two different kind of devices, both mapped under
>>>> /devices/{id}
>>>>> except that some IDs are mapped to one device type and some to
>>>> another. Is
>>>>> it possible to specify two sub-resource methods and have Jersey
>>>> instantiate
>>>>> a different sub-resource Class depending on the content-type being
>>>>> requested?
>>>>>
>>>>> Thanks,
>>>>> Gili
>>>>>
>>>>> --
>>>>> View this message in context:
>>>> http://jersey.576304.n2.nabble.com/Specifying-different-sub-resources-depending-on-the-Content-Type-tp6487299p6487299.html
>>>>> Sent from the Jersey mailing list archive at Nabble.com.
>>>>
>>>>
>>>>
>>>> If you reply to this email, your message will be added to the discussion
>>>> below:
>>>> http://jersey.576304.n2.nabble.com/Specifying-different-sub-resources-depending-on-the-Content-Type-tp6487299p6495488.html
>>>> To unsubscribe from Specifying different sub-resources depending on the
>>>> Content-Type?, click here.
>>>
>>>
>>> View this message in context: Re: Specifying different sub-resources
>>> depending on the Content-Type?
>>> Sent from the Jersey mailing list archive at Nabble.com.
>>
>
>
> --
> View this message in context: http://jersey.576304.n2.nabble.com/Specifying-different-sub-resources-depending-on-the-Content-Type-tp6487299p6496030.html
> Sent from the Jersey mailing list archive at Nabble.com.