Felipe Gaúcho works as senior software engineer at Netcetera AG in Switzerland. He is a well known Brazilian JUG leader and open-source evangelist. Felipe works with Java since its early versions and has plans to keep that Java tradition as it is. When he is not coding, he prefers to listen reggae and travel around with his lovely wife Alena and his son Rodrigo. Felipe is a DZone MVB and is not an employee of DZone and has posted 29 posts at DZone. View Full User Profile

URL Patterns are More flexible in Jersey Than in Web.xml

09.18.2009
| 5717 views |
  • submit to reddit

Rule of thumb: Avoid using {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: 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: 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") <-- 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) {
// click here to see the full code.


And the simple problem is: web.xml do 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><font color="red">/*/homework/*</font></url-pattern> <font color="red"><strong><-- <em>This is valid but useless :(</em></strong></font>
<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 Dr. Hadley is to use the @RolesAllowed annotation, what can fix the problem but eventually creating 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 don't see other alternative than refactor my code to include the static names in the beginning of all paths. Once I finish this 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 :)

The Mappings in the Servlet 2.5 specification

From the section 11.2 of the 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 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 URI path. The Javadoc for the @Path annotation describes their syntax.

    In the 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 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 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 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".

From http://weblogs.java.net/blog/felipegaucho
Published at DZone with permission of Felipe Gaúcho, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Gérald Quintana replied on Fri, 2009/09/18 - 6:23am

The first URL example "http://fgaucho.dyndns.org:8080/arena-http/institution/cejug/competition" doesn't match URL pattern "/{competition_id}/homework"

Felipe Gaúcho replied on Fri, 2009/09/18 - 7:30am

yes, it is there to show a static path in the beggining of the URL.. but perhaps it is not necessary.. I can do it more succint next time, thanks... and it is also useful to know what are the possible values for the other path .. actually, hateoas is my current mindset.. and I am looking for a good way to link one resource to another.. but it is subject to another post :)

Jochen Bedersdorfer replied on Fri, 2009/09/18 - 10:13am

I hate to say it, but the API is not particularly RESTful.

It starts with  http://fgaucho.dyndns.org:8080/arena-http/institution/cejug/competition

You are missing an opportunity here, namely to  return the URLs for the individual  GET /{competition_id}/homework calls yourself.

Instead, the user of your API has to know that the competition ID needs to be passed in. (and it is unclear what the ID actually is). Instead consider an exploratory style for your REST service. Here is an example:

<pujCompetitionEntities><pujCompetitionEntity name="PUJCE-07" version="1" href="http://fgaucho.dyndns.org:8080/arena-http/PUJCE-08/homework">
<owner acronym="CEJUG" version="1"/>
</pujCompetitionEntity>

 This reduces the implicit knowledge about your API and gives the server greater flexibility on how to use its URL namespace. It could even delegate to a different server that way (or by using HTTP code 302, 303)

 See the following link for a discussion on common mistakes in API design using RESTful architectural style:

http://www.prescod.net/rest/mistakes/

 Thanks for sharing your API. It is always nice to see people becoming RESTafarians :)

 

 

Felipe Gaúcho replied on Fri, 2009/09/18 - 1:02pm

Hi Bedersdorfer,

yes, I know that :) and I am learning and trying my best to include the hateoas asap in my project. If you have any time and if you want to contribute with this project, please join us.. you don't need to code at all, you just give us some hins and we will be eternally grateful for your contribution. 

 The project is new and absolutely open for any suggestion in its design and implementation.

 HATEOAS is our next step.. join us if you like to model this.. and if you want to code as well, why not ? :)

I have other references collected here:  http://kenai.com/projects/puj/pages/PUJ-SPEC

I will read your link and perhaps to include a ref there.. 

 

Felipe Gaúcho replied on Fri, 2009/09/18 - 1:20pm

I read your example, and now I got your point.. nothing to do with hateoas, but a collection with shared attributes (owner in this example).... remember that I am reading my resources directly from the database, in what I called "domain driven REST",... it is new and experimental.. 8a lot of fun actually)... and it is exciting and yet unstable right now..

Jochen Bedersdorfer replied on Mon, 2009/09/21 - 1:31pm

Well, it is a start going for HATEOS.

The easiest way is to get rid of IDs or augment IDs with hyperlinks and reduce the implicit knowledge required to talk to the API.

 Since REST is just an architectural style, you are free to go for a full HATEOS application or not (it may be overkill for certain simple APIs)

 

Gerd Ziegler replied on Wed, 2009/09/23 - 1:40am

See ztemplates.org for a webframework that also supports url-variables

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.