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 2017/05/11 21:17:08 UTC

[3/3] incubator-juneau git commit: Code cleanup.

Code cleanup.

Project: http://git-wip-us.apache.org/repos/asf/incubator-juneau/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-juneau/commit/98a53eb3
Tree: http://git-wip-us.apache.org/repos/asf/incubator-juneau/tree/98a53eb3
Diff: http://git-wip-us.apache.org/repos/asf/incubator-juneau/diff/98a53eb3

Branch: refs/heads/master
Commit: 98a53eb3143d1420464b25a5f8289f4dc1a0c495
Parents: 80e8061
Author: JamesBognar <ja...@apache.org>
Authored: Thu May 11 17:17:01 2017 -0400
Committer: JamesBognar <ja...@apache.org>
Committed: Thu May 11 17:17:01 2017 -0400

----------------------------------------------------------------------
 juneau-core/src/main/javadoc/overview.html      | 229 ++++++++++---------
 .../src/main/javadoc/resources/juneau-doc.css   |   1 +
 .../examples/rest/MethodExampleResource.java    | 122 +++++++---
 .../rest/addressbook/AddressBookResource.java   |  16 +-
 .../org/apache/juneau/microservice/package.html |   8 +-
 .../juneau/rest/test/OnPostCallResource.java    |   4 +-
 .../juneau/rest/test/OnPreCallResource.java     |   2 +-
 .../rest/test/OverlappingMethodsResource.java   |   8 +-
 .../apache/juneau/rest/test/ParamsResource.java |  24 +-
 .../apache/juneau/rest/test/PathsResource.java  |   4 +-
 .../apache/juneau/rest/test/UrisResource.java   |   2 +-
 .../java/org/apache/juneau/rest/CallMethod.java |  15 +-
 .../org/apache/juneau/rest/RequestBody.java     |  34 +--
 .../apache/juneau/rest/RequestPathMatch.java    | 213 +++++++++++++++++
 .../apache/juneau/rest/RequestPathParams.java   | 137 -----------
 .../apache/juneau/rest/RestParamDefaults.java   |  12 +-
 .../org/apache/juneau/rest/RestRequest.java     | 185 ++-------------
 .../org/apache/juneau/rest/annotation/Body.java |   2 +-
 .../juneau/rest/converters/Introspectable.java  |   4 +-
 .../juneau/rest/converters/Traversable.java     |   4 +-
 .../java/org/apache/juneau/rest/package.html    |  26 +--
 .../juneau/rest/response/DefaultHandler.java    |   4 +-
 .../org/apache/juneau/rest/vars/RequestVar.java |   8 +-
 23 files changed, 525 insertions(+), 539 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/98a53eb3/juneau-core/src/main/javadoc/overview.html
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/javadoc/overview.html b/juneau-core/src/main/javadoc/overview.html
index 3f5683f..f0bd247 100644
--- a/juneau-core/src/main/javadoc/overview.html
+++ b/juneau-core/src/main/javadoc/overview.html
@@ -3026,106 +3026,123 @@
 		
 		<jd>/** Example GET request that redirects to our example method */</jd> 
 		<ja>@RestMethod</ja>(name=<js>"GET"</js>, path=<js>"/"</js>) 
-		<jk>public</jk> Redirect doGetExample() <jk>throws</jk> Exception { 
-			<jk>return new</jk> Redirect(<js>"example1/xxx/123/{0}/xRemainder?p1=123&amp;p2=yyy"</js>, UUID.<jsm>randomUUID</jsm>()); 
+		<jk>public</jk> Redirect doExample() <jk>throws</jk> Exception { 
+			<jk>return new</jk> Redirect(<js>"example1/xxx/123/{0}/xRemainder?q1=123&amp;q2=yyy"</js>, UUID.<jsm>randomUUID</jsm>()); 
 		} 
 		
-		<jd>/** Example GET request using annotated attributes */</jd> 
-		<ja>@RestMethod</ja>(name=<js>"GET"</js>, path=<js>"/example1/{a1}/{a2}/{a3}/*"</js>, rc={200}) 
-		<jk>public</jk> String doGetExample1( 
-			<ja>@Method</ja> String method, 
-			<ja>@Path</ja> String a1, 
-			<ja>@Path</ja> <jk>int</jk> a2, 
-			<ja>@Path</ja> 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, 
-			<ja>@PathRemainder</ja> String remainder, 
-			<ja>@Header</ja>(<js>"Accept-Language"</js>) String lang, 
-			<ja>@Header</ja>(<js>"Accept"</js>) String accept, 
-			<ja>@Header</ja>(<js>"DNT"</js>) <jk>int</jk> doNotTrack 
-		) { 
-			String output = String.format( 
-				<js>"method=%s, a1=%s, a2=%d, a3=%s, remainder=%s, p1=%d, p2=%s, p3=%s, lang=%s, accept=%s, dnt=%d"</js>, 
-				method, a1, a2, a3, remainder, p1, p2, p3, lang, accept, doNotTrack); 
-			<jk>return</jk> output; 
-		} 
-		
-		<jd>/** Example GET request using methods on RestRequest and RestResponse */</jd> 
-		<ja>@RestMethod</ja>(name=<js>"GET"</js>, path=<js>"/example2/{a1}/{a2}/{a3}/*"</js>, rc={200}) 
-		<jk>public void</jk> doGetExample2(RestRequest req, RestResponse res) <jk>throws</jk> Exception { 
-			String method = req.getMethod(); 
-			
-			<jc>// Attributes (from URL pattern variables)</jc> 
-			RequestPathParams path = req.getPathParams();
-			String a1 = path.get(<js>"a1"</js>, String.<jk>class</jk>); 
-			<jk>int</jk> a2 = path.get(<js>"a2"</js>, <jk>int</jk>.<jk>class</jk>); 
-			UUID a3 = path.get(<js>"a3"</js>, UUID.<jk>class</jk>); 
+		<jd>/** 
+		 * Methodology #1 - GET request using annotated attributes.
+		 * This approach uses annotated parameters for retrieving input.
+		 */</jd>
+		<ja>@RestMethod</ja>(name=<js>"GET"</js>, 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>@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,
+				<ja>@PathRemainder</ja> String remainder,        <jc>// Path remainder after pattern match.</jc>
+				<ja>@Header</ja>(<js>"Accept-Language"</js>) String lang, <jc>// Headers.</jc>
+				<ja>@Header</ja>(<js>"Accept"</js>) String accept,
+				<ja>@Header</ja>(<js>"DNT"</js>) <jk>int</jk> doNotTrack
+			) {
+	
+			<jc>// Send back a simple String response</jc>
+			String output = String.<jsm>format</jsm>(
+					<js>"method=%s, p1=%s, p2=%d, p3=%s, remainder=%s, q1=%d, q2=%s, q3=%s, lang=%s, accept=%s, dnt=%d"</js>,
+					method, p1, p2, p3, remainder, q1, q2, q3, lang, accept, doNotTrack);
+			<jk>return</jk> output;
+		}
+
+		<jd>/** 
+		 * Methodology #2 - GET request using methods on RestRequest and RestResponse.
+		 * This approach uses low-level request/response objects to perform the same as above.
+		 */</jd>
+		<ja>@RestMethod</ja>(name=<js>"GET"</js>, path=<js>"/example2/{p1}/{p2}/{p3}/*"</js>)
+		<jk>public</jk> String example2(
+				RestRequest req,          <jc>// A direct subclass of HttpServletRequest.</jc>
+				RestResponse res          <jc>// A direct subclass of HttpServletResponse.</jc>
+			) {
 			
-			<jc>// Optional GET parameters</jc> 
+			<jc>// HTTP method.</jc>
+			String method = req.getMethod();
+	
+			<jc>// Path variables.</jc>
+			RequestPathMatch path = req.getPathMatch();
+			String p1 = path.get(<js>"p1"</js>, String.<jk>class</jk>);
+			<jk>int</jk> p2 = path.get(<js>"p2"</js>, <jk>int</jk>.<jk>class</jk>);
+			UUID p3 = path.get(<js>"p3"</js>, UUID.<jk>class</jk>);
+	
+			<jc>// Query parameters.</jc>
 			RequestQuery query = req.getQuery();
-			<jk>int</jk> p1 = query.get(<js>"p1"</js>, <jk>int</jk>.<jk>class</jk>, 0); 
-			String p2 = query.get(<js>"p2"</js>, String.<jk>class</jk>); 
-			UUID p3 = query.get(<js>"p3"</js>, UUID.<jk>class</jk>); 
-			
-			<jc>// URL pattern post-match</jc> 
-			String remainder = req.getPathRemainder(); 
-			
-			<jc>// Headers</jc> 
-			String lang = req.getHeader(<js>"Accept-Language"</js>); 
-			<jk>int</jk> doNotTrack = req.getHeader(<js>"DNT"</js>, <jk>int</jk>.<jk>class</jk>); 
-			
-			<jc>// Send back a simple String response</jc> 
-			String output = String.format( 
-				<js>"method=%s, a1=%s, a2=%d, a3=%s, remainder=%s, p1=%d, p2=%s, p3=%s, lang=%s, dnt=%d"</js>, 
-				method, a1, a2, a3, remainder, p1, p2, p3, lang, doNotTrack); 
-			res.setOutput(output); 
-		} 
+			<jk>int</jk> q1 = query.get(<js>"q1"</js>, 0, <jk>int</jk>.<jk>class</jk>);
+			String q2 = query.get(<js>"q2"</js>, String.<jk>class</jk>);
+			UUID q3 = query.get(<js>"q3"</js>, UUID.<jk>class</jk>);
+	
+			<jc>// Path remainder after pattern match.</jc>
+			String remainder = req.getPathMatch().getRemainder();
+	
+			<jc>// Headers.</jc>
+			String lang = req.getHeader(<js>"Accept-Language"</js>);
+			String accept = req.getHeader(<js>"Accept"</js>);
+			<jk>int</jk> doNotTrack = req.getHeaders().get(<js>"DNT"</js>, <jk>int</jk>.<jk>class</jk>);
+	
+			<jc>// Send back a simple String response</jc>
+			String output = String.format(
+					<js>"method=%s, p1=%s, p2=%d, p3=%s, remainder=%s, q1=%d, q2=%s, q3=%s, lang=%s, accept=%s, dnt=%d"</js>,
+					method, p1, p2, p3, remainder, q1, q2, q3, lang, accept, doNotTrack);
+			res.setOutput(output);  <jc>// Or use getWriter().</jc>
+		}
 
-		<jd>/** Example GET request using resolved parameter objects */</jd> 
-		<ja>@RestMethod</ja>(name=<js>"GET"</js>, path=<js>"/example3/{a1}/{a2}/{a3}/*"</js>, rc={200}) 
-		<jk>public void</jk> doGetExample3(
-				HttpMethod httpMethod, 
-				RequestPathParams path, 
-				RequestQuery query,
-				<ja>@PathRemainder</ja> String remainder,
-				AcceptLanguage acceptLanguage,
-				Accept accept,
-				DNT dnt
-			) <jk>throws</jk> Exception { 
-			
-			String method = httpMethod.toString(); 
-			
-			<jc>// Attributes (from URL pattern variables)</jc> 
-			String a1 = path.get(<js>"a1"</js>, String.<jk>class</jk>); 
-			<jk>int</jk> a2 = path.get(<js>"a2"</js>, <jk>int</jk>.<jk>class</jk>); 
-			UUID a3 = path.get(<js>"a3"</js>, UUID.<jk>class</jk>); 
-			
-			<jc>// Optional GET parameters</jc> 
-			<jk>int</jk> p1 = query.get(<js>"p1"</js>, <jk>int</jk>.<jk>class</jk>, 0); 
-			String p2 = query.get(<js>"p2"</js>, String.<jk>class</jk>); 
-			UUID p3 = query.get(<js>"p3"</js>, UUID.<jk>class</jk>); 
-			
-			<jc>// Headers</jc> 
-			String lang = acceptLanguage.toString(); 
-			<jk>int</jk> doNotTrack = dnt.asType(<jk>int</jk>.<jk>class</jk>); 
+		<jd>/** 
+		 * Methodology #3 - GET request using special objects.
+		 * This approach uses intermediate-level APIs.
+		 * The framework recognizes the parameter types and knows how to resolve them.
+		 */</jd>
+		<ja>@RestMethod</ja>(name=<js>"GET"</js>, path=<js>"/example3/{p1}/{p2}/{p3}/*"</js>)
+		<jk>public</jk> String example3(
+				HttpMethod method,           <jc>// HTTP method.</jc>
+				RequestPathMatch path,       <jc>// Path variables.</jc>
+				RequestQuery query,          <jc>// Query parameters.</jc>
+				RequestHeaders headers,      <jc>// Headers.</jc>
+				AcceptLanguage lang,         <jc>// Specific header classes.</jc>
+				Accept accept
+			) {
 			
-			<jc>// Send back a simple String response</jc> 
-			String output = String.format( 
-				<js>"method=%s, a1=%s, a2=%d, a3=%s, remainder=%s, p1=%d, p2=%s, p3=%s, lang=%s, dnt=%d"</js>, 
-				method, a1, a2, a3, remainder, p1, p2, p3, lang, doNotTrack); 
-			res.setOutput(output); 
-		} 
-	} 
+			<jc>// Path variables.</jc>
+			String p1 = path.get(<js>"p1"</js>, String.<jk>class</jk>);
+			<jk>int</jk> p2 = path.get(<js>"p2"</js>, <jk>int</jk>.<jk>class</jk>);
+			UUID p3 = path.get(<js>"p3"</js>, UUID.<jk>class</jk>);
+	
+			<jc>// Query parameters.</jc>
+			<jk>int</jk> q1 = query.get(<js>"q1"</js>, 0, <jk>int</jk>.<jk>class</jk>);
+			String q2 = query.get(<js>"q2"</js>, String.<jk>class</jk>);
+			UUID q3 = query.get(<js>"q3"</js>, UUID.<jk>class</jk>);
+	
+			<jc>// Path remainder after pattern match.</jc>
+			String remainder = path.getRemainder();
+	
+			<jc>// Headers.</jc>
+			int doNotTrack = headers.get(<js>"DNT"</js>, <jk>int</jk>.<jk>class</jk>);
+	
+			<jc>// Send back a simple String response</jc>
+			String output = String.format(
+					<js>"method=%s, p1=%s, p2=%d, p3=%s, remainder=%s, q1=%d, q2=%s, q3=%s, lang=%s, accept=%s, dnt=%d"</js>,
+					method, p1, p2, p3, remainder, q1, q2, q3, lang, accept, doNotTrack);
+			res.setOutput(output);
+		}
+	}
 		</p>
 		<p>
 			The class consists of 4 methods:
 		</p>	
 		<ul class='javahierarchy'>
-			<li class='m'><l>doGetExample()</l>
+			<li class='m'><l>doExample()</l>
 				<br>The root page. 
 				<br>Performs a simple redirection to the <l>doGetExample1()</l> method using a {@link org.apache.juneau.rest.Redirect} object.
-			<li class='m'><l>doGetExample1()</l>
+			<li class='m'><l>example1()</l>
 				<br>Shows how to use the following annotations:
 				<ul>
 					<li class='n'>{@link org.apache.juneau.rest.annotation.Path @Path}
@@ -3135,18 +3152,18 @@
 					<li class='n'>{@link org.apache.juneau.rest.annotation.PathRemainder @PathRemainder}
 				</ul>
 				Method returns a POJO to be serialized as the output.
-			<li class='m'><l>doGetExample2()</l>
+			<li class='m'><l>example2()</l>
 				<br>Identical to <l>doGetExample1()</l> but shows how to use the {@link org.apache.juneau.rest.RestRequest} and {@link org.apache.juneau.rest.RestResponse} objects:
 				<ul>
-					<li class='m'>{@link org.apache.juneau.rest.RestRequest#getPathParams()}
+					<li class='m'>{@link org.apache.juneau.rest.RestRequest#getPathMatch()}
 					<li class='m'>{@link org.apache.juneau.rest.RestRequest#getQuery()}
 					<li class='m'>{@link org.apache.juneau.rest.RestRequest#getFormData()}
 					<li class='m'>{@link org.apache.juneau.rest.RestRequest#getHeaders()}
 					<li class='m'>{@link org.apache.juneau.rest.RestRequest#getMethod()}
-					<li class='m'>{@link org.apache.juneau.rest.RestRequest#getPathRemainder()}
+					<li class='m'>{@link org.apache.juneau.rest.RequestPathMatch#getRemainder()}
 				</ul>
 				Method sets the POJO to be serialized using the {@link org.apache.juneau.rest.RestResponse#setOutput(Object)} method.
-			<li class='m'><l>doGetExample2()</l>
+			<li class='m'><l>example3()</l>
 				<br>Identical to <l>doGetExample1()</l> but uses automatically resolved parameters based on class type.
 				<br>Juneau automatically recognizes specific class types such as common header types and automatically
 					resolves them to objects for you.  
@@ -4103,14 +4120,13 @@
 		<ja>@RestMethod</ja>(name=<js>"PUT"</js>, path=<js>"/people/{id}/*"</js>, 
 			guards=AdminGuard.<jk>class</jk> 
 		) 
-		<jk>public</jk> String updatePerson(RestRequest req, <ja>@Path</ja> <jk>int</jk> id) <jk>throws</jk> Exception { 
+		<jk>public</jk> String updatePerson(RestRequest req, <ja>@Path</ja> <jk>int</jk> id, <ja>@PathRemainder</ja> String remainder) <jk>throws</jk> Exception { 
 			<jk>try</jk> { 
 				Person p = findPerson(id); 
-				String pathRemainder = req.getPathRemainder(); 
 				PojoRest r = <jk>new</jk> PojoRest(p); 
-				ClassMeta&lt;?&gt; cm = r.getClassMeta(pathRemainder); 
-				Object in = req.getBody(cm); 
-				r.put(pathRemainder, in); 
+				ClassMeta&lt;?&gt; cm = r.getClassMeta(remainder); 
+				Object in = req.getBody().asType(cm); 
+				r.put(remainder, in); 
 				<jk>return</jk> <js>"PUT successful"</js>; 
 			} <jk>catch</jk> (Exception e) { 
 				<jk>throw new</jk> RestException(<jsf>SC_BAD_REQUEST</jsf>, <js>"PUT unsuccessful"</js>).initCause(e); 
@@ -4124,14 +4140,13 @@
 		<ja>@RestMethod</ja>(name=<js>"PUT"</js>, 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>throws</jk> Exception { 
+		<jk>public</jk> String updateAddress(RestRequest req, <ja>@Path</ja> <jk>int</jk> id, <ja>@PathRemainder</ja> String remainder) <jk>throws</jk> Exception { 
 			<jk>try</jk> { 
 				Address a = findAddress(id); 
-				String pathInfo = req.getPathInfo(); 
 				PojoRest r = <jk>new</jk> PojoRest(a); 
-				ClassMeta&lt;?&gt; cm = r.getClassMeta(pathInfo); 
-				Object in = req.getBody(cm); 
-				r.put(pathInfo, in); 
+				ClassMeta&lt;?&gt; cm = r.getClassMeta(remainder); 
+				Object in = req.getBody().asType(cm); 
+				r.put(remainder, in); 
 				<jk>return</jk> <js>"PUT successful"</js>; 
 			} <jk>catch</jk> (Exception e) { 
 				<jk>throw new</jk> RestException(<jsf>SC_BAD_REQUEST</jsf>, <js>"PUT unsuccessful"</js>).initCause(e); 
@@ -5969,7 +5984,7 @@
 					<li>{@link org.apache.juneau.rest.RestRequest#getHeaders()} - The request headers.
 					<li>{@link org.apache.juneau.rest.RestRequest#getQuery()} - The request query parameters.
 					<li>{@link org.apache.juneau.rest.RestRequest#getFormData()} - The request form data parameters.
-					<li>{@link org.apache.juneau.rest.RestRequest#getPathParams()} - The path variables.
+					<li>{@link org.apache.juneau.rest.RestRequest#getPathMatch()} - The path variables and remainder.
 				</ul>	
 				The following classes have been introduced:
 				<ul>
@@ -5977,7 +5992,7 @@
 					<li>{@link org.apache.juneau.rest.RequestHeaders}
 					<li>{@link org.apache.juneau.rest.RequestQuery}
 					<li>{@link org.apache.juneau.rest.RequestFormData}
-					<li>{@link org.apache.juneau.rest.RequestPathParams}
+					<li>{@link org.apache.juneau.rest.RequestPathMatch}
 				</ul>						
 			<li>The unannotated parameter types that can be passed in through REST Java methods has been significantly expanded.
 				<br>For reference, the previous supported types were:
@@ -6030,7 +6045,7 @@
 					<li>{@link org.apache.juneau.rest.RequestHeaders} - API for accessing request headers.
 					<li>{@link org.apache.juneau.rest.RequestQuery} - API for accessing request query parameters.
 					<li>{@link org.apache.juneau.rest.RequestFormData} - API for accessing request form data.
-					<li>{@link org.apache.juneau.rest.RequestPathParams} - API for accessing path variables.
+					<li>{@link org.apache.juneau.rest.RequestPathMatch} - API for accessing path variables.
 					<li>{@link org.apache.juneau.rest.RequestBody} - API for accessing request body.
 					<li>{@link org.apache.juneau.http.HttpMethod} - The method name matched (when using <code><ja>@RestMethod</ja>(name=<js>"*"</js>)</code>)
 					<li>{@link java.util.logging.Logger} - The logger to use for logging.
@@ -7502,7 +7517,7 @@
 							<li><code><del>RestRequest.getQueryParameterMap()</del></code>
 							<li><code><del>RestRequest.getQueryParameterNames()</del></code>
 							<li>{@link org.apache.juneau.rest.RestRequest#getPathInfoUndecoded()}
-							<li>{@link org.apache.juneau.rest.RestRequest#getPathRemainderUndecoded()}
+							<li><code><del>RestRequest.getPathRemainderUndecoded()</del></code>
 							<li>{@link org.apache.juneau.rest.RestRequest#getTrimmedRequestURI()}
 							<li>{@link org.apache.juneau.rest.RestRequest#getTrimmedRequestURL()}
 							<li>{@link org.apache.juneau.rest.RestRequest#getServletTitle()}
@@ -7511,8 +7526,8 @@
 						</ul>
 					<li>Behavior changes to {@link org.apache.juneau.rest.RestRequest#getPathInfo()} to follow Servlet specs.
 						Returns <jk>null</jk> instead of blank for no path info.
-					<li>{@link org.apache.juneau.rest.RestRequest#getPathRemainder()} now automatically decodes the path remainder. 
-						Use {@link org.apache.juneau.rest.RestRequest#getPathRemainderUndecoded()} to get the unencoded path remainder.
+					<li><code><del>RestRequest.getPathRemainder()</del></code> now automatically decodes the path remainder. 
+						Use <code><del>RestRequest.getPathRemainderUndecoded()</del></code> to get the unencoded path remainder.
 					<li>Bug fixes in {@link org.apache.juneau.rest.RestRequest#getRequestParentURI()} when servlet is mapped to <js>"/*"</js>.
 					<li>Bug fixes in {@link org.apache.juneau.rest.RestRequest#getServletURI()} when servlet is mapped to <js>"/*"</js>.
 				</ul>

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/98a53eb3/juneau-core/src/main/javadoc/resources/juneau-doc.css
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/javadoc/resources/juneau-doc.css b/juneau-core/src/main/javadoc/resources/juneau-doc.css
index 572b253..1bc7c23 100755
--- a/juneau-core/src/main/javadoc/resources/juneau-doc.css
+++ b/juneau-core/src/main/javadoc/resources/juneau-doc.css
@@ -343,6 +343,7 @@ l {
 p.severe, p.warn, p.info {
 	background-repeat: no-repeat;
 	background-position: left center;
+	background-size: 16px;
 	padding-left: 30px;
     min-height: 24px;
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/98a53eb3/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/MethodExampleResource.java
----------------------------------------------------------------------
diff --git a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/MethodExampleResource.java b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/MethodExampleResource.java
index ba6ce35..4aad593 100644
--- a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/MethodExampleResource.java
+++ b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/MethodExampleResource.java
@@ -14,12 +14,14 @@ package org.apache.juneau.examples.rest;
 
 import java.util.*;
 
+import org.apache.juneau.http.*;
 import org.apache.juneau.microservice.*;
 import org.apache.juneau.rest.*;
 import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.rest.annotation.Method;
 
 /**
- * Sample REST resource that shows how to define REST methods and OPTIONS pages
+ * Sample REST resource that shows how to define REST methods.
  */
 @RestResource(
 	path="/methodExample",
@@ -31,59 +33,111 @@ public class MethodExampleResource extends Resource {
 
 	/** Example GET request that redirects to our example method */
 	@RestMethod(name="GET", path="/")
-	public Redirect doGetExample() throws Exception {
-		return new Redirect("example1/xxx/123/{0}/xRemainder?p1=123&p2=yyy", UUID.randomUUID());
+	public Redirect doExample() throws Exception {
+		return new Redirect("example1/xxx/123/{0}/xRemainder?q1=123&q2=yyy", UUID.randomUUID());
 	}
 
-	/** Example GET request using annotated attributes */
-	@RestMethod(name="GET", path="/example1/{a1}/{a2}/{a3}/*", responses={@Response(200)})
-	public String doGetExample1(
-			@Method String method,
-			@Path String a1,
-			@Path int a2,
-			@Path UUID a3,
-			@Query("p1") int p1,
-			@Query("p2") String p2,
-			@Query("p3") UUID p3,
-			@PathRemainder String remainder,
-			@Header("Accept-Language") String lang,
+	/** 
+	 * Methodology #1 - GET request using annotated attributes.
+	 * This approach uses annotated parameters for retrieving input.
+	 */
+	@RestMethod(name="GET", path="/example1/{p1}/{p2}/{p3}/*")
+	public String example1(
+			@Method String method,                  // HTTP method.
+			@Path String p1,                        // Path variables.
+			@Path int p2,
+			@Path UUID p3,
+			@Query("q1") int q1,                    // Query parameters.
+			@Query("q2") String q2,
+			@Query("q3") UUID q3,
+			@PathRemainder String remainder,        // Path remainder after pattern match.
+			@Header("Accept-Language") String lang, // Headers.
 			@Header("Accept") String accept,
 			@Header("DNT") int doNotTrack
 		) {
+
+		// Send back a simple String response
 		String output = String.format(
-				"method=%s, a1=%s, a2=%d, a3=%s, remainder=%s, p1=%d, p2=%s, p3=%s, lang=%s, accept=%s, dnt=%d",
-				method, a1, a2, a3, remainder, p1, p2, p3, lang, accept, doNotTrack);
+				"method=%s, p1=%s, p2=%d, p3=%s, remainder=%s, q1=%d, q2=%s, q3=%s, lang=%s, accept=%s, dnt=%d",
+				method, p1, p2, p3, remainder, q1, q2, q3, lang, accept, doNotTrack);
 		return output;
 	}
 
-	/** Example GET request using methods on RestRequest and RestResponse */
-	@RestMethod(name="GET", path="/example2/{a1}/{a2}/{a3}/*", responses={@Response(200)})
-	public void doGetExample2(RestRequest req, RestResponse res) throws Exception {
+	/** 
+	 * Methodology #2 - GET request using methods on RestRequest and RestResponse.
+	 * This approach uses low-level request/response objects to perform the same as above.
+	 */
+	@RestMethod(name="GET", path="/example2/{p1}/{p2}/{p3}/*")
+	public void example2(
+			RestRequest req,          // A direct subclass of HttpServletRequest.
+			RestResponse res          // A direct subclass of HttpServletResponse.
+		) throws Exception {
+		
+		// HTTP method.
 		String method = req.getMethod();
 
-		// Attributes (from URL pattern variables)
-		RequestPathParams path = req.getPathParams();
-		String a1 = path.get("a1", String.class);
-		int a2 = path.get("a2", int.class);
-		UUID a3 = path.get("a3", UUID.class);
+		// Path variables.
+		RequestPathMatch path = req.getPathMatch();
+		String p1 = path.get("p1", String.class);
+		int p2 = path.get("p2", int.class);
+		UUID p3 = path.get("p3", UUID.class);
 
-		// Optional GET parameters
+		// Query parameters.
 		RequestQuery query = req.getQuery();
-		int p1 = query.get("p1", 0, int.class);
-		String p2 = query.get("p2", String.class);
-		UUID p3 = query.get("p3", UUID.class);
+		int q1 = query.get("q1", 0, int.class);
+		String q2 = query.get("q2", String.class);
+		UUID q3 = query.get("q3", UUID.class);
 
-		// URL pattern post-match
-		String remainder = req.getPathRemainder();
+		// Path remainder after pattern match.
+		String remainder = req.getPathMatch().getRemainder();
 
-		// Headers
+		// Headers.
 		String lang = req.getHeader("Accept-Language");
+		String accept = req.getHeader("Accept");
 		int doNotTrack = req.getHeaders().get("DNT", int.class);
 
 		// Send back a simple String response
 		String output = String.format(
-				"method=%s, a1=%s, a2=%d, a3=%s, remainder=%s, p1=%d, p2=%s, p3=%s, lang=%s, dnt=%d",
-				method, a1, a2, a3, remainder, p1, p2, p3, lang, doNotTrack);
+				"method=%s, p1=%s, p2=%d, p3=%s, remainder=%s, q1=%d, q2=%s, q3=%s, lang=%s, accept=%s, dnt=%d",
+				method, p1, p2, p3, remainder, q1, q2, q3, lang, accept, doNotTrack);
 		res.setOutput(output);
 	}
+
+	/** 
+	 * Methodology #3 - GET request using special objects.
+	 * This approach uses intermediate-level APIs.
+	 * The framework recognizes the parameter types and knows how to resolve them.
+	 */
+	@RestMethod(name="GET", path="/example3/{p1}/{p2}/{p3}/*")
+	public String example3(
+		HttpMethod method,           // HTTP method.
+		RequestPathMatch path,       // Path variables.
+		RequestQuery query,          // Query parameters.
+		RequestHeaders headers,      // Headers.
+		AcceptLanguage lang,         // Specific header classes.
+		Accept accept
+	) throws Exception {
+
+		// Path variables.
+		String p1 = path.get("p1", String.class);
+		int p2 = path.get("p2", int.class);
+		UUID p3 = path.get("p3", UUID.class);
+
+		// Query parameters.
+		int q1 = query.get("q1", 0, int.class);
+		String q2 = query.get("q2", String.class);
+		UUID q3 = query.get("q3", UUID.class);
+
+		// Path remainder after pattern match.
+		String remainder = path.getRemainder();
+
+		// Headers.
+		int doNotTrack = headers.get("DNT", int.class);
+
+		// Send back a simple String response
+		String output = String.format(
+				"method=%s, p1=%s, p2=%d, p3=%s, remainder=%s, q1=%d, q2=%s, q3=%s, lang=%s, accept=%s, dnt=%d",
+				method, p1, p2, p3, remainder, q1, q2, q3, lang, accept, doNotTrack);
+		return output;
+	}	
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/98a53eb3/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/addressbook/AddressBookResource.java
----------------------------------------------------------------------
diff --git a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/addressbook/AddressBookResource.java b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/addressbook/AddressBookResource.java
index 122d949..5fed44f 100644
--- a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/addressbook/AddressBookResource.java
+++ b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/addressbook/AddressBookResource.java
@@ -203,14 +203,13 @@ public class AddressBookResource extends ResourceJena {
 	@RestMethod(name="PUT", path="/people/{id}/*",
 		guards=AdminGuard.class
 	)
-	public String updatePerson(RestRequest req, @Path int id) throws Exception {
+	public String updatePerson(RequestBody body, @Path int id, @PathRemainder String remainder) throws Exception {
 		try {
 			Person p = findPerson(id);
-			String pathRemainder = req.getPathRemainder();
 			PojoRest r = new PojoRest(p);
-			ClassMeta<?> cm = r.getClassMeta(pathRemainder);
-			Object in = req.getBody().asType(cm);
-			r.put(pathRemainder, in);
+			ClassMeta<?> cm = r.getClassMeta(remainder);
+			Object in = body.asType(cm);
+			r.put(remainder, in);
 			return "PUT successful";
 		} catch (Exception e) {
 			throw new RestException(SC_BAD_REQUEST, "PUT unsuccessful").initCause(e);
@@ -224,14 +223,13 @@ public class AddressBookResource extends ResourceJena {
 	@RestMethod(name="PUT", path="/addresses/{id}/*",
 		guards=AdminGuard.class
 	)
-	public String updateAddress(RestRequest req, @Path int id) throws Exception {
+	public String updateAddress(RestRequest req, @Path int id, @PathRemainder String remainder) throws Exception {
 		try {
 			Address a = findAddress(id);
-			String pathInfo = req.getPathInfo();
 			PojoRest r = new PojoRest(a);
-			ClassMeta<?> cm = r.getClassMeta(pathInfo);
+			ClassMeta<?> cm = r.getClassMeta(remainder);
 			Object in = req.getBody().asType(cm);
-			r.put(pathInfo, in);
+			r.put(remainder, in);
 			return "PUT successful";
 		} catch (Exception e) {
 			throw new RestException(SC_BAD_REQUEST, "PUT unsuccessful").initCause(e);

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/98a53eb3/juneau-microservice/src/main/java/org/apache/juneau/microservice/package.html
----------------------------------------------------------------------
diff --git a/juneau-microservice/src/main/java/org/apache/juneau/microservice/package.html b/juneau-microservice/src/main/java/org/apache/juneau/microservice/package.html
index 2595771..a643da5 100755
--- a/juneau-microservice/src/main/java/org/apache/juneau/microservice/package.html
+++ b/juneau-microservice/src/main/java/org/apache/juneau/microservice/package.html
@@ -652,14 +652,14 @@
 					<ul>
 			 			<li><l>$R{attribute.X}</l> - Value returned by {@link org.apache.juneau.rest.RestRequest#getAttribute(String)} converted to a string.
 			 			<li><l>$R{contextPath}</l> - Value returned by {@link org.apache.juneau.rest.RestRequest#getContextPath()}.
-			 			<li><l>$R{formData.X}</l> - Value returned by {@link org.apache.juneau.rest.RestRequest#getFormData(String)}.
-			 			<li><l>$R{header.X}</l> - Value returned by {@link org.apache.juneau.rest.RestRequest#getHeader(String)}.
+			 			<li><l>$R{formData.X}</l> - Value returned by {@link org.apache.juneau.rest.RequestFormData#getFirst(String)}.
+			 			<li><l>$R{header.X}</l> - Value returned by {@link org.apache.juneau.rest.RequestHeaders#getFirst(String)}.
 			 			<li><l>$R{method}</l> - Value returned by {@link org.apache.juneau.rest.RestRequest#getMethod()}.
 			 			<li><l>$R{methodSummary}</l> - Value returned by {@link org.apache.juneau.rest.RestRequest#getMethodSummary()}.
 			 			<li><l>$R{methodDescription}</l> - Value returned by {@link org.apache.juneau.rest.RestRequest#getMethodDescription()}.
-			 			<li><l>$R{path.X}</l> - Value returned by {@link org.apache.juneau.rest.RestRequest#getPathParameter(String)}.
+			 			<li><l>$R{path.X}</l> - Value returned by {@link org.apache.juneau.rest.RequestPathMatch#get(Object)}.
 			 			<li><l>$R{pathInfo}</l> - Value returned by {@link org.apache.juneau.rest.RestRequest#getPathInfo()}.
-			 			<li><l>$R{query.X}</l> - Value returned by {@link org.apache.juneau.rest.RestRequest#getQuery(String)}.
+			 			<li><l>$R{query.X}</l> - Value returned by {@link org.apache.juneau.rest.RequestQuery#getFirst(String)}.
 			 			<li><l>$R{requestParentURI}</l> - Value returned by {@link org.apache.juneau.rest.RestRequest#getRequestParentURI()}.
 			 			<li><l>$R{requestURI}</l> - Value returned by {@link org.apache.juneau.rest.RestRequest#getRequestURI()}.
 			 			<li><l>$R{servletDescription}</l> - Value returned by {@link org.apache.juneau.rest.RestRequest#getServletDescription()}.

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/98a53eb3/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/OnPostCallResource.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/OnPostCallResource.java b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/OnPostCallResource.java
index c18b5e3..e046779 100644
--- a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/OnPostCallResource.java
+++ b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/OnPostCallResource.java
@@ -63,7 +63,7 @@ public class OnPostCallResource extends RestServlet {
 		properties.put("p5", "xp5"); // New property
 		String overrideAccept = req.getHeader("Override-Accept");
 		if (overrideAccept != null)
-			req.setHeader("Accept", overrideAccept);
+			req.getHeaders().put("Accept", overrideAccept);
 		String overrideContentType = req.getHeader("Override-Content-Type");
 		if (overrideContentType != null)
 			properties.put("Override-Content-Type", overrideContentType);
@@ -93,7 +93,7 @@ public class OnPostCallResource extends RestServlet {
 		properties.put("p4", "pp4");
 		String accept = req.getHeader("Accept");
 		if (accept == null || accept.isEmpty())
-			req.setHeader("Accept", "text/s2");
+			req.getHeaders().put("Accept", "text/s2");
 		return "";
 	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/98a53eb3/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/OnPreCallResource.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/OnPreCallResource.java b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/OnPreCallResource.java
index 9479ab4..feefe2f 100644
--- a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/OnPreCallResource.java
+++ b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/OnPreCallResource.java
@@ -61,7 +61,7 @@ public class OnPreCallResource extends RestServlet {
 		properties.put("p5", "xp5"); // New property
 		String overrideContentType = req.getHeader("Override-Content-Type");
 		if (overrideContentType != null)
-			req.setHeader("Content-Type", overrideContentType);
+			req.getHeaders().put("Content-Type", overrideContentType);
 	}
 
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/98a53eb3/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/OverlappingMethodsResource.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/OverlappingMethodsResource.java b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/OverlappingMethodsResource.java
index c5dfce1..0e943d4 100644
--- a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/OverlappingMethodsResource.java
+++ b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/OverlappingMethodsResource.java
@@ -45,14 +45,14 @@ public class OverlappingMethodsResource extends RestServletDefault {
 	public static class Test1Guard extends RestGuard {
 		@Override /* RestGuard */
 		public boolean isRequestAllowed(RestRequest req) {
-			return req.getQuery("t1","").equals("1");
+			return req.getQuery().getFirst("t1","").equals("1");
 		}
 	}
 
 	public static class Test2Guard extends RestGuard {
 		@Override /* RestGuard */
 		public boolean isRequestAllowed(RestRequest req) {
-			return req.getQuery("t2","").equals("2");
+			return req.getQuery().getFirst("t2","").equals("2");
 		}
 	}
 
@@ -77,14 +77,14 @@ public class OverlappingMethodsResource extends RestServletDefault {
 	public static class Test3aMatcher extends RestMatcher {
 		@Override /* RestMatcher */
 		public boolean matches(RestRequest req) {
-			return req.getQuery("t1","").equals("1");
+			return req.getQuery().getFirst("t1","").equals("1");
 		}
 	}
 
 	public static class Test3bMatcher extends RestMatcher {
 		@Override /* RestMatcher */
 		public boolean matches(RestRequest req) {
-			return req.getQuery("t2","").equals("2");
+			return req.getQuery().getFirst("t2","").equals("2");
 		}
 	}
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/98a53eb3/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ParamsResource.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ParamsResource.java b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ParamsResource.java
index 0c29593..cfd7f15 100644
--- a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ParamsResource.java
+++ b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ParamsResource.java
@@ -68,25 +68,25 @@ public class ParamsResource extends RestServletDefault {
 	public void doGet3(HttpServletRequest reqx, HttpServletResponse resx, String foo, int bar) {
 		RestRequest req = (RestRequest)reqx;
 		RestResponse res = (RestResponse)resx;
-		res.setOutput("GET /get3/"+foo+"/"+bar+" remainder="+req.getPathRemainder());
+		res.setOutput("GET /get3/"+foo+"/"+bar+" remainder="+req.getPathMatch().getRemainder());
 	}
 
 	// Test method name with overlapping name, remainder allowed.
 	@RestMethod(name="GET2")
 	public void get2(RestRequest req, RestResponse res) {
-		res.setOutput("GET2 remainder="+req.getPathRemainder());
+		res.setOutput("GET2 remainder="+req.getPathMatch().getRemainder());
 	}
 
 	// Default POST
 	@RestMethod(name="POST")
 	public void doPost(RestRequest req, RestResponse res) {
-		res.setOutput("POST remainder="+req.getPathRemainder());
+		res.setOutput("POST remainder="+req.getPathMatch().getRemainder());
 	}
 
 	// Bean parameter
 	@RestMethod(name="POST", path="/person/{person}")
 	public void doPost(RestRequest req, RestResponse res, Person p) {
-		res.setOutput("POST /person/{name="+p.name+",birthDate.year="+p.birthDate.get(Calendar.YEAR)+"} remainder="+req.getPathRemainder());
+		res.setOutput("POST /person/{name="+p.name+",birthDate.year="+p.birthDate.get(Calendar.YEAR)+"} remainder="+req.getPathMatch().getRemainder());
 	}
 
 	// Various primitive types
@@ -113,7 +113,7 @@ public class ParamsResource extends RestServletDefault {
 	@RestMethod(name="GET", path="/testParamGet/*")
 	public String testParamGet(RestRequest req, @Query("p1") String p1, @Query("p2") int p2) throws Exception {
 		RequestQuery q = req.getQuery();
-		return "p1=["+p1+","+req.getQuery("p1")+","+q.get("p1", String.class)+"],p2=["+p2+","+q.getFirst("p2")+","+q.get("p2", int.class)+"]";
+		return "p1=["+p1+","+req.getQuery().getFirst("p1")+","+q.get("p1", String.class)+"],p2=["+p2+","+q.getFirst("p2")+","+q.get("p2", int.class)+"]";
 	}
 
 	//====================================================================================================
@@ -122,7 +122,7 @@ public class ParamsResource extends RestServletDefault {
 	@RestMethod(name="POST", path="/testParamPost/*")
 	public String testParamPost(RestRequest req, @FormData("p1") String p1, @FormData("p2") int p2) throws Exception {
 		RequestFormData f = req.getFormData();
-		return "p1=["+p1+","+req.getFormData("p1")+","+f.get("p1", String.class)+"],p2=["+p2+","+req.getFormData("p2")+","+f.get("p2", int.class)+"]";
+		return "p1=["+p1+","+req.getFormData().getFirst("p1")+","+f.get("p1", String.class)+"],p2=["+p2+","+req.getFormData().getFirst("p2")+","+f.get("p2", int.class)+"]";
 	}
 
 	//====================================================================================================
@@ -131,7 +131,7 @@ public class ParamsResource extends RestServletDefault {
 	@RestMethod(name="GET", path="/testQParamGet/*")
 	public String testQParamGet(RestRequest req, @Query("p1") String p1, @Query("p2") int p2) throws Exception {
 		RequestQuery q = req.getQuery();
-		return "p1=["+p1+","+req.getQuery("p1")+","+q.get("p1", String.class)+"],p2=["+p2+","+q.getFirst("p2")+","+q.get("p2", int.class)+"]";
+		return "p1=["+p1+","+req.getQuery().getFirst("p1")+","+q.get("p1", String.class)+"],p2=["+p2+","+q.getFirst("p2")+","+q.get("p2", int.class)+"]";
 	}
 
 	//====================================================================================================
@@ -140,7 +140,7 @@ public class ParamsResource extends RestServletDefault {
 	@RestMethod(name="POST", path="/testQParamPost/*")
 	public String testQParamPost(RestRequest req, @Query("p1") String p1, @Query("p2") int p2) throws Exception {
 		RequestQuery q = req.getQuery();
-		return "p1=["+p1+","+req.getQuery("p1")+","+q.get("p1", String.class)+"],p2=["+p2+","+q.getFirst("p2")+","+q.get("p2", int.class)+"]";
+		return "p1=["+p1+","+req.getQuery().getFirst("p1")+","+q.get("p1", String.class)+"],p2=["+p2+","+q.getFirst("p2")+","+q.get("p2", int.class)+"]";
 	}
 
 	//====================================================================================================
@@ -149,7 +149,7 @@ public class ParamsResource extends RestServletDefault {
 	@RestMethod(name="GET", path="/testPlainParamGet/*")
 	public String testPlainParamGet(RestRequest req, @Query(value="p1",format="PLAIN") String p1) throws Exception {
 		RequestQuery q = req.getQuery();
-		return "p1=["+p1+","+req.getQuery("p1")+","+q.get("p1", String.class)+"]";
+		return "p1=["+p1+","+req.getQuery().getFirst("p1")+","+q.get("p1", String.class)+"]";
 	}
 
 	//====================================================================================================
@@ -158,7 +158,7 @@ public class ParamsResource extends RestServletDefault {
 	@RestMethod(name="POST", path="/testPlainParamPost/*")
 	public String testPlainParamPost(RestRequest req, @FormData(value="p1",format="PLAIN") String p1) throws Exception {
 		RequestFormData f = req.getFormData();
-		return "p1=["+p1+","+req.getFormData("p1")+","+f.get("p1", String.class)+"]";
+		return "p1=["+p1+","+req.getFormData().getFirst("p1")+","+f.get("p1", String.class)+"]";
 	}
 
 	//====================================================================================================
@@ -167,7 +167,7 @@ public class ParamsResource extends RestServletDefault {
 	@RestMethod(name="GET", path="/testPlainQParamGet/*")
 	public String testPlainQParamGet(RestRequest req, @Query(value="p1",format="PLAIN") String p1) throws Exception {
 		RequestQuery q = req.getQuery();
-		return "p1=["+p1+","+req.getQuery("p1")+","+q.get("p1", String.class)+"]";
+		return "p1=["+p1+","+req.getQuery().getFirst("p1")+","+q.get("p1", String.class)+"]";
 	}
 
 	//====================================================================================================
@@ -176,7 +176,7 @@ public class ParamsResource extends RestServletDefault {
 	@RestMethod(name="POST", path="/testPlainQParamPost/*")
 	public String testPlainQParamPost(RestRequest req, @Query(value="p1",format="PLAIN") String p1) throws Exception {
 		RequestQuery q = req.getQuery();
-		return "p1=["+p1+","+req.getQuery("p1")+","+q.get("p1", String.class)+"]";
+		return "p1=["+p1+","+req.getQuery().getFirst("p1")+","+q.get("p1", String.class)+"]";
 	}
 
 	//====================================================================================================

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/98a53eb3/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/PathsResource.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/PathsResource.java b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/PathsResource.java
index c0fc94f..3621c59 100644
--- a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/PathsResource.java
+++ b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/PathsResource.java
@@ -59,8 +59,8 @@ public class PathsResource extends RestServletDefault {
 			.append("pathInfo", req.getPathInfo())
 			.append("pathInfoUndecoded", req.getPathInfoUndecoded())
 			.append("pathInfoParts", req.getPathInfoParts())
-			.append("pathRemainder", req.getPathRemainder())
-			.append("pathRemainderUndecoded", req.getPathRemainderUndecoded())
+			.append("pathRemainder", req.getPathMatch().getRemainder())
+			.append("pathRemainderUndecoded", req.getPathMatch().getRemainderUndecoded())
 			.append("requestURI", req.getRequestURI())
 			.append("requestParentURI", req.getRequestParentURI())
 			.append("requestURL", req.getRequestURL())

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/98a53eb3/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/UrisResource.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/UrisResource.java b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/UrisResource.java
index 8de0852..b5579d2 100644
--- a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/UrisResource.java
+++ b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/UrisResource.java
@@ -106,7 +106,7 @@ public class UrisResource extends RestServletDefault {
 		ObjectMap m = new ObjectMap();
 		m.put("contextPath", req.getContextPath());
 		m.put("pathInfo", req.getPathInfo());
-		m.put("pathRemainder", req.getPathRemainder());
+		m.put("pathRemainder", req.getPathMatch().getRemainder());
 		m.put("pathTranslated", req.getPathTranslated());
 		m.put("requestParentURI", req.getRequestParentURI());
 		m.put("requestURI", req.getRequestURI());

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/98a53eb3/juneau-rest/src/main/java/org/apache/juneau/rest/CallMethod.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/CallMethod.java b/juneau-rest/src/main/java/org/apache/juneau/rest/CallMethod.java
index 4fe77de..869684c 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/CallMethod.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/CallMethod.java
@@ -658,10 +658,11 @@ class CallMethod implements Comparable<CallMethod>  {
 		if (patternVals.length > pathPattern.getVars().length)
 			remainder = patternVals[pathPattern.getVars().length];
 		for (int i = 0; i < pathPattern.getVars().length; i++)
-			req.setPathParameter(pathPattern.getVars()[i], patternVals[i]);
-
+			req.getPathMatch().put(pathPattern.getVars()[i], patternVals[i]);
+		req.getPathMatch().setRemainder(remainder);
+		
 		ObjectMap requestProperties = createRequestProperties(properties, req);
-		req.init(method, remainder, requestProperties, defaultRequestHeaders, defaultCharset, serializers, parsers, urlEncodingParser, encoders, pageTitle, pageText, pageLinks);
+		req.init(method, requestProperties, defaultRequestHeaders, defaultCharset, serializers, parsers, urlEncodingParser, encoders, pageTitle, pageText, pageLinks);
 		res.init(requestProperties, defaultCharset, serializers, urlEncodingSerializer, encoders);
 
 		// Class-level guards
@@ -753,11 +754,11 @@ class CallMethod implements Comparable<CallMethod>  {
 						String prefix = k.substring(0, k.indexOf('.'));
 						String remainder = k.substring(k.indexOf('.')+1);
 						if ("path".equals(prefix))
-							return req.getPathParameter(remainder);
+							return req.getPathMatch().get(remainder);
 						if ("query".equals(prefix))
-							return req.getQuery(remainder);
+							return req.getQuery().get(remainder);
 						if ("formData".equals(prefix))
-							return req.getFormData(remainder);
+							return req.getFormData().get(remainder);
 						if ("header".equals(prefix))
 							return req.getHeader(remainder);
 					}
@@ -792,7 +793,7 @@ class CallMethod implements Comparable<CallMethod>  {
 						return req.getPageText();
 					if (k.equals(HtmlDocSerializerContext.HTMLDOC_links))
 						return req.getPageLinks();
-					o = req.getPathParameter(k);
+					o = req.getPathMatch().get(k);
 					if (o == null)
 						o = req.getHeader(k);
 				}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/98a53eb3/juneau-rest/src/main/java/org/apache/juneau/rest/RequestBody.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RequestBody.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RequestBody.java
index 1b509b4..5663bf3 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/RequestBody.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RequestBody.java
@@ -85,10 +85,10 @@ public class RequestBody {
 	/**
 	 * Reads the input from the HTTP request as JSON, XML, or HTML and converts the input to a POJO.
 	 * <p>
-	 * If {@code allowHeaderParams} init parameter is <jk>true</jk>, then first looks for {@code &body=xxx} in the URL 
+	 * If {@code allowHeaderParams} init parameter is <jk>true</jk>, then first looks for {@code &body=xxx} in the URL
 	 * query string.
 	 * <p>
-	 * If type is <jk>null</jk> or <code>Object.<jk>class</jk></code>, then the actual type will be determined 
+	 * If type is <jk>null</jk> or <code>Object.<jk>class</jk></code>, then the actual type will be determined
 	 * automatically based on the following input:
 	 * <table class='styled'>
 	 * 	<tr><th>Type</th><th>JSON input</th><th>XML input</th><th>Return type</th></tr>
@@ -130,32 +130,32 @@ public class RequestBody {
 	 * 	</tr>
 	 * </table>
 	 * <p>
-	 * Refer to <a class="doclink" href="../../../../overview-summary.html#Core.PojoCategories">POJO Categories</a> for 
+	 * Refer to <a class="doclink" href="../../../../overview-summary.html#Core.PojoCategories">POJO Categories</a> for
 	 * a complete definition of supported POJOs.
 	 * <p>
 	 * <h5 class='section'>Examples:</h5>
 	 * <p class='bcode'>
 	 * 	<jc>// Parse into an integer.</jc>
-	 * 	<jk>int</jk> body = req.getBody(<jk>int</jk>.<jk>class</jk>);
+	 * 	<jk>int</jk> body = req.getBody().asType(<jk>int</jk>.<jk>class</jk>);
 	 *
 	 * 	<jc>// Parse into an int array.</jc>
-	 * 	<jk>int</jk>[] body = req.getBody(<jk>int</jk>[].<jk>class</jk>);
+	 * 	<jk>int</jk>[] body = req.getBody().asType(<jk>int</jk>[].<jk>class</jk>);
 
 	 * 	<jc>// Parse into a bean.</jc>
-	 * 	MyBean body = req.getBody(MyBean.<jk>class</jk>);
+	 * 	MyBean body = req.getBody().asType(MyBean.<jk>class</jk>);
 	 *
 	 * 	<jc>// Parse into a linked-list of objects.</jc>
-	 * 	List body = req.getBody(LinkedList.<jk>class</jk>);
+	 * 	List body = req.getBody().asType(LinkedList.<jk>class</jk>);
 	 *
 	 * 	<jc>// Parse into a map of object keys/values.</jc>
-	 * 	Map body = req.getBody(TreeMap.<jk>class</jk>);
+	 * 	Map body = req.getBody().asType(TreeMap.<jk>class</jk>);
 	 * </p>
 	 *
 	 * @param type The class type to instantiate.
 	 * @param <T> The class type to instantiate.
 	 * @return The input parsed to a POJO.
 	 * @throws IOException If a problem occurred trying to read from the reader.
-	 * @throws ParseException If the input contains a syntax error or is malformed for the requested {@code Accept} 
+	 * @throws ParseException If the input contains a syntax error or is malformed for the requested {@code Accept}
 	 * header or is not valid for the specified type.
 	 */
 	public <T> T asType(Class<T> type) throws IOException, ParseException {
@@ -168,23 +168,23 @@ public class RequestBody {
 	 * <h5 class='section'>Examples:</h5>
 	 * <p class='bcode'>
 	 * 	<jc>// Parse into a linked-list of strings.</jc>
-	 * 	List&lt;String&gt; body = req.getBody(LinkedList.<jk>class</jk>, String.<jk>class</jk>);
+	 * 	List&lt;String&gt; body = req.getBody().asType(LinkedList.<jk>class</jk>, String.<jk>class</jk>);
 	 *
 	 * 	<jc>// Parse into a linked-list of linked-lists of strings.</jc>
-	 * 	List&lt;List&lt;String&gt;&gt; body = req.getBody(LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
+	 * 	List&lt;List&lt;String&gt;&gt; body = req.getBody().asType(LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
 	 *
 	 * 	<jc>// Parse into a map of string keys/values.</jc>
-	 * 	Map&lt;String,String&gt; body = req.getBody(TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>);
+	 * 	Map&lt;String,String&gt; body = req.getBody().asType(TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>);
 	 *
 	 * 	<jc>// Parse into a map containing string keys and values of lists containing beans.</jc>
-	 * 	Map&lt;String,List&lt;MyBean&gt;&gt; body = req.getBody(TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>);
+	 * 	Map&lt;String,List&lt;MyBean&gt;&gt; body = req.getBody().asType(TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>);
 	 * </p>
 	 *
 	 * @param type The type of object to create.
-	 * 	<br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, 
+	 * 	<br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType},
 	 * 	{@link GenericArrayType}
 	 * @param args The type arguments of the class if it's a collection or map.
-	 * 	<br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, 
+	 * 	<br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType},
 	 * 	{@link GenericArrayType}
 	 * 	<br>Ignored if the main type is not a map or collection.
 	 * @param <T> The class type to instantiate.
@@ -197,7 +197,7 @@ public class RequestBody {
 	/**
 	 * Returns the HTTP body content as a plain string.
 	 * <p>
-	 * If {@code allowHeaderParams} init parameter is true, then first looks for {@code &body=xxx} in the URL query 
+	 * If {@code allowHeaderParams} init parameter is true, then first looks for {@code &body=xxx} in the URL query
 	 * string.
 	 *
 	 * @return The incoming input from the connection as a plain string.
@@ -224,7 +224,7 @@ public class RequestBody {
 	/**
 	 * Returns the HTTP body content as a {@link Reader}.
 	 * <p>
-	 * If {@code allowHeaderParams} init parameter is true, then first looks for {@code &body=xxx} in the URL query 
+	 * If {@code allowHeaderParams} init parameter is true, then first looks for {@code &body=xxx} in the URL query
 	 * string.
 	 * <p>
 	 * Automatically handles GZipped input streams.

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/98a53eb3/juneau-rest/src/main/java/org/apache/juneau/rest/RequestPathMatch.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RequestPathMatch.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RequestPathMatch.java
new file mode 100644
index 0000000..de40e53
--- /dev/null
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RequestPathMatch.java
@@ -0,0 +1,213 @@
+// ***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                                                              *
+// *                                                                                                                         *
+// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
+// *                                                                                                                         *
+// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the License.                                              *
+// ***************************************************************************************************************************
+package org.apache.juneau.rest;
+
+import static org.apache.juneau.internal.StringUtils.*;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.urlencoding.*;
+
+/**
+ * Contains information about the matched path on the HTTP request.
+ * <p>
+ * Provides access to the matched path variables and path match remainder.
+ */
+@SuppressWarnings("unchecked")
+public class RequestPathMatch extends TreeMap<String,String> {
+	private static final long serialVersionUID = 1L;
+
+	private UrlEncodingParser parser;
+	private BeanSession beanSession;
+	private String remainder;
+
+	RequestPathMatch() {
+		super(String.CASE_INSENSITIVE_ORDER);
+	}
+
+	RequestPathMatch setParser(UrlEncodingParser parser) {
+		this.parser = parser;
+		return this;
+	}
+
+	RequestPathMatch setBeanSession(BeanSession beanSession) {
+		this.beanSession = beanSession;
+		return this;
+	}
+
+	RequestPathMatch setRemainder(String remainder) {
+		this.remainder = remainder;
+		return this;
+	}
+
+	/**
+	 * Sets a request query parameter value.
+	 *
+	 * @param name The parameter name.
+	 * @param value The parameter value.
+	 */
+	public void put(String name, Object value) {
+		put(name, value);
+	}
+
+	/**
+	 * Returns the specified path parameter converted to a POJO.
+	 * <p>
+	 * The type can be any POJO type convertable from a <code>String</code> (See <a class="doclink"
+	 * href="package-summary.html#PojosConvertableFromString">POJOs Convertable From Strings</a>).
+	 * <p>
+	 * <h5 class='section'>Examples:</h5>
+	 * <p class='bcode'>
+	 * 	<jc>// Parse into an integer.</jc>
+	 * 	<jk>int</jk> myparam = req.getPathParameter(<js>"myparam"</js>, <jk>int</jk>.<jk>class</jk>);
+	 *
+	 * 	<jc>// Parse into an int array.</jc>
+	 * 	<jk>int</jk>[] myparam = req.getPathParameter(<js>"myparam"</js>, <jk>int</jk>[].<jk>class</jk>);
+
+	 * 	<jc>// Parse into a bean.</jc>
+	 * 	MyBean myparam = req.getPathParameter(<js>"myparam"</js>, MyBean.<jk>class</jk>);
+	 *
+	 * 	<jc>// Parse into a linked-list of objects.</jc>
+	 * 	List myparam = req.getPathParameter(<js>"myparam"</js>, LinkedList.<jk>class</jk>);
+	 *
+	 * 	<jc>// Parse into a map of object keys/values.</jc>
+	 * 	Map myparam = req.getPathParameter(<js>"myparam"</js>, TreeMap.<jk>class</jk>);
+	 * </p>
+	 *
+	 * @param name The attribute name.
+	 * @param type The class type to convert the attribute value to.
+	 * @param <T> The class type to convert the attribute value to.
+	 * @return The attribute value converted to the specified class type.
+	 * @throws ParseException
+	 */
+	public <T> T get(String name, Class<T> type) throws ParseException {
+		return parse(name, beanSession.getClassMeta(type));
+	}
+
+	/**
+	 * Returns the specified path parameter converted to a POJO.
+	 * <p>
+	 * The type can be any POJO type convertable from a <code>String</code> (See <a class="doclink" href="package-summary.html#PojosConvertableFromString">POJOs Convertable From Strings</a>).
+	 * <p>
+	 * Use this method if you want to parse into a parameterized <code>Map</code>/<code>Collection</code> object.
+	 * <p>
+	 * <h5 class='section'>Examples:</h5>
+	 * <p class='bcode'>
+	 * 	<jc>// Parse into a linked-list of strings.</jc>
+	 * 	List&lt;String&gt; myparam = req.getPathParameter(<js>"myparam"</js>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
+	 *
+	 * 	<jc>// Parse into a linked-list of linked-lists of strings.</jc>
+	 * 	List&lt;List&lt;String&gt;&gt; myparam = req.getPathParameter(<js>"myparam"</js>, LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
+	 *
+	 * 	<jc>// Parse into a map of string keys/values.</jc>
+	 * 	Map&lt;String,String&gt; myparam = req.getPathParameter(<js>"myparam"</js>, TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>);
+	 *
+	 * 	<jc>// Parse into a map containing string keys and values of lists containing beans.</jc>
+	 * 	Map&lt;String,List&lt;MyBean&gt;&gt; myparam = req.getPathParameter(<js>"myparam"</js>, TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>);
+	 * </p>
+	 *
+	 * @param name The attribute name.
+	 * @param type The type of object to create.
+	 * 	<br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
+	 * @param args The type arguments of the class if it's a collection or map.
+	 * 	<br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
+	 * 	<br>Ignored if the main type is not a map or collection.
+	 * @param <T> The class type to convert the attribute value to.
+	 * @return The attribute value converted to the specified class type.
+	 * @throws ParseException
+	 */
+	public <T> T get(String name, Type type, Type...args) throws ParseException {
+		return (T)parse(name, beanSession.getClassMeta(type, args));
+	}
+
+	/* Workhorse method */
+	<T> T parse(String name, ClassMeta<T> cm) throws ParseException {
+		Object attr = get(name);
+		T t = null;
+		if (attr != null)
+			t = parser.parsePart(attr.toString(), cm);
+		if (t == null && cm.isPrimitive())
+			return cm.getPrimitiveDefault();
+		return t;
+	}
+
+	/**
+	 * Returns the decoded remainder of the URL following any path pattern matches.
+	 * <p>
+	 * The behavior of path remainder is shown below given the path pattern "/foo/*":
+	 * <p>
+	 * <table class='styled'>
+	 * 	<tr>
+	 * 		<th>URL</th>
+	 * 		<th>Path Remainder</th>
+	 * 	</tr>
+	 * 	<tr>
+	 * 		<td><code>/foo</code></td>
+	 * 		<td><jk>null</jk></td>
+	 * 	</tr>
+	 * 	<tr>
+	 * 		<td><code>/foo/</code></td>
+	 * 		<td><js>""</js></td>
+	 * 	</tr>
+	 * 	<tr>
+	 * 		<td><code>/foo//</code></td>
+	 * 		<td><js>"/"</js></td>
+	 * 	</tr>
+	 * 	<tr>
+	 * 		<td><code>/foo///</code></td>
+	 * 		<td><js>"//"</js></td>
+	 * 	</tr>
+	 * 	<tr>
+	 * 		<td><code>/foo/a/b</code></td>
+	 * 		<td><js>"a/b"</js></td>
+	 * 	</tr>
+	 * 	<tr>
+	 * 		<td><code>/foo//a/b/</code></td>
+	 * 		<td><js>"/a/b/"</js></td>
+	 * 	</tr>
+	 * 	<tr>
+	 * 		<td><code>/foo/a%2Fb</code></td>
+	 * 		<td><js>"a/b"</js></td>
+	 * 	</tr>
+	 * </table>
+	 *
+	 * <h5 class='section'>Example:</h5>
+	 * <p class='bcode'>
+	 * 	<jc>// REST method</jc>
+	 * 	<ja>@RestMethod</ja>(name=<js>"GET"</js>,path=<js>"/foo/{bar}/*"</js>)
+	 * 	<jk>public</jk> String doGetById(RequestPathParams pathParams, <jk>int</jk> bar) {
+	 * 		<jk>return</jk> pathParams.getRemainder();
+	 * 	}
+	 *
+	 * 	<jc>// Prints "path/remainder"</jc>
+	 * 	<jk>new</jk> RestCall(servletPath + <js>"/foo/123/path/remainder"</js>).connect();
+	 * </p>
+	 *
+	 * @return The path remainder string.
+	 */
+	public String getRemainder() {
+		return urlDecode(remainder);
+	}
+
+	/**
+	 * Same as {@link #getRemainder()} but doesn't decode characters.
+	 *
+	 * @return The undecoded path remainder.
+	 */
+	public String getRemainderUndecoded() {
+		return remainder;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/98a53eb3/juneau-rest/src/main/java/org/apache/juneau/rest/RequestPathParams.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RequestPathParams.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RequestPathParams.java
deleted file mode 100644
index 8a12a0e..0000000
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/RequestPathParams.java
+++ /dev/null
@@ -1,137 +0,0 @@
-// ***************************************************************************************************************************
-// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
-// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
-// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
-// * with the License.  You may obtain a copy of the License at                                                              *
-// *                                                                                                                         *
-// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
-// *                                                                                                                         *
-// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
-// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
-// * specific language governing permissions and limitations under the License.                                              *
-// ***************************************************************************************************************************
-package org.apache.juneau.rest;
-
-import java.lang.reflect.*;
-import java.util.*;
-
-import org.apache.juneau.*;
-import org.apache.juneau.parser.*;
-import org.apache.juneau.urlencoding.*;
-
-/**
- * Represents the path parameters on an HTTP request.
- */
-@SuppressWarnings("unchecked")
-public class RequestPathParams extends TreeMap<String,String> {
-	private static final long serialVersionUID = 1L;
-
-	private UrlEncodingParser parser;
-	private BeanSession beanSession;
-
-	RequestPathParams() {
-		super(String.CASE_INSENSITIVE_ORDER);
-	}
-
-	RequestPathParams setParser(UrlEncodingParser parser) {
-		this.parser = parser;
-		return this;
-	}
-
-	RequestPathParams setBeanSession(BeanSession beanSession) {
-		this.beanSession = beanSession;
-		return this;
-	}
-
-	/**
-	 * Sets a request query parameter value.
-	 *
-	 * @param name The parameter name.
-	 * @param value The parameter value.
-	 */
-	public void put(String name, Object value) {
-		put(name, value);
-	}
-
-	/**
-	 * Returns the specified path parameter converted to a POJO.
-	 * <p>
-	 * The type can be any POJO type convertable from a <code>String</code> (See <a class="doclink" 
-	 * href="package-summary.html#PojosConvertableFromString">POJOs Convertable From Strings</a>).
-	 * <p>
-	 * <h5 class='section'>Examples:</h5>
-	 * <p class='bcode'>
-	 * 	<jc>// Parse into an integer.</jc>
-	 * 	<jk>int</jk> myparam = req.getPathParameter(<js>"myparam"</js>, <jk>int</jk>.<jk>class</jk>);
-	 *
-	 * 	<jc>// Parse into an int array.</jc>
-	 * 	<jk>int</jk>[] myparam = req.getPathParameter(<js>"myparam"</js>, <jk>int</jk>[].<jk>class</jk>);
-
-	 * 	<jc>// Parse into a bean.</jc>
-	 * 	MyBean myparam = req.getPathParameter(<js>"myparam"</js>, MyBean.<jk>class</jk>);
-	 *
-	 * 	<jc>// Parse into a linked-list of objects.</jc>
-	 * 	List myparam = req.getPathParameter(<js>"myparam"</js>, LinkedList.<jk>class</jk>);
-	 *
-	 * 	<jc>// Parse into a map of object keys/values.</jc>
-	 * 	Map myparam = req.getPathParameter(<js>"myparam"</js>, TreeMap.<jk>class</jk>);
-	 * </p>
-	 *
-	 * @param name The attribute name.
-	 * @param type The class type to convert the attribute value to.
-	 * @param <T> The class type to convert the attribute value to.
-	 * @return The attribute value converted to the specified class type.
-	 * @throws ParseException
-	 */
-	public <T> T get(String name, Class<T> type) throws ParseException {
-		return parse(name, beanSession.getClassMeta(type));
-	}
-
-	/**
-	 * Returns the specified path parameter converted to a POJO.
-	 * <p>
-	 * The type can be any POJO type convertable from a <code>String</code> (See <a class="doclink" href="package-summary.html#PojosConvertableFromString">POJOs Convertable From Strings</a>).
-	 * <p>
-	 * Use this method if you want to parse into a parameterized <code>Map</code>/<code>Collection</code> object.
-	 * <p>
-	 * <h5 class='section'>Examples:</h5>
-	 * <p class='bcode'>
-	 * 	<jc>// Parse into a linked-list of strings.</jc>
-	 * 	List&lt;String&gt; myparam = req.getPathParameter(<js>"myparam"</js>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
-	 *
-	 * 	<jc>// Parse into a linked-list of linked-lists of strings.</jc>
-	 * 	List&lt;List&lt;String&gt;&gt; myparam = req.getPathParameter(<js>"myparam"</js>, LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
-	 *
-	 * 	<jc>// Parse into a map of string keys/values.</jc>
-	 * 	Map&lt;String,String&gt; myparam = req.getPathParameter(<js>"myparam"</js>, TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>);
-	 *
-	 * 	<jc>// Parse into a map containing string keys and values of lists containing beans.</jc>
-	 * 	Map&lt;String,List&lt;MyBean&gt;&gt; myparam = req.getPathParameter(<js>"myparam"</js>, TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>);
-	 * </p>
-	 *
-	 * @param name The attribute name.
-	 * @param type The type of object to create.
-	 * 	<br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
-	 * @param args The type arguments of the class if it's a collection or map.
-	 * 	<br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
-	 * 	<br>Ignored if the main type is not a map or collection.
-	 * @param <T> The class type to convert the attribute value to.
-	 * @return The attribute value converted to the specified class type.
-	 * @throws ParseException
-	 */
-	public <T> T get(String name, Type type, Type...args) throws ParseException {
-		return (T)parse(name, beanSession.getClassMeta(type, args));
-	}
-
-	/* Workhorse method */
-	<T> T parse(String name, ClassMeta<T> cm) throws ParseException {
-		Object attr = get(name);
-		T t = null;
-		if (attr != null)
-			t = parser.parsePart(attr.toString(), cm);
-		if (t == null && cm.isPrimitive())
-			return cm.getPrimitiveDefault();
-		return t;
-	}
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/98a53eb3/juneau-rest/src/main/java/org/apache/juneau/rest/RestParamDefaults.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestParamDefaults.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestParamDefaults.java
index dc29cc6..18d0e2f 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/RestParamDefaults.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestParamDefaults.java
@@ -538,7 +538,7 @@ class RestParamDefaults {
 
 		@Override /* RestParam */
 		public Object resolve(RestRequest req, RestResponse res) throws Exception {
-			return req.getPathParams().get(name, type);
+			return req.getPathMatch().get(name, type);
 		}
 	}
 
@@ -597,7 +597,7 @@ class RestParamDefaults {
 			if (multiPart)
 				return req.getFormData().getAll(name, type);
 			if (plainParams)
-				return bs.convertToType(req.getFormData(name), bs.getClassMeta(type));
+				return bs.convertToType(req.getFormData().getFirst(name), bs.getClassMeta(type));
 			return req.getFormData().get(name, type);
 		}
 	}
@@ -619,7 +619,7 @@ class RestParamDefaults {
 			if (multiPart)
 				return req.getQuery().getAll(name, type);
 			if (plainParams)
-				return bs.convertToType(req.getQuery(name), bs.getClassMeta(type));
+				return bs.convertToType(req.getQuery().getFirst(name), bs.getClassMeta(type));
 			return req.getQuery().get(name, type);
 		}
 	}
@@ -664,7 +664,7 @@ class RestParamDefaults {
 
 		@Override /* RestParam */
 		public Object resolve(RestRequest req, RestResponse res) throws Exception {
-			return req.getPathRemainder();
+			return req.getPathMatch().getRemainder();
 		}
 	}
 
@@ -905,12 +905,12 @@ class RestParamDefaults {
 	static final class RequestPathParamsObject extends RestParam {
 
 		protected RequestPathParamsObject() {
-			super(OTHER, null, RequestPathParams.class);
+			super(OTHER, null, RequestPathMatch.class);
 		}
 
 		@Override /* RestParam */
 		public Object resolve(RestRequest req, RestResponse res) throws Exception {
-			return req.getPathParams();
+			return req.getPathMatch();
 		}
 	}
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/98a53eb3/juneau-rest/src/main/java/org/apache/juneau/rest/RestRequest.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestRequest.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestRequest.java
index dfced43..774fb5c 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/RestRequest.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestRequest.java
@@ -67,7 +67,6 @@ public final class RestRequest extends HttpServletRequestWrapper {
 	private final RestContext context;
 
 	private final String method;
-	private String pathRemainder;
 	private RequestBody body;
 	private Method javaMethod;
 	private ObjectMap properties;
@@ -79,7 +78,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
 	private VarResolverSession varSession;
 	private final RequestQuery queryParams;
 	private RequestFormData formData;
-	private RequestPathParams pathParams;
+	private RequestPathMatch pathParams;
 	private boolean isPost;
 	private String servletURI, relativeServletURI;
 	private String charset, defaultCharset;
@@ -111,7 +110,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
 			// Can be overridden through a "method" GET attribute.
 			String _method = super.getMethod();
 
-			String m = getQuery("method");
+			String m = getQuery().getFirst("method");
 			if (context.allowMethodParam(m))
 				_method = m;
 
@@ -126,7 +125,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
 			body = new RequestBody(this);
 
 			if (context.isAllowBodyParam()) {
-				String b = getQuery("body");
+				String b = getQuery().getFirst("body");
 				if (b != null) {
 					headers.put("Content-Type", UonSerializer.DEFAULT.getResponseContentType());
 					body.load(b.getBytes(IOUtils.UTF8));
@@ -136,9 +135,9 @@ public final class RestRequest extends HttpServletRequestWrapper {
 			if (context.isAllowHeaderParams())
 				headers.setQueryParams(queryParams);
 
-			debug = "true".equals(getQuery("debug", "false")) || "true".equals(getHeader("Debug", "false"));
+			debug = "true".equals(getQuery().getFirst("debug", "false")) || "true".equals(getHeaders().getFirst("Debug", "false"));
 
-			this.pathParams = new RequestPathParams();
+			this.pathParams = new RequestPathMatch();
 
 		} catch (RestException e) {
 			throw e;
@@ -151,9 +150,8 @@ public final class RestRequest extends HttpServletRequestWrapper {
 	 * Called from RestServlet after a match has been made but before the guard or method invocation.
 	 */
 	@SuppressWarnings("hiding")
-	final void init(Method javaMethod, String pathRemainder, ObjectMap properties, Map<String,String> mDefaultRequestHeaders, String defaultCharset, SerializerGroup mSerializers, ParserGroup mParsers, UrlEncodingParser mUrlEncodingParser, EncoderGroup encoders, String pageTitle, String pageText, String pageLinks) {
+	final void init(Method javaMethod, ObjectMap properties, Map<String,String> mDefaultRequestHeaders, String defaultCharset, SerializerGroup mSerializers, ParserGroup mParsers, UrlEncodingParser mUrlEncodingParser, EncoderGroup encoders, String pageTitle, String pageText, String pageLinks) {
 		this.javaMethod = javaMethod;
-		this.pathRemainder = pathRemainder;
 		this.properties = properties;
 		this.urlEncodingParser = mUrlEncodingParser;
 		this.beanSession = urlEncodingParser.getBeanContext().createSession();
@@ -238,35 +236,11 @@ public final class RestRequest extends HttpServletRequestWrapper {
 		return headers;
 	}
 
-	/**
-	 * Convenience method for calling <code>getHeaders().put(name, value);</code>
-	 *
-	 * @param name The header name.
-	 * @param value The header value.
-	 */
-	public void setHeader(String name, Object value) {
-		getHeaders().put(name, value);
-	}
-
-	/**
-	 * Convenience method for calling <code>getHeaders().getFirst(name);</code>
-	 */
 	@Override /* ServletRequest */
 	public String getHeader(String name) {
 		return getHeaders().getFirst(name);
 	}
 
-	/**
-	 * Convenience method for calling <code>getHeaders().getFirst(name, def);</code>
-	 *
-	 * @param name The HTTP header name.
-	 * @param def The default value to return if the header value isn't found.
-	 * @return The header value, or the default value if the header isn't present.
-	 */
-	public String getHeader(String name, String def) {
-		return getHeaders().getFirst(name, def);
-	}
-
 	@Override /* ServletRequest */
 	public Enumeration<String> getHeaders(String name) {
 		String[] v = headers.get(name);
@@ -360,36 +334,14 @@ public final class RestRequest extends HttpServletRequestWrapper {
 	}
 
 	/**
-	 * Convenience method for calling <code>getQueryParams().put(name, value);</code>.
-	 *
-	 * @param name The parameter name.
-	 * @param value The parameter value.
-	 */
-	public void setQuery(String name, Object value) {
-		queryParams.put(name, value);
-	}
-
-	/**
-	 * Convenience method for calling <code>getQueryParams().getFirst(name);</code>
-	 *
-	 * @param name The URL parameter name.
-	 * @return The parameter value, or <jk>null</jk> if parameter not specified or has no value (e.g. <js>"&amp;foo"</js>.
+	 * Shortcut for calling <code>getQuery().getFirst(name)</code>.
+	 * @param name The query parameter name.
+	 * @return The query parameter value, or <jk>null<jk> if not found.
 	 */
 	public String getQuery(String name) {
 		return getQuery().getFirst(name);
 	}
 
-	/**
-	 * Convenience method for calling <code>getQueryParams().getFirst(name, def);</code>
-	 *
-	 * @param name The URL parameter name.
-	 * @param def The default value.
-	 * @return The parameter value, or the default value if parameter not specified or has no value (e.g. <js>"&amp;foo"</js>.
-	 */
-	public String getQuery(String name, String def) {
-		return getQuery().getFirst(name, def);
-	}
-
 
 	//--------------------------------------------------------------------------------
 	// Form data parameters
@@ -421,37 +373,15 @@ public final class RestRequest extends HttpServletRequestWrapper {
 	}
 
 	/**
-	 * Convenience method for calling <code>getFormData().put(name, value);</code>.
-	 *
-	 * @param name The parameter name.
-	 * @param value The parameter value.
-	 */
-	public void setFormData(String name, Object value) {
-		getFormData().put(name, value);
-	}
-
-	/**
-	 * Convenience method for calling <code>getFormData().getFirst(name);</code>.
-	 *
+	 * Shortcut for calling <code>getFormData().getFirst(name)</code>.
 	 * @param name The form data parameter name.
-	 * @return The parameter value, or <jk>null</jk> if parameter does not exist.
+	 * @return The form data parameter value, or <jk>null<jk> if not found.
 	 */
 	public String getFormData(String name) {
 		return getFormData().getFirst(name);
 	}
 
-	/**
-	 * Convenience method for calling <code>getFormData().getFirst(name, def);</code>.
-	 *
-	 * @param name The form data parameter name.
-	 * @param def The default value.
-	 * @return The parameter value, or the default value if <jk>null</jk> or empty.
-	 */
-	public String getFormData(String name, String def) {
-		return getFormData().getFirst(name, def);
-	}
-
-
+	
 	//--------------------------------------------------------------------------------
 	// Path parameters
 	//--------------------------------------------------------------------------------
@@ -461,30 +391,10 @@ public final class RestRequest extends HttpServletRequestWrapper {
 	 *
 	 * @return The URL-encoded form data from the request.
 	 */
-	public RequestPathParams getPathParams() {
+	public RequestPathMatch getPathMatch() {
 		return pathParams;
 	}
 
-	/**
-	 * Convenience method for calling <code>getPathParams().put(name, value);</code>.
-	 *
-	 * @param name The parameter name.
-	 * @param value The parameter value.
-	 */
-	public void setPathParameter(String name, String value) {
-		pathParams.put(name, StringUtils.toString(value));
-	}
-
-	/**
-	 * Convenience method for calling <code>getPathParams().get(name);</code>.
-	 *
-	 * @param name The parameter name.
-	 * @return The parameter value, or <jk>null</jk> if path parameter not specified.
-	 */
-	public String getPathParameter(String name) {
-		return pathParams.get(name);
-	}
-
 
 	//--------------------------------------------------------------------------------
 	// Body methods
@@ -603,73 +513,6 @@ public final class RestRequest extends HttpServletRequestWrapper {
 	}
 
 	/**
-	 * Returns the decoded remainder of the URL following any path pattern matches.
-	 * <p>
-	 * The behavior of path remainder is shown below given the path pattern "/foo/*":
-	 * <p>
-	 * <table class='styled'>
-	 * 	<tr>
-	 * 		<th>URL</th>
-	 * 		<th>Path Remainder</th>
-	 * 	</tr>
-	 * 	<tr>
-	 * 		<td><code>/foo</code></td>
-	 * 		<td><jk>null</jk></td>
-	 * 	</tr>
-	 * 	<tr>
-	 * 		<td><code>/foo/</code></td>
-	 * 		<td><js>""</js></td>
-	 * 	</tr>
-	 * 	<tr>
-	 * 		<td><code>/foo//</code></td>
-	 * 		<td><js>"/"</js></td>
-	 * 	</tr>
-	 * 	<tr>
-	 * 		<td><code>/foo///</code></td>
-	 * 		<td><js>"//"</js></td>
-	 * 	</tr>
-	 * 	<tr>
-	 * 		<td><code>/foo/a/b</code></td>
-	 * 		<td><js>"a/b"</js></td>
-	 * 	</tr>
-	 * 	<tr>
-	 * 		<td><code>/foo//a/b/</code></td>
-	 * 		<td><js>"/a/b/"</js></td>
-	 * 	</tr>
-	 * 	<tr>
-	 * 		<td><code>/foo/a%2Fb</code></td>
-	 * 		<td><js>"a/b"</js></td>
-	 * 	</tr>
-	 * </table>
-	 *
-	 * <h5 class='section'>Example:</h5>
-	 * <p class='bcode'>
-	 * 	<jc>// REST method</jc>
-	 * 	<ja>@RestMethod</ja>(name=<js>"GET"</js>,path=<js>"/foo/{bar}/*"</js>)
-	 * 	<jk>public</jk> doGetById(RestServlet res, RestResponse res, <jk>int</jk> bar) {
-	 * 		System.<jsm>err</jsm>.println(res.getRemainder());
-	 * 	}
-	 *
-	 * 	<jc>// Prints "path/remainder"</jc>
-	 * 	<jk>new</jk> RestCall(servletPath + <js>"/foo/123/path/remainder"</js>).connect();
-	 * </p>
-	 *
-	 * @return The path remainder string.
-	 */
-	public String getPathRemainder() {
-		return urlDecode(pathRemainder);
-	}
-
-	/**
-	 * Same as {@link #getPathRemainder()} but doesn't decode characters.
-	 *
-	 * @return The undecoded path remainder.
-	 */
-	public String getPathRemainderUndecoded() {
-		return pathRemainder;
-	}
-
-	/**
 	 * Returns the URI of the parent resource.
 	 * <p>
 	 * Trailing slashes in the path are ignored by this method.
@@ -935,7 +778,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
 	 * @return <jk>true</jk> if {@code &amp;plainText=true} was specified as a URL parameter
 	 */
 	public boolean isPlainText() {
-		return "true".equals(getQuery("plainText", "false"));
+		return "true".equals(getQuery().getFirst("plainText", "false"));
 	}
 
 	/**

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/98a53eb3/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/Body.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/Body.java b/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/Body.java
index db5dcbe..8641e60 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/Body.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/Body.java
@@ -35,7 +35,7 @@ import java.lang.annotation.*;
  * <p class='bcode'>
  * 	<ja>@RestMethod</ja>(name=<js>"POST"</js>)
  * 	<jk>public void</jk> doPostPerson(RestRequest req, RestResponse res) {
- * 		Person person = req.getBody(Person.<jk>class</jk>);
+ * 		Person person = req.getBody().asType(Person.<jk>class</jk>);
  * 		...
  * 	}
  * </p>

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/98a53eb3/juneau-rest/src/main/java/org/apache/juneau/rest/converters/Introspectable.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/converters/Introspectable.java b/juneau-rest/src/main/java/org/apache/juneau/rest/converters/Introspectable.java
index cf5f16d..f84368e 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/converters/Introspectable.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/converters/Introspectable.java
@@ -40,8 +40,8 @@ public final class Introspectable implements RestConverter {
 	@Override /* RestConverter */
 	@SuppressWarnings({"unchecked", "rawtypes"})
 	public Object convert(RestRequest req, Object o, ClassMeta cm) throws RestException {
-		String method = req.getQuery("invokeMethod");
-		String args = req.getQuery("invokeArgs");
+		String method = req.getQuery().getFirst("invokeMethod");
+		String args = req.getQuery().getFirst("invokeArgs");
 		if (method == null)
 			return o;
 		try {

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/98a53eb3/juneau-rest/src/main/java/org/apache/juneau/rest/converters/Traversable.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/converters/Traversable.java b/juneau-rest/src/main/java/org/apache/juneau/rest/converters/Traversable.java
index 4816453..734a804 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/converters/Traversable.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/converters/Traversable.java
@@ -49,12 +49,12 @@ public final class Traversable implements RestConverter {
 		if (o == null)
 			return null;
 
-		if (req.getPathRemainder() != null) {
+		if (req.getPathMatch().getRemainder() != null) {
 			try {
 				if (cm.getPojoSwap() != null)
 					o = cm.getPojoSwap().swap(req.getBeanSession(), o);
 				PojoRest p = new PojoRest(o, req.getBody().getReaderParser());
-				o = p.get(req.getPathRemainder());
+				o = p.get(req.getPathMatch().getRemainder());
 			} catch (SerializeException e) {
 				throw new RestException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
 			} catch (PojoRestException e) {