Skip to main content

URL patterns are more flexible in Jersey than in the web.xml

Posted by felipegaucho on September 17, 2009 at 10:26 AM PDT

Rule of thumb: Avoid to use {variables} as the first path of a
Jersey's @Path

I am working on the Arena PUJ Project, a RESTful web-service to
support PUJ competitions. We are in the early stages of the project but
we already got some resources published on the web. Let me show you a
few URL samples:

  1. An insecure GET method to read all competitions promoted by a
    JUG:

    GET /{competition_id}/homework
    Sample URL: 		href="http://fgaucho.dyndns.org:8080/arena-http/institution/cejug/competition">http://fgaucho.dyndns.org:8080/arena-http/institution/cejug/competition
    curl -v -H "Accept: application/json" -XGET
    http://fgaucho.dyndns.org:8080/arena-http/institution/cejug/competition
  2. An insecure GET method to read all homeworks from a competition
    name:

    GET /{competition_id}/homework
    Sample URL: 		href="http://fgaucho.dyndns.org:8080/arena-http/PUJCE-08/homework">http://fgaucho.dyndns.org:8080/arena-http/PUJCE-08/homework
    curl -v -H "Accept: application/json" -XGET
    http://fgaucho.dyndns.org:8080/arena-http/PUJCE-08/homework
  3. A secure method for creating a new homework. In the PUJ business
    model, only professors can submit a homework.

    POST /{competition_id}/homework/{homework_id}
    Sample URL:
    http://fgaucho.dyndns.org:8080/arena-http/PUJCE-08/homework/newHomework
    curl -v -H "Accept: application/json" -XPOST
    http://fgaucho.dyndns.org:8080/arena-http/PUJCE-08/homework/newHomework

The problem: you cannot map variables in the beggining of the
path in the web.xml

In Jersey, all the above URLs are valid, actually Jersey can also
use regular expressions to map URLs to resources. A cool feature that
looses its beauty in the web.xml file.

Observe the third example, it is a URL that starts with a
variable, in Jersey it is declared like this:

@Path("{puj}/homework") 	color="red"><-- Avoid to use {variables} as the first path of a resource
public class PujHomeworkResource {
    @POST
    @Produces( { MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
    @Path("{acronym}")
    public PujHomeworkEntity create(@PathParam("puj") String name, @PathParam("acronym") String acronym) {
        // href="http://kenai.com/projects/puj/sources/arena/content/arena-http/src/main/java/com/kenai/puj/arena/http/PujHomeworkResource.java?rev=81">click here to see the full code.
    }
}

And the simple problem is: web.xml does not support dynamic
patterns in the same way Jersey do. So, you just cannot declare a
url-pattern like /*/homework/*:

    <security-constraint>
        <display-name>Restrict advertisement pages to
            customers</display-name>
        <web-resource-collection>
            <web-resource-name>To create Homework is privileged to Professors</web-resource-name>
            <description />
            <url-pattern>/*/homework/*</url-pattern> color="red"><-- This is valid but useless :(
            <http-method>POST</http-method>
            <http-method>GET</http-method>
        </web-resource-collection>
        <auth-constraint>
            <description>PUJ Homeworks.</description>
            <role-name>professor</role-name>
        </auth-constraint>
        <user-data-constraint>
            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
        </user-data-constraint>
    </security-constraint>

Such url-pattern /*/homework/* will not map URLs
like /PUJCE-09/homework or /GOJAVA-09/homework.
It is yet possible to use the variables from Jersey, but it would
required a static mapping for each possible value in the web.xml file -
useless.

Workarounds

There is no much I can see as alternative other
than to refactor my code and do include a static path in the beginning
of my URLs. An alternative proposed by href="http://www.java.net/blogs/mhadley/">Dr. Hadley is to use
the href="http://java.sun.com/developer/technicalArticles/J2EE/security_annotation/">@RolesAllowed
annotation, what can fix the problem but eventually creates another
problem (IMO). If you declare the security of your application by
annotations, you need to recompile and re-deploy the whole application
on every security constraints update. In case you know in advance that
your application will have seldom updates, ok - go ahead and use the
annotations. For now, I will
include the static names in the beginning of all paths. Once I finish
that task, I will continue my REST trip and perhaps come back here with
more details. I included some references below in case you are looking
for what the specs say about that :)
border="0" src="http://jersey.dev.java.net/images/Jersey_yellow.png" />

The Mappings in the Servlet 2.5 specification

From the section 11.2 of the href="http://jcp.org/aboutJava/communityprocess/mrel/jsr154/index2.html">Servlet
2.5 Specification, you read:

  • In the Web application deployment descriptor, the following
    syntax is used to define mappings:

    • A string beginning with a ‘/’ character and ending with a ‘/*’
      suffix is used for path mapping.

    • A string beginning with a ‘*.’ prefix is used as an extension
      mapping.

    • A string containing only the ’/’ character indicates the
      "default" servlet of the application. In this case the servlet path is
      the request URI minus the context path and the path info is null.

    • All other strings are used for exact matches only.

The Mappings in the JAX-RS 1.0 specification

Jersey is the reference implementation for the JAX-RS
specification 1.0, and the way it defines URL patterns is a bit
different from the Servlet spec. At section 3.4 of the href="http://jcp.org/aboutJava/communityprocess/final/jsr311/index.html">JAX-RS
1.0 specification you read:

  • A root resource class is anchored in URI space using the @Path
    annotation. The value of the annotation is 23 a relative URI path
    template whose base URI is provided by the deployment context. 24 A URI
    path template is a string with zero or more embedded parameters that,
    when values are substituted 25 for all the parameters, is a valid href="http://www.ietf.org/rfc/rfc3986.txt">URI path. The Javadoc
    for the @Path annotation describes their syntax.

    In the href="https://jsr311.dev.java.net/nonav/releases/1.0/javax/ws/rs/Path.html">Jersey
    javadoc you read:

    • Embedded template parameters are allowed and are of the form:

      param = "{" *WSP name *WSP [ ":" *WSP regex *WSP ] "}"
      name = (ALPHA / DIGIT / "_")*(ALPHA / DIGIT / "." / "_" / "-" ) ; \w[\w\.-]*
      regex = *( nonbrace / "{" *nonbrace "}" ) ; where nonbrace is any char other than "{" and "}

      "See href="http://tools.ietf.org/html/rfc5234">RFC 5234 for a
      description of the syntax used above and the expansions of WSP, ALPHA
      and DIGIT. In the above name is the template parameter name and the
      optional regex specifies the contents of the capturing group for the
      parameter. If regex is not supplied then a default value of [^/]+
      which terminates at a path segment boundary, is used. Matching of
      request URIs to URI templates is performed against encoded path values
      and implementations will not escape literal characters in regex
      automatically, therefore any literals in regex should be escaped by
      the author according to the rules of href="http://tools.ietf.org/html/rfc3986#section-3.3">RFC 3986
      section 3.3. Caution is recommended in the use of regex, incorrect use
      can lead to a template parameter matching unexpected URI paths. See
      Pattern for further information on the syntax of regular expressions.
      Values of template parameters may be extracted using PathParam. The
      literal part of the supplied value (those characters that are not part
      of a template parameter) is automatically percent encoded to conform
      to the path production of href="http://tools.ietf.org/html/rfc3986#section-3.3">RFC 3986
      section 3.3. Note that percent encoded values are allowed in the
      literal part of the value, an implementation will recognize such
      values and will not double encode the '%' character
      ".

Comments

I use variables as the path

I use variables as the path often because it's just easy to do sometimes... Anyway, I wonder how MMORPG games on Java work so smoothly. Ie. Runescape.