You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2018/05/05 16:30:49 UTC

[juneau] branch master updated: Improvements to @Path annotation.

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 <ja...@apache.org>
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
jamesbognar@apache.org.