dev@jsr311.java.net

Re: JSR311: Limit extensions pre-processing

From: Marc Hadley <Marc.Hadley_at_Sun.COM>
Date: Thu, 10 Jul 2008 23:25:27 +0200

On Jul 9, 2008, at 8:30 PM, Roy T. Fielding wrote:
>> I'm curious, wouldn't it also break URI opacity if you could:
>>
>> GET /foo.html
>>
>> but only
>>
>> DELETE /foo
>>
>> I.e. the client would have to know that the .html isn't part of the
>> resource identifier ?
>
> Yikes, multiple terminology breakdowns here.

Sorry, that was pretty sloppy of me.

> First, URI opacity is irrelevant
> to origin servers (they obviously need to know how to interpret
> their own
> space of names).

Right, as I noted earlier in the thread.

> Second, each is a unique URI with its own resource. If we have a
> space like
>
> /foo/bar
> /foo/bar.html
> /foo/bar.xml
>
> then under sane circumstances those three URI paths would identify
> three
> *different* resources. Resources are what the client perceives as
> available
> sources of information/work with the same semantics over time. Each
> of those
> is going to have distinct semantics even when they share
> representations.
>
> Content negotiation is only relevant to the first resource (because
> that's
> the way we've implemented extensions in this origin server, not
> because of
> any inherent meaning in the extension itself). bar.html is limited
> to HTML
> formats. bar.xml is limited to XML formats. bar is not limited,
> which means
> its value set can include any format. Each of those are distinct
> semantics
> and therefore distinct resources. The first resource is often
> called a
> negotiated or generic resource, whereas the other two would be
> specific
> resources (note that you could negotiate over multiple dimensions, so
> this is really a tree of levels with generic at the root and specific
> at the leaves).
>
With URI content negotiation we are building something into the
framework that goes against these principles. Essentially we are
allowing a server-side application to specify a mapping from an
extension to an Accept header value. This is consistent in that a
client Accept is only relevant to a generic resource because we
overwrite the Accept with the media type mapped from the extension.
What isn't consistent is that we would treat the three resources as
equivalent unless the application made the effort to extract the
mapped extension from an injected UriInfo - by default an application
wouldn't see any difference so GET /foo/bar.html is treated and
dispatched exactly like get /foo/bar with Accept text/html.

> Perhaps it would help to think of it like a Unix filesystem. We own
> the
> meaning of directory listing, so we get to state the rules for when
> names
> appear in that listing and when they disappear. Generally, names are
> always created as a specific representation is added/enabled, like
> bar.html,
> by either creating a "file" or defining a program that maps to a
> specific
> name. Generic/negotiated resources are like symbolic links -- they
> are
> names that are created (sometimes automatically) to provide the
> generic
> alternative resource to any given set of specific representations.

In JAX-RS we use the term resource class to mean a Java class that
handles requests on a resource. Currently in JAX-RS resource classes
typically map to a generic resource rather than a specific one. Its
possible to support specific resources via a different @Path but
that's not the way we've designed the URI-based conneg support.

>
> This mapping and unmapping of URIs to representations is all defined
> by the origin server resource framework (JSR311 in our case) and is
> independent of content negotiation. Each URI is a different resource.
> "resource" has nothing whatsoever to do with "storage".
>
> Content negotiation in HTTP only impacts the selection of the content
> of the response message. It NEVER changes the target of a method.

In JAX-RS content negotiation can change the Java method selected.
E.g. you could write:

@Path("foo")
public class FooResource {

   @GET @Produces("text/html")
   public String someMethodThatProducesAnHTMLRepresentation() {...}

   @GET @Produces("text/xml")
   public String someMethodThatProducesAnXMLRepresentation() {...}
}

> To put it another way, if content negotiation is used to select the
> target resource for a method then the origin server is violating HTTP
> requirements and RESTful design.
>
In JAX-RS terms, conneg isn't used to select the target resource class
but it can be used to select the Java method. I don't think this
violates the above principle.

> Likewise, DELETE in HTTP means "unlink this name from your namespace".
>
> If DELETE is applied to a specific resource, the action should cause
> that specific representation to be unmapped and, by side-effect, also
> be removed from the value set of any generic resource that maps to it
> indirectly, which may lead to the corresponding "removal" of any
> generic resource that is left with no remaining representations after
> that DELETE. ("removal" is in quotes because an unmapped name in
> HTTP just means that a 404 response will be returned to a subsequent
> non-PUT request -- it has nothing to so with storage. since most
> systems implement generic names as simple look-up algorithms, there
> is no need to "remove" names that will result in a 404 on look-up).
>
With the current URI based conneg design, I don't see how that maps to
JAX-RS where the representation is typically dynamically generated. It
doesn't make sense to DELETE /foo.html separately from DELETE /foo
since /foo.html is just /foo represented as HTML.

> If DELETE is applied to a generic resource, the action should be to
> remove the symbolic link association (turn off negotiation on that
> name).
> If that's not allowed by the origin server, then the DELETE should
> result
> in a 300 redirection to the more specific resources; i.e., the client
> needs to be told that it must delete the specific resources if it want
> the generic resource to disappear. A DELETE on a generic resource
> must not have the effect of deleting the specific resources directly
> because that isn't what the client requested.
>
Similarly it doesn't make sense with the current URI based conneg
support to DELETE /foo without deleting /foo.html (and any other
supported formats).

> Having said all that, it is the responsibility of the web framework
> to map those HTTP semantics onto the actual actions taken to reflect
> those requests within its internal values and configuration. The best
> way to do that is to treat each URI as an individual resource first
> and use the method to select the handler defined by that resource.
> For example, a specific resource would have specific implementations
> of GET, PUT, DELETE, ... whereas a generic resource would have a very
> different implementation of those methods. AFAICT, that should be
> easy for JSR311 to provide through inheritance and overrides.
>
Given the above I think the only reasonable thing to do right now is
to remove the automatic URI-based conneg feature. Even limiting it to
safe operation would violate the mapping of specific resources to
generic resource classes. If we had a longer than the two weeks
remaining before proposed final draft we could perhaps investigate
other approaches but that's not an option for this rev of the
specification.

Marc.

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