Added: 
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestFormData.java
==============================================================================
--- 
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestFormData.java
 (added)
+++ 
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestFormData.java
 Fri Sep  8 23:25:34 2017
@@ -0,0 +1,372 @@
+// 
***************************************************************************************************************************
+// * 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.ArrayUtils.*;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.internal.*;
+import org.apache.juneau.json.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.urlencoding.*;
+
+/**
+ * Represents the parsed form data parameters in an HTTP request.
+ */
+@SuppressWarnings("unchecked")
+public class RequestFormData extends LinkedHashMap<String,String[]> {
+       private static final long serialVersionUID = 1L;
+
+       private UrlEncodingParser parser;
+       private BeanSession beanSession;
+
+       RequestFormData setParser(UrlEncodingParser parser) {
+               this.parser = parser;
+               return this;
+       }
+
+       RequestFormData setBeanSession(BeanSession beanSession) {
+               this.beanSession = beanSession;
+               return this;
+       }
+
+       /**
+        * Adds default entries to these form-data parameters.
+        *
+        * <p>
+        * This includes the default form-data parameters defined on the 
servlet and method levels.
+        *
+        * @param defaultEntries The default entries.  Can be <jk>null</jk>.
+        * @return This object (for method chaining).
+        */
+       public RequestFormData addDefault(Map<String,String> defaultEntries) {
+               if (defaultEntries != null) {
+                       for (Map.Entry<String,String> e : 
defaultEntries.entrySet()) {
+                               String key = e.getKey(), value = e.getValue();
+                               String[] v = get(key);
+                               if (v == null || v.length == 0 || 
StringUtils.isEmpty(v[0]))
+                                       put(key, new String[]{value});
+                       }
+               }
+               return this;
+       }
+
+       /**
+        * Sets a request form data parameter value.
+        *
+        * @param name The parameter name.
+        * @param value The parameter value.
+        */
+       public void put(String name, Object value) {
+               super.put(name, new String[]{StringUtils.toString(value)});
+       }
+
+       /**
+        * Returns a form data parameter value.
+        *
+        * <p>
+        * Parameter lookup is case-insensitive (consistent with WAS, but 
differs from Tomcat).
+        *
+        * <h5 class='section'>Notes:</h5>
+        * <ul>
+        *      <li>Calling this method on URL-Encoded FORM posts causes the 
body content to be loaded and parsed by the
+        *              underlying servlet API.
+        *      <li>This method returns the raw unparsed value, and differs 
from calling
+        *              <code>getFormDataParameter(name, 
String.<jk>class</js>)</code> which will convert the value from UON
+        *              notation:
+        *              <ul>
+        *                      <li><js>"null"</js> =&gt; <jk>null</jk>
+        *                      <li><js>"'null'"</js> =&gt; <js>"null"</js>
+        *                      <li><js>"'foo bar'"</js> =&gt; <js>"foo 
bar"</js>
+        *                      <li><js>"foo~~bar"</js> =&gt; <js>"foo~bar"</js>
+        *              </ul>
+        * </ul>
+        *
+        * @param name The form data parameter name.
+        * @return The parameter value, or <jk>null</jk> if parameter does not 
exist.
+        */
+       public String getString(String name) {
+               String[] v = get(name);
+               if (v == null || v.length == 0)
+                       return null;
+
+               // Fix for behavior difference between Tomcat and WAS.
+               // getParameter("foo") on "&foo" in Tomcat returns "".
+               // getParameter("foo") on "&foo" in WAS returns null.
+               if (v.length == 1 && v[0] == null)
+                       return "";
+
+               return v[0];
+       }
+
+       /**
+        * Same as {@link #getString(String)} except returns a default value if 
<jk>null</jk> or empty.
+        *
+        * @param name The form data parameter name.
+        * @param def The default value.
+        * @return The parameter value, or the default value if parameter does 
not exist or is <jk>null</jk> or empty.
+        */
+       public String getString(String name, String def) {
+               String s = getString(name);
+               return StringUtils.isEmpty(s) ? def : s;
+       }
+
+       /**
+        * Same as {@link #getString(String)} but converts the value to an 
integer.
+        *
+        * @param name The form data parameter name.
+        * @return The parameter value, or <code>0</code> if parameter does not 
exist or is <jk>null</jk> or empty.
+        */
+       public int getInt(String name) {
+               return getInt(name, 0);
+       }
+
+       /**
+        * Same as {@link #getString(String,String)} but converts the value to 
an integer.
+        *
+        * @param name The form data parameter name.
+        * @param def The default value.
+        * @return The parameter value, or the default value if parameter does 
not exist or is <jk>null</jk> or empty.
+        */
+       public int getInt(String name, int def) {
+               String s = getString(name);
+               return StringUtils.isEmpty(s) ? def : Integer.parseInt(s);
+       }
+
+       /**
+        * Same as {@link #getString(String)} but converts the value to a 
boolean.
+        *
+        * @param name The form data parameter name.
+        * @return The parameter value, or <jk>false</jk> if parameter does not 
exist or is <jk>null</jk> or empty.
+        */
+       public boolean getBoolean(String name) {
+               return getBoolean(name, false);
+       }
+
+       /**
+        * Same as {@link #getString(String,String)} but converts the value to 
a boolean.
+        *
+        * @param name The form data parameter name.
+        * @param def The default value.
+        * @return The parameter value, or the default value if parameter does 
not exist or is <jk>null</jk> or empty.
+        */
+       public boolean getBoolean(String name, boolean def) {
+               String s = getString(name);
+               return StringUtils.isEmpty(s) ? def : Boolean.parseBoolean(s);
+       }
+
+       /**
+        * Returns the specified form data parameter value converted to a POJO 
using the {@link UrlEncodingParser}
+        * registered with this servlet.
+        *
+        * <h5 class='section'>Examples:</h5>
+        * <p class='bcode'>
+        *      <jc>// Parse into an integer.</jc>
+        *      <jk>int</jk> myparam = 
req.getFormDataParameter(<js>"myparam"</js>, <jk>int</jk>.<jk>class</jk>);
+        *
+        *      <jc>// Parse into an int array.</jc>
+        *      <jk>int</jk>[] myparam = 
req.getFormDataParameter(<js>"myparam"</js>, <jk>int</jk>[].<jk>class</jk>);
+
+        *      <jc>// Parse into a bean.</jc>
+        *      MyBean myparam = req.getFormDataParameter(<js>"myparam"</js>, 
MyBean.<jk>class</jk>);
+        *
+        *      <jc>// Parse into a linked-list of objects.</jc>
+        *      List myparam = req.getFormDataParameter(<js>"myparam"</js>, 
LinkedList.<jk>class</jk>);
+        *
+        *      <jc>// Parse into a map of object keys/values.</jc>
+        *      Map myparam = req.getFormDataParameter(<js>"myparam"</js>, 
TreeMap.<jk>class</jk>);
+        * </p>
+        *
+        * <h5 class='section'>Notes:</h5>
+        * <ul>
+        *      <li>Calling this method on URL-Encoded FORM posts causes the 
body content to be loaded and parsed by the
+        *              underlying servlet API.
+        * </ul>
+        *
+        * @param name The parameter name.
+        * @param type The class type to convert the parameter value to.
+        * @param <T> The class type to convert the parameter value to.
+        * @return The parameter 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));
+       }
+
+       /**
+        * Same as {@link #get(String, Class)} except returns a default value 
if not specified.
+        *
+        * @param name The parameter name.
+        * @param def The default value if the parameter was not specified or 
is <jk>null</jk>.
+        * @param type The class type to convert the parameter value to.
+        * @param <T> The class type to convert the parameter value to.
+        * @return The parameter value converted to the specified class type.
+        * @throws ParseException
+        */
+       public <T> T get(String name, T def, Class<T> type) throws 
ParseException {
+               return parse(name, def, beanSession.getClassMeta(type));
+       }
+
+       /**
+        * Same as {@link #get(String, Class)} except for use on multi-part 
parameters
+        * (e.g. <js>"key=1&amp;key=2&amp;key=3"</js> instead of 
<js>"key=(1,2,3)"</js>)
+        *
+        * <p>
+        * This method must only be called when parsing into classes of type 
Collection or array.
+        *
+        * @param name The parameter name.
+        * @param type The class type to convert the parameter value to.
+        * @return The parameter value converted to the specified class type.
+        * @throws ParseException
+        */
+       public <T> T getAll(String name, Class<T> type) throws ParseException {
+               return parseAll(name, beanSession.getClassMeta(type));
+       }
+
+       /**
+        * Returns the specified form data parameter value converted to a POJO 
using the {@link UrlEncodingParser}
+        * registered with this servlet.
+        *
+        * <h5 class='section'>Notes:</h5>
+        * <ul>
+        *      <li>Calling this method on URL-Encoded FORM posts causes the 
body content to be loaded and parsed by the
+        *              underlying servlet API.
+        *      <li>Use this method if you want to parse into a parameterized 
<code>Map</code>/<code>Collection</code> object.
+        * </ul>
+        *
+        * <h5 class='section'>Examples:</h5>
+        * <p class='bcode'>
+        *      <jc>// Parse into a linked-list of strings.</jc>
+        *      List&lt;String&gt; myparam = 
req.getFormDataParameter(<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.getFormDataParameter(<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.getFormDataParameter(<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.getFormDataParameter(<js>"myparam"</js>, TreeMap.<jk>class</jk>, 
String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>);
+        * </p>
+        *
+        * @param name The parameter 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.
+        * @return The parameter 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));
+       }
+
+       /**
+        * Same as {@link #get(String, Type, Type...)} except for use on 
multi-part parameters
+        * (e.g. <js>"key=1&amp;key=2&amp;key=3"</js> instead of 
<js>"key=(1,2,3)"</js>)
+        *
+        * <p>
+        * This method must only be called when parsing into classes of type 
Collection or array.
+        *
+        * @param name The parameter 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.
+        * @return The parameter value converted to the specified class type.
+        * @throws ParseException
+        */
+       public <T> T getAll(String name, Type type, Type...args) throws 
ParseException {
+               return (T)parseAll(name, beanSession.getClassMeta(type, args));
+       }
+
+       /* Workhorse method */
+       <T> T parse(String name, T def, ClassMeta<T> cm) throws ParseException {
+               String val = getString(name);
+               if (val == null)
+                       return def;
+               return parseValue(val, cm);
+       }
+
+       /* Workhorse method */
+       <T> T parse(String name, ClassMeta<T> cm) throws ParseException {
+               String val = getString(name);
+               if (cm.isPrimitive() && (val == null || val.isEmpty()))
+                       return cm.getPrimitiveDefault();
+               return parseValue(val, cm);
+       }
+
+       /* Workhorse method */
+       @SuppressWarnings("rawtypes")
+       <T> T parseAll(String name, ClassMeta<T> cm) throws ParseException {
+               String[] p = get(name);
+               if (p == null)
+                       return null;
+               if (cm.isArray()) {
+                       List c = new ArrayList();
+                       for (int i = 0; i < p.length; i++)
+                               c.add(parseValue(p[i], cm.getElementType()));
+                       return (T)toArray(c, 
cm.getElementType().getInnerClass());
+               } else if (cm.isCollection()) {
+                       try {
+                               Collection c = 
(Collection)(cm.canCreateNewInstance() ? cm.newInstance() : new ObjectList());
+                               for (int i = 0; i < p.length; i++)
+                                       c.add(parseValue(p[i], 
cm.getElementType()));
+                               return (T)c;
+                       } catch (ParseException e) {
+                               throw e;
+                       } catch (Exception e) {
+                               // Typically an instantiation exception.
+                               throw new ParseException(e);
+                       }
+               }
+               throw new ParseException("Invalid call to getParameters(String, 
ClassMeta).  Class type must be a Collection or array.");
+       }
+
+       private <T> T parseValue(String val, ClassMeta<T> c) throws 
ParseException {
+               return parser.parse(PartType.FORM_DATA, val, c);
+       }
+
+       /**
+        * Converts the form data parameters to a readable string.
+        *
+        * @param sorted Sort the form data parameters by name.
+        * @return A JSON string containing the contents of the form data 
parameters.
+        */
+       public String toString(boolean sorted) {
+               Map<String,Object> m = (sorted ? new TreeMap<String,Object>() : 
new LinkedHashMap<String,Object>());
+               for (Map.Entry<String,String[]> e : this.entrySet()) {
+                       String[] v = e.getValue();
+                       m.put(e.getKey(), v.length == 1 ? v[0] : v);
+               }
+               return JsonSerializer.DEFAULT_LAX.toString(m);
+       }
+
+       @Override /* Object */
+       public String toString() {
+               return toString(false);
+       }
+}

Propchange: 
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestFormData.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: 
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestHeaders.java
==============================================================================
--- 
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestHeaders.java
 (added)
+++ 
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestHeaders.java
 Fri Sep  8 23:25:34 2017
@@ -0,0 +1,808 @@
+// 
***************************************************************************************************************************
+// * 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.ArrayUtils.*;
+import static org.apache.juneau.internal.StringUtils.*;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.http.*;
+import org.apache.juneau.http.Date;
+import org.apache.juneau.internal.*;
+import org.apache.juneau.json.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.urlencoding.*;
+
+/**
+ * Represents the headers in an HTTP request.
+ *
+ * <p>
+ * Entries are stored in a case-insensitive map.
+ */
+public class RequestHeaders extends TreeMap<String,String[]> {
+       private static final long serialVersionUID = 1L;
+
+       private UrlEncodingParser parser;
+       private BeanSession beanSession;
+       private RequestQuery queryParams;
+
+       RequestHeaders() {
+               super(String.CASE_INSENSITIVE_ORDER);
+       }
+
+       RequestHeaders setParser(UrlEncodingParser parser) {
+               this.parser = parser;
+               return this;
+       }
+
+       RequestHeaders setBeanSession(BeanSession beanSession) {
+               this.beanSession = beanSession;
+               return this;
+       }
+
+       RequestHeaders setQueryParams(RequestQuery queryParams) {
+               this.queryParams = queryParams;
+               return this;
+       }
+
+       /**
+        * Adds default entries to these headers.
+        *
+        * <p>
+        * This includes the default headers defined on the servlet and method 
levels.
+        *
+        * @param defaultEntries The default entries.  Can be <jk>null</jk>.
+        * @return This object (for method chaining).
+        */
+       public RequestHeaders addDefault(Map<String,String> defaultEntries) {
+               if (defaultEntries != null) {
+                       for (Map.Entry<String,String> e : 
defaultEntries.entrySet()) {
+                               String key = e.getKey(), value = e.getValue();
+                               String[] v = get(key);
+                               if (v == null || v.length == 0 || 
StringUtils.isEmpty(v[0]))
+                                       put(key, new String[]{value});
+                       }
+               }
+               return this;
+       }
+
+       /**
+        * Adds a set of header values to this object.
+        *
+        * @param name The header name.
+        * @param values The header values.
+        * @return This object (for method chaining).
+        */
+       public RequestHeaders put(String name, Enumeration<String> values) {
+               // Optimized for enumerations of one entry, the most-common 
case.
+               if (values.hasMoreElements()) {
+                       String v = values.nextElement();
+                       String[] s = new String[]{v};
+                       while (values.hasMoreElements())
+                               s = append(s, values.nextElement());
+                       put(name, s);
+               }
+               return this;
+       }
+
+       /**
+        * Returns the specified header value, or <jk>null</jk> if the header 
doesn't exist.
+        *
+        * <p>
+        * If {@code allowHeaderParams} init parameter is <jk>true</jk>, then 
first looks for {@code &HeaderName=x} in the
+        * URL query string.
+        *
+        * @param name The header name.
+        * @return The header value, or <jk>null</jk> if it doesn't exist.
+        */
+       public String getString(String name) {
+               String[] v = null;
+               if (queryParams != null)
+                       v = queryParams.get(name);
+               if (v == null || v.length == 0)
+                       v = get(name);
+               if (v == null || v.length == 0)
+                       return null;
+               return v[0];
+       }
+
+       /**
+        * Returns the specified header value, or a default value if the header 
doesn't exist.
+        *
+        * <p>
+        * If {@code allowHeaderParams} init parameter is <jk>true</jk>, then 
first looks for {@code &HeaderName=x} in the
+        * URL query string.
+        *
+        * @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 getString(String name, String def) {
+               String s = getString(name);
+               return StringUtils.isEmpty(s) ? def : s;
+       }
+
+       /**
+        * Same as {@link #getString(String)} but converts the value to an 
integer.
+        *
+        * @param name The HTTP header name.
+        * @return The header value, or the default value if the header isn't 
present.
+        */
+       public int getInt(String name) {
+               return getInt(name, 0);
+       }
+
+       /**
+        * Same as {@link #getString(String,String)} but converts the value to 
an integer.
+        *
+        * @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 int getInt(String name, int def) {
+               String s = getString(name);
+               return StringUtils.isEmpty(s) ? def : Integer.parseInt(s);
+       }
+
+       /**
+        * Same as {@link #getString(String)} but converts the value to a 
boolean.
+        *
+        * @param name The HTTP header name.
+        * @return The header value, or the default value if the header isn't 
present.
+        */
+       public boolean getBoolean(String name) {
+               return getBoolean(name, false);
+       }
+
+       /**
+        * Same as {@link #getString(String,String)} but converts the value to 
a boolean.
+        *
+        * @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 boolean getBoolean(String name, boolean def) {
+               String s = getString(name);
+               return StringUtils.isEmpty(s) ? def : Boolean.parseBoolean(s);
+       }
+
+       /**
+        * Sets a request header value.
+        *
+        * @param name The header name.
+        * @param value The header value.
+        */
+       public void put(String name, Object value) {
+               super.put(name, new String[]{StringUtils.toString(value)});
+       }
+
+       /**
+        * Returns the specified header value converted to a POJO.
+        *
+        * <p>
+        * The type can be any POJO type convertible from a <code>String</code>
+        * (See <a class="doclink" 
href="package-summary.html#PojosConvertableFromString">POJOs Convertible From 
Strings</a>).
+        *
+        * <h5 class='section'>Examples:</h5>
+        * <p class='bcode'>
+        *      <jc>// Parse into an integer.</jc>
+        *      <jk>int</jk> myheader = req.getHeader(<js>"My-Header"</js>, 
<jk>int</jk>.<jk>class</jk>);
+        *
+        *      <jc>// Parse a UUID.</jc>
+        *      UUID myheader = req.getHeader(<js>"My-Header"</js>, 
UUID.<jk>class</jk>);
+        * </p>
+        *
+        * @param name The HTTP header name.
+        * @param type The class type to convert the header value to.
+        * @param <T> The class type to convert the header value to.
+        * @return The parameter value converted to the specified class type.
+        */
+       public <T> T get(String name, Class<T> type) {
+               String h = getString(name);
+               return beanSession.convertToType(h, type);
+       }
+
+       /**
+        * Same as {@link #get(String, Class)} but returns a default value if 
not found.
+        *
+        * @param name The HTTP header name.
+        * @param def The default value if the header was not specified or is 
<jk>null</jk>.
+        * @param type The class type to convert the header value to.
+        * @param <T> The class type to convert the header value to.
+        * @return The parameter value converted to the specified class type.
+        */
+       public <T> T get(String name, T def, Class<T> type) {
+               String h = getString(name);
+               if (h == null)
+                       return def;
+               return beanSession.convertToType(h, type);
+       }
+
+       /**
+        * Returns the specified header value converted to a POJO.
+        *
+        * <p>
+        * The type can be any POJO type convertible from a <code>String</code>
+        * (See <a class="doclink" 
href="package-summary.html#PojosConvertableFromString">POJOs Convertible From 
Strings</a>).
+        *
+        * <h5 class='section'>Examples:</h5>
+        * <p class='bcode'>
+        *      <jc>// Parse into a linked-list of strings.</jc>
+        *      List&lt;String&gt; myheader = 
req.getHeader(<js>"My-Header"</js>, LinkedList.<jk>class</jk>, 
String.<jk>class</jk>);
+        * </p>
+        *
+        * @param name The HTTP header 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 header value to.
+        * @return The parameter value converted to the specified class type.
+        * @throws ParseException If the header could not be converted to the 
specified type.
+        */
+       @SuppressWarnings("unchecked")
+       public <T> T get(String name, Type type, Type...args) throws 
ParseException {
+               String h = getString(name);
+               return (T)parser.parse(PartType.HEADER, h, 
beanSession.getClassMeta(type, args));
+       }
+
+       /**
+        * Returns a copy of this object, but only with the specified header 
names copied.
+        *
+        * @param headers The headers to include in the copy.
+        * @return A new headers object.
+        */
+       public RequestHeaders subset(String...headers) {
+               RequestHeaders rh2 = new 
RequestHeaders().setParser(parser).setBeanSession(beanSession).setQueryParams(queryParams);
+               for (String h : headers)
+                       if (containsKey(h))
+                               rh2.put(h, get(h));
+               return rh2;
+       }
+
+       /**
+        * Same as {@link #subset(String...)}, but allows you to specify header 
names as a comma-delimited list.
+        *
+        * @param headers The headers to include in the copy.
+        * @return A new headers object.
+        */
+       public RequestHeaders subset(String headers) {
+               return subset(split(headers));
+       }
+
+       /**
+        * Returns the <code>Accept</code> header on the request.
+        *
+        * <p>
+        * Content-Types that are acceptable for the response.
+        *
+        * <h6 class='figure'>Example:</h6>
+        * <p class='bcode'>
+        *      Accept: text/plain
+        * </p>
+        *
+        * @return The parsed <code>Accept</code> header on the request, or 
<jk>null</jk> if not found.
+        */
+       public Accept getAccept() {
+               return Accept.forString(getString("Accept"));
+       }
+
+       /**
+        * Returns the <code>Accept-Charset</code> header on the request.
+        *
+        * <p>
+        * Character sets that are acceptable.
+        *
+        * <h6 class='figure'>Example:</h6>
+        * <p class='bcode'>
+        *      Accept-Charset: utf-8
+        * </p>
+        *
+        * @return The parsed <code>Accept-Charset</code> header on the 
request, or <jk>null</jk> if not found.
+        */
+       public AcceptCharset getAcceptCharset() {
+               return AcceptCharset.forString(getString("Accept-Charset"));
+       }
+
+       /**
+        * Returns the <code>Accept-Encoding</code> header on the request.
+        *
+        * <p>
+        * List of acceptable encodings.
+        *
+        * <h6 class='figure'>Example:</h6>
+        * <p class='bcode'>
+        *      Accept-Encoding: gzip, deflate
+        * </p>
+        *
+        * @return The parsed <code>Accept-Encoding</code> header on the 
request, or <jk>null</jk> if not found.
+        */
+       public AcceptEncoding getAcceptEncoding() {
+               return AcceptEncoding.forString(getString("Accept-Encoding"));
+       }
+
+       /**
+        * Returns the <code>Accept-Language</code> header on the request.
+        *
+        * <p>
+        * List of acceptable human languages for response.
+        *
+        * <h6 class='figure'>Example:</h6>
+        * <p class='bcode'>
+        *      Accept-Language: en-US
+        * </p>
+        *
+        * @return The parsed <code>Accept-Language</code> header on the 
request, or <jk>null</jk> if not found.
+        */
+       public AcceptLanguage getAcceptLanguage() {
+               return AcceptLanguage.forString(getString("Accept-Language"));
+       }
+
+       /**
+        * Returns the <code>Authorization</code> header on the request.
+        *
+        * <p>
+        * Authentication credentials for HTTP authentication.
+        *
+        * <h6 class='figure'>Example:</h6>
+        * <p class='bcode'>
+        *      Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
+        * </p>
+        *
+        * @return The parsed <code>Authorization</code> header on the request, 
or <jk>null</jk> if not found.
+        */
+       public Authorization getAuthorization() {
+               return Authorization.forString(getString("Authorization"));
+       }
+
+       /**
+        * Returns the <code>Cache-Control</code> header on the request.
+        *
+        * <p>
+        * Used to specify directives that must be obeyed by all caching 
mechanisms along the request-response chain.
+        *
+        * <h6 class='figure'>Example:</h6>
+        * <p class='bcode'>
+        *      Cache-Control: no-cache
+        * </p>
+        *
+        * @return The parsed <code>Cache-Control</code> header on the request, 
or <jk>null</jk> if not found.
+        */
+       public CacheControl getCacheControl() {
+               return CacheControl.forString(getString("Cache-Control"));
+       }
+
+       /**
+        * Returns the <code>Connection</code> header on the request.
+        *
+        * <p>
+        * Control options for the current connection and list of hop-by-hop 
request fields.
+        *
+        * <h6 class='figure'>Example:</h6>
+        * <p class='bcode'>
+        *      Connection: keep-alive
+        *      Connection: Upgrade
+        * </p>
+        *
+        * @return The parsed <code></code> header on the request, or 
<jk>null</jk> if not found.
+        */
+       public Connection getConnection() {
+               return Connection.forString(getString("Connection"));
+       }
+
+       /**
+        * Returns the <code>Content-Length</code> header on the request.
+        *
+        * <p>
+        * The length of the request body in octets (8-bit bytes).
+        *
+        * <h6 class='figure'>Example:</h6>
+        * <p class='bcode'>
+        *      Content-Length: 348
+        * </p>
+        *
+        * @return The parsed <code>Content-Length</code> header on the 
request, or <jk>null</jk> if not found.
+        */
+       public ContentLength getContentLength() {
+               return ContentLength.forString(getString("Content-Length"));
+       }
+
+       /**
+        * Returns the <code>Content-Type</code> header on the request.
+        *
+        * <p>
+        * The MIME type of the body of the request (used with POST and PUT 
requests).
+        *
+        * <h6 class='figure'>Example:</h6>
+        * <p class='bcode'>
+        *      Content-Type: application/x-www-form-urlencoded
+        * </p>
+        *
+        * @return The parsed <code>Content-Type</code> header on the request, 
or <jk>null</jk> if not found.
+        */
+       public ContentType getContentType() {
+               return ContentType.forString(getString("Content-Type"));
+       }
+
+       /**
+        * Returns the <code>Date</code> header on the request.
+        *
+        * <p>
+        * The date and time that the message was originated (in "HTTP-date" 
format as defined by RFC 7231 Date/Time Formats).
+        *
+        * <h6 class='figure'>Example:</h6>
+        * <p class='bcode'>
+        *      Date: Tue, 15 Nov 1994 08:12:31 GMT
+        * </p>
+        *
+        * @return The parsed <code>Date</code> header on the request, or 
<jk>null</jk> if not found.
+        */
+       public Date getDate() {
+               return Date.forString(getString("Date"));
+       }
+
+       /**
+        * Returns the <code>Expect</code> header on the request.
+        *
+        * <p>
+        * Indicates that particular server behaviors are required by the 
client.
+        *
+        * <h6 class='figure'>Example:</h6>
+        * <p class='bcode'>
+        *      Expect: 100-continue
+        * </p>
+        *
+        * @return The parsed <code>Expect</code> header on the request, or 
<jk>null</jk> if not found.
+        */
+       public Expect getExpect() {
+               return Expect.forString(getString("Expect"));
+       }
+
+       /**
+        * Returns the <code>From</code> header on the request.
+        *
+        * <p>
+        * The email address of the user making the request.
+        *
+        * <h6 class='figure'>Example:</h6>
+        * <p class='bcode'>
+        *      From: [email protected]
+        * </p>
+        *
+        * @return The parsed <code>From</code> header on the request, or 
<jk>null</jk> if not found.
+        */
+       public From getFrom() {
+               return From.forString(getString("From"));
+       }
+
+       /**
+        * Returns the <code>Host</code> header on the request.
+        *
+        * <p>
+        * The domain name of the server (for virtual hosting), and the TCP 
port number on which the server is listening.
+        * The port number may be omitted if the port is the standard port for 
the service requested.
+        *
+        * <h6 class='figure'>Example:</h6>
+        * <p class='bcode'>
+        *      Host: en.wikipedia.org:8080
+        *      Host: en.wikipedia.org
+        * </p>
+        *
+        * @return The parsed <code>Host</code> header on the request, or 
<jk>null</jk> if not found.
+        */
+       public Host getHost() {
+               return Host.forString(getString("Host"));
+       }
+
+       /**
+        * Returns the <code>If-Match</code> header on the request.
+        *
+        * <p>
+        * Only perform the action if the client supplied entity matches the 
same entity on the server.
+        * This is mainly for methods like PUT to only update a resource if it 
has not been modified since the user last
+        * updated it.
+        *
+        * <h6 class='figure'>Example:</h6>
+        * <p class='bcode'>
+        *      If-Match: "737060cd8c284d8af7ad3082f209582d"
+        * </p>
+        *
+        * @return The parsed <code>If-Match</code> header on the request, or 
<jk>null</jk> if not found.
+        */
+       public IfMatch getIfMatch() {
+               return IfMatch.forString(getString("If-Match"));
+       }
+
+       /**
+        * Returns the <code>If-Modified-Since</code> header on the request.
+        *
+        * <p>
+        * Allows a 304 Not Modified to be returned if content is unchanged.
+        *
+        * <h6 class='figure'>Example:</h6>
+        * <p class='bcode'>
+        *      If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT
+        * </p>
+        *
+        * @return The parsed <code>If-Modified-Since</code> header on the 
request, or <jk>null</jk> if not found.
+        */
+       public IfModifiedSince getIfModifiedSince() {
+               return 
IfModifiedSince.forString(getString("If-Modified-Since"));
+       }
+
+       /**
+        * Returns the <code>If-None-Match</code> header on the request.
+        *
+        * <p>
+        * Allows a 304 Not Modified to be returned if content is unchanged, 
see HTTP ETag.
+        *
+        * <h6 class='figure'>Example:</h6>
+        * <p class='bcode'>
+        *      If-None-Match: "737060cd8c284d8af7ad3082f209582d"
+        * </p>
+        *
+        * @return The parsed <code>If-None-Match</code> header on the request, 
or <jk>null</jk> if not found.
+        */
+       public IfNoneMatch getIfNoneMatch() {
+               return IfNoneMatch.forString(getString("If-None-Match"));
+       }
+
+       /**
+        * Returns the <code>If-Range</code> header on the request.
+        *
+        * <p>
+        * If the entity is unchanged, send me the part(s) that I am missing; 
otherwise, send me the entire new entity.
+        *
+        * <h6 class='figure'>Example:</h6>
+        * <p class='bcode'>
+        *      If-Range: "737060cd8c284d8af7ad3082f209582d"
+        * </p>
+        *
+        * @return The parsed <code>If-Range</code> header on the request, or 
<jk>null</jk> if not found.
+        */
+       public IfRange getIfRange() {
+               return IfRange.forString(getString("If-Range"));
+       }
+
+       /**
+        * Returns the <code>If-Unmodified-Since</code> header on the request.
+        *
+        * <p>
+        * Only send the response if the entity has not been modified since a 
specific time.
+        *
+        * <h6 class='figure'>Example:</h6>
+        * <p class='bcode'>
+        *      If-Unmodified-Since: Sat, 29 Oct 1994 19:43:31 GMT
+        * </p>
+        *
+        * @return The parsed <code>If-Unmodified-Since</code> header on the 
request, or <jk>null</jk> if not found.
+        */
+       public IfUnmodifiedSince getIfUnmodifiedSince() {
+               return 
IfUnmodifiedSince.forString(getString("If-Unmodified-Since"));
+       }
+
+       /**
+        * Returns the <code>Max-Forwards</code> header on the request.
+        *
+        * <p>
+        * Limit the number of times the message can be forwarded through 
proxies or gateways.
+        *
+        * <h6 class='figure'>Example:</h6>
+        * <p class='bcode'>
+        *      Max-Forwards: 10
+        * </p>
+        *
+        * @return The parsed <code>Max-Forwards</code> header on the request, 
or <jk>null</jk> if not found.
+        */
+       public MaxForwards getMaxForwards() {
+               return MaxForwards.forString(getString("Max-Forwards"));
+       }
+
+       /**
+        * Returns the <code>Pragma</code> header on the request.
+        *
+        * <p>
+        * Implementation-specific fields that may have various effects 
anywhere along the request-response chain.
+        *
+        * <h6 class='figure'>Example:</h6>
+        * <p class='bcode'>
+        *      Pragma: no-cache
+        * </p>
+        *
+        * @return The parsed <code>Pragma</code> header on the request, or 
<jk>null</jk> if not found.
+        */
+       public Pragma getPragma() {
+               return Pragma.forString(getString("Pragma"));
+       }
+
+       /**
+        * Returns the <code>Proxy-Authorization</code> header on the request.
+        *
+        * <p>
+        * Authorization credentials for connecting to a proxy.
+        *
+        * <h6 class='figure'>Example:</h6>
+        * <p class='bcode'>
+        *      Proxy-Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
+        * </p>
+        *
+        * @return The parsed <code>Proxy-Authorization</code> header on the 
request, or <jk>null</jk> if not found.
+        */
+       public ProxyAuthorization getProxyAuthorization() {
+               return 
ProxyAuthorization.forString(getString("Proxy-Authorization"));
+       }
+
+       /**
+        * Returns the <code>Range</code> header on the request.
+        *
+        * <p>
+        * Request only part of an entity. Bytes are numbered from 0.
+        *
+        * <h6 class='figure'>Example:</h6>
+        * <p class='bcode'>
+        *      Range: bytes=500-999
+        * </p>
+        *
+        * @return The parsed <code>Range</code> header on the request, or 
<jk>null</jk> if not found.
+        */
+       public Range getRange() {
+               return Range.forString(getString("Range"));
+       }
+
+       /**
+        * Returns the <code>Referer</code> header on the request.
+        *
+        * <p>
+        * This is the address of the previous web page from which a link to 
the currently requested page was followed.
+        *
+        * <h6 class='figure'>Example:</h6>
+        * <p class='bcode'>
+        *      Referer: http://en.wikipedia.org/wiki/Main_Page
+        * </p>
+        *
+        * @return The parsed <code>Referer</code> header on the request, or 
<jk>null</jk> if not found.
+        */
+       public Referer getReferer() {
+               return Referer.forString(getString("Referer"));
+       }
+
+       /**
+        * Returns the <code>TE</code> header on the request.
+        *
+        * <p>
+        * The transfer encodings the user agent is willing to accept: the same 
values as for the response header field
+        * Transfer-Encoding can be used, plus the "trailers" value (related to 
the "chunked" transfer method) to notify the
+        * server it expects to receive additional fields in the trailer after 
the last, zero-sized, chunk.
+        *
+        * <h6 class='figure'>Example:</h6>
+        * <p class='bcode'>
+        *      TE: trailers, deflate
+        * </p>
+        *
+        * @return The parsed <code>TE</code> header on the request, or 
<jk>null</jk> if not found.
+        */
+       public TE getTE() {
+               return TE.forString(getString("TE"));
+       }
+
+       /**
+        * Returns the <code>Time-Zone</code> header value on the request if 
there is one.
+        *
+        * <p>
+        * Example: <js>"GMT"</js>.
+        *
+        * @return The <code>Time-Zone</code> header value on the request, or 
<jk>null</jk> if not present.
+        */
+       public TimeZone getTimeZone() {
+               String tz = getString("Time-Zone");
+               if (tz != null)
+                       return TimeZone.getTimeZone(tz);
+               return null;
+       }
+
+       /**
+        * Returns the <code>User-Agent</code> header on the request.
+        *
+        * <p>
+        * The user agent string of the user agent.
+        *
+        * <h6 class='figure'>Example:</h6>
+        * <p class='bcode'>
+        *      User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:12.0) 
Gecko/20100101 Firefox/21.0
+        * </p>
+        *
+        * @return The parsed <code>User-Agent</code> header on the request, or 
<jk>null</jk> if not found.
+        */
+       public UserAgent getUserAgent() {
+               return UserAgent.forString(getString("User-Agent"));
+       }
+
+       /**
+        * Returns the <code>Upgrade</code> header on the request.
+        *
+        * <p>
+        * Ask the server to upgrade to another protocol.
+        *
+        * <h6 class='figure'>Example:</h6>
+        * <p class='bcode'>
+        *      Upgrade: HTTP/2.0, HTTPS/1.3, IRC/6.9, RTA/x11, websocket
+        * </p>
+        *
+        * @return The parsed <code>Upgrade</code> header on the request, or 
<jk>null</jk> if not found.
+        */
+       public Upgrade getUpgrade() {
+               return Upgrade.forString(getString("Upgrade"));
+       }
+
+       /**
+        * Returns the <code>Via</code> header on the request.
+        *
+        * <p>
+        * Informs the server of proxies through which the request was sent.
+        *
+        * <h6 class='figure'>Example:</h6>
+        * <p class='bcode'>
+        *      Via: 1.0 fred, 1.1 example.com (Apache/1.1)
+        * </p>
+        *
+        * @return The parsed <code>Via</code> header on the request, or 
<jk>null</jk> if not found.
+        */
+       public Via getVia() {
+               return Via.forString(getString("Via"));
+       }
+
+       /**
+        * Returns the <code>Warning</code> header on the request.
+        *
+        * <p>
+        * A general warning about possible problems with the entity body.
+        *
+        * <h6 class='figure'>Example:</h6>
+        * <p class='bcode'>
+        *      Warning: 199 Miscellaneous warning
+        * </p>
+        *
+        * @return The parsed <code>Warning</code> header on the request, or 
<jk>null</jk> if not found.
+        */
+       public Warning getWarning() {
+               return Warning.forString(getString("Warning"));
+       }
+
+       /**
+        * Converts the headers to a readable string.
+        *
+        * @param sorted Sort the headers by name.
+        * @return A JSON string containing the contents of the headers.
+        */
+       public String toString(boolean sorted) {
+               Map<String,Object> m = (sorted ? new TreeMap<String,Object>() : 
new LinkedHashMap<String,Object>());
+               for (Map.Entry<String,String[]> e : this.entrySet()) {
+                       String[] v = e.getValue();
+                       m.put(e.getKey(), v.length == 1 ? v[0] : v);
+               }
+               return JsonSerializer.DEFAULT_LAX.toString(m);
+       }
+
+       @Override /* Object */
+       public String toString() {
+               return toString(false);
+       }
+}

Propchange: 
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestHeaders.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: 
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestPathMatch.java
==============================================================================
--- 
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestPathMatch.java
 (added)
+++ 
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestPathMatch.java
 Fri Sep  8 23:25:34 2017
@@ -0,0 +1,222 @@
+// 
***************************************************************************************************************************
+// * 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) {
+               super.put(name, value.toString());
+       }
+
+       /**
+        * Returns the specified path parameter converted to a POJO.
+        *
+        * <p>
+        * The type can be any POJO type convertible from a <code>String</code> 
(See <a class="doclink"
+        * href="package-summary.html#PojosConvertibleFromString">POJOs 
Convertible From Strings</a>).
+        *
+        * <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 convertible from a <code>String</code> 
(See <a class="doclink"
+        * href="package-summary.html#PojosConvertibleFromString">POJOs 
Convertible From Strings</a>).
+        *
+        * <p>
+        * Use this method if you want to parse into a parameterized 
<code>Map</code>/<code>Collection</code> object.
+        *
+        * <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.parse(PartType.PATH, 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/*":
+        * <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 un-decoded path remainder.
+        */
+       public String getRemainderUndecoded() {
+               return remainder;
+       }
+}

Propchange: 
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestPathMatch.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: 
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestQuery.java
==============================================================================
--- 
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestQuery.java
 (added)
+++ 
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestQuery.java
 Fri Sep  8 23:25:34 2017
@@ -0,0 +1,505 @@
+// 
***************************************************************************************************************************
+// * 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.ArrayUtils.*;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import javax.servlet.http.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.internal.*;
+import org.apache.juneau.json.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.urlencoding.*;
+import org.apache.juneau.utils.*;
+import org.apache.juneau.xml.*;
+
+/**
+ * Represents the query parameters in an HTTP request.
+ */
+@SuppressWarnings("unchecked")
+public final class RequestQuery extends LinkedHashMap<String,String[]> {
+       private static final long serialVersionUID = 1L;
+
+       private UrlEncodingParser parser;
+       private BeanSession beanSession;
+
+       RequestQuery setParser(UrlEncodingParser parser) {
+               this.parser = parser;
+               return this;
+       }
+
+       RequestQuery setBeanSession(BeanSession beanSession) {
+               this.beanSession = beanSession;
+               return this;
+       }
+
+       /**
+        * Create a copy of the request query parameters.
+        */
+       RequestQuery copy() {
+               RequestQuery rq = new RequestQuery();
+               rq.putAll(this);
+               return rq;
+       }
+
+       /**
+        * Adds default entries to these query parameters.
+        *
+        * <p>
+        * This includes the default queries defined on the servlet and method 
levels.
+        *
+        * @param defaultEntries The default entries.  Can be <jk>null</jk>.
+        * @return This object (for method chaining).
+        */
+       public RequestQuery addDefault(Map<String,String> defaultEntries) {
+               if (defaultEntries != null) {
+                       for (Map.Entry<String,String> e : 
defaultEntries.entrySet()) {
+                               String key = e.getKey(), value = e.getValue();
+                               String[] v = get(key);
+                               if (v == null || v.length == 0 || 
StringUtils.isEmpty(v[0]))
+                                       put(key, new String[]{value});
+                       }
+               }
+               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, new String[]{StringUtils.toString(value)});
+       }
+
+       /**
+        * Returns a query parameter value.
+        *
+        * <p>
+        * Same as {@link HttpServletRequest#getParameter(String)} except only 
looks in the URL string, not parameters from
+        * URL-Encoded FORM posts.
+        *
+        * <p>
+        * This method can be used to retrieve a parameter without triggering 
the underlying servlet API to load and parse
+        * the request body.
+        *
+        * <p>
+        * If multiple query parameters have the same name, this returns only 
the first instance.
+        *
+        * @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>.
+        */
+       public String getString(String name) {
+               String[] v = get(name);
+               if (v == null || v.length == 0)
+                       return null;
+
+               // Fix for behavior difference between Tomcat and WAS.
+               // getParameter("foo") on "&foo" in Tomcat returns "".
+               // getParameter("foo") on "&foo" in WAS returns null.
+               if (v.length == 1 && v[0] == null)
+                       return "";
+
+               return v[0];
+       }
+
+       /**
+        * Same as {@link #getString(String)} but returns the specified default 
value if the query parameter was not
+        * specified.
+        *
+        * @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 getString(String name, String def) {
+               String s = getString(name);
+               return StringUtils.isEmpty(s) ? def : s;
+       }
+
+       /**
+        * Same as {@link #getString(String)} but converts the value to an 
integer.
+        *
+        * @param name The URL parameter name.
+        * @return
+        *      The parameter value, or <code>0</code> if parameter not 
specified or has no value
+        *      (e.g. <js>"&amp;foo"</js>.
+        */
+       public int getInt(String name) {
+               return getInt(name, 0);
+       }
+
+       /**
+        * Same as {@link #getString(String,String)} but converts the value to 
an integer.
+        *
+        * @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 int getInt(String name, int def) {
+               String s = getString(name);
+               return StringUtils.isEmpty(s) ? def : Integer.parseInt(s);
+       }
+
+       /**
+        * Same as {@link #getString(String)} but converts the value to a 
boolean.
+        *
+        * @param name The URL parameter name.
+        * @return
+        *      The parameter value, or <jk>false</jk> if parameter not 
specified or has no value
+        *      (e.g. <js>"&amp;foo"</js>.
+        */
+       public boolean getBoolean(String name) {
+               return getBoolean(name, false);
+       }
+
+       /**
+        * Same as {@link #getString(String,String)} but converts the value to 
a boolean.
+        *
+        * @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 boolean getBoolean(String name, boolean def) {
+               String s = getString(name);
+               return StringUtils.isEmpty(s) ? def : Boolean.parseBoolean(s);
+       }
+
+       /**
+        * Returns the specified query parameter value converted to a POJO.
+        *
+        * <p>
+        * This method can be used to retrieve a parameter without triggering 
the underlying servlet API to load and parse
+        * the request body.
+        *
+        * <h5 class='section'>Examples:</h5>
+        * <p class='bcode'>
+        *      <jc>// Parse into an integer.</jc>
+        *      <jk>int</jk> myparam = 
req.getQueryParameter(<js>"myparam"</js>, <jk>int</jk>.<jk>class</jk>);
+        *
+        *      <jc>// Parse into an int array.</jc>
+        *      <jk>int</jk>[] myparam = 
req.getQueryParameter(<js>"myparam"</js>, <jk>int</jk>[].<jk>class</jk>);
+
+        *      <jc>// Parse into a bean.</jc>
+        *      MyBean myparam = req.getQueryParameter(<js>"myparam"</js>, 
MyBean.<jk>class</jk>);
+        *
+        *      <jc>// Parse into a linked-list of objects.</jc>
+        *      List myparam = req.getQueryParameter(<js>"myparam"</js>, 
LinkedList.<jk>class</jk>);
+        *
+        *      <jc>// Parse into a map of object keys/values.</jc>
+        *      Map myparam = req.getQueryParameter(<js>"myparam"</js>, 
TreeMap.<jk>class</jk>);
+        * </p>
+        *
+        * @param name The parameter name.
+        * @param type The class type to convert the parameter value to.
+        * @param <T> The class type to convert the parameter value to.
+        * @return The parameter value converted to the specified class type.
+        * @throws ParseException
+        */
+       public <T> T get(String name, Class<T> type) throws ParseException {
+               return get(name, beanSession.getClassMeta(type));
+       }
+
+       /**
+        * Same as {@link #get(String, Class)} except returns a default value 
if not found.
+        *
+        * @param name The parameter name.
+        * @param def The default value if the parameter was not specified or 
is <jk>null</jk>.
+        * @param type The class type to convert the parameter value to.
+        * @param <T> The class type to convert the parameter value to.
+        * @return The parameter value converted to the specified class type.
+        * @throws ParseException
+        */
+       public <T> T get(String name, T def, Class<T> type) throws 
ParseException {
+               return get(name, def, beanSession.getClassMeta(type));
+       }
+
+       /**
+        * Returns the specified query parameter value converted to a POJO.
+        *
+        * <p>
+        * This method can be used to retrieve a parameter without triggering 
the underlying servlet API to load and parse
+        * the request body.
+        *
+        * <p>
+        * Use this method if you want to parse into a parameterized 
<code>Map</code>/<code>Collection</code> object.
+        *
+        * <h5 class='section'>Examples:</h5>
+        * <p class='bcode'>
+        *      <jc>// Parse into a linked-list of strings.</jc>
+        *      List&lt;String&gt; myparam = 
req.getQueryParameter(<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.getQueryParameter(<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.getQueryParameter(<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.getQueryParameter(<js>"myparam"</js>, TreeMap.<jk>class</jk>, 
String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>);
+        * </p>
+        *
+        * @param name The parameter 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 parameter value to.
+        * @return The parameter 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));
+       }
+
+       /**
+        * Same as {@link #get(String, Class)} except returns a default value 
if not found.
+        *
+        * @param name The parameter 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 def The default value if the parameter was not specified or 
is <jk>null</jk>.
+        * @param <T> The class type to convert the parameter value to.
+        * @return The parameter value converted to the specified class type.
+        * @throws ParseException
+        */
+       public <T> T get(String name, Object def, Type type, Type...args) 
throws ParseException {
+               return (T)parse(name, def, beanSession.getClassMeta(type, 
args));
+       }
+
+       /**
+        * Same as {@link #get(String, Class)} except for use on multi-part 
parameters
+        * (e.g. <js>"&amp;key=1&amp;key=2&amp;key=3"</js> instead of 
<js>"&amp;key=(1,2,3)"</js>).
+        *
+        * <p>
+        * This method must only be called when parsing into classes of type 
Collection or array.
+        *
+        * @param name The query parameter name.
+        * @param c The class type to convert the parameter value to.
+        * @param <T> The class type to convert the parameter value to.
+        * @return The query parameter value converted to the specified class 
type.
+        * @throws ParseException
+        */
+       public <T> T getAll(String name, Class<T> c) throws ParseException {
+               return getAll(name, beanSession.getClassMeta(c));
+       }
+
+       /**
+        * Same as {@link #get(String, Type, Type...)} except for use on 
multi-part parameters
+        * (e.g. <js>"&amp;key=1&amp;key=2&amp;key=3"</js> instead of 
<js>"&amp;key=(1,2,3)"</js>).
+        *
+        * <p>
+        * This method must only be called when parsing into classes of type 
Collection or array.
+        *
+        * @param name The query parameter 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 parameter value to.
+        * @return The query parameter value converted to the specified class 
type.
+        * @throws ParseException
+        */
+       public <T> T getAll(String name, Type type, Type...args) throws 
ParseException {
+               return (T)parseAll(name, beanSession.getClassMeta(type, args));
+       }
+
+       /**
+        * Returns <jk>true</jk> if the request contains any of the specified 
query parameters.
+        *
+        * @param params The list of parameters to check for.
+        * @return <jk>true</jk> if the request contains any of the specified 
query parameters.
+        */
+       public boolean containsAnyKeys(String...params) {
+               for (String p : params)
+                       if (containsKey(p))
+                               return true;
+               return false;
+       }
+
+       /**
+        * Locates the special search query arguments in the query and returns 
them as a {@link SearchArgs} object.
+        *
+        * <p>
+        * The query arguments are as follows:
+        * <ul>
+        *      <li>
+        *              <js>"&amp;s="</js> - A comma-delimited list of 
column-name/search-token pairs.
+        *              <br>Example: <js>"&amp;s=column1=foo*,column2=*bar"</js>
+        *      <li>
+        *              <js>"&amp;v="</js> - A comma-delimited list column 
names to view.
+        *              <br>Example: <js>"&amp;v=column1,column2"</js>
+        *      <li>
+        *              <js>"&amp;o="</js> - A comma-delimited list column 
names to sort by.
+        *              <br>Column names can be suffixed with <js>'-'</js> to 
indicate descending order.
+        *              <br>Example: <js>"&amp;o=column1,column2-"</js>
+        *      <li>
+        *              <js>"&amp;p="</js> - The zero-index row number of the 
first row to display.
+        *              <br>Example: <js>"&amp;p=100"</js>
+        *      <li>
+        *              <js>"&amp;l="</js> - The number of rows to return.
+        *              <br><code>0</code> implies return all rows.
+        *              <br>Example: <js>"&amp;l=100"</js>
+        *      <li>
+        *              <js>"&amp;i="</js> - The case-insensitive search flag.
+        *              <br>Example: <js>"&amp;i=true"</js>
+        * </ul>
+        *
+        * <p>
+        * Whitespace is trimmed in the parameters.
+        *
+        * @return
+        *      A new {@link SearchArgs} object initialized with the special 
search query arguments.
+        *      <jk>null</jk> if no search arguments were found.
+        */
+       public SearchArgs getSearchArgs() {
+               if (hasAny("s","v","o","p","l","i")) {
+                       return new SearchArgs.Builder()
+                               .search(getString("s"))
+                               .view(getString("v"))
+                               .sort(getString("o"))
+                               .position(getInt("p"))
+                               .limit(getInt("l"))
+                               .ignoreCase(getBoolean("i"))
+                               .build();
+               }
+               return null;
+       }
+
+       /**
+        * Returns <jk>true</jk> if the query parameters contains any of the 
specified names.
+        *
+        * @param paramNames The parameter names to check for.
+        * @return <jk>true</jk> if the query parameters contains any of the 
specified names.
+        */
+       public boolean hasAny(String...paramNames) {
+               for (String p : paramNames)
+                       if (containsKey(p))
+                               return true;
+               return false;
+       }
+
+       /* Workhorse method */
+       private <T> T parse(String name, T def, ClassMeta<T> cm) throws 
ParseException {
+               String val = getString(name);
+               if (val == null)
+                       return def;
+               return parseValue(val, cm);
+       }
+
+       /* Workhorse method */
+       private <T> T parse(String name, ClassMeta<T> cm) throws ParseException 
{
+               String val = getString(name);
+               if (cm.isPrimitive() && (val == null || val.isEmpty()))
+                       return cm.getPrimitiveDefault();
+               return parseValue(val, cm);
+       }
+
+       /* Workhorse method */
+       @SuppressWarnings("rawtypes")
+       private <T> T parseAll(String name, ClassMeta<T> cm) throws 
ParseException {
+               String[] p = get(name);
+               if (p == null)
+                       return null;
+               if (cm.isArray()) {
+                       List c = new ArrayList();
+                       for (int i = 0; i < p.length; i++)
+                               c.add(parseValue(p[i], cm.getElementType()));
+                       return (T)toArray(c, 
cm.getElementType().getInnerClass());
+               } else if (cm.isCollection()) {
+                       try {
+                               Collection c = 
(Collection)(cm.canCreateNewInstance() ? cm.newInstance() : new ObjectList());
+                               for (int i = 0; i < p.length; i++)
+                                       c.add(parseValue(p[i], 
cm.getElementType()));
+                               return (T)c;
+                       } catch (ParseException e) {
+                               throw e;
+                       } catch (Exception e) {
+                               // Typically an instantiation exception.
+                               throw new ParseException(e);
+                       }
+               }
+               throw new ParseException("Invalid call to 
getQueryParameters(String, ClassMeta).  Class type must be a Collection or 
array.");
+       }
+
+       private <T> T parseValue(String val, ClassMeta<T> c) throws 
ParseException {
+               return parser.parse(PartType.QUERY, val, c);
+       }
+
+       /**
+        * Converts the query parameters to a readable string.
+        *
+        * @param sorted Sort the query parameters by name.
+        * @return A JSON string containing the contents of the query 
parameters.
+        */
+       public String toString(boolean sorted) {
+               Map<String,Object> m = (sorted ? new TreeMap<String,Object>() : 
new LinkedHashMap<String,Object>());
+               for (Map.Entry<String,String[]> e : this.entrySet()) {
+                       String[] v = e.getValue();
+                       m.put(e.getKey(), v.length == 1 ? v[0] : v);
+               }
+               return JsonSerializer.DEFAULT_LAX.toString(m);
+       }
+
+       /**
+        * Converts this object to a query string.
+        *
+        * <p>
+        * Returned query string does not start with <js>'?'</js>.
+        *
+        * @return A new query string, or an empty string if this object is 
empty.
+        */
+       public String toQueryString() {
+               StringBuilder sb = new StringBuilder();
+               for (Map.Entry<String,String[]> e : this.entrySet()) {
+                       for (int i = 0; i < e.getValue().length; i++) {
+                               if (sb.length() > 0)
+                                       sb.append("&");
+                               
sb.append(XmlUtils.urlEncode(e.getKey())).append('=').append(XmlUtils.urlEncode(e.getValue()[i]));
+                       }
+               }
+               return sb.toString();
+       }
+
+       @Override /* Object */
+       public String toString() {
+               return toString(false);
+       }
+}

Propchange: 
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestQuery.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: 
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/ResponseHandler.java
==============================================================================
--- 
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/ResponseHandler.java
 (added)
+++ 
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/ResponseHandler.java
 Fri Sep  8 23:25:34 2017
@@ -0,0 +1,112 @@
+// 
***************************************************************************************************************************
+// * 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.io.*;
+
+import javax.servlet.http.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.rest.response.*;
+
+/**
+ * Defines the interface for handlers that convert POJOs to appropriate HTTP 
responses.
+ *
+ * <p>
+ * The {@link RestServlet} API uses the concept of registered response 
handlers for converting objects returned by REST
+ * methods or set through {@link RestResponse#setOutput(Object)} into 
appropriate HTTP responses.
+ *
+ * <p>
+ * Response handlers can be associated with {@link RestServlet RestServlets} 
through the following ways:
+ * <ul class='spaced-list'>
+ *     <li>
+ *             Through the {@link RestResource#responseHandlers 
@RestResource.responseHandlers} annotation.
+ *     <li>
+ *             By calling the {@link RestConfig#addResponseHandlers(Class...)} 
and augmenting or creating your
+ *             own list of handlers.
+ * </ul>
+ *
+ * <p>
+ * By default, {@link RestServlet RestServlets} are registered with the 
following response handlers:
+ * <ul class='spaced-list'>
+ *     <li>
+ *             {@link DefaultHandler} - Serializes POJOs using the Juneau 
serializer API.
+ *     <li>
+ *             {@link ReaderHandler} - Pipes the output of {@link Reader 
Readers} to the response writer
+ *             ({@link RestResponse#getWriter()}).
+ *     <li>
+ *             {@link InputStreamHandler} - Pipes the output of {@link 
InputStream InputStreams} to the response output
+ *             stream ({@link RestResponse#getOutputStream()}).
+ *     <li>
+ *             {@link RedirectHandler} - Handles {@link Redirect} objects.
+ *     <li>
+ *             {@link WritableHandler} - Handles {@link Writable} objects.
+ *     <li>
+ *             {@link StreamableHandler} - Handles {@link Streamable} objects.
+ * </ul>
+ *
+ * <p>
+ * Response handlers can be used to process POJOs that cannot normally be 
handled through Juneau serializers, or
+ * because it's simply easier to define response handlers for special cases.
+ *
+ * <p>
+ * The following example shows how to create a response handler to handle 
special <code>Foo</code> objects outside the
+ * normal Juneau architecture.
+ * <p class='bcode'>
+ *     <ja>@RestResource</ja>(
+ *             path=<js>"/example"</js>,
+ *             responseHandlers=FooHandler.<jk>class</jk>
+ *     )
+ *     <jk>public class</jk> Example <jk>extends</jk> RestServlet {
+ *
+ *             <ja>@RestMethod</ja>(name=<js>"GET"</js>, path=<js>"/"</js>)
+ *             <jk>public</jk> Foo test1() {
+ *                     <jk>return new</jk> Foo(<js>"123"</js>);
+ *             }
+ *
+ *             <jk>public static class</jk> FooHandler <jk>implements</jk> 
ResponseHandler {
+ *                     <ja>@Override</ja>
+ *                     <jk>public boolean</jk> handle(RestRequest req, 
RestResponse res, Object output) <jk>throws</jk> IOException, RestException {
+ *                             <jk>if</jk> (output <jk>instanceof</jk> Foo) {
+ *                                     Foo foo = (Foo)output;
+ *                                     <jc>// Set some headers and body 
content.</jc>
+ *                                     res.setHeader(<js>"Foo-ID"</js>, 
foo.getId());
+ *                                     
res.getWriter().write(<js>"foo.id="</js> + foo.getId());
+ *                                     <jk>return true</jk>;  <jc>// We 
handled it.</jc>
+ *                             }
+ *                             <jk>return false</jk>;  <jc>// We didn't handle 
it.</jc>
+ *                     }
+ *             }
+ *     }
+ * </p>
+ */
+public interface ResponseHandler {
+
+       /**
+        * Process this response if possible.
+        * This method should return <jk>false</jk> if it wasn't able to 
process the response.
+        *
+        * @param req The HTTP servlet request.
+        * @param res The HTTP servlet response;
+        * @param output The POJO returned by the REST method that now needs to 
be sent to the response.
+        * @return true If this handler handled the response.
+        * @throws IOException
+        *      If low-level exception occurred on output stream.
+        *      Results in a {@link 
HttpServletResponse#SC_INTERNAL_SERVER_ERROR} error.
+        * @throws RestException
+        *      If some other exception occurred.
+        *      Can be used to provide an appropriate HTTP response code and 
message.
+        */
+       boolean handle(RestRequest req, RestResponse res, Object output) throws 
IOException, RestException;
+}

Propchange: 
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/ResponseHandler.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain


Reply via email to