URL Patterns are More flexible in Jersey Than in Web.xml
Join the DZone community and get the full member experience.
Join For FreeRule 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:
-
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
-
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
-
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".
-
Opinions expressed by DZone contributors are their own.
Comments