This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new 5423fc9  Improvements to @Path annotation.
5423fc9 is described below

commit 5423fc9d60c477231b0f3636d895d1a5a4fd0ba9
Author: JamesBognar <[email protected]>
AuthorDate: Sat May 5 12:30:33 2018 -0400

    Improvements to @Path annotation.
---
 juneau-doc/src/main/javadoc/overview.html          |  69 +++---
 .../examples/rest/MethodExampleResource.java       |   6 +-
 .../juneau/examples/rest/PhotosResource.java       |   6 +-
 .../examples/rest/SystemPropertiesResource.java    |   6 +-
 .../rest/addressbook/AddressBookResource.java      |  14 +-
 .../examples/rest/petstore/PetStoreResource.java   |  18 +-
 .../juneau/rest/test/ErrorConditionsResource.java  |   2 +-
 .../apache/juneau/rest/test/ParamsResource.java    |  34 ++-
 .../juneau/rest/test/PathVariablesResource.java    |   6 +-
 .../juneau/rest/test/TransformsResource.java       |   4 +-
 .../java/org/apache/juneau/rest/RestContext.java   |  32 +--
 .../org/apache/juneau/rest/RestMethodParam.java    |   8 +
 .../org/apache/juneau/rest/RestParamDefaults.java  |  79 +++++--
 .../org/apache/juneau/rest/annotation/Path.java    |  38 +---
 .../juneau/rest/BasicRestInfoProviderTest.java     | 233 +++++++++++++++++++--
 15 files changed, 381 insertions(+), 174 deletions(-)

diff --git a/juneau-doc/src/main/javadoc/overview.html 
b/juneau-doc/src/main/javadoc/overview.html
index 75f9ce4..ecf0eb7 100644
--- a/juneau-doc/src/main/javadoc/overview.html
+++ b/juneau-doc/src/main/javadoc/overview.html
@@ -11562,9 +11562,9 @@
                RestRequest req,
                RestResponse res,
                <ja>@Method</ja> String method,
-               <ja>@Path</ja> String a1,
-               <ja>@Path</ja> <jk>int</jk> a2,
-               <ja>@Path</ja> UUID a3,
+               <ja>@Path</ja>(<js>"a1"</js>) String a1,
+               <ja>@Path</ja>(<js>"a2"</js>) <jk>int</jk> a2,
+               <ja>@Path</ja>(<js>"a3"</js>) UUID a3,
                <ja>@Query</ja>(<js>"p1"</js>) <jk>int</jk> p1,
                <ja>@Query</ja>(<js>"p2"</js>) String p2,
                <ja>@Query</ja>(<js>"p3"</js>) UUID p3,
@@ -11852,7 +11852,7 @@
 
        <jc>// Method with path pattern with arguments</jc>
        <ja>@RestMethod</ja>(name=<jsf>GET</jsf>, 
path=<js>"/xxx/{foo}/{bar}/{baz}/{bing}"</js>)
-       <jk>public void</jk> doGetWithArgs(<ja>@Path</ja> String foo, 
<ja>@Path</ja> <jk>int</jk> bar, <ja>@Path</ja> MyEnum baz, <ja>@Path</ja> UUID 
bing) {
+       <jk>public void</jk> doGetWithArgs(<ja>@Path</ja>(<js>"foo"</js>) 
String foo, <ja>@Path</ja>(<js>"bar"</js>) <jk>int</jk> bar, 
<ja>@Path</ja>(<js>"baz"</js>) MyEnum baz, <ja>@Path</ja>(<js>"bing"</js>) UUID 
bing) {
                ...
        }
                        </p>
@@ -12045,14 +12045,14 @@
                        <p class='bcode w800'>
        <jc>// Equivalent method 1</jc>
        <ja>@RestMethod</ja>(name=<jsf>GET</jsf>, 
path=<js>"/example1/{personId}"</js>)
-       <jk>public</jk> Person doGet1(<ja>@Path</ja> UUID personId) {
+       <jk>public</jk> Person doGet1(<ja>@Path</ja>(<js>"personId"</js>) UUID 
personId) {
                Person p = getPersonById(personId);
                <jk>return</jk> p;
        }
 
        <jc>// Equivalent method 2</jc>
        <ja>@RestMethod</ja>(name=<jsf>GET</jsf>, 
path=<js>"/example2/{personId}"</js>)
-       <jk>public void</jk> doGet2(RestResponse res, <ja>@Path</ja> UUID 
personId) {
+       <jk>public void</jk> doGet2(RestResponse res, 
<ja>@Path</ja>(<js>"personId"</js>) UUID personId) {
                Person p = getPersonById(personId);
                res.setOutput(p);
        }
@@ -12060,7 +12060,7 @@
        <jc>// (Sorta) Equivalent method 3</jc>
        <jc>// (Ignores any converters or method-level properties)</jc>
        <ja>@RestMethod</ja>(name=<jsf>GET</jsf>, 
path=<js>"/example3/{personId}"</js>)
-       <jk>public void</jk> doGet3(RestRequest req, RestResponse res, 
<ja>@Path</ja> UUID personId) {
+       <jk>public void</jk> doGet3(RestRequest req, RestResponse res, 
<ja>@Path</ja>(<js>"personId"</js>) UUID personId) {
                Person p = getPersonById(personId);
                String accept = req.getHeader(<js>"Accept"</js>, 
<js>"text/json"</js>);
                WriterSerializer s = 
res.getSerializerGroup().getWriterSerializer(accept);
@@ -12976,7 +12976,7 @@
                name=<jsf>GET</jsf>, path=<js>"/people/{id}/*"</js>,
                converters={Traversable.<jk>class</jk>,Queryable.<jk>class</jk>}
        )
-       <jk>public</jk> Person getPerson(<ja>@Path</ja> <jk>int</jk> id) {
+       <jk>public</jk> Person getPerson(<ja>@Path</ja>(<js>"id"</js>) 
<jk>int</jk> id) {
                <jk>return</jk> findPerson(id);
        }
                </p>    
@@ -14651,7 +14651,7 @@
                
                <jd>/** GET request handler for single photo */</jd>
                <ja>@RestMethod</ja>(name=<jsf>GET</jsf>, 
path=<js>"/{id}"</js>, serializers=ImageSerializer.<jk>class</jk>)
-               <jk>public</jk> BufferedImage getPhoto(RestRequest req, 
<ja>@Path</ja> int id) <jk>throws</jk> Exception {
+               <jk>public</jk> BufferedImage getPhoto(RestRequest req, 
<ja>@Path</ja>(<js>"id"</js>) int id) <jk>throws</jk> Exception {
                        Photo p = photos.get(id);
                        if (p == <jk>null</jk>)
                                <jk>throw new</jk> 
RestException(<jsf>SC_NOT_FOUND</jsf>, <js>"Photo not found"</js>);
@@ -14660,7 +14660,7 @@
                
                <jd>/** PUT request handler */</jd>
                <ja>@RestMethod</ja>(name=<jsf>PUT</jsf>, 
path=<js>"/{id}"</js>, parsers=ImageParser.<jk>class</jk>)
-               <jk>public</jk> String addPhoto(RestRequest req, <ja>@Path</ja> 
<jk>int</jk> id, <ja>@Body</ja> BufferedImage image) <jk>throws</jk> Exception {
+               <jk>public</jk> String addPhoto(RestRequest req, 
<ja>@Path</ja>(<js>"id"</js>) <jk>int</jk> id, <ja>@Body</ja> BufferedImage 
image) <jk>throws</jk> Exception {
                        photos.put(id, <jk>new</jk> Photo(id, image));
                        <jk>return</jk> <js>"OK"</js>;
                }
@@ -14676,7 +14676,7 @@
                
                <jd>/** DELETE request handler */</jd>
                <ja>@RestMethod</ja>(name=<jsf>DELETE</jsf>, 
path=<js>"/{id}"</js>)
-               <jk>public</jk> String deletePhoto(RestRequest req, 
<ja>@Path</ja> <jk>int</jk> id) <jk>throws</jk> Exception {
+               <jk>public</jk> String deletePhoto(RestRequest req, 
<ja>@Path</ja>(<js>"id"</js>) <jk>int</jk> id) <jk>throws</jk> Exception {
                        Photo p = photos.remove(id);
                        if (p == <jk>null</jk>)
                                <jk>throw new</jk> 
RestException(<jsf>SC_NOT_FOUND</jsf>, <js>"Photo not found"</js>);
@@ -17707,7 +17707,7 @@
                                <js>"}"</js>
                        }
                )
-               <jk>public</jk> String getSystemProperty(<ja>@Path</ja> String 
propertyName) <jk>throws</jk> Throwable {
+               <jk>public</jk> String 
getSystemProperty(<ja>@Path</ja>(<js>"propertyName"</js>) String propertyName) 
<jk>throws</jk> Throwable {
                        <jk>return</jk> 
System.<jsm>getProperty</jsm>(propertyName);
                }
        
@@ -17727,7 +17727,7 @@
                                <js>"}"</js>
                        }
                )
-               <jk>public</jk> Redirect setSystemProperty(<ja>@Path</ja> 
String propertyName, <ja>@Body</ja> String value) {
+               <jk>public</jk> Redirect 
setSystemProperty(<ja>@Path</ja>(<js>"propertyName"</js>) String propertyName, 
<ja>@Body</ja> String value) {
                        System.<jsm>setProperty</jsm>(propertyName, value);
                        <jk>return new</jk> Redirect(<js>"servlet:/"</js>);
                }
@@ -17768,7 +17768,7 @@
                                <js>"}"</js>
                        }
                )
-               <jk>public</jk> Redirect deleteSystemProperty(<ja>@Path</ja> 
String propertyName) {
+               <jk>public</jk> Redirect 
deleteSystemProperty(<ja>@Path</ja>(<js>"propertyName"</js>) String 
propertyName) {
                        System.<jsm>clearProperty</jsm>(propertyName);
                        <jk>return new</jk> Redirect(<js>"servlet:/"</js>);
                }
@@ -17913,9 +17913,9 @@
                <ja>@RestMethod</ja>(name=<jsf>GET</jsf>, 
path=<js>"/example1/{p1}/{p2}/{p3}/*"</js>)
                <jk>public</jk> String example1(
                                <ja>@Method</ja> String method,                 
 <jc>// HTTP method.</jc>
-                               <ja>@Path</ja> String p1,                       
 <jc>// Path variables.</jc>
-                               <ja>@Path</ja> <jk>int</jk> p2,
-                               <ja>@Path</ja> UUID p3,
+                               <ja>@Path</ja>(<js>"p1"</js>) String p1,        
                <jc>// Path variables.</jc>
+                               <ja>@Path</ja>(<js>"p2"</js>) <jk>int</jk> p2,
+                               <ja>@Path</ja>(<js>"p3"</js>) UUID p3,
                                <ja>@Query</ja>(<js>"q1"</js>) <jk>int</jk> q1, 
                   <jc>// Query parameters.</jc>
                                <ja>@Query</ja>(<js>"q2"</js>) String q2,
                                <ja>@Query</ja>(<js>"q3"</js>) UUID q3,
@@ -18982,7 +18982,7 @@
                <ja>@RestMethod</ja>(name=<jsf>GET</jsf>, 
path=<js>"/people/{id}/*"</js>, 
                        
converters={Traversable.<jk>class</jk>,Queryable.<jk>class</jk>,Introspectable.<jk>class</jk>}
 
                ) 
-               <jk>public</jk> Person getPerson(<ja>@Path</ja> <jk>int</jk> 
id) <jk>throws</jk> Exception { 
+               <jk>public</jk> Person getPerson(<ja>@Path</ja>(<js>"id"</js>) 
<jk>int</jk> id) <jk>throws</jk> Exception { 
                        <jk>return</jk> findPerson(id); 
                } 
                
@@ -19004,7 +19004,7 @@
                <ja>@RestMethod</ja>(name=<jsf>GET</jsf>, 
path=<js>"/addresses/{id}/*"</js>, 
                        
converters={Traversable.<jk>class</jk>,Queryable.<jk>class</jk>} 
                ) 
-               <jk>public</jk> Address getAddress(<ja>@Path</ja> <jk>int</jk> 
id) <jk>throws</jk> Exception { 
+               <jk>public</jk> Address 
getAddress(<ja>@Path</ja>(<js>"id"</js>) <jk>int</jk> id) <jk>throws</jk> 
Exception { 
                        <jk>return</jk> findAddress(id); 
                } 
                
@@ -19027,7 +19027,7 @@
                <ja>@RestMethod</ja>(name=<jsf>POST</jsf>, 
path=<js>"/people/{id}/addresses"</js>, 
                        guards=AdminGuard.<jk>class</jk> 
                ) 
-               <jk>public</jk> Redirect createAddress(<ja>@Path</ja> 
<jk>int</jk> id, <ja>@Body</ja> CreateAddress ca) <jk>throws</jk> Exception { 
+               <jk>public</jk> Redirect 
createAddress(<ja>@Path</ja>(<js>"id"</js>) <jk>int</jk> id, <ja>@Body</ja> 
CreateAddress ca) <jk>throws</jk> Exception { 
                        Person p = findPerson(id); 
                        Address a = p.createAddress(ca); 
                        <jk>return new</jk> Redirect(<js>"addresses/{0}"</js>, 
a.id); 
@@ -19040,7 +19040,7 @@
                <ja>@RestMethod</ja>(name=<jsf>DELETE</jsf>, 
path=<js>"/people/{id}"</js>, 
                        guards=AdminGuard.<jk>class</jk>, 
                ) 
-               <jk>public</jk> String deletePerson(<ja>@Path</ja> <jk>int</jk> 
id) <jk>throws</jk> Exception { 
+               <jk>public</jk> String 
deletePerson(<ja>@Path</ja>(<js>"id"</js>) <jk>int</jk> id) <jk>throws</jk> 
Exception { 
                        <jf>addressBook</jf>.removePerson(id); 
                        <jk>return</jk> <js>"DELETE successful"</js>; 
                } 
@@ -19052,7 +19052,7 @@
                <ja>@RestMethod</ja>(name=<jsf>DELETE</jsf>, 
path=<js>"/addresses/{id}"</js>, 
                        guards=AdminGuard.<jk>class</jk> 
                ) 
-               <jk>public</jk> String deleteAddress(<ja>@Path</ja> 
<jk>int</jk> addressId) <jk>throws</jk> Exception { 
+               <jk>public</jk> String 
deleteAddress(<ja>@Path</ja>(<js>"id"</js>) <jk>int</jk> addressId) 
<jk>throws</jk> Exception { 
                        Person p = 
<jf>addressBook</jf>.findPersonWithAddress(addressId); 
                        <jk>if</jk> (p == <jk>null</jk>) 
                                <jk>throw new</jk> 
RestException(<jsf>SC_NOT_FOUND</jsf>, <js>"Person not found"</js>); 
@@ -19068,7 +19068,7 @@
                <ja>@RestMethod</ja>(name=<jsf>PUT</jsf>, 
path=<js>"/people/{id}/*"</js>, 
                        guards=AdminGuard.<jk>class</jk> 
                ) 
-               <jk>public</jk> String updatePerson(RestRequest req, 
<ja>@Path</ja> <jk>int</jk> id, <ja>@PathRemainder</ja> 
+               <jk>public</jk> String updatePerson(RestRequest req, 
<ja>@Path</ja>(<js>"id"</js>) <jk>int</jk> id, <ja>@PathRemainder</ja> 
                                String remainder) <jk>throws</jk> Exception { 
                        <jk>try</jk> { 
                                Person p = findPerson(id); 
@@ -19089,7 +19089,7 @@
                <ja>@RestMethod</ja>(name=<jsf>PUT</jsf>, 
path=<js>"/addresses/{id}/*"</js>, 
                        guards=AdminGuard.<jk>class</jk> 
                ) 
-               <jk>public</jk> String updateAddress(RestRequest req, 
<ja>@Path</ja> <jk>int</jk> id, 
+               <jk>public</jk> String updateAddress(RestRequest req, 
<ja>@Path</ja>(<js>"id"</js>) <jk>int</jk> id, 
                                <ja>@PathRemainder</ja> String remainder) 
<jk>throws</jk> Exception { 
                        <jk>try</jk> { 
                                Address a = findAddress(id); 
@@ -20109,7 +20109,7 @@
        
                <jd>/** GET request handler for single photo */</jd>
                <ja>@RestMethod</ja>(name=<jsf>GET</jsf>, 
path=<js>"/{id}"</js>, serializers=ImageSerializer.<jk>class</jk>, 
summary=<js>"Get a photo by ID"</js>)
-               <jk>public</jk> BufferedImage getPhoto(<ja>@Path</ja> String 
id) <jk>throws</jk> Exception {
+               <jk>public</jk> BufferedImage 
getPhoto(<ja>@Path</ja>(<js>"id"</js>) String id) <jk>throws</jk> Exception {
                        Photo p = <jf>photos</jf>.get(id);
                        <jk>if</jk> (p == <jk>null</jk>)
                                <jk>throw new</jk> 
RestException(<jsf>SC_NOT_FOUND</jsf>, <js>"Photo not found"</js>);
@@ -20118,7 +20118,7 @@
        
                <jd>/** PUT request handler */</jd>
                <ja>@RestMethod</ja>(name=<jsf>PUT</jsf>, 
path=<js>"/{id}"</js>, parsers=ImageParser.<jk>class</jk>, summary=<js>"Add or 
overwrite a photo"</js>)
-               <jk>public</jk> String addPhoto(<ja>@Path</ja> String id, 
<ja>@Body</ja> BufferedImage image) <jk>throws</jk> Exception {
+               <jk>public</jk> String addPhoto(<ja>@Path</ja>(<js>"id"</js>) 
String id, <ja>@Body</ja> BufferedImage image) <jk>throws</jk> Exception {
                        <jf>photos</jf>.put(id, <jk>new</jk> Photo(id, image));
                        <jk>return</jk> <js>"OK"</js>;
                }
@@ -20133,7 +20133,7 @@
        
                <jd>/** DELETE request handler */</jd>
                <ja>@RestMethod</ja>(name=<jsf>DELETE</jsf>, 
path=<js>"/{id}"</js>, summary=<js>"Delete a photo by ID"</js>)
-               <jk>public</jk> String deletePhoto(<ja>@Path</ja> String id) 
<jk>throws</jk> Exception {
+               <jk>public</jk> String 
deletePhoto(<ja>@Path</ja>(<js>"id"</js>) String id) <jk>throws</jk> Exception {
                        Photo p = <jf>photos</jf>.remove(id);
                        <jk>if</jk> (p == <jk>null</jk>)
                                <jk>throw new</jk> 
RestException(<jsf>SC_NOT_FOUND</jsf>, <js>"Photo not found"</js>);
@@ -23066,9 +23066,9 @@
        <ja>@RestMethod</ja>(name=<js>"*"</js>, 
path=<js>"/example1/{a1}/{a2}/{a3}/*"</js>) 
        <jk>public</jk> String example1( 
                <ja>@Method</ja> String method, 
-               <ja>@Path</ja> String a1, 
-               <ja>@Path</ja> <jk>int</jk> a2, 
-               <ja>@Path</ja> UUID a3, 
+               <ja>@Path</ja>(<js>"a1"</js>) String a1, 
+               <ja>@Path</ja>(<js>"a2"</js>) <jk>int</jk> a2, 
+               <ja>@Path</ja>(<js>"a3"</js>) UUID a3, 
                <ja>@Query</ja>(<js>"p1"</js>) <jk>int</jk> p1, 
                <ja>@Query</ja>(<js>"p2"</js>) String p2, 
                <ja>@Query</ja>(<js>"p3"</js>) UUID p3, 
@@ -23424,15 +23424,6 @@
                                See {@link 
org.apache.juneau.rest.annotation.RestMethod#name() @RestMethod.name()} for 
more information.
                        <li>{@link 
org.apache.juneau.rest.RestRequest#toString()} can be called at any time to 
view the headers and content of the request
                                without affecting functionality.  Very useful 
for debugging.
-                       <li>You can now use numeric values in path annotations. 
 
-                               <br>When using numeric variable names, you 
don't need to specify the variable name in the <ja>@Path</ja> annoation:
-                               <p class='bcode w800'>
-       <ja>@RestMethod</ja>(name=<js>"GET"</js>, 
path=<js>"/myurl/{0}/{1}/{2}/*"</js>)
-       <jk>public void</jk> doGet(RestRequest req, RestResponse res,
-                       <ja>@Path</ja> String foo, <ja>@Path</ja> <jk>int</jk> 
bar, <ja>@Path</ja> UUID baz) {
-               ...
-       }
-                               </p>
                        <li>{@link 
org.apache.juneau.rest.annotation.RestMethod#name() @RestMethod.name()} 
annotation is now optional.  Defaults to <js>"GET"</js>.
                </ul>
 
diff --git 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/MethodExampleResource.java
 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/MethodExampleResource.java
index 04e2511..25aefd8 100644
--- 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/MethodExampleResource.java
+++ 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/MethodExampleResource.java
@@ -81,9 +81,9 @@ public class MethodExampleResource extends BasicRestServlet {
        )
        public Map<String,Object> example1(
                        @Method String method,                  // HTTP method.
-                       @Path String p1,                        // Path 
variables.
-                       @Path int p2,
-                       @Path UUID p3,
+                       @Path("p1") String p1,                        // Path 
variables.
+                       @Path("p2") int p2,
+                       @Path("p3") UUID p3,
                        @Query("q1") int q1,                    // Query 
parameters.
                        @Query("q2") String q2,
                        @Query(name="q3",_default=SAMPLE_UUID_STRING) UUID q3,
diff --git 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/PhotosResource.java
 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/PhotosResource.java
index 58b576a..eb5a66e 100644
--- 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/PhotosResource.java
+++ 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/PhotosResource.java
@@ -102,7 +102,7 @@ public class PhotosResource extends BasicRestServlet {
 
        /** GET request handler for single photo */
        @RestMethod(name=GET, path="/{id}", serializers=ImageSerializer.class, 
summary="Get a photo by ID")
-       public BufferedImage getPhoto(@Path String id) throws NotFound {
+       public BufferedImage getPhoto(@Path("id") String id) throws NotFound {
                Photo p = photos.get(id);
                if (p == null)
                        throw new NotFound("Photo not found");
@@ -111,7 +111,7 @@ public class PhotosResource extends BasicRestServlet {
 
        /** PUT request handler */
        @RestMethod(name=PUT, path="/{id}", parsers=ImageParser.class, 
summary="Add or overwrite a photo")
-       public String addPhoto(@Path String id, @Body BufferedImage image) 
throws Exception {
+       public String addPhoto(@Path("id") String id, @Body BufferedImage 
image) throws Exception {
                photos.put(id, new Photo(id, image));
                return "OK";
        }
@@ -126,7 +126,7 @@ public class PhotosResource extends BasicRestServlet {
 
        /** DELETE request handler */
        @RestMethod(name=DELETE, path="/{id}", summary="Delete a photo by ID")
-       public String deletePhoto(@Path String id) throws NotFound {
+       public String deletePhoto(@Path("id") String id) throws NotFound {
                Photo p = photos.remove(id);
                if (p == null)
                        throw new NotFound("Photo not found");
diff --git 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SystemPropertiesResource.java
 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SystemPropertiesResource.java
index e2fcf1e..83ab40f 100644
--- 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SystemPropertiesResource.java
+++ 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SystemPropertiesResource.java
@@ -123,7 +123,7 @@ public class SystemPropertiesResource extends 
BasicRestServlet {
                )
        )
        public String getSystemProperty(
-                       @Path(description="The system property name.", 
example="PATH") String propertyName
+                       @Path(name="propertyName", description="The system 
property name.", example="PATH") String propertyName
                ) throws NotAcceptable {
                
                return System.getProperty(propertyName);
@@ -136,7 +136,7 @@ public class SystemPropertiesResource extends 
BasicRestServlet {
                guards=AdminGuard.class
        )
        public RedirectToServletRoot setSystemProperty(
-                       @Path(description="The system property name") String 
propertyName, 
+                       @Path(name="propertyName", description="The system 
property name") String propertyName, 
                        @Body(description="The new system property value") 
String value
                ) throws UserNotAdminException, NotAcceptable, 
UnsupportedMediaType {
                
@@ -165,7 +165,7 @@ public class SystemPropertiesResource extends 
BasicRestServlet {
                guards=AdminGuard.class
        )
        public RedirectToServletRoot deleteSystemProperty(
-                       @Path(description="The system property name", 
example="PATH") String propertyName
+                       @Path(name="propertyName", description="The system 
property name", example="PATH") String propertyName
                ) throws UserNotAdminException, NotAcceptable {
                
                System.clearProperty(propertyName);
diff --git 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/addressbook/AddressBookResource.java
 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/addressbook/AddressBookResource.java
index 14f4637..ed11413 100644
--- 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/addressbook/AddressBookResource.java
+++ 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/addressbook/AddressBookResource.java
@@ -185,7 +185,7 @@ public class AddressBookResource extends 
BasicRestServletJena {
        @RestMethod(name=GET, path="/people/{id}/*",
                converters={Traversable.class,Introspectable.class}
        )
-       public Person getPerson(@Path int id) throws Exception {
+       public Person getPerson(@Path("id") int id) throws Exception {
                return findPerson(id);
        }
 
@@ -213,7 +213,7 @@ public class AddressBookResource extends 
BasicRestServletJena {
        @RestMethod(name=GET, path="/addresses/{id}/*",
                converters={Traversable.class}
        )
-       public Address getAddress(@Path int id) throws Exception {
+       public Address getAddress(@Path("id") int id) throws Exception {
                return findAddress(id);
        }
 
@@ -236,7 +236,7 @@ public class AddressBookResource extends 
BasicRestServletJena {
        @RestMethod(name=POST, path="/people/{id}/addresses",
                guards=AdminGuard.class
        )
-       public Redirect createAddress(@Path int id, @Body CreateAddress ca) 
throws Exception {
+       public Redirect createAddress(@Path("id") int id, @Body CreateAddress 
ca) throws Exception {
                Person p = findPerson(id);
                Address a = p.createAddress(ca);
                return new Redirect("addresses/{0}", a.id);
@@ -249,7 +249,7 @@ public class AddressBookResource extends 
BasicRestServletJena {
        @RestMethod(name=DELETE, path="/people/{id}",
                guards=AdminGuard.class
        )
-       public String deletePerson(@Path int id) throws Exception {
+       public String deletePerson(@Path("id") int id) throws Exception {
                addressBook.removePerson(id);
                return "DELETE successful";
        }
@@ -261,7 +261,7 @@ public class AddressBookResource extends 
BasicRestServletJena {
        @RestMethod(name=DELETE, path="/addresses/{id}",
                guards=AdminGuard.class
        )
-       public String deleteAddress(@Path int addressId) throws NotFound {
+       public String deleteAddress(@Path("id") int addressId) throws NotFound {
                Person p = addressBook.findPersonWithAddress(addressId);
                if (p == null)
                        throw new NotFound("Person not found");
@@ -277,7 +277,7 @@ public class AddressBookResource extends 
BasicRestServletJena {
        @RestMethod(name=PUT, path="/people/{id}/*",
                guards=AdminGuard.class
        )
-       public String updatePerson(RequestBody body, @Path int id, 
@PathRemainder String remainder) throws BadRequest {
+       public String updatePerson(RequestBody body, @Path("id") int id, 
@PathRemainder String remainder) throws BadRequest {
                try {
                        Person p = findPerson(id);
                        PojoRest r = new PojoRest(p);
@@ -297,7 +297,7 @@ public class AddressBookResource extends 
BasicRestServletJena {
        @RestMethod(name=PUT, path="/addresses/{id}/*",
                guards=AdminGuard.class
        )
-       public String updateAddress(RestRequest req, @Path int id, 
@PathRemainder String remainder) throws BadRequest {
+       public String updateAddress(RestRequest req, @Path("id") int id, 
@PathRemainder String remainder) throws BadRequest {
                try {
                        Address a = findAddress(id);
                        PojoRest r = new PojoRest(a);
diff --git 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetStoreResource.java
 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetStoreResource.java
index 7aafeec..beaf3b2 100644
--- 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetStoreResource.java
+++ 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetStoreResource.java
@@ -149,7 +149,7 @@ public class PetStoreResource extends BasicRestServletJena {
                )
        )
        public Pet getPet(
-                       @Path(description="ID of pet to return", example="123") 
long petId
+                       @Path(name="petId", description="ID of pet to return", 
example="123") long petId
                ) throws IdNotFound, NotAcceptable {
                
                return store.getPet(petId);
@@ -206,7 +206,7 @@ public class PetStoreResource extends BasicRestServletJena {
                )
        )
        public Div editPetPage(
-                       @Path(description="ID of pet to return", example="123") 
long petId
+                       @Path(name="petId", description="ID of pet to return", 
example="123") long petId
                ) throws IdConflict, NotAcceptable, UnsupportedMediaType {
                
                Pet pet = getPet(petId);
@@ -328,7 +328,7 @@ public class PetStoreResource extends BasicRestServletJena {
        )
        public Ok deletePet(
                        @Header(name="api_key", example="foobar") String 
apiKey, 
-                       @Path(description="Pet id to delete", example="123") 
long petId
+                       @Path(name="petId", description="Pet id to delete", 
example="123") long petId
                ) throws IdNotFound, NotAcceptable {
                
                store.removePet(petId);
@@ -347,7 +347,7 @@ public class PetStoreResource extends BasicRestServletJena {
                )
        )
        public Ok uploadImage(
-                       @Path(description="ID of pet to update", example="123") 
long petId, 
+                       @Path(name="petId", description="ID of pet to update", 
example="123") long petId, 
                        @FormData(name="additionalMetadata", 
description="Additional data to pass to server", example="Foobar") String 
additionalMetadata, 
                        @FormData(name="file", description="file to upload", 
required="true", type="file") byte[] file
                ) throws NotAcceptable, UnsupportedMediaType {
@@ -407,7 +407,7 @@ public class PetStoreResource extends BasicRestServletJena {
                )
        )
        public Order getOrder(
-                       @Path(description="ID of order to fetch", 
maximum="1000", minimum="101", example="123") long orderId
+                       @Path(name="orderId", description="ID of order to 
fetch", maximum="1000", minimum="101", example="123") long orderId
                ) throws InvalidId, IdNotFound, NotAcceptable {
                
                if (orderId < 101 || orderId > 1000)
@@ -448,7 +448,7 @@ public class PetStoreResource extends BasicRestServletJena {
                )
        )
        public Ok deletePurchaseOrder(
-                       @Path(description="ID of the order that needs to be 
deleted", minimum="1", example="5") long orderId
+                       @Path(name="orderId", description="ID of the order that 
needs to be deleted", minimum="1", example="5") long orderId
                ) throws InvalidId, IdNotFound, NotAcceptable {
                
                if (orderId < 0)
@@ -502,7 +502,7 @@ public class PetStoreResource extends BasicRestServletJena {
                )
        )
        public User getUser(
-                       @Path(description="The name that needs to be fetched. 
Use user1 for testing.") String username
+                       @Path(name="username", description="The name that needs 
to be fetched. Use user1 for testing.") String username
                ) throws InvalidUsername, IdNotFound, NotAcceptable {
                
                return store.getUser(username);
@@ -552,7 +552,7 @@ public class PetStoreResource extends BasicRestServletJena {
                )
        )
        public Ok updateUser(
-                       @Path(description="Name that need to be updated") 
String username, 
+                       @Path(name="username", description="Name that need to 
be updated") String username, 
                        @Body(description="Updated user object") User user
                ) throws InvalidUsername, IdNotFound, NotAcceptable, 
UnsupportedMediaType {
                
@@ -570,7 +570,7 @@ public class PetStoreResource extends BasicRestServletJena {
                )
        )
        public Ok deleteUser(
-                       @Path(description="The name that needs to be deleted") 
String username
+                       @Path(name="username", description="The name that needs 
to be deleted") String username
                ) throws InvalidUsername, IdNotFound, NotAcceptable {
                
                store.removeUser(username);
diff --git 
a/juneau-microservice/juneau-microservice-test/src/main/java/org/apache/juneau/rest/test/ErrorConditionsResource.java
 
b/juneau-microservice/juneau-microservice-test/src/main/java/org/apache/juneau/rest/test/ErrorConditionsResource.java
index a7a03af..1a6bf1f 100644
--- 
a/juneau-microservice/juneau-microservice-test/src/main/java/org/apache/juneau/rest/test/ErrorConditionsResource.java
+++ 
b/juneau-microservice/juneau-microservice-test/src/main/java/org/apache/juneau/rest/test/ErrorConditionsResource.java
@@ -108,7 +108,7 @@ public class ErrorConditionsResource extends 
BasicRestServlet {
        // Test trying to set parameters to invalid types.
        
//====================================================================================================
        @RestMethod(name=PUT, path="/testSetParameterToInvalidTypes/{a1}")
-       public String testSetParameterToInvalidTypes(@Query("p1") int t1, @Path 
int a1, @Header("h1") int h1) {
+       public String testSetParameterToInvalidTypes(@Query("p1") int t1, 
@Path("a1") int a1, @Header("h1") int h1) {
                return "OK";
        }
 
diff --git 
a/juneau-microservice/juneau-microservice-test/src/main/java/org/apache/juneau/rest/test/ParamsResource.java
 
b/juneau-microservice/juneau-microservice-test/src/main/java/org/apache/juneau/rest/test/ParamsResource.java
index 1c73265..f3c3fef 100644
--- 
a/juneau-microservice/juneau-microservice-test/src/main/java/org/apache/juneau/rest/test/ParamsResource.java
+++ 
b/juneau-microservice/juneau-microservice-test/src/main/java/org/apache/juneau/rest/test/ParamsResource.java
@@ -63,17 +63,17 @@ public class ParamsResource extends BasicRestServlet {
        }
 
        @RestMethod(name=GET, path="/get1/{foo}")
-       public void doGet1a(RestResponse res, String foo) {
+       public void doGet1a(RestResponse res, @Path("foo") String foo) {
                res.setOutput("GET /get1a " + foo);
        }
 
        @RestMethod(name=GET, path="/get1/{foo}/{bar}")
-       public void doGet1b(RestResponse res, String foo, String bar) {
+       public void doGet1b(RestResponse res, @Path("foo") String foo, 
@Path("bar") String bar) {
                res.setOutput("GET /get1b " + foo + "," + bar);
        }
 
        @RestMethod(name=GET, path="/get3/{foo}/{bar}/*")
-       public void doGet3(HttpServletRequest reqx, HttpServletResponse resx, 
String foo, int bar) {
+       public void doGet3(HttpServletRequest reqx, HttpServletResponse resx, 
@Path("foo") String foo, @Path("bar") int bar) {
                RestRequest req = (RestRequest)reqx;
                RestResponse res = (RestResponse)resx;
                res.setOutput("GET /get3/"+foo+"/"+bar+" 
remainder="+req.getPathMatch().getRemainder());
@@ -93,25 +93,45 @@ public class ParamsResource extends BasicRestServlet {
 
        // Bean parameter
        @RestMethod(name=POST, path="/person/{person}")
-       public void doPost(RestRequest req, RestResponse res, Person p) {
+       public void doPost(RestRequest req, RestResponse res, @Path("person") 
Person p) {
                res.setOutput("POST 
/person/{name="+p.name+",birthDate.year="+p.birthDate.get(Calendar.YEAR)+"} 
remainder="+req.getPathMatch().getRemainder());
        }
 
        // Various primitive types
        @RestMethod(name=PUT, 
path="/primitives/{xInt}/{xShort}/{xLong}/{xChar}/{xFloat}/{xDouble}/{xByte}/{xBoolean}")
-       public void doPut1(RestResponse res, int xInt, short xShort, long 
xLong, char xChar, float xFloat, double xDouble, byte xByte, boolean xBoolean) {
+       public void doPut1(
+                       RestResponse res, 
+                       @Path("xInt") int xInt, 
+                       @Path("xShort") short xShort, 
+                       @Path("xLong") long xLong, 
+                       @Path("xChar") char xChar, 
+                       @Path("xFloat") float xFloat, 
+                       @Path("xDouble") double xDouble, 
+                       @Path("xByte") byte xByte, 
+                       @Path("xBoolean") boolean xBoolean
+               ) {
                res.setOutput("PUT 
/primitives/"+xInt+"/"+xShort+"/"+xLong+"/"+xChar+"/"+xFloat+"/"+xDouble+"/"+xByte+"/"+xBoolean);
        }
 
        // Various primitive objects
        @RestMethod(name=PUT, 
path="/primitiveObjects/{xInt}/{xShort}/{xLong}/{xChar}/{xFloat}/{xDouble}/{xByte}/{xBoolean}")
-       public void doPut2(RestResponse res, Integer xInt, Short xShort, Long 
xLong, Character xChar, Float xFloat, Double xDouble, Byte xByte, Boolean 
xBoolean) {
+       public void doPut2(
+                       RestResponse res, 
+                       @Path("xInt") Integer xInt, 
+                       @Path("xShort") Short xShort, 
+                       @Path("xLong") Long xLong, 
+                       @Path("xChar") Character xChar, 
+                       @Path("xFloat") Float xFloat, 
+                       @Path("xDouble") Double xDouble, 
+                       @Path("xByte") Byte xByte, 
+                       @Path("xBoolean") Boolean xBoolean
+               ) {
                res.setOutput("PUT 
/primitiveObjects/"+xInt+"/"+xShort+"/"+xLong+"/"+xChar+"/"+xFloat+"/"+xDouble+"/"+xByte+"/"+xBoolean);
        }
 
        // Object with forString(String) method
        @RestMethod(name=PUT, path="/uuid/{uuid}")
-       public void doPut1(RestResponse res, UUID uuid) {
+       public void doPut1(RestResponse res, @Path("uuid") UUID uuid) {
                res.setOutput("PUT /uuid/"+uuid);
        }
 
diff --git 
a/juneau-microservice/juneau-microservice-test/src/main/java/org/apache/juneau/rest/test/PathVariablesResource.java
 
b/juneau-microservice/juneau-microservice-test/src/main/java/org/apache/juneau/rest/test/PathVariablesResource.java
index 806f516..2689b2f 100644
--- 
a/juneau-microservice/juneau-microservice-test/src/main/java/org/apache/juneau/rest/test/PathVariablesResource.java
+++ 
b/juneau-microservice/juneau-microservice-test/src/main/java/org/apache/juneau/rest/test/PathVariablesResource.java
@@ -29,7 +29,7 @@ public class PathVariablesResource extends BasicRestServlet {
        private static final long serialVersionUID = 1L;
 
        @RestMethod(name=GET, path="/test1/{x}/foo/{y}/bar/{z}/*")
-       public StringMessage test1(@Path String x, @Path int y, @Path boolean 
z) {
+       public StringMessage test1(@Path("x") String x, @Path("y") int y, 
@Path("z") boolean z) {
                return new StringMessage("x={0},y={1},z={2}", x, y, z);
        }
 
@@ -39,12 +39,12 @@ public class PathVariablesResource extends BasicRestServlet 
{
        }
 
        @RestMethod(name=GET, path="/test3/{0}/foo/{1}/bar/{2}/*")
-       public StringMessage test3(@Path String x, @Path int y, @Path boolean 
z) {
+       public StringMessage test3(@Path("0") String x, @Path("1") int y, 
@Path("2") boolean z) {
                return new StringMessage("x={0},y={1},z={2}", x, y, z);
        }
 
        @RestMethod(name=GET, path="/test4/{2}/foo/{1}/bar/{0}/*")
-       public StringMessage test4(@Path String x, @Path int y, @Path boolean 
z) {
+       public StringMessage test4(@Path("0") String x, @Path("1") int y, 
@Path("2") boolean z) {
                return new StringMessage("x={0},y={1},z={2}", x, y, z);
        }
 }
diff --git 
a/juneau-microservice/juneau-microservice-test/src/main/java/org/apache/juneau/rest/test/TransformsResource.java
 
b/juneau-microservice/juneau-microservice-test/src/main/java/org/apache/juneau/rest/test/TransformsResource.java
index 18e6a8a..ae0f3f1 100644
--- 
a/juneau-microservice/juneau-microservice-test/src/main/java/org/apache/juneau/rest/test/TransformsResource.java
+++ 
b/juneau-microservice/juneau-microservice-test/src/main/java/org/apache/juneau/rest/test/TransformsResource.java
@@ -43,7 +43,7 @@ public class TransformsResource extends 
TransformsParentResource {
                return a;
        }
        @RestMethod(name=PUT, 
path="/testClassTransformOverridesParentClassTransform/{a}")
-       public A test1c(@Path A a) {
+       public A test1c(@Path("a") A a) {
                return a;
        }
 
@@ -60,7 +60,7 @@ public class TransformsResource extends 
TransformsParentResource {
                return a;
        }
        @RestMethod(name=PUT, 
path="/testMethodTransformOverridesClassTransform/{a}", 
pojoSwaps={SwapA3.class})
-       public A test2c(@Path A a) {
+       public A test2c(@Path("a") A a) {
                return a;
        }
 
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
index cb2cc5b..3e8e096 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
@@ -4264,7 +4264,6 @@ public final class RestContext extends BeanContext {
                Type[] pt = method.getGenericParameterTypes();
                Annotation[][] pa = method.getParameterAnnotations();
                RestMethodParam[] rp = new RestMethodParam[pt.length];
-               int attrIndex = 0;
                PropertyStore ps = getPropertyStore();
 
                for (int i = 0; i < pt.length; i++) {
@@ -4288,6 +4287,8 @@ public final class RestContext extends BeanContext {
                                                rp[i] = new 
RestParamDefaults.HasQueryObject(method, (HasQuery)a, t);
                                        else if (a instanceof Body)
                                                rp[i] = new 
RestParamDefaults.BodyObject(method, (Body)a, t, null);
+                                       else if (a instanceof Path)
+                                               rp[i] = new 
RestParamDefaults.PathObject(method, (Path)a, t, ps, rp[i]);                    
                    
                                        else if (a instanceof PathRemainder)
                                                rp[i] = new 
RestParamDefaults.PathRemainderObject(method, t);                               
    
                                        else if (a instanceof Response)
@@ -4323,6 +4324,8 @@ public final class RestContext extends BeanContext {
                                        rp[i] = new 
RestParamDefaults.BodyObject(method, (Body)a, t, rp[i]);
                                else if (a instanceof 
org.apache.juneau.rest.annotation.Method)
                                        rp[i] = new 
RestParamDefaults.MethodObject(method, t);
+                               else if (a instanceof Path)
+                                       rp[i] = new 
RestParamDefaults.PathObject(method, (Path)a, t, ps, rp[i]);
                                else if (a instanceof PathRemainder)
                                        rp[i] = new 
RestParamDefaults.PathRemainderObject(method, t);
                                else if (a instanceof Response)
@@ -4343,33 +4346,10 @@ public final class RestContext extends BeanContext {
                        }
 
                        if (rp[i] == null) {
-
                                if (isPreOrPost)
                                        throw new RestServletException("Invalid 
parameter specified for method ''{0}'' at index position {1}", method, i);
-
-                               Path p = null;
-                               for (Annotation a : pa[i])
-                                       if (a instanceof Path)
-                                               p = (Path)a;
-
-                               String name = (p == null ? "" : 
firstNonEmpty(p.name(), p.value()));
-
-                               if (isEmpty(name)) {
-                                       int idx = attrIndex++;
-                                       String[] vars = pathPattern.getVars();
-                                       if (vars.length <= idx)
-                                               throw new 
RestServletException("Number of attribute parameters in method ''{0}'' exceeds 
the number of URL pattern variables.", method);
-
-                                       // Check for {#} variables.
-                                       String idxs = String.valueOf(idx);
-                                       for (int j = 0; j < vars.length; j++)
-                                               if (isNumeric(vars[j]) && 
vars[j].equals(idxs))
-                                                       name = vars[j];
-
-                                       if (isEmpty(name))
-                                               name = 
pathPattern.getVars()[idx];
-                               }
-                               rp[i] = new 
RestParamDefaults.PathParameterObject(name, p, t);
+                       } else {
+                               rp[i].validate();
                        }
                }
 
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestMethodParam.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestMethodParam.java
index f0b2bd4..ebaf289 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestMethodParam.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestMethodParam.java
@@ -25,6 +25,7 @@ import org.apache.juneau.dto.swagger.*;
 import org.apache.juneau.http.*;
 import org.apache.juneau.http.Date;
 import org.apache.juneau.parser.*;
+import org.apache.juneau.rest.exception.*;
 import org.apache.juneau.utils.*;
 
 /**
@@ -204,4 +205,11 @@ public abstract class RestMethodParam {
        public Type getType() {
                return type;
        }
+       
+       /**
+        * Performs validation on the parameter.
+        * 
+        * @throws InternalServerError
+        */
+       public void validate() throws InternalServerError {}
 }
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestParamDefaults.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestParamDefaults.java
index a4346ff..d8caed2 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestParamDefaults.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestParamDefaults.java
@@ -34,6 +34,7 @@ import org.apache.juneau.internal.*;
 import org.apache.juneau.json.*;
 import org.apache.juneau.parser.*;
 import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.rest.exception.*;
 import org.apache.juneau.utils.*;
 
 /**
@@ -545,10 +546,12 @@ class RestParamDefaults {
        // Annotated retrievers
        
//-------------------------------------------------------------------------------------------------------------------
 
-       static final class PathParameterObject extends RestMethodParam {
+       static final class PathObject extends RestMethodParam {
+               private final Method method;
 
-               protected PathParameterObject(String name, Path a, Type type) {
-                       super(PATH, name, type, getMetaData(a));
+               protected PathObject(Method method, Path a, Type type, 
PropertyStore ps, RestMethodParam existing) {
+                       super(PATH, firstNonEmpty(a.name(), a.value(), existing 
== null ? null : existing.name), type, getMetaData(a, castOrNull(existing, 
PathObject.class)));
+                       this.method = method;
                }
 
                @Override /* RestMethodParam */
@@ -556,10 +559,11 @@ class RestParamDefaults {
                        return req.getPathMatch().get(name, type);
                }
                
-               private static final ObjectMap getMetaData(Path a) {
+               private static final ObjectMap getMetaData(Path a, PathObject 
existing) {
+                       ObjectMap om = existing == null ? new ObjectMap() : 
existing.metaData;
                        if (a == null)
-                               return ObjectMap.EMPTY_MAP;
-                       return new ObjectMap()
+                               return om;
+                       return om
                                .appendSkipEmpty("description", 
joinnl(a.description()))
                                .appendSkipEmpty("type", a.type())
                                .appendSkipEmpty("format", a.format())
@@ -577,6 +581,12 @@ class RestParamDefaults {
                                .appendSkipEmpty("example", joinnl(a.example()))
                        ;
                }
+               
+               @Override /* RestMethodParqm */
+               public void validate() throws InternalServerError {
+                       if (isEmpty(name))
+                               throw new InternalServerError("@Path used 
without name or value on method ''{0}''.", method);
+               }
        }
 
        static final class BodyObject extends RestMethodParam {
@@ -623,10 +633,12 @@ class RestParamDefaults {
        }
 
        static final class HeaderObject extends RestMethodParam {
+               private final Method method;
                private final HttpPartParser partParser;
 
                protected HeaderObject(Method method, Header a, Type type, 
PropertyStore ps, RestMethodParam existing) {
-                       super(HEADER, firstNonEmpty(a.name(), a.value()), type, 
getMetaData(a, castOrNull(existing, HeaderObject.class)));
+                       super(HEADER, firstNonEmpty(a.name(), a.value(), 
existing == null ? null : existing.name), type, getMetaData(a, 
castOrNull(existing, HeaderObject.class)));
+                       this.method = method;
                        this.partParser = a.parser() == 
HttpPartParser.Null.class ? null : ClassUtils.newInstance(HttpPartParser.class, 
a.parser(), true, ps);
                }
 
@@ -664,13 +676,21 @@ class RestParamDefaults {
                                .appendSkipEmpty("example", joinnl(a.example()))
                        ;
                }
+               
+               @Override /* RestMethodParqm */
+               public void validate() throws InternalServerError {
+                       if (isEmpty(name))
+                               throw new InternalServerError("@Header used 
without name or value on method ''{0}''.", method);
+               }
        }
 
        static final class ResponseHeaderObject extends RestMethodParam {
+               private final Method method;
                final HttpPartSerializer partSerializer;
 
                protected ResponseHeaderObject(Method method, ResponseHeader a, 
Type type, PropertyStore ps, RestMethodParam existing) {
-                       super(RESPONSE_HEADER, firstNonEmpty(a.name(), 
a.value(), "Unknown"), type, getMetaData(a, castOrNull(existing, 
ResponseHeaderObject.class)));
+                       super(RESPONSE_HEADER, firstNonEmpty(a.name(), 
a.value(), existing == null ? null : existing.name), type, getMetaData(a, 
castOrNull(existing, ResponseHeaderObject.class)));
+                       this.method = method;
                        this.partSerializer = a.serializer() == 
HttpPartSerializer.Null.class ? null : 
ClassUtils.newInstance(HttpPartSerializer.class, a.serializer(), true, ps);
                }
 
@@ -737,6 +757,12 @@ class RestParamDefaults {
                        }
                        return om;
                }
+               
+               @Override /* RestMethodParqm */
+               public void validate() throws InternalServerError {
+                       if (isEmpty(name))
+                               throw new InternalServerError("@ResponseHeader 
used without name or value on method ''{0}''.", method);
+               }
        }
 
        static final class ResponseObject extends RestMethodParam {
@@ -854,13 +880,15 @@ class RestParamDefaults {
        }
 
        static final class FormDataObject extends RestMethodParam {
+               private final Method method;
                private final boolean multiPart;
                private final HttpPartParser partParser;
+               private final Type type;
 
-               protected FormDataObject(Method method, FormData a, Type type, 
PropertyStore ps, RestMethodParam existing) throws ServletException {
-                       super(FORM_DATA, firstNonEmpty(a.name(), a.value()), 
type, getMetaData(a, castOrNull(existing, FormDataObject.class)));
-                       if (a.multipart() && ! isCollection(type))
-                                       throw new RestServletException("Use of 
multipart flag on @FormData parameter that's not an array or Collection on 
method ''{0}''", method);
+               protected FormDataObject(Method method, FormData a, Type type, 
PropertyStore ps, RestMethodParam existing) {
+                       super(FORM_DATA, firstNonEmpty(a.name(), a.value(), 
existing == null ? null : existing.name), type, getMetaData(a, 
castOrNull(existing, FormDataObject.class)));
+                       this.method = method;
+                       this.type = type;
                        this.multiPart = a.multipart();
                        this.partParser = a.parser() == 
HttpPartParser.Null.class ? null : ClassUtils.newInstance(HttpPartParser.class, 
a.parser(), true, ps);
                }
@@ -901,16 +929,26 @@ class RestParamDefaults {
                                .appendSkipEmpty("example", joinnl(a.example()))
                        ;
                }
+               
+               @Override /* RestMethodParqm */
+               public void validate() throws InternalServerError {
+                       if (isEmpty(name))
+                               throw new InternalServerError("@FormData used 
without name or value on method ''{0}''.", method);
+                       if (multiPart && ! isCollection(type))
+                               throw new InternalServerError("Use of multipart 
flag on @FormData parameter that's not an array or Collection on method 
''{0}''", method);
+               }
        }
 
        static final class QueryObject extends RestMethodParam {
                private final boolean multiPart;
+               private final Type type;
+               private final Method method;
                private final HttpPartParser partParser;
 
-               protected QueryObject(Method method, Query a, Type type, 
PropertyStore ps, RestMethodParam existing) throws ServletException {
-                       super(QUERY, firstNonEmpty(a.name(), a.value()), type, 
getMetaData(a, castOrNull(existing, QueryObject.class)));
-                       if (a.multipart() && ! isCollection(type))
-                                       throw new RestServletException("Use of 
multipart flag on @Query parameter that's not an array or Collection on method 
''{0}''", method);
+               protected QueryObject(Method method, Query a, Type type, 
PropertyStore ps, RestMethodParam existing) {
+                       super(QUERY, firstNonEmpty(a.name(), a.value(), 
existing == null ? null : existing.name), type, getMetaData(a, 
castOrNull(existing, QueryObject.class)));
+                       this.type = type;
+                       this.method = method;
                        this.multiPart = a.multipart();
                        this.partParser = a.parser() == 
HttpPartParser.Null.class ? null : ClassUtils.newInstance(HttpPartParser.class, 
a.parser(), true, ps);
                }
@@ -951,7 +989,14 @@ class RestParamDefaults {
                                .appendSkipEmpty("example", joinnl(a.example()))
                        ;
                }
-
+               
+               @Override /* RestMethodParqm */
+               public void validate() throws InternalServerError {
+                       if (isEmpty(name))
+                               throw new InternalServerError("@Query used 
without name or value on method ''{0}''.", method);
+                       if (multiPart && ! isCollection(type))
+                               throw new InternalServerError("Use of multipart 
flag on @Query parameter that's not an array or Collection on method ''{0}''", 
method);
+               }
        }
 
        static final class HasFormDataObject extends RestMethodParam {
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Path.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Path.java
index 1e304f7..3e4cdf6 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Path.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Path.java
@@ -28,43 +28,7 @@ import org.apache.juneau.rest.*;
  * <p class='bcode'>
  *     <ja>@RestMethod</ja>(name=<jsf>GET</jsf>, 
path=<js>"/myurl/{foo}/{bar}/{baz}/*"</js>)
  *     <jk>public void</jk> doGet(RestRequest req, RestResponse res,
- *                     <ja>@Path</ja> String foo, <ja>@Path</ja> <jk>int</jk> 
bar, <ja>@Path</ja> UUID baz) {
- *             ...
- *     }
- * </p>
- * 
- * <p>
- * The <ja>@Path</ja> annotation is optional if the parameters are specified 
immediately following the
- * <code>RestRequest</code> and <code>RestResponse</code> parameters, and are 
specified in the same order as the
- * variables in the URL path pattern.
- * The following example is equivalent to the previous example.
- * <p class='bcode'>
- *     <ja>@RestMethod</ja>(name=<jsf>GET</jsf>, 
path=<js>"/myurl/{foo}/{bar}/{baz}/*"</js>)
- *     <jk>public void</jk> doGet(RestRequest req, RestResponse res,
- *                     String foo, <jk>int</jk> bar, UUID baz) {
- *             ...
- *     }
- * </p>
- * 
- * <p>
- * If the order of parameters is not the default order shown above, the 
attribute names must be specified (since
- * parameter names are lost during compilation).
- * The following example is equivalent to the previous example, except the 
parameter order has been switched, requiring
- * the use of the <ja>@Path</ja> annotations.
- * <p class='bcode'>
- *     <ja>@RestMethod</ja>(name=<jsf>GET</jsf>, 
path=<js>"/myurl/{foo}/{bar}/{baz}/*"</js>)
- *     <jk>public void</jk> doGet(RestRequest req, RestResponse res,
- *                     <ja>@Path</ja>(<js>"baz"</js>) UUID baz, 
<ja>@Path</ja>(<js>"foo"</js>) String foo, <ja>@Path</ja>(<js>"bar"</js>) 
<jk>int</jk> bar) {
- *             ...
- *     }
- * </p>
- * 
- * <p>
- * You can also use <code>{#}</code> notation to specify path parameters 
without specifying names.
- * <p class='bcode'>
- *     <ja>@RestMethod</ja>(name=<jsf>GET</jsf>, 
path=<js>"/myurl/{0}/{1}/{2}/*"</js>)
- *     <jk>public void</jk> doGet(RestRequest req, RestResponse res,
- *                     <ja>@Path</ja> String foo, <ja>@Path</ja> <jk>int</jk> 
bar, <ja>@Path</ja> UUID baz) {
+ *                     <ja>@Path</ja>(<js>"foo"</js>) String foo, 
<ja>@Path</ja>(<js>"bar"</js>) <jk>int</jk> bar, <ja>@Path</ja>(<js>"baz"</js>) 
UUID baz) {
  *             ...
  *     }
  * </p>
diff --git 
a/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/BasicRestInfoProviderTest.java
 
b/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/BasicRestInfoProviderTest.java
index 933ec19..6a7b997 100644
--- 
a/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/BasicRestInfoProviderTest.java
+++ 
b/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/BasicRestInfoProviderTest.java
@@ -5067,23 +5067,222 @@ public class BasicRestInfoProviderTest {
 
        @RestResource()
        public static class SA {
-//     String name() default "";
-//     String value() default "";
-//     String[] description() default {};
-//     String type() default "";
-//     String format() default "";
-//     String pattern() default "";
-//     String maximum() default "";
-//     String minimum() default "";
-//     String multipleOf() default "";
-//     String maxLength() default "";
-//     String minLength() default "";
-//     String allowEmptyValue() default "";
-//     String exclusiveMaximum() default "";
-//     String exclusiveMinimum() default "";
-//     String[] schema() default {};
-//     String[] _enum() default {};
-//     String[] example() default {};
+
+               @Path(name="P")
+               public static class SA01h {}
+               
+               @RestMethod(name=GET,path="/name/{P}")
+               public Foo sa01(SA01h h) { return null; }
+
+               //      String value() default "";
+               @Path("P")
+               public static class SA02h {}
+               
+               @RestMethod(name=GET,path="/value/{P}")
+               public Foo sa02(SA02h h) { return null; }
+
+               @Path(name="P", description="a")
+               public static class SA03h {}
+               
+               @RestMethod(name=GET,path="/description1/{P}")
+               public Foo sa03(SA03h h) { return null; }
+
+               @Path(name="P", description={"a","b"})
+               public static class SA04h {}
+               
+               @RestMethod(name=GET,path="/description2/{P}")
+               public Foo sa04(SA04h h) { return null; }
+
+               @Path(name="P", type="a")
+               public static class SA05h {}
+               
+               @RestMethod(name=GET,path="/type/{P}")
+               public Foo sa05(SA05h h) { return null; }
+
+               @Path(name="P", format="a")
+               public static class SA06h {}
+               
+               @RestMethod(name=GET,path="/format/{P}")
+               public Foo sa06(SA06h h) { return null; }
+
+               @Path(name="P", pattern="a")
+               public static class SA07h {}
+               
+               @RestMethod(name=GET,path="/pattern/{P}")
+               public Foo sa07(SA07h h) { return null; }
+
+               @Path(name="P", maximum="1")
+               public static class SA08h {}
+               
+               @RestMethod(name=GET,path="/maximum/{P}")
+               public Foo sa08(SA08h h) { return null; }
+
+               @Path(name="P", minimum="1")
+               public static class SA09h {}
+               
+               @RestMethod(name=GET,path="/minimum/{P}")
+               public Foo sa09(SA09h h) { return null; }
+
+               @Path(name="P", multipleOf="1")
+               public static class SA10h {}
+               
+               @RestMethod(name=GET,path="/multipleOf/{P}")
+               public Foo sa10(SA10h h) { return null; }
+
+               @Path(name="P", maxLength="1")
+               public static class SA11h {}
+               
+               @RestMethod(name=GET,path="/maxLength/{P}")
+               public Foo sa11(SA11h h) { return null; }
+
+               @Path(name="P", minLength="1")
+               public static class SA12h {}
+               
+               @RestMethod(name=GET,path="/minLength/{P}")
+               public Foo sa12(SA12h h) { return null; }
+
+               @Path(name="P", allowEmptyValue="true")
+               public static class SA13h {}
+               
+               @RestMethod(name=GET,path="/allowEmptyValue/{P}")
+               public Foo sa13(SA13h h) { return null; }
+
+               @Path(name="P", exclusiveMaximum="true")
+               public static class SA14h {}
+               
+               @RestMethod(name=GET,path="/exclusiveMaximum/{P}")
+               public Foo sa14(SA14h h) { return null; }
+
+               @Path(name="P", exclusiveMinimum="true")
+               public static class SA15h {}
+               
+               @RestMethod(name=GET,path="/exclusiveMinimum/{P}")
+               public Foo sa15(SA15h h) { return null; }
+
+               @Path(name="P", schema=" {type:'a'} ")
+               public static class SA16h {}
+               
+               @RestMethod(name=GET,path="/schema1/{P}")
+               public Foo sa16(SA16h h) { return null; }
+
+               @Path(name="P", schema= {" type:'b' "})
+               public static class SA17h {}
+               
+               @RestMethod(name=GET,path="/schema2/{P}")
+               public Foo sa17(SA17h h) { return null; }
+
+               @Path(name="P", _enum="a,b")
+               public static class SA18h {}
+               
+               @RestMethod(name=GET,path="/_enum1/{P}")
+               public Foo sa18(SA18h h) { return null; }
+
+               @Path(name="P", _enum={" ['a','b'] "})
+               public static class SA19h {}
+               
+               @RestMethod(name=GET,path="/_enum2/{P}")
+               public Foo sa19(SA19h h) { return null; }
+
+               @Path(name="P", example="'a'")
+               public static class SA20h {
+                       public SA20h(String value) {}
+               }
+               
+               @RestMethod(name=GET,path="/example1/{P}")
+               public Foo sa20(SA20h h) { return null; }
+
+               @Path(name="P", example={" {f1:'a'} "})
+               public static class SA21h {
+                       public String f1;
+               }
+               
+               @RestMethod(name=GET,path="/example2/{P}")
+               public Foo sa21(SA21h h) { return null; }
+       }
+
+       @Test
+       public void sa01_Path_onPojo_name() throws Exception {
+               assertEquals("P", getSwagger(new 
SA()).getPaths().get("/name/{P}").get("get").getParameter("path", 
"P").getName());
+       }
+       @Test
+       public void sa02_Path_onPojo_value() throws Exception {
+               assertEquals("P", getSwagger(new 
SA()).getPaths().get("/value/{P}").get("get").getParameter("path", 
"P").getName());
+       }
+       @Test
+       public void sa03_Path_onPojo_description() throws Exception {
+               assertEquals("a", getSwagger(new 
SA()).getPaths().get("/description1/{P}").get("get").getParameter("path", 
"P").getDescription());
+       }
+       @Test
+       public void sa04_Path_onPojo_description() throws Exception {
+               assertEquals("a\nb", getSwagger(new 
SA()).getPaths().get("/description2/{P}").get("get").getParameter("path", 
"P").getDescription());
+       }
+       @Test
+       public void sa05_Path_onPojo_type() throws Exception {
+               assertEquals("a", getSwagger(new 
SA()).getPaths().get("/type/{P}").get("get").getParameter("path", 
"P").getType());
+       }
+       @Test
+       public void sa06_Path_onPojo_format() throws Exception {
+               assertEquals("a", getSwagger(new 
SA()).getPaths().get("/format/{P}").get("get").getParameter("path", 
"P").getFormat());
+       }
+       @Test
+       public void sa07_Path_onPojo_pattern() throws Exception {
+               assertEquals("a", getSwagger(new 
SA()).getPaths().get("/pattern/{P}").get("get").getParameter("path", 
"P").getPattern());
+       }
+       @Test
+       public void sa08_Path_onPojo_maximum() throws Exception {
+               assertObjectEquals("1", getSwagger(new 
SA()).getPaths().get("/maximum/{P}").get("get").getParameter("path", 
"P").getMaximum());
+       }
+       @Test
+       public void sa09_Path_onPojo_minimum() throws Exception {
+               assertObjectEquals("1", getSwagger(new 
SA()).getPaths().get("/minimum/{P}").get("get").getParameter("path", 
"P").getMinimum());
+       }
+       @Test
+       public void sa10_Path_onPojo_multipleOf() throws Exception {
+               assertObjectEquals("1", getSwagger(new 
SA()).getPaths().get("/multipleOf/{P}").get("get").getParameter("path", 
"P").getMultipleOf());
+       }
+       @Test
+       public void sa11_Path_onPojo_maxLength() throws Exception {
+               assertObjectEquals("1", getSwagger(new 
SA()).getPaths().get("/maxLength/{P}").get("get").getParameter("path", 
"P").getMaxLength());
+       }
+       @Test
+       public void sa12_Path_onPojo_minLength() throws Exception {
+               assertObjectEquals("1", getSwagger(new 
SA()).getPaths().get("/minLength/{P}").get("get").getParameter("path", 
"P").getMinLength());
+       }
+       @Test
+       public void sa13_Path_onPojo_allowEmptyValue() throws Exception {
+               assertObjectEquals("true", getSwagger(new 
SA()).getPaths().get("/allowEmptyValue/{P}").get("get").getParameter("path", 
"P").getAllowEmptyValue());
+       }
+       @Test
+       public void sa14_Path_onPojo_exclusiveMaximum() throws Exception {
+               assertObjectEquals("true", getSwagger(new 
SA()).getPaths().get("/exclusiveMaximum/{P}").get("get").getParameter("path", 
"P").getExclusiveMaximum());
+       }
+       @Test
+       public void sa15_Path_onPojo_exclusiveMinimum() throws Exception {
+               assertObjectEquals("true", getSwagger(new 
SA()).getPaths().get("/exclusiveMinimum/{P}").get("get").getParameter("path", 
"P").getExclusiveMinimum());
+       }
+       @Test
+       public void sa16_Path_onPojo_schema1() throws Exception {
+               assertObjectEquals("{type:'a'}", getSwagger(new 
SA()).getPaths().get("/schema1/{P}").get("get").getParameter("path", 
"P").getSchema());
+       }
+       @Test
+       public void sa17_Path_onPojo_schema2() throws Exception {
+               assertObjectEquals("{type:'b'}", getSwagger(new 
SA()).getPaths().get("/schema2/{P}").get("get").getParameter("path", 
"P").getSchema());
+       }
+       @Test
+       public void sa18_Path_onPojo__enum1() throws Exception {
+               assertObjectEquals("['a','b']", getSwagger(new 
SA()).getPaths().get("/_enum1/{P}").get("get").getParameter("path", 
"P").getEnum());
+       }
+       @Test
+       public void sa19_Path_onPojo__enum2() throws Exception {
+               assertObjectEquals("['a','b']", getSwagger(new 
SA()).getPaths().get("/_enum2/{P}").get("get").getParameter("path", 
"P").getEnum());
+       }
+       @Test
+       public void sa20_Path_onPojo_example1() throws Exception {
+               assertObjectEquals("'a'", getSwagger(new 
SA()).getPaths().get("/example1/{P}").get("get").getParameter("path", 
"P").getExample());
+       }
+       @Test
+       public void sa21_Path_onPojo_example2() throws Exception {
+               assertObjectEquals("{f1:'a'}", getSwagger(new 
SA()).getPaths().get("/example2/{P}").get("get").getParameter("path", 
"P").getExample());
        }
 
        
//-----------------------------------------------------------------------------------------------------------------

-- 
To stop receiving notification emails like this one, please contact
[email protected].

Reply via email to