Re: JSR311: URI-based conneg

From: Marc Hadley <Marc.Hadley_at_Sun.COM>
Date: Thu, 21 Feb 2008 10:24:03 -0500

On Feb 21, 2008, at 9:18 AM, Bill Burke wrote:

> Not sure I like this approach. I just don't like a configuration
> switch changing the behavior of how a request is interpreted. It
> should really be defined by the resource implementation itself.
> With what you're suggesting I cannot just look at the resource
> implementation code to figure out how to invoke on the service. I
> have to look at how its configured as well.
You can still look at the code to figure out how to invoke the
service, the existing @Path and @ProduceMime semantics are preserved
by this proposal, i.e. you can still use the "platonic" URI with an
Accept header to select the representation.

> I think supporting the URI template standard Paul linked in a
> previous alternate thread gives us much more power than what you're
> suggesting and is a cleaner approach to implement this idea.
> @Path("/{resource}.xml")
> @GET
> public String getXML(@PathParam("resource") String id) {}
> @Path("/{resource}.html")
> @GET
> public String getHTML(@PathParam("resource") String id) {}
The problem with that is that you have to have separate methods or
classes for each media type/extension you want to support and
potentially separate ones for platonic uris with Accept. Essentially
you are putting media type information into two places (@Path and

The proposal would support extension-based media type selection using
the existing @ProduceMime media type, e.g.:

@GET @Path("{resource}")
@ProduceMime({"application/xml", "text/html"})
public Object get(@PathParam("resource") String id) {}

I think this is a cleaner separation of concerns.


> Marc Hadley wrote:
>> I'd like to revive a thread started way back in Sept last year,
>> see:
>> The topic was support for URI-based content negotiation,
>> essentially allowing a client to
>> GET /foo.xml
>> as an alternative to
>> GET /foo
>> Accept: application/xml
>> I'd like to offer the following concrete proposal:
>> - We only offer automatic support for content types, nothing for
>> language or charset negotiation.
>> - When the feature is enabled:
>> * A request URI that ends with an extension is matched as if that
>> extension were not present. E.g. @Path("widgets") would match
>> requests for widgets, widgets.xml and widgets.json
>> * If a URI template ends in a variable then the variable value is
>> injected without the extension. E.g. @Path("widgets/{id}") with
>> request for widgets/1.xml and @PathParam("id") String id would
>> result in a value of "1" for id.
>> * The extension is compared to the keys in
>> ApplicationConfig.getExtensionMappings (Map<String, MediaType). If
>> a match is found any Accept header value is replaced with the value
>> for the matching key.
>> - An @Path property style is provided to control the behavior. A
>> value of 'platonic' means that the path should be treated as part
>> of a platonic URI and the above behaviour is enabled. A value of
>> 'distinct' means that the path is distinct and disables the above
>> behaviour. The default value of 'default' defers to an application
>> wide default specified as a property of ApplicationConfig (this
>> will default to not enabled).
>> - Existing UriInfo methods are not affected, any extension in the
>> URI is included in the path returned by any of the methods. For
>> convenience we add UriInfo.getPathExtension() that returns the
>> extension or null if there isn't one, and
>> UriInfo.getPlatonicRequestUriBuilder which returns a URI builder
>> for the request minus the extension. We also add a
>> UriBuilder.extension(String) that adds the supplied extension to
>> the current final path segment.
>> An example:
>> @Path("widgets")
>> public class WidgetsResource {
>> @Context UriInfo uris;
>> @GET
>> @ProduceMime({"application/xml", "application/json")
>> public WidgetList getWidgets() {
>> ....
>> }
>> @POST
>> @ProduceMime({"application/xml", "application/json"})
>> public Response addWidget(Widget input) {
>> Widget w = createWidgetEntry(input);
>> URI platonic = uris.getBaseUriBuilder()
>> .path(WidgetResource.class)
>> .build(w.getId());
>> URI distinct = uris.getBaseUriBuilder()
>> .path(WidgetResource.class)
>> .extension(uris.getExtension())
>> .build(w.getId());
>> return Response.created(platonic)
>> .contentLocation(distinct);
>> }
>> }
>> @Path("widgets/{id}")
>> public class WidgetResource {
>> ...
>> }
>> Assume you have an app config that maps "xml" to application/xml
>> and "json" to application/json. Also assume you have the required
>> msg body readers and writers for XML and JSON.
>> GET /widgets.xml will get you XML, GET /widgets.json will get you
>> JSON, GET /widgets will get whichever matches your accept header
>> most closely.
>> You can POST XML or JSON to /widgets.xml but you'll always get back
>> XML. Same for /widgets.json but you'll always get back JSON.
>> If you POST to a distinct URI you'll get a platonic location and a
>> distinct content location. If you post to a platonic you'll get
>> platonic location and content location (the latter is unfortunate
>> but a custom message body writer could patch the value once the
>> format is known.
>> Thoughts, comments ?
>> Marc.
>> ---
>> Marc Hadley <marc.hadley at>
>> CTO Office, Sun Microsystems.
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail:
>> For additional commands, e-mail:
> --
> Bill Burke
> JBoss, a division of Red Hat
> ---------------------------------------------------------------------
> To unsubscribe, e-mail:
> For additional commands, e-mail:

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