You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2016/08/01 16:03:33 UTC

[28/51] [abbrv] [partial] incubator-juneau git commit: Merge changes from GitHub repo.

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2c3a7cb5/com.ibm.team.juno.server/src/main/java/org/apache/juneau/server/RestConverter.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.server/src/main/java/org/apache/juneau/server/RestConverter.java b/com.ibm.team.juno.server/src/main/java/org/apache/juneau/server/RestConverter.java
deleted file mode 100755
index 0c1c9ec..0000000
--- a/com.ibm.team.juno.server/src/main/java/org/apache/juneau/server/RestConverter.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/***************************************************************************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations under the License.
- ***************************************************************************************************************************/
-package org.apache.juneau.server;
-
-import org.apache.juneau.*;
-import org.apache.juneau.serializer.*;
-import org.apache.juneau.server.annotation.*;
-import org.apache.juneau.server.converters.*;
-
-/**
- * REST method response converter.
- * <p>
- * 	Implements a filter mechanism for REST method calls that allows response objects to be
- * 	converted to some other POJO after invocation of the REST method.
- * <p>
- * 	Converters are associated with REST methods through the {@link RestMethod#converters()} annotation.
- * <h6 class='topic'>Example</h6>
- * <p class='bcode'>
- * 	<jk>public class</jk> RequestEchoResource <jk>extends</jk> RestServlet {
- *
- * 		<jc>// GET request handler</jc>
- * 		<ja>@RestMethod</ja>(name=<js>"GET"</js>, path=<js>"/*"</js>, converters={Queryable.<jk>class</jk>,Traversable.<jk>class</jk>})
- * 		<jk>public</jk> HttpServletRequest doGet(RestRequest req) {
- * 			res.setTitle(<js>"Contents of HttpServletRequest object"</js>);
- * 			<jk>return</jk> req;
- * 		}
- * 	}
- * </p>
- * <p>
- * 	Converters can also be associated at the servlet level using the {@link RestResource#converters()} annotation.
- * 	Applying converters at the resource level is equivalent to applying converters to each resource method individually.
- *
- * <h6 class='topic'>How to implement</h6>
- * <p>
- * 	Implementers should simply implement the {@link #convert(RestRequest, Object, ClassMeta)} and
- * 		return back a 'converted' object.
- * 	It's up to the implementer to decide what this means.
- * <p>
- * 	Converters must implement a no-args constructor.
- *
- * <h6 class='topic'>Predefined converters</h6>
- * <p>
- * 	The following converters are available by default.
- * <ul class='spaced-list'>
- * 	<li>{@link Traversable} - Allows URL additional path info to address individual elements in a POJO tree.
- * 	<li>{@link Queryable} - Allows query/view/sort functions to be performed on POJOs.
- * 	<li>{@link Introspectable} - Allows Java public methods to be invoked on the returned POJOs.
- * </ul>
- */
-public interface RestConverter {
-
-	/**
-	 * Performs post-call conversion on the specified response object.
-	 *
-	 * @param req The servlet request.
-	 * @param res The response object set by the REST method through the {@link RestResponse#setOutput(Object)} method.
-	 * @param cm The {@link ClassMeta} on the object from the bean context of the servlet.
-	 * 	Can be used to check if the object has any filters.
-	 * @return The converted object.
-	 * @throws RestException Thrown if any errors occur during conversion.
-	 * @throws SerializeException
-	 */
-	public Object convert(RestRequest req, Object res, ClassMeta<?> cm) throws RestException, SerializeException;
-}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2c3a7cb5/com.ibm.team.juno.server/src/main/java/org/apache/juneau/server/RestException.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.server/src/main/java/org/apache/juneau/server/RestException.java b/com.ibm.team.juno.server/src/main/java/org/apache/juneau/server/RestException.java
deleted file mode 100755
index 62ea6ae..0000000
--- a/com.ibm.team.juno.server/src/main/java/org/apache/juneau/server/RestException.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/***************************************************************************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations under the License.
- ***************************************************************************************************************************/
-package org.apache.juneau.server;
-
-import java.text.*;
-
-/**
- * Exception thrown to trigger an error HTTP status.
- * <p>
- * 	REST methods on subclasses of {@link RestServlet} can throw
- * 	this exception to trigger an HTTP status other than the automatically-generated
- * 	<code>404</code>, <code>405</code>, and <code>500</code> statuses.
- *
- * @author James Bognar (james.bognar@salesforce.com)
- */
-public class RestException extends RuntimeException {
-
-	private static final long serialVersionUID = 1L;
-
-	private int status;
-	private int occurrence;
-
-	/**
-	 * Constructor.
-	 *
-	 * @param status The HTTP status code.
-	 * @param msg The status message.
-	 * @param args Optional string format arguments.
-	 */
-	public RestException(int status, String msg, Object...args) {
-		super(args.length == 0 ? msg : MessageFormat.format(msg, args));
-		this.status = status;
-	}
-
-	/**
-	 * Constructor.
-	 *
-	 * @param status The HTTP status code.
-	 * @param cause The root exception.
-	 */
-	public RestException(int status, Throwable cause) {
-		this(status, cause.getLocalizedMessage());
-		initCause(cause);
-	}
-
-
-	/**
-	 * Sets the inner cause for this exception.
-	 *
-	 * @param cause The inner cause.
-	 * @return This object (for method chaining).
-	 */
-	@Override /* Throwable */
-	public synchronized RestException initCause(Throwable cause) {
-		super.initCause(cause);
-		return this;
-	}
-
-
-	/**
-	 * Returns all error messages from all errors in this stack.
-	 * <p>
-	 * Typically useful if you want to render all the error messages in the stack, but don't
-	 * want to render all the stack traces too.
-	 *
-	 * @param scrubForXssVulnerabilities If <jk>true</jk>, replaces <js>'&lt;'</js>, <js>'&gt;'</js>, and <js>'&amp;'</js> characters with spaces.
-	 * @return All error messages from all errors in this stack.
-	 */
-	public String getFullStackMessage(boolean scrubForXssVulnerabilities) {
-		String msg = getMessage();
-		StringBuilder sb = new StringBuilder();
-		if (msg != null) {
-			if (scrubForXssVulnerabilities)
-				msg = msg.replace('<', ' ').replace('>', ' ').replace('&', ' ');
-			sb.append(msg);
-		}
-		Throwable e = getCause();
-		while (e != null) {
-			msg = e.getMessage();
-			if (msg != null && scrubForXssVulnerabilities)
-				msg = msg.replace('<', ' ').replace('>', ' ').replace('&', ' ');
-			String cls = e.getClass().getSimpleName();
-			if (msg == null)
-				sb.append(MessageFormat.format("\nCaused by ({0})", cls));
-			else
-				sb.append(MessageFormat.format("\nCaused by ({0}): {1}", cls, msg));
-			e = e.getCause();
-		}
-		return sb.toString();
-	}
-
-	@Override /* Object */
-	public int hashCode() {
-		int i = 0;
-		Throwable t = this;
-		while (t != null) {
-			for (StackTraceElement e : t.getStackTrace())
-			i ^= e.hashCode();
-			t = t.getCause();
-		}
-		return i;
-	}
-
-	void setOccurrence(int occurrence) {
-		this.occurrence = occurrence;
-	}
-
-	/**
-	 * Returns the number of times this exception occurred on this servlet.
-	 * <p>
-	 * This only gets set if {@link RestServletContext#REST_useStackTraceHashes} is enabled on the servlet.
-	 *
-	 * @return The occurrence number if {@link RestServletContext#REST_useStackTraceHashes} is enabled, or <code>0</code> otherwise.
-	 */
-	public int getOccurrence() {
-		return occurrence;
-	}
-
-	/**
-	 * Returns the HTTP status code.
-	 *
-	 * @return The HTTP status code.
-	 */
-	public int getStatus() {
-		return status;
-	}
-}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2c3a7cb5/com.ibm.team.juno.server/src/main/java/org/apache/juneau/server/RestGuard.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.server/src/main/java/org/apache/juneau/server/RestGuard.java b/com.ibm.team.juno.server/src/main/java/org/apache/juneau/server/RestGuard.java
deleted file mode 100755
index 46475ce..0000000
--- a/com.ibm.team.juno.server/src/main/java/org/apache/juneau/server/RestGuard.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/***************************************************************************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations under the License.
- ***************************************************************************************************************************/
-package org.apache.juneau.server;
-
-import static javax.servlet.http.HttpServletResponse.*;
-
-import org.apache.juneau.server.annotation.*;
-
-/**
- * REST method guard.
- *
- *
- * <h6 class='topic'>Description</h6>
- * <p>
- * 	Implements a guard mechanism for REST method calls that allows requests to be
- * 		rejected before invocation of the REST method.
- * 	For example, guards can be used to ensure that only administrators can call certain methods.
- * <p>
- * 	Guards are applied to REST methods declaratively through the {@link RestResource#guards()} or {@link RestMethod#guards()} annotations.
- * <p>
- * 	If multiple guards are specified, ALL guards must pass in order for the request to proceed.
- *
- *
- * <h6 class='topic'>How to implement</h6>
- * <p>
- * 	Typically, guards will be used for permissions checking on the user making the request,
- * 		but it can also be used for other purposes like pre-call validation of a request.
- * <p>
- * 	Implementers should simply throw a {@link RestException} from the {@link #guard(RestRequest, RestResponse)}
- * 		method to abort processing on the current request.
- * <p>
- * 	Guards must implement a no-args constructor.
- *
- *
- * <h6 class='topic'>Example usage</h6>
- * <p class='bcode'>
- * 	<jk>public</jk> MyResource <jk>extends</jk> RestServlet {
- *
- * 		<jc>// Delete method with guard that only allows Billy to call it.</jc>
- * 		<ja>@RestMethod</ja>(name=<js>"DELETE"</js>, guards=BillyGuard.<jk>class</jk>)
- * 		<jk>public</jk> doDelete(RestRequest req, RestResponse res) <jk>throws</jk> Exception {...}
- * 	}
- * </p>
- *
- *
- * <h6 class='topic'>Example implementation</h6>
- * <p class='bcode'>
- * 	<jc>// Define a guard that only lets Billy make a request</jc>
- * 	<jk>public</jk> BillyGuard <jk>extends</jk> RestGuard {
- *
- * 		<ja>@Override</ja>
- * 		<jk>public boolean</jk> isRequestAllowed(RestRequest req) {
- * 			return req.getUserPrincipal().getName().contains(<js>"Billy"</js>);
- * 		}
- * 	}
- * </p>
- */
-public abstract class RestGuard {
-
-	/**
-	 * Checks the current HTTP request and throws a {@link RestException} if the guard
-	 * 	does not permit the request.
-	 * <p>
-	 * 	By default, throws an <jsf>SC_FORBIDDEN</jsf> exception if {@link #isRequestAllowed(RestRequest)}
-	 * 	returns <jk>false</jk>.
-	 * <p>
-	 * 	Subclasses are free to override this method to tailor the behavior of how to handle unauthorized
-	 * 	requests.
-	 *
-	 * @param req The servlet request.
-	 * @param res The servlet response.
-	 * @throws RestException Thrown to abort processing on current request.
-	 * @return <jk>true</jk> if request can proceed.
-	 * 	Specify <jk>false</jk> if you're doing something like a redirection to a login page.
-	 */
-	public boolean guard(RestRequest req, RestResponse res) throws RestException {
-		if (! isRequestAllowed(req))
-			throw new RestException(SC_FORBIDDEN, "Access denied by guard");
-		return true;
-	}
-
-	/**
-	 * Returns <jk>true</jk> if the specified request can pass through this guard.
-	 *
-	 * @param req The servlet request.
-	 * @return <jk>true</jk> if the specified request can pass through this guard.
-	 */
-	public abstract boolean isRequestAllowed(RestRequest req);
-}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2c3a7cb5/com.ibm.team.juno.server/src/main/java/org/apache/juneau/server/RestMatcher.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.server/src/main/java/org/apache/juneau/server/RestMatcher.java b/com.ibm.team.juno.server/src/main/java/org/apache/juneau/server/RestMatcher.java
deleted file mode 100755
index c2513f7..0000000
--- a/com.ibm.team.juno.server/src/main/java/org/apache/juneau/server/RestMatcher.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/***************************************************************************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations under the License.
- ***************************************************************************************************************************/
-package org.apache.juneau.server;
-
-import org.apache.juneau.server.annotation.*;
-
-/**
- * Class used for defining method-level matchers using the {@link RestMethod#matchers()} annotation.
- * <p>
- * Matchers are used to allow multiple Java methods to handle requests assigned to the same
- * 	URL path pattern, but differing based on some request attribute, such as a specific header value.
- * For example, matchers can be used to provide two different methods for handling requests
- * 	from two different client versions.
- * <p>
- * Java methods with matchers associated with them are always attempted before Java methods
- * 	without matchers.
- * This allows a 'default' method to be defined to handle requests where no matchers match.
- * <p>
- * When multiple matchers are specified on a method, only one matcher is required to match.
- * This is opposite from the {@link RestMethod#guards()} annotation, where all guards
- * 	are required to match in order to execute the method.
- *
- * <h6 class='topic'>Example</h6>
- * <p class='bcode'>
- * 	<jk>public class</jk> MyResource <jk>extends</jk> RestServlet {
- *
- * 		<ja>@RestMethod</ja>(name=<js>"GET"</js>, path=<js>"/foo"</js>, matchers=IsDNT.<jk>class</jk>)
- * 		<jk>public</jk> Object doGetWithDNT() {
- * 			<jc>// Handle request with Do-Not-Track specified</jc>
- * 		}
- *
- * 		<ja>@RestMethod</ja>(name=<js>"GET"</js>, path=<js>"/foo"</js>)
- * 		<jk>public</jk> Object doGetWithoutDNT() {
- * 			<jc>// Handle request without Do-Not-Track specified</jc>
- * 		}
- * 	}
- *
- * 	<jk>public class</jk> IsDNT <jk>extends</jk> RestMatcher {
- * 		<ja>@Override</ja>
- * 		<jk>public boolean</jk> matches(RestRequest req) {
- * 			<jk>return</jk> req.getHeader(<jk>int</jk>.<jk>class</jk>, <js>"DNT"</js>, 0) == 1;
- * 		}
- * 	}
- * </p>
- */
-public abstract class RestMatcher {
-
-	/**
-	 * Returns <jk>true</jk> if the specified request matches this matcher.
-	 *
-	 * @param req The servlet request.
-	 * @return <jk>true</jk> if the specified request matches this matcher.
-	 */
-	public abstract boolean matches(RestRequest req);
-
-	/**
-	 * Returns <jk>true</jk> if this matcher is required to match in order for the method to be invoked.
-	 * <p>
-	 * If <jk>false</jk>, then only one of the matchers must match.
-	 *
-	 * @return <jk>true</jk> if this matcher is required to match in order for the method to be invoked.
-	 */
-	public boolean mustMatch() {
-		return false;
-	}
-}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2c3a7cb5/com.ibm.team.juno.server/src/main/java/org/apache/juneau/server/RestMatcherReflecting.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.server/src/main/java/org/apache/juneau/server/RestMatcherReflecting.java b/com.ibm.team.juno.server/src/main/java/org/apache/juneau/server/RestMatcherReflecting.java
deleted file mode 100644
index 3140559..0000000
--- a/com.ibm.team.juno.server/src/main/java/org/apache/juneau/server/RestMatcherReflecting.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/***************************************************************************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations under the License.
- ***************************************************************************************************************************/
-package org.apache.juneau.server;
-
-import java.lang.reflect.*;
-
-/**
- * Subclass of {@link RestMatcher} that gives access to the servlet and Java method it's applied to.
- * <p>
- * 	Essentially the same as {@link RestMatcher} except has a constructor where the
- * 	Java method is passed in so that you can access annotations defined on it to tailor
- * 	the behavior of the matcher.
- *
- * @author James Bognar (james.bognar@salesforce.com)
- */
-public abstract class RestMatcherReflecting extends RestMatcher {
-
-	/**
-	 * Constructor.
-	 *
-	 * @param servlet The REST servlet.
-	 * @param javaMethod The Java method that this rest matcher is defined on.
-	 */
-	protected RestMatcherReflecting(RestServlet servlet, Method javaMethod) {}
-}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2c3a7cb5/com.ibm.team.juno.server/src/main/java/org/apache/juneau/server/RestRequest.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.server/src/main/java/org/apache/juneau/server/RestRequest.java b/com.ibm.team.juno.server/src/main/java/org/apache/juneau/server/RestRequest.java
deleted file mode 100755
index 29ce04d..0000000
--- a/com.ibm.team.juno.server/src/main/java/org/apache/juneau/server/RestRequest.java
+++ /dev/null
@@ -1,1764 +0,0 @@
-/***************************************************************************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations under the License.
- ***************************************************************************************************************************/
-package org.apache.juneau.server;
-
-import static java.util.Collections.*;
-import static java.util.logging.Level.*;
-import static javax.servlet.http.HttpServletResponse.*;
-
-import java.io.*;
-import java.lang.reflect.*;
-import java.net.*;
-import java.text.*;
-import java.util.*;
-import java.util.logging.*;
-
-import javax.servlet.*;
-import javax.servlet.http.*;
-
-import org.apache.juneau.*;
-import org.apache.juneau.encoders.*;
-import org.apache.juneau.encoders.Encoder;
-import org.apache.juneau.ini.*;
-import org.apache.juneau.internal.*;
-import org.apache.juneau.parser.*;
-import org.apache.juneau.parser.ParseException;
-import org.apache.juneau.serializer.*;
-import org.apache.juneau.server.labels.*;
-import org.apache.juneau.server.labels.Var;
-import org.apache.juneau.svl.*;
-import org.apache.juneau.urlencoding.*;
-import org.apache.juneau.utils.*;
-
-/**
- * Represents an HTTP request for a REST resource.
- * <p>
- * 	Equivalent to {@link HttpServletRequest} except with some additional convenience methods.
- * </p>
- * <p>
- * 	For reference, given the URL <js>"http://localhost:9080/contextRoot/servletPath/foo?bar=baz#qux"</js>, the
- * 	following methods return the following values....
- * </p>
- * <table class='styled'>
- * 	<tr><th>Method</th><th>Value</th></tr>
- * 	<tr><td>{@code getContextPath()}</td><td>{@code /contextRoot}</td></tr>
- * 	<tr><td>{@code getPathInfo()}</td><td>{@code /foo}</td></tr>
- * 	<tr><td>{@code getPathTranslated()}</td><td>{@code path-to-deployed-war-on-filesystem/foo}</td></tr>
- * 	<tr><td>{@code getQueryString()}</td><td>{@code bar=baz}</td></tr>
- * 	<tr><td>{@code getRequestURI()}</td><td>{@code /contextRoot/servletPath/foo}</td></tr>
- * 	<tr><td>{@code getRequestURL()}</td><td>{@code http://localhost:9080/contextRoot/servletPath/foo}</td></tr>
- * 	<tr><td>{@code getServletPath()}</td><td>{@code /servletPath}</td></tr>
- * </table>
- * <p>
- * 	Refer to <a class='doclink' href='package-summary.html#TOC'>REST Servlet API</a> for information about using this class.
- * </p>
- *
- * @author jbognar
- */
-@SuppressWarnings("unchecked")
-public final class RestRequest extends HttpServletRequestWrapper {
-
-	private final RestServlet servlet;
-	private String method, pathRemainder, content;
-	Method javaMethod;
-	private ObjectMap properties;
-	private SerializerGroup serializerGroup;
-	private ParserGroup parserGroup;
-	private Encoder encoder;
-	private int contentLength;
-	private final boolean debug;
-	private UrlEncodingParser urlEncodingParser;   // The parser used to parse URL attributes and parameters (beanContext also used to parse headers)
-	private BeanContext beanContext;
-	private VarResolverSession varSession;
-	private Map<String,String[]> queryParams;
-	private Map<String,String> defaultServletHeaders, defaultMethodHeaders, overriddenHeaders, overriddenParams;
-	private boolean isPost;
-	private String servletURI, relativeServletURI;
-	private String charset, defaultCharset;
-	private ObjectMap headers;
-	private ConfigFile cf;
-
-	/**
-	 * Constructor.
-	 */
-	RestRequest(RestServlet servlet, HttpServletRequest req) throws ServletException {
-		super(req);
-
-		try {
-			this.servlet = servlet;
-			isPost = req.getMethod().equalsIgnoreCase("POST");
-
-			// If this is a POST, we want to parse the query parameters ourselves to prevent
-			// the servlet code from processing the HTTP body as URL-Encoded parameters.
-			if (isPost)
-				queryParams = servlet.getUrlEncodingParser().parseIntoSimpleMap(getQueryString());
-			else {
-				queryParams = req.getParameterMap();
-			}
-
-			// Get the HTTP method.
-			// Can be overridden through a "method" GET attribute.
-			method = super.getMethod();
-
-			String m = getQueryParameter("method");
-			if (! StringUtils.isEmpty(m) && (servlet.context.allowMethodParams.contains(m) || servlet.context.allowMethodParams.contains("*")))
-				method = m;
-
-			if (servlet.context.allowContentParam)
-				content = getQueryParameter("content");
-
-			defaultServletHeaders = servlet.getDefaultRequestHeaders();
-
-			debug = "true".equals(getQueryParameter("debug", "false"));
-
-			if (debug) {
-				servlet.log(Level.INFO, toString());
-			}
-
-		} catch (RestException e) {
-			throw e;
-		} catch (Exception e) {
-			throw new ServletException(e);
-		}
-	}
-
-	/*
-	 * Called from RestServlet after a match has been made but before the guard or method invocation.
-	 */
-	@SuppressWarnings("hiding")
-	final void init(Method javaMethod, String pathRemainder, ObjectMap properties, Map<String,String> mDefaultRequestHeaders, String defaultCharset, SerializerGroup mSerializers, ParserGroup mParsers, UrlEncodingParser mUrlEncodingParser) {
-		this.javaMethod = javaMethod;
-		this.pathRemainder = pathRemainder;
-		this.properties = properties;
-		this.defaultMethodHeaders = mDefaultRequestHeaders;
-		this.serializerGroup = mSerializers;
-		this.parserGroup = mParsers;
-		this.urlEncodingParser = mUrlEncodingParser;
-		this.beanContext = urlEncodingParser.getBeanContext();
-		this.defaultCharset = defaultCharset;
-	}
-
-	/**
-	 * Returns <jk>true</jk> if the request contains any of the specified parameters.
-	 *
-	 * @param params The list of parameters to check for.
-	 * @return <jk>true</jk> if the request contains any of the specified parameters.
-	 */
-	public boolean hasAnyQueryParameters(String...params) {
-		for (String p : params)
-			if (hasQueryParameter(p))
-				return true;
-		return false;
-	}
-
-	/**
-	 * Returns a string of the form <js>"HTTP method-name full-url"</js>
-	 *
-	 * @return A description of the request.
-	 */
-	public String getDescription() {
-		String qs = getQueryString();
-		return "HTTP " + getMethod() + " " + getRequestURI() + (qs == null ? "" : "?" + qs);
-	}
-
-	/**
-	 * Servlet calls this method to initialize the properties.
-	 */
-	RestRequest setProperties(ObjectMap properties) {
-		this.properties = properties;
-		return this;
-	}
-
-	/**
-	 * Retrieve the properties active for this request.
-	 * These properties can be modified by the request.
-	 *
-	 * @return The properties active for this request.
-	 */
-	public ObjectMap getProperties() {
-		return this.properties;
-	}
-
-	/**
-	 * Returns the <code>Content-Type</code> header value on the request, stripped
-	 * 	of any parameters such as <js>";charset=X"</js>.
-	 * <p>
-	 * Example: <js>"text/json"</js>.
-	 * <p>
-	 * If the content type is not specified, and the content is specified via a
-	 * 	<code>&content</code> query parameter, the content type is assumed to be
-	 * 	<js>"text/uon"</js>.  Otherwise, the
-	 * 	content type is assumed to be <js>"text/json"</js>.
-	 *
-	 * @return The <code>Accept</code> media-type header values on the request.
-	 */
-	public String getMediaType() {
-		String cm = getHeader("Content-Type");
-		if (cm == null) {
-			if (content != null)
-				return "text/uon";
-			return "text/json";
-		}
-		int j = cm.indexOf(';');
-		if (j != -1)
-			cm = cm.substring(0, j);
-		return cm;
-	}
-
-	/**
-	 * Returns the media types that are valid for <code>Content-Type</code> headers on the request.
-	 *
-	 * @return The set of media types registered in the parser group of this request.
-	 */
-	public List<String> getSupportedMediaTypes() {
-		return parserGroup.getSupportedMediaTypes();
-	}
-
-	/**
-	 * Returns the charset specified on the <code>Content-Type</code> header, or
-	 * <js>"UTF-8"</js> if not specified.
-	 */
-	@Override /* ServletRequest */
-	public String getCharacterEncoding() {
-		if (charset == null) {
-			// Determine charset
-			// NOTE:  Don't use super.getCharacterEncoding() because the spec is implemented inconsistently.
-			// Jetty returns the default charset instead of null if the character is not specified on the request.
-			String h = getHeader("Content-Type");
-			if (h != null) {
-				int i = h.indexOf(";charset=");
-				if (i > 0)
-					charset = h.substring(i+9).trim();
-			}
-			if (charset == null)
-				charset = defaultCharset;
-			if (! RestServlet.availableCharsets.containsKey(charset))
-				throw new RestException(SC_UNSUPPORTED_MEDIA_TYPE, "Unsupported charset in header ''Content-Type'': ''{0}''", h);
-		}
-		return charset;
-	}
-
-	/**
-	 * Sets the charset to expect on the request body.
-	 */
-	@Override /* ServletRequest */
-	public void setCharacterEncoding(String charset) {
-		this.charset = charset;
-	}
-
-	/**
-	 * Returns the specified header value, or <jk>null</jk> if the header value isn't present.
-	 * <p>
-	 * 	If {@code allowHeaderParams} init parameter is <jk>true</jk>, then first looks
-	 * 	for {@code &HeaderName=x} in the URL query string.
-	 */
-	@Override /* ServletRequest */
-	public String getHeader(String name) {
-		return getHeader(name, (String)null);
-	}
-
-	/**
-	 * Returns all the request headers as an {@link ObjectMap}.
-	 * <p>
-	 * Altering entries in this map does not alter headers in the underlying request.
-	 *
-	 * @return The request headers.  Never <jk>null</jk>.
-	 */
-	public ObjectMap getHeaders() {
-		if (headers == null) {
-			headers = new ObjectMap();
-			for (Enumeration<String> e = getHeaderNames(); e.hasMoreElements();) {
-				String key = e.nextElement();
-				headers.put(key, getHeader(key));
-			}
-		}
-		return headers;
-	}
-
-	/**
-	 * Set the request header to the specified value.
-	 *
-	 * @param name The header name.
-	 * @param value The header value.
-	 */
-	public void setHeader(String name, String value) {
-		if (overriddenHeaders == null)
-			overriddenHeaders = new TreeMap<String,String>(String.CASE_INSENSITIVE_ORDER);
-		overriddenHeaders.put(name, value);
-	}
-
-	/**
-	 * Set the request parameter to the specified value.
-	 *
-	 * @param name The parameter name.
-	 * @param value The parameter value.
-	 */
-	public void setParameter(String name, Object value) {
-		if (overriddenParams == null)
-			overriddenParams = new TreeMap<String,String>(String.CASE_INSENSITIVE_ORDER);
-		overriddenParams.put(name, value == null ? null : value.toString());
-	}
-
-	/**
-	 * Returns the specified header value, or the specified default value if the
-	 * 	header value isn't present.
-	 * <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 getHeader(String name, String def) {
-		String h = getOverriddenHeader(name);
-		if (h != null)
-			return h;
-		h = super.getHeader(name);
-		if (h != null && ! h.isEmpty())
-			return h;
-		if (defaultMethodHeaders != null) {
-			h = defaultMethodHeaders.get(name);
-			if (h != null)
-				return h;
-		}
-		h = defaultServletHeaders.get(name);
-		if (h != null)
-			return h;
-		return def;
-	}
-
-	@Override /* ServletRequest */
-	public Enumeration<String> getHeaders(String name) {
-		String h = getOverriddenHeader(name);
-		if (h != null)
-			return enumeration(singleton(h));
-		return super.getHeaders(name);
-	}
-
-	/*
-	 * Returns header value from URL-parameters or set via setHeader() meant
-	 * to override actual header values on the request.
-	 */
-	private String getOverriddenHeader(String name) {
-		String h = null;
-		if (servlet.context.allowHeaderParams)
-			h = getQueryParameter(name);
-		if (h != null)
-			return h;
-		if (overriddenHeaders != null) {
-			h = overriddenHeaders.get(name);
-			if (h != null)
-				return h;
-		}
-		return h;
-	}
-
-	@Override /* ServletRequest */
-	public Locale getLocale() {
-		String h = getOverriddenHeader("Accept-Language");
-		if (h != null) {
-			MediaRange[] mr = MediaRange.parse(h);
-			if (mr.length > 0)
-				return toLocale(mr[0].getType());
-		}
-		return super.getLocale();
-	}
-
-	@Override /* ServletRequest */
-	public Enumeration<Locale> getLocales() {
-		String h = getOverriddenHeader("Accept-Language");
-		if (h != null) {
-			MediaRange[] mr = MediaRange.parse(h);
-			if (mr.length > 0) {
-				List<Locale> l = new ArrayList<Locale>(mr.length);
-				for (MediaRange r : mr)
-					l.add(toLocale(r.getType()));
-				return enumeration(l);
-			}
-		}
-		return super.getLocales();
-	}
-
-	/**
-	 * Converts an Accept-Header value entry to a Locale.
-	 */
-	private Locale toLocale(String lang) {
-      String country = "";
-      int i = lang.indexOf('-');
-      if (i > -1) {
-          country = lang.substring(i+1).trim();
-          lang = lang.substring(0,i).trim();
-      }
-      return new Locale(lang, country);
-	}
-
-	/**
-	 * Returns the serializers associated with this request.
-	 *
-	 * @return The serializers associated with this request.
-	 */
-	public SerializerGroup getSerializerGroup() {
-		return serializerGroup;
-	}
-
-	/**
-	 * Returns the parsers associated with this request.
-	 *
-	 * @return The parsers associated with this request.
-	 */
-	public ParserGroup getParserGroup() {
-		return parserGroup;
-	}
-
-	/**
-	 * Returns the method of this request.
-	 * <p>
-	 * 	If <code>allowHeaderParams</code> init parameter is <jk>true</jk>, then first looks
-	 * 	for <code>&method=xxx</code> in the URL query string.
-	 */
-	@Override /* ServletRequest */
-	public String getMethod() {
-		return method;
-	}
-
-	/**
-	 * Returns the parameter with the specified name.
-	 * <p>
-	 * Returns <jk>null</jk> for parameters with no value (e.g. <js>"&foo"</js>).
-	 * This is consistent with WAS, but differs from Tomcat behavior.
-	 * The presence of parameter <js>"&foo"</js> in this case can be determined using {@link #hasParameter(String)}.
-	 * <p>
-	 * Parameter lookup is case-insensitive (consistent with WAS, but differs from Tomcat).
-	 * <p>
-	 * <i>Note:</i> Calling this method on URL-Encoded FORM posts causes the body content to be loaded and parsed by
-	 * 	the underlying servlet API.
-	 * <p>
-	 * <i>Note:</i> This method returns the raw unparsed value, and differs from calling <code>getParameter(name, String.<jk>class</js>)</code>
-	 * 	which will convert the value from UON notation:
-	 * <ul>
-	 * 	<li><js>"\u0000"</js> =&gt; <jk>null</jk>
-	 * 	<li><js>"$s(foo)"</js> =&gt; <js>"foo"</js>
-	 * 	<li><js>"(foo)"</js> =&gt; <js>"foo"</js>
-	 * </ul>
-	 */
-	@Override /* ServletRequest */
-	public String getParameter(String name) {
-		String s = null;
-		if (overriddenParams != null)
-			s = overriddenParams.get(name);
-		if (s != null)
-			return s;
-
-		String val = super.getParameter(name);
-
-		// Fix for behavior difference between Tomcat and WAS.
-		// getParameter("foo") on "&foo" in Tomcat returns "".
-		// getParameter("foo") on "&foo" in WAS returns null.
-		if (val != null && val.isEmpty())
-			if (queryParams.containsKey(name))
-				val = null;
-
-		return val;
-	}
-
-	/**
-	 * Same as {@link #getParameter(String)} except returns the default value
-	 * 	if <jk>null</jk> or empty.
-	 *
-	 * @param name The query parameter name.
-	 * @param def The default value.
-	 * @return The parameter value, or the default value if <jk>null</jk> or empty.
-	 */
-	public String getParameter(String name, String def) {
-		String val = getParameter(name);
-		if (val == null || val.isEmpty())
-			return def;
-		return val;
-	}
-
-	/**
-	 * Returns the specified URL parameter value parsed to the specified class type using the
-	 * 	{@link UrlEncodingParser} registered with this servlet.
-	 * <p>
-	 * <i>Note:</i> Calling this method on URL-Encoded FORM posts causes the body content to be loaded and parsed by
-	 * 	the underlying servlet API.
-	 *
-	 * @param name The parameter name.
-	 * @param c The class type to convert the parameter value to.
-	 * @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 getParameter(String name, Class<T> c, T def) throws ParseException {
-		return getParameter(name, beanContext.getClassMeta(c), def);
-	}
-
-	/**
-	 * Returns the specified URL parameter value parsed to the specified class type using the
-	 * 	{@link UrlEncodingParser} registered with this servlet.
-	 * <p>
-	 * <i>Note:</i> Calling this method on URL-Encoded FORM posts causes the body content to be loaded and parsed by
-	 * 	the underlying servlet API.
-	 * <p>
-	 * Unlike {@link #getParameter(String, Class, Object)}, this method can be used to parse parameters
-	 * 	of complex types involving JCF classes.
-	 * <p class='bcode'>
-	 * 	ClassMeta&ltMap&lt;String,Integer&gt;&gt; cm = request.getBeanContext().getMapClassMeta(TreeMap.<jk>class</jk>, String.<jk>class</jk>, Integer.<jk>class</jk>);
-	 * 	Map&lt;String,Integer&gt; m = request.getParameter(<js>"myParameter"</js>, cm, <jk>new</jk> TreeMap&lt;String,Integer&gt;());
-	 * </p>
-	 *
-	 * @param name The parameter name.
-	 * @param cm The class type to convert the parameter value to.
-	 * @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 getParameter(String name, ClassMeta<T> cm, T def) throws ParseException {
-		String val = getParameter(name);
-		if (val == null)
-			return def;
-		return parseParameter(val, cm);
-	}
-
-	/**
-	 * Returns the specified URL parameter value parsed to the specified class type using the
-	 * 	{@link UrlEncodingParser} registered with this servlet.
-	 * <p>
-	 * <i>Note:</i> Calling this method on URL-Encoded FORM posts causes the body content to be loaded and parsed by
-	 * 	the underlying servlet API.
-	 *
-	 * @param name The 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 parameter value converted to the specified class type.
-	 * @throws ParseException
-	 */
-	public <T> T getParameter(String name, Class<T> c) throws ParseException {
-		return getParameter(name, beanContext.getClassMeta(c));
-	}
-
-	/**
-	 * Same as {@link #getParameter(String, Class)} except for use on multi-part parameters
-	 * 	(e.g. <js>"&key=1&key=2&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 c The class type to convert the parameter value to.
-	 * @return The parameter value converted to the specified class type.
-	 * @throws ParseException
-	 */
-	public <T> T getParameters(String name, Class<T> c) throws ParseException {
-		return getParameters(name, beanContext.getClassMeta(c));
-	}
-
-	/**
-	 * Same as {@link #getParameter(String, Class)} except works on parameterized
-	 * types such as those returned by {@link Method#getGenericParameterTypes()}
-	 *
-	 * @param name The parameter name.
-	 * @param c The class type to convert the parameter value to.
-	 * @return The parameter value converted to the specified class type.
-	 * @throws ParseException
-	 */
-	public <T> T getParameter(String name, Type c) throws ParseException {
-		return (T)getParameter(name, beanContext.getClassMeta(c));
-	}
-
-	/**
-	 * Same as {@link #getParameter(String, Class)} except for use on multi-part parameters
-	 * 	(e.g. <js>"&key=1&key=2&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 c The class type to convert the parameter value to.
-	 * @return The parameter value converted to the specified class type.
-	 * @throws ParseException
-	 */
-	public <T> T getParameters(String name, Type c) throws ParseException {
-		return (T)getParameters(name, beanContext.getClassMeta(c));
-	}
-
-	/**
-	 * Returns the specified URL parameter value parsed to the specified class type using the
-	 * 	{@link UrlEncodingParser} registered with this servlet.
-	 * <p>
-	 * <i>Note:</i> Calling this method on URL-Encoded FORM posts causes the body content to be loaded and parsed by
-	 * 	the underlying servlet API.
-	 * <p>
-	 * Unlike {@link #getParameter(String, Class)}, this method can be used to parse parameters
-	 * 	of complex types involving JCF classes.
-	 * <p class='bcode'>
-	 * 	ClassMeta&lt;Map&lt;String,Integer&gt;&gt; cm = request.getBeanContext().getMapClassMeta(TreeMap.<jk>class</jk>, String.<jk>class</jk>, Integer.<jk>class</jk>);
-	 * 	Map&lt;String,Integer&gt; m = request.getParameter(<js>"myParameter"</js>, cm);
-	 * </p>
-	 *
-	 * @param name The parameter name.
-	 * @param cm 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 getParameter(String name, ClassMeta<T> cm) throws ParseException {
-
-		String val = getParameter(name);
-
-		if (cm.isPrimitive() && (val == null || val.isEmpty()))
-			return cm.getPrimitiveDefault();
-
-		return parseParameter(val, cm);
-	}
-
-	/**
-	 * Same as {@link #getParameter(String, ClassMeta)} except for use on multi-part parameters
-	 * 	(e.g. <js>"&key=1&key=2&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 cm 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
-	 */
-	@SuppressWarnings("rawtypes")
-	public <T> T getParameters(String name, ClassMeta<T> cm) throws ParseException {
-		String[] p = getParameterValues(name);
-		if (p == null)
-			return null;
-		if (cm.isArray()) {
-			List c = new ArrayList();
-			for (int i = 0; i < p.length; i++)
-				c.add(parseParameter(p[i], cm.getElementType()));
-			return (T)ArrayUtils.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(parseParameter(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.");
-	}
-
-	/**
-	 * Returns <jk>true</jk> if the URL parameters on this request contains the specified entry.
-	 * <p>
-	 * Note that this returns <jk>true</jk> even if the value is set to null (e.g. <js>"?key"</js>).
-	 *
-	 * @param name The URL parameter name.
-	 * @return <jk>true</jk> if the URL parameters on this request contains the specified entry.
-	 */
-	public boolean hasParameter(String name) {
-		return getParameterMap().containsKey(name);
-	}
-
-	/**
-	 * Same as {@link #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.
-	 *
-	 * @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>"&foo"</js>.
-	 */
-	public String getQueryParameter(String name) {
-		String s = null;
-		if (overriddenParams != null)
-			s = overriddenParams.get(name);
-		if (s != null)
-			return s;
-		String[] v = queryParams.get(name);
-		if (v == null || v.length == 0)
-			return null;
-		if (v.length == 1 && v[0] != null && v[0].isEmpty()) {
-			// Fix for behavior difference between Tomcat and WAS.
-			// getParameter("foo") on "&foo" in Tomcat returns "".
-			// getParameter("foo") on "&foo" in WAS returns null.
-			if (queryParams.containsKey(name))
-				return null;
-		}
-
-		return v[0];
-	}
-
-	/**
-	 * Same as {@link #getQueryParameter(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>"&foo"</js>.
-	 */
-	public String getQueryParameter(String name, String def) {
-		String s = getQueryParameter(name);
-		return s == null ? def : s;
-	}
-
-	/**
-	 * Same as {@link #getParameter(String, Class, Object)} 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.
-	 *
-	 * @param name The parameter name.
-	 * @param c The class type to convert the parameter value to.
-	 * @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 getQueryParameter(String name, Class<T> c, T def) throws ParseException {
-		return getQueryParameter(name, beanContext.getClassMeta(c), def);
-	}
-
-	/**
-	 * Same as {@link #getParameter(String, ClassMeta, Object)} 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.
-	 *
-	 * @param name The parameter name.
-	 * @param cm The class type to convert the parameter value to.
-	 * @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 getQueryParameter(String name, ClassMeta<T> cm, T def) throws ParseException {
-		String val = getQueryParameter(name);
-		if (val == null)
-			return def;
-		return parseParameter(val, cm);
-	}
-
-	/**
-	 * Same as {@link #getParameter(String, ClassMeta, Object)} 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.
-	 *
-	 * @param name The 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 parameter value converted to the specified class type.
-	 * @throws ParseException
-	 */
-	public <T> T getQueryParameter(String name, Class<T> c) throws ParseException {
-		return getQueryParameter(name, beanContext.getClassMeta(c));
-	}
-
-	/**
-	 * Same as {@link #getQueryParameter(String, Class)} except for use on multi-part parameters
-	 * 	(e.g. <js>"&key=1&key=2&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 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 getQueryParameters(String name, Class<T> c) throws ParseException {
-		return getQueryParameters(name, beanContext.getClassMeta(c));
-	}
-
-	/**
-	 * Same as {@link #getQueryParameter(String, Class)} except works on parameterized
-	 * types such as those returned by {@link Method#getGenericParameterTypes()}
-	 *
-	 * @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 getQueryParameter(String name, Type c) throws ParseException {
-		return (T)getQueryParameter(name, beanContext.getClassMeta(c));
-	}
-
-	/**
-	 * Same as {@link #getQueryParameter(String, Type)} except for use on multi-part parameters
-	 * 	(e.g. <js>"&key=1&key=2&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 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 getQueryParameters(String name, Type c) throws ParseException {
-		return (T)getQueryParameters(name, beanContext.getClassMeta(c));
-	}
-
-	/**
-	 * Same as {@link #getParameter(String, ClassMeta)} 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.
-	 *
-	 * @param name The parameter name.
-	 * @param cm 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 getQueryParameter(String name, ClassMeta<T> cm) throws ParseException {
-
-		String val = getQueryParameter(name);
-
-		if (cm.isPrimitive() && (val == null || val.isEmpty()))
-			return cm.getPrimitiveDefault();
-		return parseParameter(val, cm);
-	}
-
-	/**
-	 * Same as {@link #getQueryParameter(String, ClassMeta)} except for use on multi-part parameters
-	 * 	(e.g. <js>"&key=1&key=2&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 cm 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
-	 */
-	@SuppressWarnings("rawtypes")
-	public <T> T getQueryParameters(String name, ClassMeta<T> cm) throws ParseException {
-		String[] p = getQueryParameters(name);
-		if (p == null)
-			return null;
-		if (cm.isArray()) {
-			List c = new ArrayList();
-			for (int i = 0; i < p.length; i++)
-				c.add(parseParameter(p[i], cm.getElementType()));
-			return (T)ArrayUtils.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(parseParameter(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.");
-	}
-
-	/**
-	 * Returns the list of all query parameters with the specified name.
-	 * Same as {@link #getParameterValues(String)} except only looks in the URL string,
-	 * 	not parameters from URL-Encoded FORM posts.
-	 * <p>
-	 * This method can be used to retrieve parameters without triggering the underlying
-	 * 	servlet API to load and parse the request body.
-	 *
-	 * @param name
-	 * @return the list of query parameters, or <jk>null</jk> if the parameter does not exist.
-	 */
-	public String[] getQueryParameters(String name) {
-		return queryParams.get(name);
-	}
-
-	/**
-	 * Returns <jk>true</jk> if the URL parameters on this request contains the specified entry.
-	 * <p>
-	 * Note that this returns <jk>true</jk> even if the value is set to null (e.g. <js>"?key"</js>).
-	 * <p>
-	 * This method can be used to check the existence of a parameter without triggering the underlying
-	 * 	servlet API to load and parse the request body.
-	 *
-	 * @param name The URL parameter name.
-	 * @return <jk>true</jk> if the URL parameters on this request contains the specified entry.
-	 */
-	public boolean hasQueryParameter(String name) {
-		return queryParams.containsKey(name);
-	}
-
-	/**
-	 * Equivalent to {@link #getParameterMap()}, but only looks for query parameters in the URL, not form posts.
-	 * <p>
-	 * This method can be used to retrieve query parameters without triggering the underlying
-	 * 	servlet API to load and parse the request body.
-	 * <p>
-	 * This object is modifiable.
-	 *
-	 * @return The query parameters as a modifiable map.
-	 */
-	public Map<String,String[]> getQueryParameterMap() {
-		return queryParams;
-	}
-
-	/**
-	 * Equivalent to {@link #getParameterNames()}, but only looks for query parameters in the URL, not form posts.
-	 * <p>
-	 * This method can be used to retrieve query parameters without triggering the underlying
-	 * 	servlet API to load and parse the request body.
-	 * <p>
-	 * This object is modifiable.
-	 *
-	 * @return An iterator of query parameter names.
-	 */
-	public Iterator<String> getQueryParameterNames() {
-		return queryParams.keySet().iterator();
-	}
-
-	private <T> T parseParameter(String val, ClassMeta<T> c) throws ParseException {
-		if (val == null)
-			return null;
-		// Shortcut - If we're returning a string and the value doesn't start with '$' or '(', then
-		// just return the string since it's a plain value.
-		if (c.getInnerClass() == String.class && val.length() > 0) {
-			char x = val.charAt(0);
-			if (x != '(' && x != '$' && x != '\u0000' && val.indexOf('~') == -1)
-				return (T)val;
-		}
-		return urlEncodingParser.parseParameter(val, c);
-	}
-
-	/**
-	 * Shortcut for calling <code>getHeaders().get(c, name, def);</code>
-	 * <p>
-	 * 	The type can be any POJO type convertable from a <code>String</code> (See <a class='doclink' href='package-summary.html#PojosConvertableFromString'>POJOs Convertable From Strings</a>).
-	 *
-	 * @param name The HTTP header name.
-	 * @param c The class type to convert the header value to.
-	 * @param def The default value if the header was not specified or is <jk>null</jk>.
-	 * @param <T> The class type to convert the header value to.
-	 * @return The parameter value converted to the specified class type.
-	 */
-	public <T> T getHeader(String name, Class<T> c, T def) {
-		String h = getHeader(name);
-		if (h == null)
-			return def;
-		return beanContext.convertToType(h, c);
-	}
-
-	/**
-	 * Shortcut for calling <code>getHeaders().get(c, name);</code>
-	 * <p>
-	 * 	The type can be any POJO type convertable from a <code>String</code> (See <a class='doclink' href='package-summary.html#PojosConvertableFromString'>POJOs Convertable From Strings</a>).
-	 *
-	 * @param name The HTTP header name.
-	 * @param c 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 getHeader(String name, Class<T> c) {
-		String h = getHeader(name);
-		return beanContext.convertToType(h, c);
-	}
-
-	/**
-	 * Same as {@link #getHeader(String, Class)} except works on parameterized
-	 * types such as those returned by {@link Method#getGenericParameterTypes()}
-	 *
-	 * @param name The HTTP header name.
-	 * @param c 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 getHeader(String name, Type c) {
-		String h = getHeader(name);
-		return (T)beanContext.convertToType(null, h, beanContext.getClassMeta(c));
-	}
-
-	/**
-	 * Returns the specified request attribute converted to the specified class type.
-	 * <p>
-	 * 	The type can be any POJO type convertable from a <code>String</code> (See <a class='doclink' href='package-summary.html#PojosConvertableFromString'>POJOs Convertable From Strings</a>).
-	 *
-	 * @param name The attribute name.
-	 * @param c 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 getAttribute(String name, Class<T> c) throws ParseException {
-		return getAttribute(name, beanContext.getClassMeta(c));
-	}
-
-	/**
-	 * Same as {@link #getAttribute(String, Class)} except works on parameterized
-	 * types such as those returned by {@link Method#getGenericParameterTypes()}
-	 *
-	 * @param name The attribute name.
-	 * @param c 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 getAttribute(String name, Type c) throws ParseException {
-		return (T)getAttribute(name, beanContext.getClassMeta(c));
-	}
-
-	/**
-	 * Returns the specified request attribute converted to the specified class type.
-	 * <p>
-	 * 	The type can be any POJO type convertable from a <code>String</code> (See <a class='doclink' href='package-summary.html#PojosConvertableFromString'>POJOs Convertable From Strings</a>).
-	 *
-	 * @param name The attribute name.
-	 * @param cm 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 getAttribute(String name, ClassMeta<T> cm) throws ParseException {
-		Object attr = getAttribute(name);
-		T t = null;
-		if (attr != null)
-			t = urlEncodingParser.parseParameter(attr.toString(), cm);
-		if (t == null && cm.isPrimitive())
-			return cm.getPrimitiveDefault();
-		return t;
-	}
-
-	/**
-	 * Same as {@link HttpServletRequest#getPathInfo()} except returns the path undecoded.
-	 *
-	 * @return The undecoded portion of the URL after the resource URL path pattern match.
-	 */
-	public String getPathInfoUndecoded() {
-		return RestUtils.getPathInfoUndecoded(this);
-	}
-
-	/**
-	 * Returns the value {@link #getPathInfo()} split on the <js>'/'</js> character.
-	 * <p>
-	 * If path info is <jk>null</jk>, returns an empty list.
-	 * <p>
-	 * URL-encoded characters in segments are automatically decoded by this method.
-	 *
-	 * @return The decoded segments, or an empty list if path info is <jk>null</jk>.
-	 */
-	public String[] getPathInfoParts() {
-		String s = getPathInfoUndecoded();
-		if (s == null || s.isEmpty() || s.equals("/"))
-			return new String[0];
-		s = s.substring(1);
-		if (s.endsWith("/"))
-			s = s.substring(0, s.length()-1);
-		boolean needsDecode = (s.indexOf('%') != -1 || s.indexOf('+') != -1);
-		String[] l = s.split("/", Integer.MAX_VALUE);
-		try {
-			if (needsDecode)
-				for (int i = 0; i < l.length; i++)
-					l[i] = URLDecoder.decode(l[i], "UTF-8");
-		} catch (UnsupportedEncodingException e) {
-			e.printStackTrace();  // Won't happen.
-		}
-		return l;
-	}
-
-	/**
-	 * Same as {@link #getInput(ClassMeta)}, except a shortcut for passing in regular {@link Class} objects
-	 * 	instead of having to look up {@link ClassMeta} objects.
-	 *
-	 * @param type The class type to instantiate.
-	 * @param <T> The class type to instantiate.
-	 * @return The input parsed to a POJO.
-	 * @throws IOException If a problem occurred trying to read from the reader.
-	 * @throws ParseException If the input contains a syntax error or is malformed for the requested {@code Accept} header or is not valid for the specified type.
-	 */
-	public <T> T getInput(Class<T> type) throws IOException, ParseException {
-		return getInput(beanContext.getClassMeta(type));
-	}
-
-	/**
-	 * Same as {@link #getInput(Class)} except works on parameterized
-	 * types such as those returned by {@link Method#getGenericParameterTypes()}
-	 *
-	 * @param type The class type to instantiate.
-	 * @param <T> The class type to instantiate.
-	 * @return The input parsed to a POJO.
-	 */
-	public <T> T getInput(Type type) {
-		return (T)getInput(beanContext.getClassMeta(type));
-	}
-
-	/**
-	 * Reads the input from the HTTP request as JSON, XML, or HTML and converts the input to the specified class type.
-	 * <p>
-	 * 	If {@code allowHeaderParams} init parameter is <jk>true</jk>, then first looks
-	 * 	for {@code &content=xxx} in the URL query string.
-	 * <p>
-	 * 	If type is <jk>null</jk> or <code>Object.<jk>class</jk></code>, then the actual type will be determined automatically based on the
-	 * 	following input:
-	 * 	<table class='styled'>
-	 * 		<tr><th>Type</th><th>JSON input</th><th>XML input</th><th>Return type</th></tr>
-	 * 		<tr>
-	 * 			<td>object</td>
-	 * 			<td><js>"{...}"</js></td>
-	 * 			<td><code><xt>&lt;object&gt;</xt>...<xt>&lt;/object&gt;</xt></code><br><code><xt>&lt;x</xt> <xa>type</xa>=<xs>'object'</xs><xt>&gt;</xt>...<xt>&lt;/x&gt;</xt></code></td>
-	 * 			<td>{@link ObjectMap}</td>
-	 * 		</tr>
-	 * 		<tr>
-	 * 			<td>array</td>
-	 * 			<td><js>"[...]"</js></td>
-	 * 			<td><code><xt>&lt;array&gt;</xt>...<xt>&lt;/array&gt;</xt></code><br><code><xt>&lt;x</xt> <xa>type</xa>=<xs>'array'</xs><xt>&gt;</xt>...<xt>&lt;/x&gt;</xt></code></td>
-	 * 			<td>{@link ObjectList}</td>
-	 * 		</tr>
-	 * 		<tr>
-	 * 			<td>string</td>
-	 * 			<td><js>"'...'"</js></td>
-	 * 			<td><code><xt>&lt;string&gt;</xt>...<xt>&lt;/string&gt;</xt></code><br><code><xt>&lt;x</xt> <xa>type</xa>=<xs>'string'</xs><xt>&gt;</xt>...<xt>&lt;/x&gt;</xt></code></td>
-	 * 			<td>{@link String}</td>
-	 * 		</tr>
-	 * 		<tr>
-	 * 			<td>number</td>
-	 * 			<td><code>123</code></td>
-	 * 			<td><code><xt>&lt;number&gt;</xt>123<xt>&lt;/number&gt;</xt></code><br><code><xt>&lt;x</xt> <xa>type</xa>=<xs>'number'</xs><xt>&gt;</xt>...<xt>&lt;/x&gt;</xt></code></td>
-	 * 			<td>{@link Number}</td>
-	 * 		</tr>
-	 * 		<tr>
-	 * 			<td>boolean</td>
-	 * 			<td><jk>true</jk></td>
-	 * 			<td><code><xt>&lt;boolean&gt;</xt>true<xt>&lt;/boolean&gt;</xt></code><br><code><xt>&lt;x</xt> <xa>type</xa>=<xs>'boolean'</xs><xt>&gt;</xt>...<xt>&lt;/x&gt;</xt></code></td>
-	 * 			<td>{@link Boolean}</td>
-	 * 		</tr>
-	 * 		<tr>
-	 * 			<td>null</td>
-	 * 			<td><jk>null</jk> or blank</td>
-	 * 			<td><code><xt>&lt;null/&gt;</xt></code> or blank<br><code><xt>&lt;x</xt> <xa>type</xa>=<xs>'null'</xs><xt>/&gt;</xt></code></td>
-	 * 			<td><jk>null</jk></td>
-	 * 		</tr>
-	 * 	</table>
-	 * <p>
-	 * 	Refer to <a href='../core/package-summary.html#PojoCategories' class='doclink'>POJO Categories</a> for a complete definition of supported POJOs.
-	 *
-	 * @param type The class type to instantiate.
-	 * @param <T> The class type to instantiate.
-	 * @return The input parsed to a POJO.
-	 * @throws RestException If a problem occurred trying to read the input.
-	 */
-	public <T> T getInput(ClassMeta<T> type) throws RestException {
-
-		try {
-			if (type.isReader())
-				return (T)getReader();
-
-			if (type.isInputStream())
-				return (T)getInputStream();
-
-			String mediaType = getMediaType();
-			Parser p = getParser();
-
-			if (p != null) {
-				try {
-					properties.append("mediaType", mediaType).append("characterEncoding", getCharacterEncoding());
-					if (! p.isReaderParser()) {
-						InputStreamParser p2 = (InputStreamParser)p;
-						ParserSession session = p2.createSession(getInputStream(), properties, getJavaMethod(), getServlet());
-						return p2.parse(session, type);
-					}
-					ReaderParser p2 = (ReaderParser)p;
-					ParserSession session = p2.createSession(getUnbufferedReader(), properties, getJavaMethod(), getServlet());
-					return p2.parse(session, type);
-				} catch (ParseException e) {
-					throw new RestException(SC_BAD_REQUEST,
-						"Could not convert request body content to class type ''{0}'' using parser ''{1}''.",
-						type, p.getClass().getName()
-					).initCause(e);
-				}
-			}
-
-			throw new RestException(SC_UNSUPPORTED_MEDIA_TYPE,
-				"Unsupported media-type in request header ''Content-Type'': ''{0}''\n\tSupported media-types: {1}",
-				getHeader("Content-Type"), parserGroup.getSupportedMediaTypes()
-			);
-
-		} catch (IOException e) {
-			throw new RestException(SC_INTERNAL_SERVER_ERROR,
-				"I/O exception occurred while attempting to handle request ''{0}''.",
-				getDescription()
-			).initCause(e);
-		}
-	}
-
-	/**
-	 * Returns the parser matching the request <code>Accept</code> header.
-	 *
-	 * @return The parser matching the request <code>Accept</code> header, or <jk>null</jk>
-	 * 	if no matching parser was found.
-	 */
-	public Parser getParser() {
-		String mediaType = getMediaType();
-		Parser p = parserGroup.getParser(mediaType);
-
-		// If no patching parser for URL-encoding, use the one defined on the servlet.
-		if (p == null && mediaType.equals("application/x-www-form-urlencoded"))
-			p = urlEncodingParser;
-
-		return p;
-	}
-
-	/**
-	 * Returns the reader parser matching the request <code>Accept</code> header.
-	 *
-	 * @return The reader parser matching the request <code>Accept</code> header, or <jk>null</jk>
-	 * 	if no matching reader parser was found, or the matching parser was an input stream parser.
-	 */
-	public ReaderParser getReaderParser() {
-		Parser p = getParser();
-		if (p.isReaderParser())
-			return (ReaderParser)p;
-		return null;
-	}
-
-	/**
-	 * Returns the HTTP body content as a plain string.
-	 * <p>
-	 * 	If {@code allowHeaderParams} init parameter is true, then first looks
-	 * 	for {@code &content=xxx} in the URL query string.
-	 *
-	 * @return The incoming input from the connection as a plain string.
-	 * @throws IOException If a problem occurred trying to read from the reader.
-	 */
-	public String getInputAsString() throws IOException {
-		if (content != null)
-			return content;
-		content = IOUtils.read(getReader()).toString();
-		return content;
-	}
-
-	/**
-	 * Returns a resolved URL.
-	 * <p>
-	 * <ul class='spaced-list'>
-	 * 	<li>Fully-qualified absolute URLs (e.g. <js>"http://..."</js>, <js>"https://"</js>) are simply converted to a URL.
-	 * 	<li>Absolute URLs (e.g. <js>"/foo/..."</js>) are interpreted as relative to the server hostname.
-	 * 	<li>Relative URLs (e.g. <js>"foo/..."</js>) are interpreted as relative to this servlet path.
-	 * </ul>
-	 *
-	 * @param path The URL path to resolve.
-	 * @return The resolved URL.
-	 * @throws MalformedURLException If path is not a valid URL component.
-	 */
-	public URL getURL(String path) throws MalformedURLException {
-		if (path.startsWith("http://") || path.startsWith("https://"))
-			return new URL(path);
-		if (StringUtils.startsWith(path, '/'))
-			return new URL(getScheme(), getLocalName(), getLocalPort(), path);
-		return new URL(getScheme(), getLocalName(), getLocalPort(), getContextPath() + getServletPath() + (StringUtils.isEmpty(path) ? "" : ('/' + path)));
-	}
-
-	/**
-	 * Returns the HTTP body content as a {@link Reader}.
-	 * <p>
-	 * 	If {@code allowHeaderParams} init parameter is true, then first looks
-	 * 	for {@code &content=xxx} in the URL query string.
-	 * <p>
-	 * 	Automatically handles GZipped input streams.
-	 */
-	@Override /* ServletRequest */
-	public BufferedReader getReader() throws IOException {
-		Reader r = getUnbufferedReader();
-		if (r instanceof BufferedReader)
-			return (BufferedReader)r;
-		int len = getContentLength();
-		int buffSize = len <= 0 ? 8192 : Math.max(len, 8192);
-		return new BufferedReader(r, buffSize);
-	}
-
-	/**
-	 * Same as {@link #getReader()}, but doesn't encapsulate the result in a {@link BufferedReader};
-	 *
-	 * @return An unbuffered reader.
-	 * @throws IOException
-	 */
-	protected Reader getUnbufferedReader() throws IOException {
-		if (content != null)
-			return new CharSequenceReader(content);
-		return new InputStreamReader(getInputStream(), getCharacterEncoding());
-	}
-
-	/**
-	 * Returns the HTTP body content as an {@link InputStream}.
-	 * <p>
-	 * 	Automatically handles GZipped input streams.
-	 *
-	 * @return The negotiated input stream.
-	 * @throws IOException If any error occurred while trying to get the input stream or wrap it
-	 * 	in the GZIP wrapper.
-	 */
-	@Override /* ServletRequest */
-	public ServletInputStream getInputStream() throws IOException {
-
-		Encoder enc = getEncoder();
-
-		ServletInputStream is = super.getInputStream();
-		if (enc != null) {
-			final InputStream is2 = enc.getInputStream(is);
-			return new ServletInputStream() {
-				@Override /* InputStream */
-				public final int read() throws IOException {
-					return is2.read();
-				}
-				@Override /* InputStream */
-				public final void close() throws IOException {
-					is2.close();
-				}
-			};
-		}
-		return is;
-	}
-
-	private Encoder getEncoder() {
-		if (encoder == null) {
-			String ce = getHeader("content-encoding");
-			if (! (ce == null || ce.isEmpty())) {
-				ce = ce.trim();
-				encoder = servlet.getEncoders().getEncoder(ce);
-				if (encoder == null)
-					throw new RestException(SC_UNSUPPORTED_MEDIA_TYPE,
-						"Unsupported encoding in request header ''Content-Encoding'': ''{0}''\n\tSupported codings: {1}",
-						getHeader("content-encoding"), servlet.getEncoders().getSupportedEncodings()
-					);
-			}
-
-			if (encoder != null)
-				contentLength = -1;
-		}
-		// Note that if this is the identity encoder, we want to return null
-		// so that we don't needlessly wrap the input stream.
-		if (encoder == IdentityEncoder.INSTANCE)
-			return null;
-		return encoder;
-	}
-
-	@Override /* ServletRequest */
-	public int getContentLength() {
-		return contentLength == 0 ? super.getContentLength() : contentLength;
-	}
-
-	/**
-	 * Returns <jk>true</jk> if <code>&plainText=true</code> was specified as a URL parameter.
-	 * <p>
-	 * 	This indicates that the <code>Content-Type</code> of the output should always be set to <js>"text/plain"</js>
-	 * 	to make it easy to render in a browser.
-	 * <p>
-	 * 	This feature is useful for debugging.
-	 *
-	 * @return <jk>true</jk> if {@code &plainText=true} was specified as a URL parameter
-	 */
-	public boolean isPlainText() {
-		return "true".equals(getQueryParameter("plainText", "false"));
-	}
-
-	/**
-	 * Returns the decoded remainder of the URL following any path pattern matches.
-	 * <p>
-	 * 	The behavior of path remainder is shown below given the path pattern "/foo/*":
-	 * <p>
-	 * 	<table class='styled'>
-	 * 		<tr>
-	 * 			<th>URL</th>
-	 * 			<th>Path Remainder</th>
-	 * 		</tr>
-	 * 		<tr>
-	 * 			<th><code>/foo</code></th>
-	 * 			<th><jk>null</jk></th>
-	 * 		</tr>
-	 * 		<tr>
-	 * 			<th><code>/foo/</code></th>
-	 * 			<th><js>""</js></th>
-	 * 		</tr>
-	 * 		<tr>
-	 * 			<th><code>/foo//</code></th>
-	 * 			<th><js>"/"</js></th>
-	 * 		</tr>
-	 * 		<tr>
-	 * 			<th><code>/foo///</code></th>
-	 * 			<th><js>"//"</js></th>
-	 * 		</tr>
-	 * 		<tr>
-	 * 			<th><code>/foo/a/b</code></th>
-	 * 			<th><js>"a/b"</js></th>
-	 * 		</tr>
-	 * 		<tr>
-	 * 			<th><code>/foo//a/b/</code></th>
-	 * 			<th><js>"/a/b/"</js></th>
-	 * 		</tr>
-	 * 		<tr>
-	 * 			<th><code>/foo/a%2Fb</code></th>
-	 * 			<th><js>"a/b"</js></th>
-	 * 		</tr>
-	 * 	</table>
-	 *
-	 * <dl>
-	 * 	<dt>Example:</dt>
-	 * 	<dd>
-	 * 		<p class='bcode'>
-	 * 	<jc>// REST method</jc>
-	 * 	<ja>@RestMethod</ja>(name=<js>"GET"</js>,path=<js>"/foo/{bar}/*"</js>)
-	 * 	<jk>public</jk> doGetById(RestServlet res, RestResponse res, <jk>int</jk> bar) {
-	 * 		System.<jsm>err</jsm>.println(res.getRemainder());
-	 * 	}
-	 *
-	 * 	<jc>// Prints "path/remainder"</jc>
-	 * 	<jk>new</jk> RestCall(servletPath + <js>"/foo/123/path/remainder"</js>).connect();
-	 * 		</p>
-	 * 	</dd>
-	 * </dl>
-	 *
-	 * @return The path remainder string.
-	 */
-	public String getPathRemainder() {
-		return RestUtils.decode(pathRemainder);
-	}
-
-	/**
-	 * Same as {@link #getPathRemainder()} but doesn't decode characters.
-	 *
-	 * @return The undecoded path remainder.
-	 */
-	public String getPathRemainderUndecoded() {
-		return pathRemainder;
-	}
-
-	/**
-	 * Shortcut method for calling {@link RestServlet#getMessage(Locale, String, Object...)} based
-	 * 	on the request locale.
-	 *
-	 * @param key The message key.
-	 * @param args Optional {@link MessageFormat} variable values in the value.
-	 * @return The localized message.
-	 */
-	public String getMessage(String key, Object...args) {
-		return servlet.getMessage(getLocale(), key, args);
-	}
-
-	/**
-	 * Shortcut method for calling {@link RestServlet#getMethodDescriptions(RestRequest)} based
-	 * 	on the request locale.
-	 *
-	 * @return The localized method descriptions.
-	 * @throws RestServletException
-	 */
-	public Collection<MethodDescription> getMethodDescriptions() throws RestServletException {
-		return servlet.getMethodDescriptions(this);
-	}
-
-	/**
-	 * Returns the resource bundle for the request locale.
-	 *
-	 * @return The resource bundle.  Never <jk>null</jk>.
-	 */
-	public MessageBundle getResourceBundle() {
-		return servlet.getMessages(getLocale());
-	}
-
-	/**
-	 * Returns the servlet handling the request.
-	 * <p>
-	 * Can be used to access servlet-init parameters or annotations during requests,
-	 * 	such as in calls to {@link RestGuard#guard(RestRequest, RestResponse)}..
-	 *
-	 * @return The servlet handling the request.
-	 */
-	public RestServlet getServlet() {
-		return servlet;
-	}
-
-	/**
-	 * Returns the java method handling the request.
-	 * <p>
-	 * Can be used to access the method name or method annotations during requests, such
-	 * 	as in calls to {@link RestGuard#guard(RestRequest, RestResponse)}.
-	 * <p>
-	 * Note:  This returns null when evaluating servlet-level guards since the method
-	 * 	has not been resolved at that point of execution.
-	 *
-	 * @return The Java method handling the request, or <code>null</code> if the method
-	 * 	has not yet been resolved.
-	 */
-	public Method getJavaMethod() {
-		return javaMethod;
-	}
-
-	/**
-	 * Returns the URI of the parent resource.
-	 * <p>
-	 * Trailing slashes in the path are ignored by this method.
-	 * <p>
-	 * The behavior is shown below:
-	 * 	<table class='styled'>
-	 * 		<tr>
-	 * 			<th>getRequestURI</th>
-	 * 			<th>getRequestParentURI</th>
-	 * 		</tr>
-	 * 		<tr>
-	 * 			<th><code>/foo/bar</code></th>
-	 * 			<th><code>/foo</code></th>
-	 * 		</tr>
-	 * 		<tr>
-	 * 			<th><code>/foo/bar?baz=bing</code></th>
-	 * 			<th><code>/foo</code></th>
-	 * 		</tr>
-	 * 		<tr>
-	 * 			<th><code>/foo/bar/</code></th>
-	 * 			<th><code>/foo</code></th>
-	 * 		</tr>
-	 * 		<tr>
-	 * 			<th><code>/foo/bar//</code></th>
-	 * 			<th><code>/foo</code></th>
-	 * 		</tr>
-	 * 		<tr>
-	 * 			<th><code>/foo//bar//</code></th>
-	 * 			<th><code>/foo/</code></th>
-	 * 		</tr>
-	 * 		<tr>
-	 * 			<th><code>/foo</code></th>
-	 * 			<th>/</th>
-	 * 		</tr>
-	 * 	</table>
-	 *
-	 * @return The request parent URI.
-	 */
-	public String getRequestParentURI() {
-		String uri = getRequestURI();
-		while (StringUtils.endsWith(uri, '/'))
-			uri = uri.substring(0, uri.length()-1);
-		int i = uri.lastIndexOf('/');
-		if (i <= 0)
-			return "/";
-		return uri.substring(0, i);
-	}
-
-	/**
-	 * Same as {@link #getRequestURI()} but trims trailing slashes from the result.
-	 *
-	 * @return The trimmed request URI.
-	 */
-	public String getTrimmedRequestURI() {
-		return RestUtils.trimTrailingSlashes(getRequestURI());
-	}
-
-	/**
-	 * Same as {@link #getRequestURL()} but trims trailing slashes from the result.
-	 *
-	 * @return The trimmed request URL.
-	 */
-	public StringBuffer getTrimmedRequestURL() {
-		return RestUtils.trimTrailingSlashes(getRequestURL());
-	}
-
-	/**
-	 * Gets the URI of the servlet (e.g. <js>"https://localhost:9080/contextPath/servletPath"</js>).
-	 *
-	 * @return The servlet URI.
-	 */
-	public String getServletURI() {
-		if (servletURI == null) {
-			// Note that we can't use getPathInfo() to calculate this since it replaces
-			// URL-encoded chars (e.g. %2F) which throws off the length calculation
-			// because getRequestURL() does not replace those chars.
-			servletURI = getServletURIBuilder().toString();
-		}
-		return servletURI;
-	}
-
-	/**
-	 * Gets the path-absolute relative URI of the servlet (e.g. <js>"/contextPath/servletPath"</js>).
-	 *
-	 * @return The relative servlet URI.
-	 */
-	public String getRelativeServletURI() {
-		if (relativeServletURI == null)
-			relativeServletURI = getContextPath() + getServletPath();
-		return relativeServletURI;
-	}
-
-	/**
-	 * Returns a <code>StringBuffer</code> prefilled with the string <code><js>"/[contextPath]/[servletPath]"</js></code>.
-	 *
-	 * @return The servlet URI string builder.
-	 */
-	public StringBuffer getServletURIBuilder() {
-		return RestUtils.trimPathInfo(getRequestURL(), getContextPath(), getServletPath());
-	}
-
-	/**
-	 * Returns the {@link BeanContext} associated with this request.
-	 *
-	 * @return The request bean context.
-	 */
-	public BeanContext getBeanContext() {
-		return beanContext;
-	}
-
-	/**
-	 * Returns the localized servlet label.
-	 * Equivalent to calling {@link RestServlet#getLabel(RestRequest)} with this object.
-	 *
-	 * @return The localized servlet label.
-	 */
-	public String getServletLabel() {
-		return servlet.getLabel(this);
-	}
-
-	/**
-	 * Returns the localized servlet description.
-	 * Equivalent to calling {@link RestServlet#getDescription(RestRequest)} with this object.
-	 *
-	 * @return The localized servlet description.
-	 */
-	public String getServletDescription() {
-		return servlet.getDescription(this);
-	}
-
-	/**
-	 * Returns the localized method description.
-	 * Equivalent to calling {@link RestServlet#getMethodDescription(String, RestRequest)} with this object.
-	 *
-	 * @return The localized method description.
-	 */
-	public String getMethodDescription() {
-		return servlet.getMethodDescription(javaMethod.getName(), this);
-	}
-
-	/**
-	 * Returns the variable resolver session for this request using session objects created by {@link RestServlet#getSessionObjects(RestRequest)}.
-	 *
-	 * @return The variable resolver for this request.
-	 */
-	public VarResolverSession getVarResolverSession() {
-		if (varSession == null)
-			varSession = servlet.getVarResolver().createSession(servlet.getSessionObjects(this));
-		return varSession;
-	}
-
-	/**
-	 * Shortcut for calling <code>getVarResolverSession().resolve(input)</code>.
-	 *
-	 * @param input The input string to resolve variables in.
-	 * @return The string with variables resolved, or <jk>null</jk> if input is null.
-	 */
-	public String resolveVars(String input) {
-		return getVarResolverSession().resolve(input);
-	}
-
-	/**
-	 * Returns an instance of a {@link ReaderResource} that represents the contents of a resource text file from the classpath.
-	 * <p>
-	 *
-	 * @param name The name of the resource (i.e. the value normally passed to {@link Class#getResourceAsStream(String)}.
-	 * @param resolveVars If <jk>true</jk>, any {@link Var} variables will be resolved by the variable resolver returned
-	 * 	by {@link #getVarResolverSession()}.
-	 * @param contentType The value to set as the <js>"Content-Type"</js> header for this object.
-	 * @return A new reader resource, or <jk>null</jk> if resource could not be found.
-	 * @throws IOException
-	 */
-	public ReaderResource getReaderResource(String name, boolean resolveVars, String contentType) throws IOException {
-		String s = servlet.getResourceAsString(name);
-		if (s == null)
-			return null;
-		ReaderResource rr = new ReaderResource(s, contentType);
-		if (resolveVars)
-			rr.setVarSession(getVarResolverSession());
-		return rr;
-	}
-
-	/**
-	 * Same as {@link #getReaderResource(String, boolean, String)} except uses {@link RestServlet#getMimetypesFileTypeMap()}
-	 * to determine the media type.
-	 *
-	 * @param name The name of the resource (i.e. the value normally passed to {@link Class#getResourceAsStream(String)}.
-	 * @param resolveVars If <jk>true</jk>, any {@link Var} variables will be resolved by the variable resolver returned
-	 * 	by {@link #getVarResolverSession()}.
-	 * @return A new reader resource, or <jk>null</jk> if resource could not be found.
-	 * @throws IOException
-	 */
-	public ReaderResource getReaderResource(String name, boolean resolveVars) throws IOException {
-		return getReaderResource(name, resolveVars, servlet.getMimetypesFileTypeMap().getContentType(name));
-	}
-
-	/**
-	 * Same as {@link #getReaderResource(String, boolean)} with <code>resolveVars == <jk>false</jk></code>
-	 *
-	 * @param name The name of the resource (i.e. the value normally passed to {@link Class#getResourceAsStream(String)}.
-	 * @return A new reader resource, or <jk>null</jk> if resource could not be found.
-	 * @throws IOException
-	 */
-	public ReaderResource getReaderResource(String name) throws IOException {
-		return getReaderResource(name, false, servlet.getMimetypesFileTypeMap().getContentType(name));
-	}
-
-	/**
-	 * Returns the config file associated with the servlet.
-	 *
-	 * @return The config file associated with the servlet, or <jk>null</jk> if servlet does not have a config file associated with it.
-	 */
-	public ConfigFile getConfig() {
-		if (cf == null)
-			cf = servlet.getConfig().getResolving(getVarResolverSession());
-		return cf;
-	}
-
-	@Override /* Object */
-	public String toString() {
-		StringBuilder sb = new StringBuilder("\n").append(getDescription()).append("\n");
-		sb.append("---Headers---\n");
-		for (Enumeration<String> e = getHeaderNames(); e.hasMoreElements();) {
-			String h = e.nextElement();
-			sb.append("\t").append(h).append(": ").append(getHeader(h)).append("\n");
-		}
-		sb.append("---Default Servlet Headers---\n");
-		for (Map.Entry<String,String> e : defaultServletHeaders.entrySet()) {
-			sb.append("\t").append(e.getKey()).append(": ").append(e.getValue()).append("\n");
-		}
-		if (method.equals("PUT") || method.equals("POST")) {
-			sb.append("---Content---\n");
-			try {
-				sb.append(getInputAsString()).append("\n");
-			} catch (Exception e1) {
-				sb.append(e1.getLocalizedMessage());
-				servlet.log(WARNING, e1, "Error occurred while trying to read debug input.");
-			}
-		}
-		return sb.toString();
-	}
-
-	/**
-	 * Returns the URI of the parent of this servlet.
-	 *
-	 * @return The URI of the parent of this servlet.
-	 */
-	public String getServletParentURI() {
-		String s = getServletURI();
-		return s.substring(0, s.lastIndexOf('/'));
-	}
-}
\ No newline at end of file