Note that there are some explanatory texts on larger screens.

plurals
  1. POWhy is it that @MatrixParam values seem to only come from the final segment?
    text
    copied!<p>I am exploring the use of <a href="http://www.w3.org/DesignIssues/MatrixURIs.html" rel="noreferrer">MatrixURIs</a> in a JAX-RS application. In the process I've noticed that the <a href="http://jsr311.java.net/nonav/releases/1.1/javax/ws/rs/MatrixParam.html" rel="noreferrer">@MatrixParam</a> annotated parameters seem to only get the matrix parameters from the final segment (which makes trying to use <a href="http://jersey.java.net/nonav/documentation/1.15/user-guide.html#d4e374" rel="noreferrer">sub-resource locators</a> a bit painful). I would like to know if this is a bug, or is part of the spec (and why?), or is simply an undefined crack. </p> <p>I know that I likely could devise a way to use the @Context injected <a href="http://jsr311.java.net/nonav/releases/1.1/javax/ws/rs/core/UriInfo.html" rel="noreferrer">UriInfo</a> object (which does change depending upon where in the locator chain one finds oneself), but this feels ugly.</p> <p>Thumbing quickly through the <a href="http://jcp.org/aboutJava/communityprocess/mrel/jsr311/index.html" rel="noreferrer">JAX-RS 1.1 spec</a>... the closest I could find to this not being supported was around: §3.2 </p> <blockquote> <p>Because injection occurs at object creation time, use of these annotations (with the exception of @Context) on resource class fields and bean properties is only supported for the default per-request resource class lifecycle.</p> </blockquote> <p>But that's talking about Constructor/Field injection, not method parameters like in §3.3.2: </p> <blockquote> <p>When a resource method is invoked, parameters annotated with @FormParam or one of the annotations listed in section 3.2 are mapped from the request according to the semantics of the annotation.</p> </blockquote> <p>But of course the semantics of the annotation are seemingly vague. </p> <h1>Runtime Environment Details:</h1> <ul> <li>Jersey 1.13, 1.15</li> <li>Tomcat 7.0.29</li> <li>Java 1.6.0_31 (Apple)</li> <li>MacOS X 10.7.5</li> </ul> <h1>Example resources:</h1> <pre><code>public class Zero { public static final String[] IDS = { "1", "2", "3" }; @GET @Produces(MediaType.APPLICATION_JSON) public Map&lt;String, Object&gt; getMe(@Context final UriInfo info) { final UriBuilder builder = info.getAbsolutePathBuilder().path("one"); final UriBuilder two = builder.clone().matrixParam("id", "{one}", "{two}"); final List&lt;String&gt; links = new ArrayList&lt;String&gt;(); for (int i = 0; i &lt; IDS.length; i++) { final String first = IDS[i]; for (int j = i + 1; j &lt; IDS.length; j++) { final String second = IDS[j]; links.add(two.build(first, second).toASCIIString()); } } final Map&lt;String, Object&gt; toReturn = new HashMap&lt;String, Object&gt;(); toReturn.put("class", getClass().getSimpleName()); toReturn.put("next", builder.build().toASCIIString()); toReturn.put("skip", links); return toReturn; } /*************************/ /**** &lt;PROBLEM CHILD&gt; ****/ /*************************/ @Path("one") public Object getNext(@MatrixParam("id") final Set&lt;String&gt; ids) { if (ids.isEmpty()) { return new One(); } return new Two(ids.toArray(new String[ids.size()])); } /*************************/ /**** &lt;/PROBLEM CHILD&gt; ***/ /*************************/ public static class One { @GET @Produces(MediaType.APPLICATION_JSON) public Map&lt;String, Object&gt; getMe(@Context final UriInfo info) { final UriBuilder builder = info.getAbsolutePathBuilder().path("{id}"); final List&lt;String&gt; links = new ArrayList&lt;String&gt;(); for (final String id : IDS) { links.add(builder.build(id).toASCIIString()); } final Map&lt;String, Object&gt; toReturn = new HashMap&lt;String, Object&gt;(); toReturn.put("class", getClass().getSimpleName()); toReturn.put("next", links); toReturn.put("last", getLastURI(info)); return toReturn; } @Path("{id}") public Two getNext(@PathParam("id") final String id) { return new Two(id); } } public static class Two { private final String[] myids; private final Three three; public Two(final String... ids) { three = new Three(ids); myids = ids; } @GET @Produces(MediaType.APPLICATION_JSON) public Map&lt;String, Object&gt; getMe(@Context final UriInfo info) { final UriBuilder builder = info.getAbsolutePathBuilder().path("three"); final Map&lt;String, Object&gt; toReturn = new HashMap&lt;String, Object&gt;(); toReturn.put("class", getClass().getSimpleName()); toReturn.put("ids", myids); toReturn.put("next", builder.build().toASCIIString()); toReturn.put("last", getLastURI(info)); return toReturn; } @Path("three") public Three getNext() { return three; } } public static class Three { private final String[] myids; public Three(final String... ids) { myids = ids; } @GET @Produces(MediaType.APPLICATION_JSON) public Map&lt;String, Object&gt; getMe(@Context final UriInfo info) { final Map&lt;String, Object&gt; toReturn = new HashMap&lt;String, Object&gt;(); toReturn.put("class", getClass().getSimpleName()); toReturn.put("ids", myids); toReturn.put("last", getLastURI(info)); return toReturn; } } /** * Helper method since info.getMatchedURIs doesn't play nice with Matrix Params * @param info info object * @return parent URI */ public static final String getLastURI(final UriInfo info) { final List&lt;PathSegment&gt; segments = info.getPathSegments(false); final UriBuilder builder = info.getBaseUriBuilder(); for (int i = 0; i &lt; segments.size() - 1; i++) { final PathSegment segment = segments.get(i); builder.path(segment.getPath()); final MultivaluedMap&lt;String, String&gt; matrixParams = segment.getMatrixParameters(); if (!matrixParams.isEmpty()) { for (final Map.Entry&lt;String, List&lt;String&gt;&gt; param : matrixParams.entrySet()) { final String name = param.getKey(); final String[] values = param.getValue().toArray(new String[param.getValue().size()]); builder.matrixParam(name, values); } } } return builder.build().toASCIIString(); } } </code></pre> <h1>Example Outputs from above:</h1> <h2><code>http://localhost:8080/context/zero/one/2</code></h2> <pre><code>{ "last":"http://localhost:8080/context/zero/one", "ids":["2"], "next":"http://localhost:8080/context/zero/one/2/three", "class":"Two" } </code></pre> <h2><code>http://localhost:8080/context/zero/one/2/three</code></h2> <pre><code>{ "last":"http://localhost:8080/context/zero/one/2", "ids":["2"], "class":"Three" } </code></pre> <h2><code>http://localhost:8080/context/zero/one;id=1;id=2</code></h2> <pre><code>{ "last":"http://localhost:8080/context/zero", "ids":["2","1"], "next":"http://localhost:8080/context/zero/one;id=1;id=2/three", "class":"Two" } </code></pre> <h2><code>http://localhost:8080/context/zero/one;id=1;id=2/three</code></h2> <pre><code>{ "last": "http://localhost:8080/context/zero/one;id=1;id=2", "ids": ["three"], "next": "http://localhost:8080/context/zero/one;id=1;id=2/three/three", "class": "Two" } </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload