Proposal take 2:
- We only offer automatic support for content types and language,
nothing for charset or encoding negotiation.
- The feature is controlled by a combination of annotation and config:
* A property of ApplicationConfig defines the default (enabled/
disabled).
* A "style" property of the @Path annotation can be used to
override the ApplicationConfig defined default. A value of 'platonic'
means that the path should be treated as part of a platonic URI and
that automated extension mapping should be enabled. A value of
'distinct' means that the path is distinct and disables extension
mapping. The default property value of 'default' defers to an
application wide default.
- When the feature is enabled:
* A request URI that ends with one or more extensions is matched as
if those extensions were not present. E.g. @Path("widgets") would
match requests for widgets, widgets.xml, widgets.xml.en,
widgets.en.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.en and @PathParam("id") String id would
result in a value of "1" for id.
* Each extension is compared to the keys in
ApplicationConfig.getMediaTypesMap (Map<String, MediaType) and
ApplicationConfig.getLanguageMap (Map<String, String). If a match is
found the corresponding Accept or Accept-Language header value is
replaced with the value for the matching key.
- Existing UriInfo methods are not affected, any extensions in the URI
are included in the path returned by any of the methods. For
convenience we add:
* UriInfo.getPathExtension() that returns the extensions as a
String or null if there isn't one,
* UriInfo.getPlatonicRequestUriBuilder which returns a URI builder
for the request minus the extensions
* UriBuilder.extension(String) that adds the supplied extension to
the current final path segment.
An example:
@Path("widgets")
public class WidgetsResource {
@Context UriInfo uris;
@Context Request req;
@GET
@ProduceMime({"application/xml", "application/json")
public WidgetList getWidgets() {
....
}
@POST
@ConsumeMime({"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,
"json" to application/json, en to en and fr to fr. 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. GET /widgets.xml.en or GET /widgets.en.xml will get you XML
and cause the Accept-Language header to be replaced with Accept-
Language: en. Application code will need to process this as usual.
With the code above, 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.
Issues:
(i) Because matching is controlled on a per-resource basis we can't
just preprocess the request URI and then use that for matching. It
would be simpler but less flexible without the per-resource control.
Is that a tradeoff folks would consider ?
(ii) A result of (i) is that for efficiency we essentially treat a URI
with any extensions as matching a platonic URI. E.g.
widgets.foo.bar.fr.xml would match WidgetsResource above and be
treated the same as widgets.fr.xml or widgets.xml.fr. Rejecting URIs
with extensions that don't match one of the configured extensions on a
per-resource basis would be expensive, rejecting them globally would
mean you couldn't have a distinct resource path that had a non-
conforming extension.
Thoughts, comments ?
Marc.
---
Marc Hadley <marc.hadley at sun.com>
CTO Office, Sun Microsystems.