You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2017/03/17 02:17:06 UTC

[1/7] incubator-juneau git commit: Allow @RestResource/@RestMethod annotations to be used on any classes.

Repository: incubator-juneau
Updated Branches:
  refs/heads/master db0cf72ba -> 07843d641


http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/RestServletContext.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestServletContext.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestServletContext.java
deleted file mode 100644
index f01c339..0000000
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/RestServletContext.java
+++ /dev/null
@@ -1,306 +0,0 @@
-// ***************************************************************************************************************************
-// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
-// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
-// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
-// * with the License.  You may obtain a copy of the License at                                                              *
-// *                                                                                                                         *
-// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
-// *                                                                                                                         *
-// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
-// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
-// * specific language governing permissions and limitations under the License.                                              *
-// ***************************************************************************************************************************
-package org.apache.juneau.rest;
-
-import java.util.*;
-
-import org.apache.juneau.*;
-import org.apache.juneau.internal.*;
-import org.apache.juneau.parser.*;
-import org.apache.juneau.rest.annotation.*;
-import org.apache.juneau.serializer.*;
-
-/**
- * Configurable properties on the {@link RestServlet} class.
- * <p>
- * Properties can be set on the {@link RestServlet} class using the {@link RestResource#properties} or {@link RestMethod#properties} annotations.
- * <p>
- * These properties can also be passed in as servlet init parameters or system properties.
- * <p>
- * Some of these properties are only applicable on the servlet class, and others can be specified on the servlet class or method.<br>
- * These distinctions are noted below.
- * <p>
- * See {@link PropertyStore} for more information about context properties.
- */
-public final class RestServletContext extends Context {
-
-	/**
-	 * <b>Configuration property:</b>  Enable header URL parameters.
-	 * <p>
-	 * <ul>
-	 * 	<li><b>Name:</b> <js>"RestServlet.allowHeaderParams"</js>
-	 * 	<li><b>Data type:</b> <code>Boolean</code>
-	 * 	<li><b>Default:</b> <jk>true</jk>
-	 * </ul>
-	 * <p>
-	 * When enabled, headers such as <js>"Accept"</js> and <js>"Content-Type"</js> to be passed in as URL query parameters.
-	 * For example:  <js>"?Accept=text/json&amp;Content-Type=text/json"</js>
-	 * <p>
-	 * Parameter names are case-insensitive.
-	 * <p>
-	 * Useful for debugging REST interface using only a browser.
-	 * <p>
-	 * Applicable to servlet class only.
-	 */
-	public static final String REST_allowHeaderParams = "RestServlet.allowHeaderParams";
-
-	/**
-	 * <b>Configuration property:</b>  Enable <js>"method"</js> URL parameter for specific HTTP methods.
-	 * <p>
-	 * <ul>
-	 * 	<li><b>Name:</b> <js>"RestServlet.allowMethodParam"</js>
-	 * 	<li><b>Data type:</b> <code>String</code>
-	 * 	<li><b>Default:</b> <js>""</js>
-	 * </ul>
-	 * <p>
-	 * When specified, the HTTP method can be overridden by passing in a <js>"method"</js> URL parameter on a regular GET request.
-	 * For example:  <js>"?method=OPTIONS"</js>
-	 * <p>
-	 * Format is a comma-delimited list of HTTP method names that can be passed in as a method parameter.
-	 * Parameter name is case-insensitive.
-	 * Use "*" to represent all methods.
-	 * For backwards compatibility, "true" also means "*".
-	 * <p>
-	 * Note that per the <a class="doclink" href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html">HTTP specification</a>, special care should
-	 * 	be taken when allowing non-safe (POST, PUT, DELETE) methods to be invoked through GET requests.
-	 * <p>
-	 * Applicable to servlet class only.
-	 * <p>
-	 * Example: <js>"HEAD,OPTIONS"</js>
-	 */
-	public static final String REST_allowMethodParam = "RestServlet.allowMethodParam";
-
-	/**
-	 * <b>Configuration property:</b>  Enable <js>"body"</js> URL parameter.
-	 * <p>
-	 * <ul>
-	 * 	<li><b>Name:</b> <js>"RestServlet.allowBodyParam"</js>
-	 * 	<li><b>Data type:</b> <code>Boolean</code>
-	 * 	<li><b>Default:</b> <jk>true</jk>
-	 * </ul>
-	 * <p>
-	 * When enabled, the HTTP body content on PUT and POST requests can be passed in as text using the <js>"body"</js> URL parameter.
-	 * For example:  <js>"?body={name:'John%20Smith',age:45}"</js>
-	 * <p>
-	 * Parameter name is case-insensitive.
-	 * <p>
-	 * Useful for debugging PUT and POST methods using only a browser.
-	 * <p>
-	 * Applicable to servlet class only.
-	 */
-	public static final String REST_allowBodyParam = "RestServlet.allowBodyParam";
-
-	/**
-	 * <b>Configuration property:</b>  Render stack traces.
-	 * <p>
-	 * <ul>
-	 * 	<li><b>Name:</b> <js>"RestServlet.renderResponseStackTraces"</js>
-	 * 	<li><b>Data type:</b> <code>Boolean</code>
-	 * 	<li><b>Default:</b> <jk>false</jk>
-	 * </ul>
-	 * <p>
-	 * Render stack traces in HTTP response bodies when errors occur.
-	 * <p>
-	 * When enabled, Java stack traces will be rendered in the output response.
-	 * Useful for debugging, although allowing stack traces to be rendered may cause security concerns.
-	 * <p>
-	 * Applicable to servlet class only.
-	 */
-	public static final String REST_renderResponseStackTraces = "RestServlet.renderResponseStackTraces";
-
-	/**
-	 * <b>Configuration property:</b>  Use stack trace hashes.
-	 * <p>
-	 * <ul>
-	 * 	<li><b>Name:</b> <js>"RestServlet.useStackTraceHashes"</js>
-	 * 	<li><b>Data type:</b> <code>Boolean</code>
-	 * 	<li><b>Default:</b> <jk>true</jk>
-	 * </ul>
-	 * <p>
-	 * When enabled, the number of times an exception has occurred will be determined based on stack trace hashsums,
-	 * 	made available through the {@link RestException#getOccurrence()} method.
-	 * <p>
-	 * Applicable to servlet class only.
-	 */
-	public static final String REST_useStackTraceHashes = "RestServlet.useStackTraceHashes";
-
-	/**
-	 * <b>Configuration property:</b>  Default character encoding.
-	 * <p>
-	 * <ul>
-	 * 	<li><b>Name:</b> <js>"RestServlet.defaultCharset"</js>
-	 * 	<li><b>Data type:</b> <code>String</code>
-	 * 	<li><b>Default:</b> <js>"utf-8"</js>
-	 * </ul>
-	 * <p>
-	 * The default character encoding for the request and response if not specified on the request.
-	 * <p>
-	 * Applicable to servlet class and methods.
-	 */
-	public static final String REST_defaultCharset = "RestServlet.defaultCharset";
-
-	/**
-	 * <b>Configuration property:</b>  Expected format of request parameters.
-	 * <p>
-	 * <ul>
-	 * 	<li><b>Name:</b> <js>"RestServlet.paramFormat"</js>
-	 * 	<li><b>Data type:</b> <code>String</code>
-	 * 	<li><b>Default:</b> <js>"UON"</js>
-	 * </ul>
-	 * <p>
-	 * Possible values:
-	 * <ul class='spaced-list'>
-	 * 	<li><js>"UON"</js> - URL-Encoded Object Notation.<br>
-	 * 		This notation allows for request parameters to contain arbitrarily complex POJOs.
-	 * 	<li><js>"PLAIN"</js> - Plain text.<br>
-	 * 		This treats request parameters as plain text.<br>
-	 * 		Only POJOs directly convertable from <l>Strings</l> can be represented in parameters when using this mode.
-	 * </ul>
-	 * <p>
-	 * Note that the parameter value <js>"(foo)"</js> is interpreted as <js>"(foo)"</js> when using plain mode, but
-	 * 	<js>"foo"</js> when using UON mode.
-	 * <p>
-	 * The format can also be specified per-parameter using the {@link FormData#format() @FormData.format()} and {@link Query#format() @Query.format()}
-	 * 	annotations.
-	 * <p>
-	 * Applicable to servlet class and methods.
-	 */
-	public static final String REST_paramFormat = "RestServlet.paramFormat";
-
-	//--------------------------------------------------------------------------------
-	// Automatically added properties.
-	//--------------------------------------------------------------------------------
-
-	/**
-	 * The request servlet path.
-	 * <p>
-	 * Automatically added to properties return by {@link RestServlet#createRequestProperties(org.apache.juneau.ObjectMap, RestRequest)}
-	 * 	and are therefore available through {@link SerializerSession#getProperties()} and {@link ParserSession#getProperties()}.
-	 * <p>
-	 * Equivalent to the value returned by {@link RestRequest#getServletPath()}
-	 */
-	public static final String REST_servletPath = "RestServlet.servletPath";
-
-	/**
-	 * The request servlet URI.
-	 * <p>
-	 * Equivalent to the value returned by {@link RestRequest#getServletURI()}
-	 */
-	public static final String REST_servletURI = "RestServlet.servletURI";
-
-	/**
-	 * The request servlet URI.
-	 * <p>
-	 * Equivalent to the value returned by {@link RestRequest#getRelativeServletURI()}
-	 */
-	public static final String REST_relativeServletURI = "RestServlet.relativeServletURI";
-
-	/**
-	 * The request URI path info.
-	 * <p>
-	 * Automatically added to properties return by {@link RestServlet#createRequestProperties(org.apache.juneau.ObjectMap, RestRequest)}
-	 * 	and are therefore available through {@link SerializerSession#getProperties()} and {@link ParserSession#getProperties()}.
-	 * <p>
-	 * Equivalent to the value returned by {@link RestRequest#getPathInfo()}
-	 */
-	public static final String REST_pathInfo = "RestServlet.pathInfo";
-
-	/**
-	 * The request URI.
-	 * <p>
-	 * Automatically added to properties return by {@link RestServlet#createRequestProperties(org.apache.juneau.ObjectMap, RestRequest)}
-	 * 	and are therefore available through {@link SerializerSession#getProperties()} and {@link ParserSession#getProperties()}.
-	 * <p>
-	 * Equivalent to the value returned by {@link RestRequest#getRequestURI()}
-	 */
-	public static final String REST_requestURI = "RestServlet.requestURI";
-
-	/**
-	 * The request method.
-	 * <p>
-	 * Automatically added to properties return by {@link RestServlet#createRequestProperties(org.apache.juneau.ObjectMap, RestRequest)}
-	 * 	and are therefore available through {@link SerializerSession#getProperties()} and {@link ParserSession#getProperties()}.
-	 * <p>
-	 * Equivalent to the value returned by {@link RestRequest#getMethod()}
-	 */
-	public static final String REST_method = "RestServlet.method";
-
-	/**
-	 * The localized servlet title.
-	 * <p>
-	 * Automatically added to properties return by {@link RestServlet#createRequestProperties(org.apache.juneau.ObjectMap, RestRequest)}
-	 * 	and are therefore available through {@link SerializerSession#getProperties()} and {@link ParserSession#getProperties()}.
-	 * <p>
-	 * Equivalent to the value returned by {@link RestRequest#getServletTitle()}
-	 */
-	public static final String REST_servletTitle = "RestServlet.servletTitle";
-
-	/**
-	 * The localized servlet description.
-	 * <p>
-	 * Automatically added to properties return by {@link RestServlet#createRequestProperties(org.apache.juneau.ObjectMap, RestRequest)}
-	 * 	and are therefore available through {@link SerializerSession#getProperties()} and {@link ParserSession#getProperties()}.
-	 * <p>
-	 * Equivalent to the value returned by {@link RestRequest#getServletDescription()}
-	 */
-	public static final String REST_servletDescription = "RestServlet.servletDescription";
-
-	/**
-	 * The localized method summary.
-	 * <p>
-	 * Automatically added to properties return by {@link RestServlet#createRequestProperties(org.apache.juneau.ObjectMap, RestRequest)}
-	 * 	and are therefore available through {@link SerializerSession#getProperties()} and {@link ParserSession#getProperties()}.
-	 * <p>
-	 * Equivalent to the value returned by {@link RestRequest#getMethodSummary()}
-	 */
-	public static final String REST_methodSummary = "RestServlet.methodSummary";
-
-	/**
-	 * The localized method description.
-	 * <p>
-	 * Automatically added to properties return by {@link RestServlet#createRequestProperties(org.apache.juneau.ObjectMap, RestRequest)}
-	 * 	and are therefore available through {@link SerializerSession#getProperties()} and {@link ParserSession#getProperties()}.
-	 * <p>
-	 * Equivalent to the value returned by {@link RestRequest#getMethodDescription()}
-	 */
-	public static final String REST_methodDescription = "RestServlet.methodDescription";
-
-	final boolean allowHeaderParams, allowBodyParam, renderResponseStackTraces, useStackTraceHashes;
-	final String defaultCharset, paramFormat;
-	final Set<String> allowMethodParams;
-
-	/**
-	 * Constructor.
-	 * <p>
-	 * Typically only called from {@link PropertyStore#getContext(Class)}.
-	 *
-	 * @param ps The property store that created this context.
-	 */
-	public RestServletContext(PropertyStore ps) {
-		super(ps);
-		allowHeaderParams = ps.getProperty(REST_allowHeaderParams, boolean.class, true);
-		allowBodyParam = ps.getProperty(REST_allowBodyParam, boolean.class, true);
-		renderResponseStackTraces = ps.getProperty(REST_renderResponseStackTraces, boolean.class, false);
-		useStackTraceHashes = ps.getProperty(REST_useStackTraceHashes, boolean.class, true);
-		defaultCharset = ps.getProperty(REST_defaultCharset, String.class, "utf-8");
-		paramFormat = ps.getProperty(REST_paramFormat, String.class, "");
-
-		Set<String> s = new LinkedHashSet<String>();
-		for (String m : StringUtils.split(ps.getProperty(REST_allowMethodParam, String.class, ""), ','))
-			if (m.equals("true"))  // For backwards compatibility when this was a boolean field.
-				s.add("*");
-			else
-				s.add(m.toUpperCase());
-		allowMethodParams = Collections.unmodifiableSet(s);
-	}
-}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/RestServletDefault.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestServletDefault.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestServletDefault.java
index 8af7efe..8117108 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/RestServletDefault.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestServletDefault.java
@@ -13,7 +13,7 @@
 package org.apache.juneau.rest;
 
 import static org.apache.juneau.html.HtmlDocSerializerContext.*;
-import static org.apache.juneau.rest.RestServletContext.*;
+import static org.apache.juneau.rest.RestContext.*;
 
 import org.apache.juneau.dto.swagger.*;
 import org.apache.juneau.html.*;
@@ -211,9 +211,4 @@ public abstract class RestServletDefault extends RestServlet {
 	public Swagger getOptions(RestRequest req) {
 		return req.getSwagger();
 	}
-
-	@Override /* RestServlet */
-	public boolean hasOptionsPage() {
-		return true;
-	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/RestServletGroupDefault.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestServletGroupDefault.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestServletGroupDefault.java
index 872d372..0864d54 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/RestServletGroupDefault.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestServletGroupDefault.java
@@ -35,7 +35,7 @@ public abstract class RestServletGroupDefault extends RestServletDefault {
 	 */
 	@RestMethod(name="GET", path="/", description="Child resources")
 	public ChildResourceDescriptions getChildren(RestRequest req) {
-		return new ChildResourceDescriptions(this, req);
+		return new ChildResourceDescriptions(getContext(), req);
 	}
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/RestUtils.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestUtils.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestUtils.java
index b8f017b..713da69 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/RestUtils.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestUtils.java
@@ -245,4 +245,13 @@ public final class RestUtils {
 			throw new RuntimeException("Could not find servlet path in request URI.  URI=["+requestURI+"], servletPath=["+servletPath+"]", e);
 		}
 	}
+
+	static String[] parseHeader(String s) {
+		int i = s.indexOf(':');
+		if (i == -1)
+			return null;
+		String name = s.substring(0, i).trim().toLowerCase(Locale.ENGLISH);
+		String val = s.substring(i+1).trim();
+		return new String[]{name,val};
+	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/FormData.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/FormData.java b/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/FormData.java
index 055b805..9a16812 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/FormData.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/FormData.java
@@ -86,7 +86,7 @@ public @interface FormData {
 	 * 	<li><js>"PLAIN"</js> - Plain text.<br>
 	 * 		This treats request parameters as plain text.<br>
 	 * 		Only POJOs directly convertable from <l>Strings</l> can be represented in parameters when using this mode.
-	 * 	<li><js>"INHERIT"</js> (default) - Inherit from the {@link RestServletContext#REST_paramFormat} property on the servlet method or class.
+	 * 	<li><js>"INHERIT"</js> (default) - Inherit from the {@link RestContext#REST_paramFormat} property on the servlet method or class.
 	 * </ul>
 	 * <p>
 	 * Note that the parameter value <js>"(foo)"</js> is interpreted as <js>"(foo)"</js> when using plain mode, but

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/Query.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/Query.java b/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/Query.java
index 4bac857..8e411d3 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/Query.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/Query.java
@@ -82,7 +82,7 @@ public @interface Query {
 	 * 	<li><js>"PLAIN"</js> - Plain text.<br>
 	 * 		This treats request parameters as plain text.<br>
 	 * 		Only POJOs directly convertable from <l>Strings</l> can be represented in parameters when using this mode.
-	 * 	<li><js>"INHERIT"</js> (default) - Inherit from the {@link RestServletContext#REST_paramFormat} property on the servlet method or class.
+	 * 	<li><js>"INHERIT"</js> (default) - Inherit from the {@link RestContext#REST_paramFormat} property on the servlet method or class.
 	 * </ul>
 	 * <p>
 	 * Note that the parameter value <js>"(foo)"</js> is interpreted as <js>"(foo)"</js> when using plain mode, but

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/RestMethod.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/RestMethod.java b/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/RestMethod.java
index 6b0a991..4856070 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/RestMethod.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/RestMethod.java
@@ -502,4 +502,9 @@ public @interface RestMethod {
 	 * </ul>
 	 */
 	String clientVersion() default "";
+
+	/**
+	 * TODO
+	 */
+	String[] links() default "";
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/RestResource.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/RestResource.java b/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/RestResource.java
index 13adbc2..b49bdd3 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/RestResource.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/RestResource.java
@@ -20,7 +20,8 @@ import java.lang.annotation.*;
 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.jena.*;
 import org.apache.juneau.json.*;
 import org.apache.juneau.parser.*;
@@ -31,8 +32,10 @@ import org.apache.juneau.utils.*;
 import org.apache.juneau.xml.*;
 
 /**
- * Optionally used to associate metadata on an instance of {@link RestServlet}.
+ * Used to denote that a class is a REST resource and to associate metadata on it.
  * <p>
+ * Usually used on a subclass of {@link RestServlet}, but can be used to annotate any class that you want to expose as a REST resource.
+ *
  * Refer to <a class='doclink' href='../package-summary.html#TOC'>org.apache.juneau.rest</a> doc for information on using this class.
  */
 @Documented
@@ -46,9 +49,8 @@ public @interface RestResource {
 	 * <p>
 	 * This annotation is used to provide localized messages for the following methods:
 	 * <ul>
-	 * 	<li>{@link RestServlet#getMessage(java.util.Locale, String, Object...)}
-	 * 	<li>{@link RestServlet#getTitle(RestRequest)}
-	 * 	<li>{@link RestServlet#getDescription(RestRequest)}
+	 * 	<li>{@link RestRequest#getMessage(String, Object...)}
+	 * 	<li>{@link RestContext#getMessages()}
 	 * </ul>
 	 * <p>
 	 * Refer to the {@link MessageBundle} class for a description of the message key formats
@@ -69,7 +71,9 @@ public @interface RestResource {
 	 * <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>
+	 * The programmatic equivalent to this annotation are the {@link RestConfig#addGuards(Class...)}/{@link RestConfig#addGuards(RestGuard...)} methods.
+ 	 */
 	Class<? extends RestGuard>[] guards() default {};
 
 	/**
@@ -82,6 +86,8 @@ public @interface RestResource {
 	 * Can be used for performing post-processing on the response object before serialization.
 	 * <p>
 	 * Default converter implementations are provided in the <a class='doclink' href='../converters/package-summary.html#TOC'>org.apache.juneau.rest.converters</a> package.
+	 * <p>
+	 * The programmatic equivalent to this annotation are the {@link RestConfig#addConverters(Class...)}/{@link RestConfig#addConverters(RestConverter...)} methods.
 	 */
 	Class<? extends RestConverter>[] converters() default {};
 
@@ -90,14 +96,16 @@ public @interface RestResource {
 	 * <p>
 	 * Shortcut to add bean filters to the bean contexts of the objects returned by the following methods:
 	 * <ul>
-	 * 	<li>{@link RestServlet#getBeanContext()}
-	 * 	<li>{@link RestServlet#getSerializers()}
-	 * 	<li>{@link RestServlet#getParsers()}
+	 * 	<li>{@link RestContext#getBeanContext()}
+	 * 	<li>{@link RestContext#getSerializers()}
+	 * 	<li>{@link RestContext#getParsers()}
 	 * </ul>
 	 * <p>
 	 * If the specified class is an instance of {@link BeanFilterBuilder}, then a filter built from that builder is added.
 	 * Any other classes are wrapped in a {@link InterfaceBeanFilterBuilder} to indicate that subclasses should
 	 * 	be treated as the specified class type.
+	 * <p>
+	 * The programmatic equivalent to this annotation is the {@link RestConfig#addBeanFilters(Class...)} method.
 	 */
 	Class<?>[] beanFilters() default {};
 
@@ -106,13 +114,15 @@ public @interface RestResource {
 	 * <p>
 	 * Shortcut to add POJO swaps to the bean contexts of the objects returned by the following methods:
 	 * <ul>
-	 * 	<li>{@link RestServlet#getBeanContext()}
-	 * 	<li>{@link RestServlet#getSerializers()}
-	 * 	<li>{@link RestServlet#getParsers()}
+	 * 	<li>{@link RestContext#getBeanContext()}
+	 * 	<li>{@link RestContext#getSerializers()}
+	 * 	<li>{@link RestContext#getParsers()}
 	 * </ul>
 	 * <p>
 	 * If the specified class is an instance of {@link PojoSwap}, then that swap is added.
 	 * Any other classes are wrapped in a {@link SurrogateSwap}.
+	 * <p>
+	 * The programmatic equivalent to this annotation is the {@link RestConfig#addPojoSwaps(Class...)} method.
 	 */
 	Class<?>[] pojoSwaps() default {};
 
@@ -121,14 +131,14 @@ public @interface RestResource {
 	 * <p>
 	 * Shortcut for specifying class-level properties on this servlet to the objects returned by the following methods:
 	 * <ul>
-	 * 	<li>{@link RestServlet#getBeanContext()}
-	 * 	<li>{@link RestServlet#getSerializers()}
-	 * 	<li>{@link RestServlet#getParsers()}
+	 * 	<li>{@link RestContext#getBeanContext()}
+	 * 	<li>{@link RestContext#getSerializers()}
+	 * 	<li>{@link RestContext#getParsers()}
 	 * </ul>
 	 * <p>
 	 * Any of the following property names can be specified:
 	 * <ul>
-	 * 	<li>{@link RestServletContext}
+	 * 	<li>{@link RestContext}
 	 * 	<li>{@link BeanContext}
 	 * 	<li>{@link SerializerContext}
 	 * 	<li>{@link ParserContext}
@@ -144,6 +154,8 @@ public @interface RestResource {
 	 * <p>
 	 * In some cases, properties can be overridden at runtime through the {@link RestResponse#setProperty(String, Object)} method
 	 * 	or through a {@link Properties @Properties} annotated method parameter.
+	 * <p>
+	 * The programmatic equivalent to this annotation are the {@link RestConfig#setProperty(String, Object)}/{@link RestConfig#setProperties(java.util.Map)} methods.
 	 */
 	Property[] properties() default {};
 
@@ -151,6 +163,8 @@ public @interface RestResource {
 	 * Specifies a list of {@link Serializer} classes to add to the list of serializers available for this servlet.
 	 * <p>
 	 * This annotation can only be used on {@link Serializer} classes that have no-arg constructors.
+	 * <p>
+	 * The programmatic equivalent to this annotation are the {@link RestConfig#addSerializers(Class...)}/{@link RestConfig#addSerializers(Serializer...)} methods.
 	 */
 	Class<? extends Serializer>[] serializers() default {};
 
@@ -158,6 +172,8 @@ public @interface RestResource {
 	 * Specifies a list of {@link Parser} classes to add to the list of parsers available for this servlet.
 	 * <p>
 	 * This annotation can only be used on {@link Parser} classes that have no-arg constructors.
+	 * <p>
+	 * The programmatic equivalent to this annotation are the {@link RestConfig#addParsers(Class...)}/{@link RestConfig#addParsers(Parser...)} methods.
 	 */
 	Class<? extends Parser>[] parsers() default {};
 
@@ -167,6 +183,8 @@ public @interface RestResource {
 	 * 	HTTP responses.
 	 * <p>
 	 * See {@link ResponseHandler} for details.
+	 * <p>
+	 * The programmatic equivalent to this annotation are the {@link RestConfig#addResponseHandlers(Class...)}/{@link RestConfig#addResponseHandlers(ResponseHandler...)} methods.
 	 */
 	Class<? extends ResponseHandler>[] responseHandlers() default {};
 
@@ -185,6 +203,8 @@ public @interface RestResource {
 	 * 		...
 	 * 	}
 	 * </p>
+	 * <p>
+	 * The programmatic equivalent to this annotation are the {@link RestConfig#addEncoders(Class...)}/{@link RestConfig#addEncoders(Encoder...)} methods.
 	 */
 	Class<? extends Encoder>[] encoders() default {};
 
@@ -208,6 +228,8 @@ public @interface RestResource {
 	 * 		...
 	 * 	}
 	 * </p>
+	 * <p>
+	 * The programmatic equivalent to this annotation are the {@link RestConfig#addDefaultRequestHeader(String, Object)}/{@link RestConfig#addDefaultRequestHeaders(String...)} methods.
 	 */
 	String[] defaultRequestHeaders() default {};
 
@@ -230,6 +252,8 @@ public @interface RestResource {
 	 * 		...
 	 * 	}
 	 * </p>
+	 * <p>
+	 * The programmatic equivalent to this annotation are the {@link RestConfig#addDefaultResponseHeader(String, Object)}/{@link RestConfig#addDefaultResponseHeaders(String...)} methods.
 	 */
 	String[] defaultResponseHeaders() default {};
 
@@ -270,6 +294,8 @@ public @interface RestResource {
 	 * 		</p>
 	 * 	</dd>
 	 * </dl>
+	 * <p>
+	 * The programmatic equivalent to this annotation are the {@link RestConfig#addChildResource(String, Object)}/{@link RestConfig#addChildResources(Class...)}/{@link RestConfig#addChildResources(Object...)} methods.
 	 */
 	Class<?>[] children() default {};
 
@@ -281,6 +307,8 @@ public @interface RestResource {
 	 * <p>
 	 * This annotation is ignored on top-level servlets (i.e. servlets defined in <code>web.xml</code> files).
 	 * Therefore, implementers can optionally specify a path value for documentation purposes.
+	 * <p>
+	 * The programmatic equivalent to this annotation is the {@link RestConfig#setPath(String)} method.
 	 */
 	String path() default "";
 
@@ -288,7 +316,7 @@ public @interface RestResource {
 	 * Optional servlet title.
 	 * <p>
 	 * It is used to populate the Swagger title field and to display on HTML pages.
-	 * This value can be retrieved programmatically through the {@link RestServlet#getTitle(RestRequest)} method.
+	 * This value can be retrieved programmatically through the {@link RestRequest#getServletTitle()} method.
 	 * <p>
 	 * The default value pulls the label from the <code>label</code> entry in the servlet resource bundle.
 	 * 	(e.g. <js>"title = foo"</js> or <js>"MyServlet.title = foo"</js>).
@@ -296,6 +324,8 @@ public @interface RestResource {
 	 * This field can contain variables (e.g. "$L{my.localized.variable}").
 	 * <p>
 	 * Corresponds to the swagger field <code>/info/title</code>.
+	 * <p>
+	 * The programmatic equivalent to this annotation is the {@link RestInfoProvider#getTitle(RestRequest)} method.
 	 */
 	String title() default "";
 
@@ -303,7 +333,7 @@ public @interface RestResource {
 	 * Optional servlet description.
 	 * <p>
 	 * It is used to populate the Swagger description field and to display on HTML pages.
-	 * This value can be retrieved programmatically through the {@link RestServlet#getDescription(RestRequest)} method.
+	 * This value can be retrieved programmatically through the {@link RestRequest#getServletDescription()} method.
 	 * <p>
 	 * The default value pulls the description from the <code>description</code> entry in the servlet resource bundle.
 	 * 	(e.g. <js>"description = foo"</js> or <js>"MyServlet.description = foo"</js>).
@@ -311,6 +341,8 @@ public @interface RestResource {
 	 * This field can contain variables (e.g. "$L{my.localized.variable}").
 	 * <p>
 	 * Corresponds to the swagger field <code>/info/description</code>.
+	 * <p>
+	 * The programmatic equivalent to this annotation is the {@link RestInfoProvider#getDescription(RestRequest)} method.
 	 */
 	String description() default "";
 
@@ -318,7 +350,6 @@ public @interface RestResource {
 	 * Optional servlet terms-of-service for this API.
 	 * <p>
 	 * It is used to populate the Swagger terms-of-service field.
-	 * This value can be retrieved programmatically through the {@link RestServlet#getTermsOfService(RestRequest)} method.
 	 * <p>
 	 * The default value pulls the description from the <code>termsOfService</code> entry in the servlet resource bundle.
 	 * 	(e.g. <js>"termsOfService = foo"</js> or <js>"MyServlet.termsOfService = foo"</js>).
@@ -326,6 +357,8 @@ public @interface RestResource {
 	 * This field can contain variables (e.g. "$L{my.localized.variable}").
 	 * <p>
 	 * Corresponds to the swagger field <code>/info/termsOfService</code>.
+	 * <p>
+	 * The programmatic equivalent to this annotation is the {@link RestInfoProvider#getTermsOfService(RestRequest)} method.
 	 */
 	String termsOfService() default "";
 
@@ -333,7 +366,6 @@ public @interface RestResource {
 	 * Optional contact information for the exposed API.
 	 * <p>
 	 * It is used to populate the Swagger contact field and to display on HTML pages.
-	 * This value can be retrieved programmatically through the {@link RestServlet#getContact(RestRequest)} method.
 	 * <p>
 	 * A simplified JSON string with the following fields:
 	 * <p class='bcode'>
@@ -355,6 +387,8 @@ public @interface RestResource {
 	 * This field can contain variables (e.g. "$L{my.localized.variable}").
 	 * <p>
 	 * Corresponds to the swagger field <code>/info/contact</code>.
+	 * <p>
+	 * The programmatic equivalent to this annotation is the {@link RestInfoProvider#getContact(RestRequest)} method.
 	 */
 	String contact() default "";
 
@@ -362,7 +396,6 @@ public @interface RestResource {
 	 * Optional license information for the exposed API.
 	 * <p>
 	 * It is used to populate the Swagger license field and to display on HTML pages.
-	 * This value can be retrieved programmatically through the {@link RestServlet#getLicense(RestRequest)} method.
 	 * <p>
 	 * A simplified JSON string with the following fields:
 	 * <p class='bcode'>
@@ -383,6 +416,8 @@ public @interface RestResource {
 	 * This field can contain variables (e.g. "$L{my.localized.variable}").
 	 * <p>
 	 * Corresponds to the swagger field <code>/info/license</code>.
+	 * <p>
+	 * The programmatic equivalent to this annotation is the {@link RestInfoProvider#getLicense(RestRequest)} method.
 	 */
 	String license() default "";
 
@@ -390,7 +425,6 @@ public @interface RestResource {
 	 * Provides the version of the application API (not to be confused with the specification version).
 	 * <p>
 	 * It is used to populate the Swagger version field and to display on HTML pages.
-	 * This value can be retrieved programmatically through the {@link RestServlet#getVersion(RestRequest)} method.
 	 * <p>
 	 * The default value pulls the description from the <code>version</code> entry in the servlet resource bundle.
 	 * 	(e.g. <js>"version = 2.0"</js> or <js>"MyServlet.version = 2.0"</js>).
@@ -398,6 +432,8 @@ public @interface RestResource {
 	 * This field can contain variables (e.g. "$L{my.localized.variable}").
 	 * <p>
 	 * Corresponds to the swagger field <code>/info/version</code>.
+	 * <p>
+	 * The programmatic equivalent to this annotation is the {@link RestInfoProvider#getVersion(RestRequest)} method.
 	 */
 	String version() default "";
 
@@ -405,7 +441,6 @@ public @interface RestResource {
 	 * Optional tagging information for the exposed API.
 	 * <p>
 	 * It is used to populate the Swagger tags field and to display on HTML pages.
-	 * This value can be retrieved programmatically through the {@link RestServlet#getTags(RestRequest)} method.
 	 * <p>
 	 * A simplified JSON string with the following fields:
 	 * <p class='bcode'>
@@ -432,6 +467,8 @@ public @interface RestResource {
 	 * This field can contain variables (e.g. "$L{my.localized.variable}").
 	 * <p>
 	 * Corresponds to the swagger field <code>/tags</code>.
+	 * <p>
+	 * The programmatic equivalent to this annotation is the {@link RestInfoProvider#getTags(RestRequest)} method.
 	 */
 	String tags() default "";
 
@@ -439,7 +476,6 @@ public @interface RestResource {
 	 * Optional external documentation information for the exposed API.
 	 * <p>
 	 * It is used to populate the Swagger external documentation field and to display on HTML pages.
-	 * This value can be retrieved programmatically through the {@link RestServlet#getExternalDocs(RestRequest)} method.
 	 * <p>
 	 * A simplified JSON string with the following fields:
 	 * <p class='bcode'>
@@ -460,6 +496,8 @@ public @interface RestResource {
 	 * This field can contain variables (e.g. "$L{my.localized.variable}").
 	 * <p>
 	 * Corresponds to the swagger field <code>/tags</code>.
+	 * <p>
+	 * The programmatic equivalent to this annotation is the {@link RestInfoProvider#getExternalDocs(RestRequest)} method.
 	 */
 	String externalDocs() default "";
 
@@ -469,6 +507,8 @@ public @interface RestResource {
 	 * The configuration file .
 	 * <p>
 	 * This field can contain variables (e.g. "$L{my.localized.variable}").
+	 * <p>
+	 * The programmatic equivalent to this annotation is the {@link RestConfig#setConfigFile(ConfigFile)} method.
 	 */
 	String config() default "";
 
@@ -517,6 +557,8 @@ public @interface RestResource {
 	 * Multiple stylesheets can be specified as a comma-delimited list.
 	 * When multiple stylesheets are specified, their contents will be concatenated and return in the order specified
 	 * in the list.
+	 * <p>
+	 * The programmatic equivalent to this annotation are the {@link RestConfig#addStyleSheet(Object...)}/{@link RestConfig#addStyleSheet(Class, String)} methods.
 	 */
 	String stylesheet() default "";
 
@@ -547,6 +589,8 @@ public @interface RestResource {
 	 * 	<li><code>org.apache.juneau.rest.mydocs</code> package (since <code>RestServletDefault</code> is in <code>org.apache.juneau.rest</code>).
 	 * 	<li><code>[working-dir]/mydocs</code> directory.
 	 * </ol>
+	 * <p>
+	 * The programmatic equivalent to this annotation are the {@link RestConfig#setFavIcon(Object)}/{@link RestConfig#setFavIcon(Class, String)} methods.
 	 */
 	String favicon() default "";
 
@@ -558,8 +602,6 @@ public @interface RestResource {
 	 * Mappings are cumulative from parent to child.  Child resources can override mappings made on parent resources.
 	 * <p>
 	 * If the file cannot be located, the request will return {@link HttpServletResponse#SC_NOT_FOUND}.
-	 * <p>
-	 * The media type on the response is determined by the {@link RestServlet#getMimetypesFileTypeMap()} method.
 	 *
 	 * <h5 class='section'>Example:</h5>
 	 * <p class='bcode'>
@@ -581,6 +623,8 @@ public @interface RestResource {
 	 * 	<li><code>org.apache.juneau.rest.docs</code> package (since <code>RestServletDefault</code> is in <code>org.apache.juneau.rest</code>).
 	 * 	<li><code>[working-dir]/docs</code> directory.
 	 * </ol>
+	 * <p>
+	 * The programmatic equivalent to this annotation is the {@link RestConfig#addStaticFiles(Class, String)} method.
 	 */
 	String staticFiles() default "";
 
@@ -591,10 +635,66 @@ public @interface RestResource {
 	 * 	changes.  Used in conjunction with {@link RestMethod#clientVersion()} annotation.
 	 * <p>
 	 * If not specified, uses <js>"X-Client-Version"</js>.
+	 * <p>
+	 * The programmatic equivalent to this annotation is the {@link RestConfig#setClientVersionHeader(String)} method.
 	 */
 	String clientVersionHeader() default "";
 
 	/**
+	 * Specifies the resolver class to use for resolving child resources by class name.
+	 * <p>
+	 * The default implementation simply instantiates the class using one of the following constructors:
+	 * <ul>
+	 * 	<li><code><jk>public</jk> T(RestConfig)</code>
+	 * 	<li><code><jk>public</jk> T()</code>
+	 * </ul>
+	 * The former constructor can be used to get access to the {@link RestConfig} object to get access to the
+	 * config file and initialization information or make programmatic modifications to the resource before
+	 * full initialization.
+	 * <p>
+	 * Non-<code>RestServlet</code> classes can also add the following two methods to get access to the
+	 * {@link RestConfig} and {@link RestContext} objects:
+	 * <ul>
+	 * 	<li><jk>public void</jk> init(RestConfig);</code>
+	 * 	<li><jk>public void</jk> init(RestContext);</code>
+	 * </ul>
+	 * <p>
+	 * Subclasses can be used to provide customized resolution of REST resource class instances.
+	 * <p>
+	 * The programmatic equivalent to this annotation are the {@link RestConfig#setResourceResolver(Class)}/{@link RestConfig#setResourceResolver(RestResourceResolver)} methods.
+	 */
+	Class<? extends RestResourceResolver> resourceResolver() default RestResourceResolver.class;
+
+	/**
+	 * Specifies the logger class to use for logging.
+	 * <p>
+	 * The default logger performs basic error logging to the Java logger.
+	 * Subclasses can be used to customize logging behavior on the resource.
+	 * <p>
+	 * The programmatic equivalent to this annotation are the {@link RestConfig#setLogger(Class)}/{@link RestConfig#setLogger(RestLogger)} methods.
+	 */
+	Class<? extends RestLogger> logger() default RestLogger.Normal.class;
+
+	/**
+	 * Specifies the REST call handler class.
+	 * <p>
+	 * This class handles the basic lifecycle of an HTTP REST call.
+	 * Subclasses can be used to customize how these HTTP calls are handled.
+	 * <p>
+	 * The programmatic equivalent to this annotation are the {@link RestConfig#setCallHandler(Class)}/{@link RestConfig#setCallHandler(RestCallHandler)} methods.
+	 */
+	Class<? extends RestCallHandler> callHandler() default RestCallHandler.class;
+
+	/**
+	 * Specifies the class used to retrieve title/description/swagger information about a resource.
+	 * <p>
+	 * Subclasses can be used to customize the documentation on a resource.
+	 * <p>
+	 * The programmatic equivalent to this annotation are the {@link RestConfig#setInfoProvider(Class)}/{@link RestConfig#setInfoProvider(RestInfoProvider)} methods.
+	 */
+	Class<? extends RestInfoProvider> infoProvider() default RestInfoProvider.class;
+
+	/**
 	 * TODO
 	 */
 	String[] links() default "";

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/jena/RestServletJenaDefault.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/jena/RestServletJenaDefault.java b/juneau-rest/src/main/java/org/apache/juneau/rest/jena/RestServletJenaDefault.java
index fdfd0bb..be489da 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/jena/RestServletJenaDefault.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/jena/RestServletJenaDefault.java
@@ -13,7 +13,7 @@
 package org.apache.juneau.rest.jena;
 
 import static org.apache.juneau.html.HtmlDocSerializerContext.*;
-import static org.apache.juneau.rest.RestServletContext.*;
+import static org.apache.juneau.rest.RestContext.*;
 
 import org.apache.juneau.dto.swagger.*;
 import org.apache.juneau.html.*;
@@ -252,9 +252,4 @@ public abstract class RestServletJenaDefault extends RestServlet {
 	public Swagger getOptions(RestRequest req) {
 		return req.getSwagger();
 	}
-
-	@Override /* RestServlet */
-	public boolean hasOptionsPage() {
-		return true;
-	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/jena/RestServletJenaGroupDefault.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/jena/RestServletJenaGroupDefault.java b/juneau-rest/src/main/java/org/apache/juneau/rest/jena/RestServletJenaGroupDefault.java
index 8c6e635..130ab25 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/jena/RestServletJenaGroupDefault.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/jena/RestServletJenaGroupDefault.java
@@ -36,6 +36,6 @@ public abstract class RestServletJenaGroupDefault extends RestServletJenaDefault
 	 */
 	@RestMethod(name="GET", path="/", description="Child resources")
 	public ChildResourceDescriptions getChildren(RestRequest req) {
-		return new ChildResourceDescriptions(this, req);
+		return new ChildResourceDescriptions(getContext(), req);
 	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/labels/ChildResourceDescriptions.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/labels/ChildResourceDescriptions.java b/juneau-rest/src/main/java/org/apache/juneau/rest/labels/ChildResourceDescriptions.java
index 216b34d..23ed8bf 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/labels/ChildResourceDescriptions.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/labels/ChildResourceDescriptions.java
@@ -29,25 +29,25 @@ public class ChildResourceDescriptions extends LinkedList<ResourceDescription> {
 	/**
 	 * Constructor.
 	 *
-	 * @param servlet The servlet that this bean describes.
+	 * @param context The servlet context that this bean describes.
 	 * @param req The HTTP servlet request.
 	 */
-	public ChildResourceDescriptions(RestServlet servlet, RestRequest req) {
-		this(servlet, req, false);
+	public ChildResourceDescriptions(RestContext context, RestRequest req) {
+		this(context, req, false);
 	}
 
 	/**
 	 * Constructor.
 	 *
-	 * @param servlet The servlet that this bean describes.
+	 * @param context The servlet context that this bean describes.
 	 * @param req The HTTP servlet request.
 	 * @param sort If <jk>true</jk>, list will be ordered by name alphabetically.
 	 * Default is to maintain the order as specified in the annotation.
 	 */
-	public ChildResourceDescriptions(RestServlet servlet, RestRequest req, boolean sort) {
+	public ChildResourceDescriptions(RestContext context, RestRequest req, boolean sort) {
 		String uri = req.getTrimmedRequestURI();
-		for (Map.Entry<String,RestServlet> e : servlet.getChildResources().entrySet())
-			add(new ResourceDescription(uri, e.getKey(), e.getValue().getTitle(req)));
+		for (Map.Entry<String,RestContext> e : context.getChildResources().entrySet())
+			add(new ResourceDescription(uri, e.getKey(), e.getValue().getInfoProvider().getTitle(req)));
 		if (sort)
 			Collections.sort(this);
 	}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/package.html
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/package.html b/juneau-rest/src/main/java/org/apache/juneau/rest/package.html
index da2ff80..79c7753 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/package.html
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/package.html
@@ -1053,7 +1053,7 @@
 	 	</p>
 	 	<ul>
 	 		<li>Annotations:  {@link org.apache.juneau.rest.annotation.RestResource#title()}, {@link org.apache.juneau.rest.annotation.RestResource#description()}
-	 		<li>By overriding methods on the servlet class:  {@link org.apache.juneau.rest.RestServlet#getTitle(RestRequest)}, {@link org.apache.juneau.rest.RestServlet#getDescription(RestRequest)} 
+	 		<li>By overriding methods on the servlet class:  {@link org.apache.juneau.rest.RestInfoProvider#getTitle(RestRequest)}, {@link org.apache.juneau.rest.RestInfoProvider#getDescription(RestRequest)} 
 	 		<li>By defining properties in the resource bundle.
 	 		<li>By specifying them in a Swagger JSON file.
 	 	</ul>
@@ -1139,8 +1139,8 @@
 	<jk>public class</jk> ExampleResource <jk>extends</jk> RestServletDefault {
 		</p>
 		<p>
-			Another option is to override the {@link org.apache.juneau.rest.RestServlet#getTitle(RestRequest)} 
-				and {@link org.apache.juneau.rest.RestServlet#getDescription(RestRequest)} methods.
+			Another option is to override the {@link org.apache.juneau.rest.RestInfoProvider#getTitle(RestRequest)} 
+				and {@link org.apache.juneau.rest.RestInfoProvider#getDescription(RestRequest)} methods.
 		</p>	 	
 	 	<h6 class='topic'>Method Description, Input, and Responses</h6>
 	 	<p>
@@ -1213,13 +1213,13 @@
 				<td><ck>label</ck></td>
 				<td>Servlet label</td>
 				<td>{@link org.apache.juneau.rest.annotation.RestResource#title() @RestResource.title()}</td>
-				<td>{@link org.apache.juneau.rest.RestServlet#getTitle(RestRequest)}</td>
+				<td>{@link org.apache.juneau.rest.RestInfoProvider#getTitle(RestRequest)}</td>
 			</tr>
 			<tr>
 				<td><ck>description</ck></td>
 				<td>Servlet description</td>
 				<td>{@link org.apache.juneau.rest.annotation.RestResource#description() @RestResource.description()}</td>
-				<td>{@link org.apache.juneau.rest.RestServlet#getDescription(RestRequest)}</td>
+				<td>{@link org.apache.juneau.rest.RestInfoProvider#getDescription(RestRequest)}</td>
 			</tr>
 			<tr>
 				<td><ck>[javaMethodName].summary</ck></td>
@@ -1297,7 +1297,7 @@
 		<ul class='javahierarchy'>
 			<li class='n'>{@link org.apache.juneau.rest.annotation.RestResource#serializers() @RestResource.serializers()} - Annotation on servlet class.
 			<li class='n'>{@link org.apache.juneau.rest.annotation.RestMethod#serializers() @RestMethod.serializers()} - Annotation on individual servlet methods.
-			<li class='m'>{@link org.apache.juneau.rest.RestServlet#createSerializers(ObjectMap,Class[],Class[])} - Override method to set the serializers programmatically.
+			<li class='m'>{@link org.apache.juneau.rest.RestConfig#addSerializers(Class[])} - Override method to set the serializers programmatically.
 		</ul>
 		<p>
 			The following are equivalent ways of defining serializers used by a servlet...
@@ -1356,7 +1356,7 @@
 		<ul class='javahierarchy'>
 			<li class='n'>{@link org.apache.juneau.rest.annotation.RestResource#parsers() @RestResource.parsers()} - Annotation on servlet class.
 			<li class='n'>{@link org.apache.juneau.rest.annotation.RestMethod#parsers() @RestMethod.parsers()} - Annotation on individual servlet methods.
-			<li class='m'>{@link org.apache.juneau.rest.RestServlet#createParsers(ObjectMap,Class[],Class[])} - Override method to set the parsers programmatically.
+			<li class='m'>{@link org.apache.juneau.rest.RestConfig#addParsers(Class[])} - Override method to set the parsers programmatically.
 		</ul>
 		<p>
 			The following are equivalent ways of defining parsers used by a servlet...
@@ -1464,22 +1464,22 @@
 	}
 		</p>
 		<p>
-			In particular, the {@link org.apache.juneau.rest.RestServletContext} class has a variety of properties
+			In particular, the {@link org.apache.juneau.rest.RestContext} class has a variety of properties
 			for controlling the behavior of the {@link org.apache.juneau.rest.RestServlet} class itself.
 		</p>
 		<p>
 			There are also ways to provide properties programmatically.
 		</p>
 		<ul class='spaced-list'>
-			<li>By overriding the {@link org.apache.juneau.rest.RestServlet#createProperties()} method.
-			<li>By overriding the {@link org.apache.juneau.rest.RestServlet#createSerializers(ObjectMap,Class[],Class[])} and 
-				{@link org.apache.juneau.rest.RestServlet#createParsers(ObjectMap,Class[],Class[])} methods and setting properties on the 
+			<li>By overriding the {@link org.apache.juneau.rest.RestConfig#setProperties(Map)} method.
+			<li>By overriding the {@link org.apache.juneau.rest.RestConfig#addSerializers(Class[])} and 
+				{@link org.apache.juneau.rest.RestConfig#addParsers(Class[])} methods and setting properties on the 
 				serializers and parsers directly.
 			
 		</ul>
 		<h6 class='topic'>Additional Information</h6>
 		<ul class='javahierarchy'>
-			<li class='c'>{@link org.apache.juneau.rest.RestServletContext}
+			<li class='c'>{@link org.apache.juneau.rest.RestContext}
 				<br>Properties associated with the {@link org.apache.juneau.rest.RestServlet} class.
 			<li class='n'>{@link org.apache.juneau.rest.annotation.RestMethod#serializersInherit @RestMethod.serializersInherit()} 
 				<br>Controls how serializers inherit properties from the servlet class.
@@ -1531,9 +1531,9 @@
 			Transforms can also be defined programmatically through the following:
 		</p>
 		<ul class='spaced-list'>
-			<li>By overriding the {@link org.apache.juneau.rest.RestServlet#createBeanFilters()} and {@link org.apache.juneau.rest.RestServlet#createPojoSwaps()} methods.
-			<li>By overriding the {@link org.apache.juneau.rest.RestServlet#createSerializers(ObjectMap,Class[],Class[])} and 
-				{@link org.apache.juneau.rest.RestServlet#createParsers(ObjectMap,Class[],Class[])} methods and setting transforms on the 
+			<li>By overriding the {@link org.apache.juneau.rest.RestConfig#addBeanFilters(Class[])} and {@link org.apache.juneau.rest.RestConfig#addPojoSwaps(Class[])} methods.
+			<li>By overriding the {@link org.apache.juneau.rest.RestConfig#addSerializers(Class[])} and 
+				{@link org.apache.juneau.rest.RestConfig#addParsers(Class[])} methods and setting transforms on the 
 				serializers and parsers directly.
 			
 		</ul>
@@ -1601,7 +1601,7 @@
 			When guards are associated at the class-level, it's equivalent to associating guards on all Java methods on the servlet.
 		</p>
 		<p>
-			Class-level guards can also be created programmatically by overriding the {@link org.apache.juneau.rest.RestServlet#createGuards(ObjectMap)} method.
+			Class-level guards can also be created programmatically by overriding the {@link org.apache.juneau.rest.RestConfig#addGuards(Class[])} method.
 		</p>
 		<h6 class='topic'>Additional Information</h6>
 		<ul class='javahierarchy'>
@@ -1692,7 +1692,7 @@
 			</ul>
 		</ul>
 		<p>
-			Class-level converters can be created programmatically by overriding the {@link org.apache.juneau.rest.RestServlet#createConverters(ObjectMap)} method.
+			Class-level converters can be created programmatically by overriding the {@link org.apache.juneau.rest.RestConfig#addConverters(Class[])} method.
 		</p>
 		<p>
 			Note that from the example above, you can specify more than one converter.
@@ -1812,21 +1812,11 @@
 			Children can also be defined programmatically by overriding any of the following methods:
 		</p>
 		<ul class='javahierarchy'>
-			<li class='a'>{@link org.apache.juneau.rest.RestServlet}
+			<li class='a'>{@link org.apache.juneau.rest.RestConfig}
 			<ul>
-				<li class='m'>{@link org.apache.juneau.rest.RestServlet#getChildClasses() getChildClasses()}
-					<br>Programmatic equivalent to {@link org.apache.juneau.rest.annotation.RestResource#children() @RestResource.children()} annotation.
-				<li class='m'>{@link org.apache.juneau.rest.RestServlet#createChildren() createChildren()}
-					<br>Creates instances of classes returned by {@link org.apache.juneau.rest.RestServlet#getChildClasses() getChildClasses()}.
-				<li class='m'>{@link org.apache.juneau.rest.RestServlet#createChildrenMap() createChildrenMap()}
-					<br>Uses {@link org.apache.juneau.rest.RestServlet#createChildren() createChildren()} and 
-						{@link org.apache.juneau.rest.annotation.RestResource#path() @RestResource.path()} to create a map of child URLs to child servlets.
-				<li class='m'>{@link org.apache.juneau.rest.RestServlet#addChildResource(String,RestServlet) addChildResource(String,RestServlet)}
-					<br>Can be used to programmatically add a REST servlet.
-				<li class='m'>{@link org.apache.juneau.rest.RestServlet#resolveChild(Class) resolveChild(Class)}
-					<br>An interceptor method that can be used to provide custom resolution of a child resource.
-				<li class='m'>{@link org.apache.juneau.rest.RestServlet#replaceChild(RestServlet) replaceChild(RestServlet)}
-					<br>Allows a child to be replaced at runtime without affecting the parent.
+				<li class='m'>{@link org.apache.juneau.rest.RestConfig#addChildResource(String,Object)}
+				<li class='m'>{@link org.apache.juneau.rest.RestConfig#addChildResources(Class[])}
+				<li class='m'>{@link org.apache.juneau.rest.RestConfig#addChildResources(Object[])}
 			</ul> 
 		</ul>
 	</div>
@@ -1965,18 +1955,18 @@
 		<ul class='javahierarchy'>
 			<li class='a'>{@link org.apache.juneau.rest.RestServlet}
 			<ul>
-				<li class='m'>{@link org.apache.juneau.rest.RestServlet#getVarResolver()}
+				<li class='m'>{@link org.apache.juneau.rest.RestContext#getVarResolver()}
 					<br>Returns the <l>VarResolver</l> associated with a servlet.
-				<li class='m'>{@link org.apache.juneau.rest.RestServlet#createVarResolver()}
+				<li class='m'>{@link org.apache.juneau.rest.RestConfig#addVars(Class[])}
 					<br>The method used to create the servlet <l>VarResolver</l>.
 					<br>Subclasses can override this method to provide their own resolver.
-				<li class='m'>{@link org.apache.juneau.rest.RestServlet#getSessionObjects(RestRequest)}
+				<li class='m'>{@link org.apache.juneau.rest.RestCallHandler#getSessionObjects(RestRequest)}
 					<br>Defines the session objects for the var resolver.
 					<br>Subclasses can override this method to provide additional session objects.
 			</ul>
 		</ul>
 		<p>
-			The default {@link org.apache.juneau.rest.RestServlet#createVarResolver()} method provides 
+			The default {@link org.apache.juneau.rest.RestContext#getVarResolver()} method provides 
 				support the following string variable types:
 		</p>
 		<table class='styled'>
@@ -1998,7 +1988,7 @@
 			</tr>
 			<tr>
 				<td><ck>$C{key}</ck><br><ck>$C{key,default}</ck></td>
-				<td>Values from the config file returned by {@link org.apache.juneau.rest.RestServlet#getConfig()}.</td>
+				<td>Values from the config file returned by {@link org.apache.juneau.rest.RestContext#getConfigFile()}.</td>
 			</tr>
 		</table>
 		<p>
@@ -2041,7 +2031,7 @@
 	}
 		</p>
 		<p>
-			The default {@link org.apache.juneau.rest.RestServlet#createVarResolver()} method provides 
+			The default {@link org.apache.juneau.rest.RestRequest#getVarResolverSession()} method provides 
 				support for all the servlet-level variables, and augments it with the following request-specific
 				variable types:
 		</p>
@@ -2235,7 +2225,7 @@
 		<h6 class='topic'>Notes:</h6>
 		<ul class='spaced-list'>
 			<li>Mappings are cumulative from parent to child.  Child resources can override mappings made on parent resources.
-			<li>The media type on the response is determined by the {@link org.apache.juneau.rest.RestServlet#getMimetypesFileTypeMap()} method.
+			<li>The media type on the response is determined by the {@link org.apache.juneau.rest.RestContext#getMediaTypeForName(String)} method.
 		</ul>
 	</div>
 
@@ -2244,19 +2234,19 @@
 	<h3 class='topic' onclick='toggle(this)'>4.16 - Listener Methods</h3>	
 	<div class='topic'>
 		<p>
-			Various convenience listener methods are provided on the {@link org.apache.juneau.rest.RestServlet} class
+			Various convenience listener methods are provided on the {@link org.apache.juneau.rest.RestCallHandler} class
 				that subclasses can use to intercept requests:
 		</p>
 		<ul class='javahierarchy'>
-			<li class='a'>{@link org.apache.juneau.rest.RestServlet}
+			<li class='a'>{@link org.apache.juneau.rest.RestCallHandler}
 			<ul>
-				<li class='m'>{@link org.apache.juneau.rest.RestServlet#onPreCall(RestRequest) onPreCall(RestRequest)}
+				<li class='m'>{@link org.apache.juneau.rest.RestCallHandler#onPreCall(RestRequest) onPreCall(RestRequest)}
 					<br>Callback method that gets invoked right before the REST Java method is invoked.
-				<li class='m'>{@link org.apache.juneau.rest.RestServlet#onPostCall(RestRequest,RestResponse) onPostCall(RestRequest,RestResponse)}
+				<li class='m'>{@link org.apache.juneau.rest.RestCallHandler#onPostCall(RestRequest,RestResponse) onPostCall(RestRequest,RestResponse)}
 					<br>Callback method that gets invoked right after the REST Java method is invoked, but before the serializer is invoked.
-				<li class='m'>{@link org.apache.juneau.rest.RestServlet#onSuccess(RestRequest,RestResponse,long) onSuccess(RestRequest,RestResponse,long)}
+				<li class='m'>{@link org.apache.juneau.rest.RestCallHandler#onSuccess(RestRequest,RestResponse,long) onSuccess(RestRequest,RestResponse,long)}
 					<br>Callback method for listening for successful completion of requests.
-				<li class='m'>{@link org.apache.juneau.rest.RestServlet#onError(HttpServletRequest,HttpServletResponse,RestException) onError(HttpServletRequest,HttpServletResponse,RestException)}
+				<li class='m'>{@link org.apache.juneau.rest.RestLogger#onError(HttpServletRequest,HttpServletResponse,RestException) onError(HttpServletRequest,HttpServletResponse,RestException)}
 					<br>Callback method for logging errors during HTTP requests.
 			</ul>
 		</ul>
@@ -2349,10 +2339,10 @@
 			Default headers can also be specified programmatically by overriding the following methods:
 		</p>
 		<ul class='javahierarchy'>
-			<li class='a'>{@link org.apache.juneau.rest.RestServlet}
+			<li class='a'>{@link org.apache.juneau.rest.RestConfig}
 			<ul>
-				<li class='m'>{@link org.apache.juneau.rest.RestServlet#createDefaultRequestHeaders(ObjectMap) createDefaultRequestHeaders()}
-				<li class='m'>{@link org.apache.juneau.rest.RestServlet#createDefaultResponseHeaders(ObjectMap) createDefaultResponseHeaders()}
+				<li class='m'>{@link org.apache.juneau.rest.RestConfig#addDefaultRequestHeaders(String[])}
+				<li class='m'>{@link org.apache.juneau.rest.RestConfig#addDefaultResponseHeaders(String[])}
 			</ul>
 		</ul>
 	</div>
@@ -2365,14 +2355,11 @@
 			The following overridable methods are provided for handling errors on requests:
 		</p>
 		<ul class='javahierarchy'>
-			<li class='a'>{@link org.apache.juneau.rest.RestServlet}
+			<li class='a'>{@link org.apache.juneau.rest.RestCallHandler}
 			<ul>
-				<li class='m'>{@link org.apache.juneau.rest.RestServlet#onError(HttpServletRequest,HttpServletResponse,RestException) onError(HttpServletRequest,HttpServletResponse,RestException)}
-					<br>Gets called when an error occurs on a request call.
-					<br>Default implementation logs the error.
-				<li class='m'>{@link org.apache.juneau.rest.RestServlet#renderError(HttpServletRequest,HttpServletResponse,RestException) renderError(HttpServletRequest,HttpServletResponse,RestException)}
+				<li class='m'>{@link org.apache.juneau.rest.RestCallHandler#renderError(HttpServletRequest,HttpServletResponse,RestException) renderError(HttpServletRequest,HttpServletResponse,RestException)}
 					<br>Method that produces the error message on the HTTP response.
-				<li class='m'>{@link org.apache.juneau.rest.RestServlet#handleNotFound(int,RestRequest,RestResponse) handleNotFound(int,RestRequest,RestResponse)}
+				<li class='m'>{@link org.apache.juneau.rest.RestCallHandler#handleNotFound(int,RestRequest,RestResponse) handleNotFound(int,RestRequest,RestResponse)}
 					<br>Method that gets called when no method/path pattern match the incoming request.
 
 			</ul>
@@ -2381,12 +2368,15 @@
 			The following convenience methods are provided for logging:
 		</p>
 		<ul class='javahierarchy'>
-			<li class='a'>{@link org.apache.juneau.rest.RestServlet}
+			<li class='a'>{@link org.apache.juneau.rest.RestLogger}
 			<ul>
-				<li class='m'>{@link org.apache.juneau.rest.RestServlet#getLogger() getLogger()}
-				<li class='m'>{@link org.apache.juneau.rest.RestServlet#log(Level,String,Object[]) log(Level,String,Object[])}
-				<li class='m'>{@link org.apache.juneau.rest.RestServlet#log(Level,Throwable,String,Object[]) log(Level,Throwable,String,Object[])}
-				<li class='m'>{@link org.apache.juneau.rest.RestServlet#logObjects(Level,String,Object[]) logObject(Level,String,Object[])}
+				<li class='m'>{@link org.apache.juneau.rest.RestLogger#getLogger() getLogger()}
+				<li class='m'>{@link org.apache.juneau.rest.RestLogger#onError(HttpServletRequest,HttpServletResponse,RestException) onError(HttpServletRequest,HttpServletResponse,RestException)}
+					<br>Gets called when an error occurs on a request call.
+					<br>Default implementation logs the error.
+				<li class='m'>{@link org.apache.juneau.rest.RestLogger#log(Level,String,Object[]) log(Level,String,Object[])}
+				<li class='m'>{@link org.apache.juneau.rest.RestLogger#log(Level,Throwable,String,Object[]) log(Level,Throwable,String,Object[])}
+				<li class='m'>{@link org.apache.juneau.rest.RestLogger#logObjects(Level,String,Object[]) logObject(Level,String,Object[])}
 			</ul>
 		</ul>
 		<p>
@@ -2485,7 +2475,7 @@
 			It should be noted that the Configuration API is used extensively in the Microservice API in order to externally configure microservices.
 		</p>
 		<p>
-			Once a config file has been associated with a REST servlet, it can be accessed through the {@link org.apache.juneau.rest.RestServlet#getConfig()}
+			Once a config file has been associated with a REST servlet, it can be accessed through the {@link org.apache.juneau.rest.RestContext#getConfigFile()}
 				method.
 		</p>
 		<p>
@@ -2513,7 +2503,7 @@
 	<jk>public</jk> MyRestServlet <jk>extends</jk> RestServlet {
 		</p>
 		<p>
-			It's even possible to reference request-level variables in your config file if you use {@link org.apache.juneau.rest.RestRequest#getConfig()}
+			It's even possible to reference request-level variables in your config file if you use {@link org.apache.juneau.rest.RestRequest#getConfigFile()}
 				to access the config file:
 		</p>
 		<p class='bcode'>
@@ -2588,8 +2578,7 @@
 				<br>Juneau Configuration API Javadocs.
 			<li class='p'><a href='../microservice/package-summary.html#TOC'><l>org.apache.juneau.microservice</l></a>
 				<br>Juneau Microservice API Javadocs.
-			<li class='m'>{@link org.apache.juneau.rest.RestServlet#getConfig()}
-			<li class='m'>{@link org.apache.juneau.rest.RestServlet#getConfigMgr()}
+			<li class='m'>{@link org.apache.juneau.rest.RestContext#getConfigFile()}
 		</ul>
 	</div>
 
@@ -2813,7 +2802,7 @@
 	}
 		</p>
 		<p>
-			To support overloaded methods, the {@link org.apache.juneau.rest.RestServletContext#REST_allowMethodParam} property
+			To support overloaded methods, the {@link org.apache.juneau.rest.RestContext#REST_allowMethodParam} property
 				must be set on your servlet.
 		</p>
 		<p class='bcode'>
@@ -2864,14 +2853,14 @@
 				<td class='code'>&amp;method=X</td>
 				<td>
 					Overload the HTTP method as a GET parameter (e.g <l>"POST"</l>).
-					<br>Must be enabled via {@link org.apache.juneau.rest.RestServletContext#REST_allowMethodParam} property.
+					<br>Must be enabled via {@link org.apache.juneau.rest.RestContext#REST_allowMethodParam} property.
 				</td>
 			</tr>
 			<tr>
 				<td class='code'>&amp;X=headerValue</td>
 				<td>
 					Specify a header value as a GET parameter.
-					<br>Must be enabled via {@link org.apache.juneau.rest.RestServletContext#REST_allowHeaderParams} property.
+					<br>Must be enabled via {@link org.apache.juneau.rest.RestContext#REST_allowHeaderParams} property.
 				</td>
 			</tr>
 			<tr>
@@ -2879,7 +2868,7 @@
 				<td>
 					Pass in the HTTP body content on PUT and POST methods as a UON-encoded GET parameter.
 					<br>
-					<br>Must be enabled via {@link org.apache.juneau.rest.RestServletContext#REST_allowBodyParam} property.
+					<br>Must be enabled via {@link org.apache.juneau.rest.RestContext#REST_allowBodyParam} property.
 				</td>
 			</tr>
 		</table>
@@ -3041,10 +3030,10 @@
 		</p>
 		<ul class='spaced-list'>
 			<li>Specifying additional handlers through the {@link org.apache.juneau.rest.annotation.RestResource#responseHandlers() @RestResource.responseHandlers()} annotation.
-			<li>Overriding and extending the {@link org.apache.juneau.rest.RestServlet#createResponseHandlers(ObjectMap)} method.
+			<li>Overriding and extending the {@link org.apache.juneau.rest.RestConfig#addResponseHandlers(Class[])} method.
 		</ul>
 		<p>
-			The {@link org.apache.juneau.rest.RestServlet#handleResponse(RestRequest,RestResponse,Object)} method can also be 
+			The {@link org.apache.juneau.rest.RestCallHandler#handleResponse(RestRequest,RestResponse,Object)} method can also be 
 				overridden to bypass the response handler API and process the POJO yourself.
 		</p>
 	</div>

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/RemoteableServlet.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/RemoteableServlet.java b/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/RemoteableServlet.java
index 6ba3997..15a96ad 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/RemoteableServlet.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/RemoteableServlet.java
@@ -68,7 +68,7 @@ public abstract class RemoteableServlet extends RestServletDefault {
 		List<Link> l = new LinkedList<Link>();
 		boolean useAll = ! useOnlyAnnotated();
 		for (Class<?> c : getServiceMap().keySet()) {
-			if (useAll || getBeanContext().getClassMeta(c).isRemoteable())
+			if (useAll || getContext().getBeanContext().getClassMeta(c).isRemoteable())
 				l.add(new Link(c.getName(), "{0}/{1}", req.getRequestURI(), c.getName())); //$NON-NLS-1$
 		}
 		return l;
@@ -131,7 +131,7 @@ public abstract class RemoteableServlet extends RestServletDefault {
 
 	private Map<String,java.lang.reflect.Method> getMethods(String javaInterface) throws Exception {
 		Class<?> c = getInterfaceClass(javaInterface);
-		ClassMeta<?> cm = getBeanContext().getClassMeta(c);
+		ClassMeta<?> cm = getContext().getBeanContext().getClassMeta(c);
 		return (useOnlyAnnotated() ? cm.getRemoteableMethods() : cm.getPublicMethods());
 	}
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/response/DefaultHandler.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/response/DefaultHandler.java b/juneau-rest/src/main/java/org/apache/juneau/rest/response/DefaultHandler.java
index 52650e1..f7f6df3 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/response/DefaultHandler.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/response/DefaultHandler.java
@@ -30,9 +30,6 @@ import org.apache.juneau.serializer.*;
  * <p>
  * The <code>Content-Type</code> header is set to the mime-type defined on the selected
  * 	serializer based on the {@link Produces#contentType() @Produces.contentType} annotation.
- * <p>
- * This handler is registered by default on {@link RestServlet RestServlets} via the
- * 	default implementation of the {@link RestServlet#createResponseHandlers} method.
  */
 public class DefaultHandler implements ResponseHandler {
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/response/InputStreamHandler.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/response/InputStreamHandler.java b/juneau-rest/src/main/java/org/apache/juneau/rest/response/InputStreamHandler.java
index 1eebcfb..78d3948 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/response/InputStreamHandler.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/response/InputStreamHandler.java
@@ -23,9 +23,6 @@ import org.apache.juneau.utils.*;
  * Simply pipes the contents of the {@link InputStream} to {@link RestResponse#getNegotiatedOutputStream()}.
  * <p>
  * Sets the <code>Content-Type</code> response header to whatever was set via {@link RestResponse#setContentType(String)}.
- * <p>
- * This handler is registered by default on {@link RestServlet RestServlets} via the
- * 	default implementation of the {@link RestServlet#createResponseHandlers} method.
  */
 public final class InputStreamHandler implements ResponseHandler {
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/response/ReaderHandler.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/response/ReaderHandler.java b/juneau-rest/src/main/java/org/apache/juneau/rest/response/ReaderHandler.java
index 6f5b6ef..d34f280 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/response/ReaderHandler.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/response/ReaderHandler.java
@@ -21,9 +21,6 @@ import org.apache.juneau.utils.*;
  * Response handler for {@link Reader} objects.
  * <p>
  * Simply pipes the contents of the {@link Reader} to {@link RestResponse#getNegotiatedWriter()}.
- * <p>
- * This handler is registered by default on {@link RestServlet RestServlets} via the
- * 	default implementation of the {@link RestServlet#createResponseHandlers} method.
  */
 public final class ReaderHandler implements ResponseHandler {
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/response/RedirectHandler.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/response/RedirectHandler.java b/juneau-rest/src/main/java/org/apache/juneau/rest/response/RedirectHandler.java
index b2054ce..499a834 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/response/RedirectHandler.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/response/RedirectHandler.java
@@ -19,9 +19,6 @@ import org.apache.juneau.rest.*;
 
 /**
  * Response handler for {@link Redirect} objects.
- * <p>
- * This handler is registered by default on {@link RestServlet RestServlets} via the
- * 	default implementation of the {@link RestServlet#createResponseHandlers} method.
  */
 public final class RedirectHandler implements ResponseHandler {
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/response/StreamableHandler.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/response/StreamableHandler.java b/juneau-rest/src/main/java/org/apache/juneau/rest/response/StreamableHandler.java
index 041665b..8a2f107 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/response/StreamableHandler.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/response/StreamableHandler.java
@@ -22,9 +22,6 @@ import org.apache.juneau.rest.*;
  * Response handler for {@link Writable} and {@link ReaderResource} objects.
  * <p>
  * Uses the {@link Writable#writeTo(Writer)} method to send the contents to the {@link RestResponse#getNegotiatedWriter()} writer.
- * <p>
- * This handler is registered by default on {@link RestServlet RestServlets} via the
- * 	default implementation of the {@link RestServlet#createResponseHandlers} method.
  */
 public final class StreamableHandler implements ResponseHandler {
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/response/WritableHandler.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/response/WritableHandler.java b/juneau-rest/src/main/java/org/apache/juneau/rest/response/WritableHandler.java
index 7d84368..b453d1f 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/response/WritableHandler.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/response/WritableHandler.java
@@ -22,9 +22,6 @@ import org.apache.juneau.rest.*;
  * Response handler for {@link Writable} and {@link ReaderResource} objects.
  * <p>
  * Uses the {@link Writable#writeTo(Writer)} method to send the contents to the {@link RestResponse#getNegotiatedWriter()} writer.
- * <p>
- * This handler is registered by default on {@link RestServlet RestServlets} via the
- * 	default implementation of the {@link RestServlet#createResponseHandlers} method.
  */
 public final class WritableHandler implements ResponseHandler {
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/vars/ServletInitParamVar.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/vars/ServletInitParamVar.java b/juneau-rest/src/main/java/org/apache/juneau/rest/vars/ServletInitParamVar.java
index 7f534ce..b141695 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/vars/ServletInitParamVar.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/vars/ServletInitParamVar.java
@@ -44,6 +44,6 @@ public class ServletInitParamVar extends DefaultingVar {
 
 	@Override /* Parameter */
 	public String resolve(VarResolverSession session, String key) {
-		return session.getSessionObject(RestRequest.class, RequestVar.SESSION_req).getServlet().getServletConfig().getInitParameter(key);
+		return session.getSessionObject(RestRequest.class, RequestVar.SESSION_req).getContext().getServletInitParameter(key);
 	}
 }


[6/7] incubator-juneau git commit: Allow @RestResource/@RestMethod annotations to be used on any classes.

Posted by ja...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ParamsResource.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ParamsResource.java b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ParamsResource.java
index e690d88..d9424e7 100644
--- a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ParamsResource.java
+++ b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ParamsResource.java
@@ -12,7 +12,7 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.rest.test;
 
-import static org.apache.juneau.rest.RestServletContext.*;
+import static org.apache.juneau.rest.RestContext.*;
 
 import java.util.*;
 
@@ -35,7 +35,8 @@ import org.apache.juneau.urlencoding.*;
 	serializers=PlainTextSerializer.class,
 	properties={
 		@Property(name=REST_allowMethodParam, value="*")
-	}
+	},
+	pojoSwaps={CalendarSwap.DateMedium.class}
 )
 public class ParamsResource extends RestServletDefault {
 	private static final long serialVersionUID = 1L;
@@ -106,11 +107,6 @@ public class ParamsResource extends RestServletDefault {
 		res.setOutput("PUT /uuid/"+uuid);
 	}
 
-	@Override /* RestServlet */
-	public Class<?>[] createPojoSwaps() {
-		return new Class[]{CalendarSwap.DateMedium.class};
-	}
-
 	//====================================================================================================
 	// @FormData annotation - GET
 	//====================================================================================================

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/PathResource.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/PathResource.java b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/PathResource.java
index a38cb68..e1b7f5c 100644
--- a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/PathResource.java
+++ b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/PathResource.java
@@ -33,7 +33,7 @@ public class PathResource extends RestServletDefault {
 	//====================================================================================================
 	@RestMethod(name="GET", path="/")
 	public String doGet() {
-		return getPath();
+		return getContext().getPath();
 	}
 
 	@RestResource(
@@ -47,7 +47,7 @@ public class PathResource extends RestServletDefault {
 		// Basic tests
 		@RestMethod(name="GET", path="/")
 		public String doGet() {
-			return getPath();
+			return getContext().getPath();
 		}
 	}
 
@@ -59,7 +59,7 @@ public class PathResource extends RestServletDefault {
 		// Basic tests
 		@RestMethod(name="GET", path="/")
 		public String doGet() {
-			return getPath();
+			return getContext().getPath();
 		}
 	}
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/Root.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/Root.java b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/Root.java
index 67634e4..f24c34c 100644
--- a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/Root.java
+++ b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/Root.java
@@ -66,6 +66,6 @@ public class Root extends RestServletDefault {
 
 	@RestMethod(name="GET", path="/")
 	public ChildResourceDescriptions doGet(RestRequest req) {
-		return new ChildResourceDescriptions(this, req);
+		return new ChildResourceDescriptions(getContext(), req);
 	}
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ErrorConditionsTest.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ErrorConditionsTest.java b/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ErrorConditionsTest.java
index eb46760..81bce1d 100644
--- a/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ErrorConditionsTest.java
+++ b/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ErrorConditionsTest.java
@@ -127,7 +127,7 @@ public class ErrorConditionsTest extends RestTestcase {
 		} catch (RestCallException e) {
 			checkErrorResponse(debug, e, SC_BAD_REQUEST,
 				"Could not convert request body content to class type 'org.apache.juneau.rest.test.ErrorConditionsResource$Test3c' using parser 'org.apache.juneau.json.JsonParser'.",
-				"Caused by (RuntimeException): Test error");
+				"Test error");
 		}
 	}
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/TestUtils.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/TestUtils.java b/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/TestUtils.java
index 22d84e0..caf9041 100644
--- a/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/TestUtils.java
+++ b/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/TestUtils.java
@@ -49,14 +49,24 @@ public class TestUtils {
 			System.err.println(r); // NOT DEBUG
 			e.printStackTrace();
 		}
-		if (status != e.getResponseCode())
+		if (status != e.getResponseCode()) {
+			dumpResponse(r, "Response status code was not correct.  Expected: ''{0}''.  Actual: ''{1}''", status, e.getResponseCode());
 			throw new AssertionFailedError(MessageFormat.format("Response status code was not correct.  Expected: ''{0}''.  Actual: ''{1}''", status, e.getResponseCode()));
+		}
 		for (String s : contains) {
 			if (r == null || ! r.contains(s)) {
 				if (! debug)
-					System.err.println(r); // NOT DEBUG
+					dumpResponse(r, "Response did not have the following expected text: ''{0}''", s);
 				throw new AssertionFailedError(MessageFormat.format("Response did not have the following expected text: ''{0}''", s));
 			}
 		}
 	}
+
+	private static void dumpResponse(String r, String msg, Object...args) {
+		System.err.println("*** Failure ****************************************************************************************"); // NOT DEBUG
+		System.err.println(MessageFormat.format(msg, args));
+		System.err.println("*** Response-Start *********************************************************************************"); // NOT DEBUG
+		System.err.println(r); // NOT DEBUG
+		System.err.println("*** Response-End ***********************************************************************************"); // NOT DEBUG
+	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/CallMethod.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/CallMethod.java b/juneau-rest/src/main/java/org/apache/juneau/rest/CallMethod.java
new file mode 100644
index 0000000..5cfa4e5
--- /dev/null
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/CallMethod.java
@@ -0,0 +1,996 @@
+// ***************************************************************************************************************************
+// * 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 javax.servlet.http.HttpServletResponse.*;
+import static org.apache.juneau.dto.swagger.SwaggerBuilder.*;
+import static org.apache.juneau.internal.ClassUtils.*;
+import static org.apache.juneau.rest.CallMethod.ParamType.*;
+import static org.apache.juneau.rest.RestContext.*;
+import static org.apache.juneau.rest.annotation.Inherit.*;
+import static org.apache.juneau.serializer.SerializerContext.*;
+
+import java.lang.annotation.*;
+import java.lang.reflect.*;
+import java.lang.reflect.Method;
+import java.util.*;
+
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.dto.swagger.*;
+import org.apache.juneau.encoders.*;
+import org.apache.juneau.internal.*;
+import org.apache.juneau.json.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.rest.annotation.Properties;
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.svl.*;
+import org.apache.juneau.urlencoding.*;
+
+/**
+ * Represents a single Java servlet/resource method annotated with {@link RestMethod @RestMethod}.
+ */
+@SuppressWarnings("hiding")
+final class CallMethod implements Comparable<CallMethod>  {
+	private final java.lang.reflect.Method method;
+	private final String httpMethod;
+	private final UrlPathPattern pathPattern;
+	private final CallMethod.MethodParam[] params;
+	private final RestGuard[] guards;
+	private final RestMatcher[] optionalMatchers;
+	private final RestMatcher[] requiredMatchers;
+	private final RestConverter[] converters;
+	private final SerializerGroup serializers;
+	private final ParserGroup parsers;
+	private final EncoderGroup encoders;
+	private final UrlEncodingParser urlEncodingParser;
+	private final UrlEncodingSerializer urlEncodingSerializer;
+	private final ObjectMap properties;
+	private final Map<String,String> defaultRequestHeaders;
+	private final String defaultEncoding;
+	private final boolean deprecated;
+	private final String description, tags, summary, externalDocs;
+	private final Integer priority;
+	private final org.apache.juneau.rest.annotation.Parameter[] parameters;
+	private final Response[] responses;
+	private final RestContext context;
+
+	CallMethod(Object servlet, java.lang.reflect.Method method, RestContext context) throws RestServletException {
+		Builder b = new Builder(servlet, method, context);
+		this.context = context;
+		this.method = method;
+		this.httpMethod = b.httpMethod;
+		this.pathPattern = b.pathPattern;
+		this.params = b.params;
+		this.guards = b.guards;
+		this.optionalMatchers = b.optionalMatchers;
+		this.requiredMatchers = b.requiredMatchers;
+		this.converters = b.converters;
+		this.serializers = b.serializers;
+		this.parsers = b.parsers;
+		this.encoders = b.encoders;
+		this.urlEncodingParser = b.urlEncodingParser;
+		this.urlEncodingSerializer = b.urlEncodingSerializer;
+		this.properties = b.properties;
+		this.defaultRequestHeaders = b.defaultRequestHeaders;
+		this.defaultEncoding = b.defaultEncoding;
+		this.deprecated = b.deprecated;
+		this.description = b.description;
+		this.tags = b.tags;
+		this.summary = b.summary;
+		this.externalDocs = b.externalDocs;
+		this.priority = b.priority;
+		this.parameters = b.parameters;
+		this.responses = b.responses;
+	}
+
+	private static class Builder  {
+		private String httpMethod, defaultEncoding, description, tags, summary, externalDocs;
+		private UrlPathPattern pathPattern;
+		private CallMethod.MethodParam[] params;
+		private RestGuard[] guards;
+		private RestMatcher[] optionalMatchers, requiredMatchers;
+		private RestConverter[] converters;
+		private SerializerGroup serializers;
+		private ParserGroup parsers;
+		private EncoderGroup encoders;
+		private UrlEncodingParser urlEncodingParser;
+		private UrlEncodingSerializer urlEncodingSerializer;
+		private ObjectMap properties;
+		private Map<String,String> defaultRequestHeaders;
+		private boolean plainParams, deprecated;
+		private Integer priority;
+		private org.apache.juneau.rest.annotation.Parameter[] parameters;
+		private Response[] responses;
+
+		private Builder(Object servlet, java.lang.reflect.Method method, RestContext context) throws RestServletException {
+			try {
+
+				RestMethod m = method.getAnnotation(RestMethod.class);
+				if (m == null)
+					throw new RestServletException("@RestMethod annotation not found on method ''{0}.{1}''", method.getDeclaringClass().getName(), method.getName());
+
+				if (! m.description().isEmpty())
+					description = m.description();
+				if (! m.tags().isEmpty())
+					tags = m.tags();
+				if (! m.summary().isEmpty())
+					summary = m.summary();
+				if (! m.externalDocs().isEmpty())
+					externalDocs = m.externalDocs();
+				deprecated = m.deprecated();
+				parameters = m.parameters();
+				responses = m.responses();
+				serializers = context.getSerializers();
+				parsers = context.getParsers();
+				urlEncodingSerializer = context.getUrlEncodingSerializer();
+				urlEncodingParser = context.getUrlEncodingParser();
+				encoders = context.getEncoders();
+				properties = context.getProperties();
+
+				List<Inherit> si = Arrays.asList(m.serializersInherit());
+				List<Inherit> pi = Arrays.asList(m.parsersInherit());
+
+				SerializerGroupBuilder sgb = null;
+				ParserGroupBuilder pgb = null;
+				UrlEncodingParserBuilder uepb = null;
+
+				if (m.serializers().length > 0 || m.parsers().length > 0 || m.properties().length > 0 || m.beanFilters().length > 0 || m.pojoSwaps().length > 0) {
+					sgb = new SerializerGroupBuilder();
+					pgb = new ParserGroupBuilder();
+					uepb = new UrlEncodingParserBuilder(urlEncodingParser.createPropertyStore());
+
+					if (si.contains(SERIALIZERS) || m.serializers().length == 0)
+						sgb.append(serializers.getSerializers());
+
+					if (pi.contains(PARSERS) || m.parsers().length == 0)
+						pgb.append(parsers.getParsers());
+				}
+
+				httpMethod = m.name().toUpperCase(Locale.ENGLISH);
+				if (httpMethod.equals("") && method.getName().startsWith("do"))
+					httpMethod = method.getName().substring(2).toUpperCase(Locale.ENGLISH);
+				if (httpMethod.equals(""))
+					throw new RestServletException("@RestMethod name not specified on method ''{0}.{1}''", method.getDeclaringClass().getName(), method.getName());
+				if (httpMethod.equals("METHOD"))
+					httpMethod = "*";
+
+				priority = m.priority();
+
+				String p = m.path();
+				converters = new RestConverter[m.converters().length];
+				for (int i = 0; i < converters.length; i++)
+					converters[i] = m.converters()[i].newInstance();
+
+				guards = new RestGuard[m.guards().length];
+				for (int i = 0; i < guards.length; i++)
+					guards[i] = m.guards()[i].newInstance();
+
+				List<RestMatcher> optionalMatchers = new LinkedList<RestMatcher>(), requiredMatchers = new LinkedList<RestMatcher>();
+				for (int i = 0; i < m.matchers().length; i++) {
+					Class<? extends RestMatcher> c = m.matchers()[i];
+					RestMatcher matcher = null;
+					if (ClassUtils.isParentClass(RestMatcherReflecting.class, c))
+						matcher = c.getConstructor(Object.class, Method.class).newInstance(servlet, method);
+					else
+						matcher = c.newInstance();
+					if (matcher.mustMatch())
+						requiredMatchers.add(matcher);
+					else
+						optionalMatchers.add(matcher);
+				}
+				if (! m.clientVersion().isEmpty())
+					requiredMatchers.add(new ClientVersionMatcher(context.getClientVersionHeader(), method));
+
+				this.requiredMatchers = requiredMatchers.toArray(new RestMatcher[requiredMatchers.size()]);
+				this.optionalMatchers = optionalMatchers.toArray(new RestMatcher[optionalMatchers.size()]);
+
+				Class<?>[] beanFilters = context.getBeanFilters(), pojoSwaps = context.getPojoSwaps();
+
+				if (sgb != null) {
+					sgb.append(m.serializers());
+					if (si.contains(TRANSFORMS))
+						sgb.beanFilters(beanFilters).pojoSwaps(pojoSwaps);
+					if (si.contains(PROPERTIES))
+						sgb.properties(properties);
+					for (Property p1 : m.properties())
+						sgb.property(p1.name(), p1.value());
+					sgb.beanFilters(m.beanFilters());
+					sgb.pojoSwaps(m.pojoSwaps());
+				}
+
+				if (pgb != null) {
+					pgb.append(m.parsers());
+					if (pi.contains(TRANSFORMS))
+						pgb.beanFilters(beanFilters).pojoSwaps(pojoSwaps);
+					if (pi.contains(PROPERTIES))
+						pgb.properties(properties);
+					for (Property p1 : m.properties())
+						pgb.property(p1.name(), p1.value());
+					pgb.beanFilters(m.beanFilters());
+					pgb.pojoSwaps(m.pojoSwaps());
+				}
+
+				if (uepb != null) {
+					for (Property p1 : m.properties())
+						uepb.property(p1.name(), p1.value());
+					uepb.beanFilters(m.beanFilters());
+					uepb.pojoSwaps(m.pojoSwaps());
+				}
+
+				if (m.properties().length > 0) {
+					properties = new ObjectMap().setInner(properties);
+					for (Property p1 : m.properties()) {
+						properties.put(p1.name(), p1.value());
+					}
+				}
+
+				if (m.encoders().length > 0 || ! m.inheritEncoders()) {
+					EncoderGroupBuilder g = new EncoderGroupBuilder();
+					if (m.inheritEncoders())
+						g.append(encoders);
+					else
+						g.append(IdentityEncoder.INSTANCE);
+
+					for (Class<? extends Encoder> c : m.encoders()) {
+						try {
+							g.append(c);
+						} catch (Exception e) {
+							throw new RestServletException("Exception occurred while trying to instantiate Encoder ''{0}''", c.getSimpleName()).initCause(e);
+						}
+					}
+					encoders = g.build();
+				}
+
+				defaultRequestHeaders = new TreeMap<String,String>(String.CASE_INSENSITIVE_ORDER);
+				for (String s : m.defaultRequestHeaders()) {
+					String[] h = RestUtils.parseHeader(s);
+					if (h == null)
+						throw new RestServletException("Invalid default request header specified: ''{0}''.  Must be in the format: ''Header-Name: header-value''", s);
+					defaultRequestHeaders.put(h[0], h[1]);
+				}
+
+				defaultEncoding = properties.getString(REST_defaultCharset, context.getDefaultCharset());
+				String paramFormat = properties.getString(REST_paramFormat, context.getParamFormat());
+				plainParams = paramFormat.equals("PLAIN");
+
+				pathPattern = new UrlPathPattern(p);
+
+				int attrIdx = 0;
+				Type[] pt = method.getGenericParameterTypes();
+				Annotation[][] pa = method.getParameterAnnotations();
+				params = new CallMethod.MethodParam[pt.length];
+				for (int i = 0; i < params.length; i++) {
+					params[i] = new CallMethod.MethodParam(pt[i], method, pa[i], plainParams, pathPattern, attrIdx);
+					attrIdx = params[i].attrIdx;
+				}
+
+				if (sgb != null)
+					serializers = sgb.build();
+				if (pgb != null)
+					parsers = pgb.build();
+				if (uepb != null)
+					urlEncodingParser = uepb.build();
+
+				// Need this to access methods in anonymous inner classes.
+				method.setAccessible(true);
+			} catch (Exception e) {
+				throw new RestServletException("Exception occurred while initializing method ''{0}''", method.getName()).initCause(e);
+			}
+		}
+	}
+
+	/**
+	 * Represents a single parameter in the Java method.
+	 */
+	private static class MethodParam {
+
+		private final ParamType paramType;
+		private final Type type;
+		private final String name;
+		private final boolean multiPart, plainParams;
+		private final int attrIdx;
+
+		private MethodParam(Type type, Method method, Annotation[] annotations, boolean methodPlainParams, UrlPathPattern pathPattern, int attrIdx) throws ServletException {
+			this.type = type;
+
+			ParamType _paramType = null;
+			String _name = "";
+			boolean _multiPart = false, _plainParams = false;
+
+			boolean isClass = type instanceof Class;
+			if (isClass && isParentClass(HttpServletRequest.class, (Class<?>)type))
+				_paramType = REQ;
+			else if (isClass && isParentClass(HttpServletResponse.class, (Class<?>)type))
+				_paramType = RES;
+			else for (Annotation a : annotations) {
+				if (a instanceof Path) {
+					Path a2 = (Path)a;
+					_paramType = PATH;
+					_name = a2.value();
+				} else if (a instanceof Header) {
+					Header h = (Header)a;
+					_paramType = HEADER;
+					_name = h.value();
+				} else if (a instanceof FormData) {
+					FormData p = (FormData)a;
+					if (p.multipart())
+						assertCollection(type, method);
+					_paramType = FORMDATA;
+					_multiPart = p.multipart();
+					_plainParams = p.format().equals("INHERIT") ? methodPlainParams : p.format().equals("PLAIN");
+					_name = p.value();
+				} else if (a instanceof Query) {
+					Query p = (Query)a;
+					if (p.multipart())
+						assertCollection(type, method);
+					_paramType = QUERY;
+					_multiPart = p.multipart();
+					_plainParams = p.format().equals("INHERIT") ? methodPlainParams : p.format().equals("PLAIN");
+					_name = p.value();
+				} else if (a instanceof HasFormData) {
+					HasFormData p = (HasFormData)a;
+					_paramType = HASFORMDATA;
+					_name = p.value();
+				} else if (a instanceof HasQuery) {
+					HasQuery p = (HasQuery)a;
+					_paramType = HASQUERY;
+					_name = p.value();
+				} else if (a instanceof Body) {
+					_paramType = BODY;
+				} else if (a instanceof org.apache.juneau.rest.annotation.Method) {
+					_paramType = METHOD;
+					if (type != String.class)
+						throw new ServletException("@Method parameters must be of type String");
+				} else if (a instanceof PathRemainder) {
+					_paramType = PATHREMAINDER;
+					if (type != String.class)
+						throw new ServletException("@PathRemainder parameters must be of type String");
+				} else if (a instanceof Properties) {
+					_paramType = PROPS;
+					_name = "PROPERTIES";
+				} else if (a instanceof Messages) {
+					_paramType = MESSAGES;
+					_name = "MESSAGES";
+				}
+			}
+			if (_paramType == null)
+				_paramType = PATH;
+
+			if (_paramType == PATH && _name.isEmpty()) {
+				if (pathPattern.getVars().length <= attrIdx)
+					throw new RestServletException("Number of attribute parameters in method ''{0}'' exceeds the number of URL pattern variables.", method.getName());
+				_name = pathPattern.getVars()[attrIdx++];
+			}
+
+			this.paramType = _paramType;
+			this.name = _name;
+			this.multiPart = _multiPart;
+			this.plainParams = _plainParams;
+			this.attrIdx = attrIdx;
+		}
+
+		/**
+		 * Throws an exception if the specified type isn't an array or collection.
+		 */
+		private static void assertCollection(Type t, Method m) throws ServletException {
+			ClassMeta<?> cm = BeanContext.DEFAULT.getClassMeta(t);
+			if (! cm.isCollectionOrArray())
+				throw new ServletException("Use of multipart flag on parameter that's not an array or Collection on method" + m);
+		}
+
+		private Object getValue(RestRequest req, RestResponse res) throws Exception {
+			BeanSession session = req.getBeanSession();
+			switch(paramType) {
+				case REQ:        return req;
+				case RES:        return res;
+				case PATH:       return req.getPathParameter(name, type);
+				case BODY:       return req.getBody(type);
+				case HEADER:     return req.getHeader(name, type);
+				case METHOD:     return req.getMethod();
+				case FORMDATA: {
+					if (multiPart)
+						return req.getFormDataParameters(name, type);
+					if (plainParams)
+						return session.convertToType(req.getFormDataParameter(name), session.getClassMeta(type));
+					return req.getFormDataParameter(name, type);
+				}
+				case QUERY: {
+					if (multiPart)
+						return req.getQueryParameters(name, type);
+					if (plainParams)
+						return session.convertToType(req.getQueryParameter(name), session.getClassMeta(type));
+					return req.getQueryParameter(name, type);
+				}
+				case HASFORMDATA:   return session.convertToType(req.hasFormDataParameter(name), session.getClassMeta(type));
+				case HASQUERY:      return session.convertToType(req.hasQueryParameter(name), session.getClassMeta(type));
+				case PATHREMAINDER: return req.getPathRemainder();
+				case PROPS:         return res.getProperties();
+				case MESSAGES:      return req.getResourceBundle();
+				default:            return null;
+			}
+		}
+	}
+
+	static enum ParamType {
+		REQ, RES, PATH, BODY, HEADER, METHOD, FORMDATA, QUERY, HASFORMDATA, HASQUERY, PATHREMAINDER, PROPS, MESSAGES;
+
+		private String getSwaggerParameterType() {
+			switch(this) {
+				case PATH: return "path";
+				case HEADER: return "header";
+				case FORMDATA: return "formData";
+				case QUERY: return "query";
+				case BODY: return "body";
+				default: return null;
+			}
+		}
+	}
+
+	/**
+	 * Returns <jk>true</jk> if this Java method has any guards or matchers.
+	 */
+	boolean hasGuardsOrMatchers() {
+		return (guards.length != 0 || requiredMatchers.length != 0 || optionalMatchers.length != 0);
+	}
+
+	/**
+	 * Returns the HTTP method name (e.g. <js>"GET"</js>).
+	 */
+	String getHttpMethod() {
+		return httpMethod;
+	}
+
+	/**
+	 * Returns the path pattern for this method.
+	 */
+	String getPathPattern() {
+		return pathPattern.toString();
+	}
+
+	/**
+	 * Returns the localized Swagger for this Java method.
+	 */
+	Operation getSwaggerOperation(RestRequest req) throws ParseException {
+		Operation o = operation()
+			.operationId(method.getName())
+			.description(getDescription(req))
+			.tags(getTags(req))
+			.summary(getSummary(req))
+			.externalDocs(getExternalDocs(req))
+			.parameters(getParameters(req))
+			.responses(getResponses(req));
+
+		if (isDeprecated())
+			o.deprecated(true);
+
+		if (! parsers.getSupportedMediaTypes().equals(context.getParsers().getSupportedMediaTypes()))
+			o.consumes(parsers.getSupportedMediaTypes());
+
+		if (! serializers.getSupportedMediaTypes().equals(context.getSerializers().getSupportedMediaTypes()))
+			o.produces(serializers.getSupportedMediaTypes());
+
+		return o;
+	}
+
+	private Operation getSwaggerOperationFromFile(RestRequest req) {
+		Swagger s = req.getSwaggerFromFile();
+		if (s != null && s.getPaths() != null && s.getPaths().get(pathPattern.getPatternString()) != null)
+			return s.getPaths().get(pathPattern.getPatternString()).get(httpMethod);
+		return null;
+	}
+
+	/**
+	 * Returns the localized summary for this Java method.
+	 */
+	String getSummary(RestRequest req) {
+		VarResolverSession vr = req.getVarResolverSession();
+		if (summary != null)
+			return vr.resolve(summary);
+		String summary = context.getMessages().findFirstString(req.getLocale(), method.getName() + ".summary");
+		if (summary != null)
+			return vr.resolve(summary);
+		Operation o = getSwaggerOperationFromFile(req);
+		if (o != null)
+			return o.getSummary();
+		return null;
+	}
+
+	/**
+	 * Returns the localized description for this Java method.
+	 */
+	String getDescription(RestRequest req) {
+		VarResolverSession vr = req.getVarResolverSession();
+		if (description != null)
+			return vr.resolve(description);
+		String description = context.getMessages().findFirstString(req.getLocale(), method.getName() + ".description");
+		if (description != null)
+			return vr.resolve(description);
+		Operation o = getSwaggerOperationFromFile(req);
+		if (o != null)
+			return o.getDescription();
+		return null;
+	}
+
+	/**
+	 * Returns the localized Swagger tags for this Java method.
+	 */
+	private List<String> getTags(RestRequest req) {
+		VarResolverSession vr = req.getVarResolverSession();
+		JsonParser jp = JsonParser.DEFAULT;
+		try {
+			if (tags != null)
+				return jp.parse(vr.resolve(tags), ArrayList.class, String.class);
+			String tags = context.getMessages().findFirstString(req.getLocale(), method.getName() + ".tags");
+			if (tags != null)
+				return jp.parse(vr.resolve(tags), ArrayList.class, String.class);
+			Operation o = getSwaggerOperationFromFile(req);
+			if (o != null)
+				return o.getTags();
+			return null;
+		} catch (Exception e) {
+			throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
+		}
+	}
+
+	/**
+	 * Returns the localized Swagger external docs for this Java method.
+	 */
+	private ExternalDocumentation getExternalDocs(RestRequest req) {
+		VarResolverSession vr = req.getVarResolverSession();
+		JsonParser jp = JsonParser.DEFAULT;
+		try {
+			if (externalDocs != null)
+				return jp.parse(vr.resolve(externalDocs), ExternalDocumentation.class);
+			String externalDocs = context.getMessages().findFirstString(req.getLocale(), method.getName() + ".externalDocs");
+			if (externalDocs != null)
+				return jp.parse(vr.resolve(externalDocs), ExternalDocumentation.class);
+			Operation o = getSwaggerOperationFromFile(req);
+			if (o != null)
+				return o.getExternalDocs();
+			return null;
+		} catch (Exception e) {
+			throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
+		}
+	}
+
+	/**
+	 * Returns the Swagger deprecated flag for this Java method.
+	 */
+	private boolean isDeprecated() {
+		return deprecated;
+	}
+
+	/**
+	 * Returns the localized Swagger parameter information for this Java method.
+	 */
+	private List<ParameterInfo> getParameters(RestRequest req) throws ParseException {
+		Operation o = getSwaggerOperationFromFile(req);
+		if (o != null && o.getParameters() != null)
+			return o.getParameters();
+
+		VarResolverSession vr = req.getVarResolverSession();
+		JsonParser jp = JsonParser.DEFAULT;
+		Map<String,ParameterInfo> m = new TreeMap<String,ParameterInfo>();
+
+		// First parse @RestMethod.parameters() annotation.
+		for (org.apache.juneau.rest.annotation.Parameter v : parameters) {
+			String in = vr.resolve(v.in());
+			ParameterInfo p = parameterInfo(in, vr.resolve(v.name()));
+
+			if (! v.description().isEmpty())
+				p.description(vr.resolve(v.description()));
+			if (v.required())
+				p.required(v.required());
+
+			if ("body".equals(in)) {
+				if (! v.schema().isEmpty())
+					p.schema(jp.parse(vr.resolve(v.schema()), SchemaInfo.class));
+			} else {
+				if (v.allowEmptyValue())
+					p.allowEmptyValue(v.allowEmptyValue());
+				if (! v.collectionFormat().isEmpty())
+					p.collectionFormat(vr.resolve(v.collectionFormat()));
+				if (! v._default().isEmpty())
+					p._default(vr.resolve(v._default()));
+				if (! v.format().isEmpty())
+					p.format(vr.resolve(v.format()));
+				if (! v.items().isEmpty())
+					p.items(jp.parse(vr.resolve(v.items()), Items.class));
+				p.type(vr.resolve(v.type()));
+			}
+			m.put(p.getIn() + '.' + p.getName(), p);
+		}
+
+		// Next, look in resource bundle.
+		String prefix = method.getName() + ".req";
+		for (String key : context.getMessages().keySet(prefix)) {
+			if (key.length() > prefix.length()) {
+				String value = vr.resolve(context.getMessages().getString(key));
+				String[] parts = key.substring(prefix.length() + 1).split("\\.");
+				String in = parts[0], name, field;
+				boolean isBody = "body".equals(in);
+				if (parts.length == (isBody ? 2 : 3)) {
+					if ("body".equals(in)) {
+						name = null;
+						field = parts[1];
+					} else {
+						name = parts[1];
+						field = parts[2];
+					}
+					String k2 = in + '.' + name;
+					ParameterInfo p = m.get(k2);
+					if (p == null) {
+						p = parameterInfoStrict(in, name);
+						m.put(k2, p);
+					}
+
+					if (field.equals("description"))
+						p.description(value);
+					else if (field.equals("required"))
+						p.required(Boolean.valueOf(value));
+
+					if ("body".equals(in)) {
+						if (field.equals("schema"))
+							p.schema(jp.parse(value, SchemaInfo.class));
+					} else {
+						if (field.equals("allowEmptyValue"))
+							p.allowEmptyValue(Boolean.valueOf(value));
+						else if (field.equals("collectionFormat"))
+							p.collectionFormat(value);
+						else if (field.equals("default"))
+							p._default(value);
+						else if (field.equals("format"))
+							p.format(value);
+						else if (field.equals("items"))
+							p.items(jp.parse(value, Items.class));
+						else if (field.equals("type"))
+							p.type(value);
+					}
+				} else {
+					System.err.println("Unknown bundle key '"+key+"'");
+				}
+			}
+		}
+
+		// Finally, look for parameters defined on method.
+		for (CallMethod.MethodParam mp : this.params) {
+			String in = mp.paramType.getSwaggerParameterType();
+			if (in != null) {
+				String k2 = in + '.' + ("body".equals(in) ? null : mp.name);
+				ParameterInfo p = m.get(k2);
+				if (p == null) {
+					p = parameterInfoStrict(in, mp.name);
+					m.put(k2, p);
+				}
+			}
+		}
+
+		if (m.isEmpty())
+			return null;
+		return new ArrayList<ParameterInfo>(m.values());
+	}
+
+	/**
+	 * Returns the localized Swagger response information about this Java method.
+	 */
+	@SuppressWarnings("unchecked")
+	private Map<Integer,ResponseInfo> getResponses(RestRequest req) throws ParseException {
+		Operation o = getSwaggerOperationFromFile(req);
+		if (o != null && o.getResponses() != null)
+			return o.getResponses();
+
+		VarResolverSession vr = req.getVarResolverSession();
+		JsonParser jp = JsonParser.DEFAULT;
+		Map<Integer,ResponseInfo> m = new TreeMap<Integer,ResponseInfo>();
+		Map<String,HeaderInfo> m2 = new TreeMap<String,HeaderInfo>();
+
+		// First parse @RestMethod.parameters() annotation.
+		for (Response r : responses) {
+			int httpCode = r.value();
+			String description = r.description().isEmpty() ? RestUtils.getHttpResponseText(r.value()) : vr.resolve(r.description());
+			ResponseInfo r2 = responseInfo(description);
+
+			if (r.headers().length > 0) {
+				for (org.apache.juneau.rest.annotation.Parameter v : r.headers()) {
+					HeaderInfo h = headerInfoStrict(vr.resolve(v.type()));
+					if (! v.collectionFormat().isEmpty())
+						h.collectionFormat(vr.resolve(v.collectionFormat()));
+					if (! v._default().isEmpty())
+						h._default(vr.resolve(v._default()));
+					if (! v.description().isEmpty())
+						h.description(vr.resolve(v.description()));
+					if (! v.format().isEmpty())
+						h.format(vr.resolve(v.format()));
+					if (! v.items().isEmpty())
+						h.items(jp.parse(vr.resolve(v.items()), Items.class));
+					r2.header(v.name(), h);
+					m2.put(httpCode + '.' + v.name(), h);
+				}
+			}
+			m.put(httpCode, r2);
+		}
+
+		// Next, look in resource bundle.
+		String prefix = method.getName() + ".res";
+		for (String key : context.getMessages().keySet(prefix)) {
+			if (key.length() > prefix.length()) {
+				String value = vr.resolve(context.getMessages().getString(key));
+				String[] parts = key.substring(prefix.length() + 1).split("\\.");
+				int httpCode = Integer.parseInt(parts[0]);
+				ResponseInfo r2 = m.get(httpCode);
+				if (r2 == null) {
+					r2 = responseInfo(null);
+					m.put(httpCode, r2);
+				}
+
+				String name = parts.length > 1 ? parts[1] : "";
+
+				if ("header".equals(name) && parts.length > 3) {
+					String headerName = parts[2];
+					String field = parts[3];
+
+					String k2 = httpCode + '.' + headerName;
+					HeaderInfo h = m2.get(k2);
+					if (h == null) {
+						h = headerInfoStrict("string");
+						m2.put(k2, h);
+						r2.header(name, h);
+					}
+					if (field.equals("collectionFormat"))
+						h.collectionFormat(value);
+					else if (field.equals("default"))
+						h._default(value);
+					else if (field.equals("description"))
+						h.description(value);
+					else if (field.equals("format"))
+						h.format(value);
+					else if (field.equals("items"))
+						h.items(jp.parse(value, Items.class));
+					else if (field.equals("type"))
+						h.type(value);
+
+				} else if ("description".equals(name)) {
+					r2.description(value);
+				} else if ("schema".equals(name)) {
+					r2.schema(jp.parse(value, SchemaInfo.class));
+				} else if ("examples".equals(name)) {
+					r2.examples(jp.parse(value, TreeMap.class));
+				} else {
+					System.err.println("Unknown bundle key '"+key+"'");
+				}
+			}
+		}
+
+		return m.isEmpty() ? null : m;
+	}
+
+	/**
+	 * Returns <jk>true</jk> if the specified request object can call this method.
+	 */
+	boolean isRequestAllowed(RestRequest req) {
+		for (RestGuard guard : guards) {
+			req.setJavaMethod(method);
+			if (! guard.isRequestAllowed(req))
+				return false;
+		}
+		return true;
+	}
+
+	/**
+	 * Workhorse method.
+	 *
+	 * @param pathInfo The value of {@link HttpServletRequest#getPathInfo()} (sorta)
+	 * @return The HTTP response code.
+	 */
+	int invoke(String pathInfo, RestRequest req, RestResponse res) throws RestException {
+
+		String[] patternVals = pathPattern.match(pathInfo);
+		if (patternVals == null)
+			return SC_NOT_FOUND;
+
+		String remainder = null;
+		if (patternVals.length > pathPattern.getVars().length)
+			remainder = patternVals[pathPattern.getVars().length];
+		for (int i = 0; i < pathPattern.getVars().length; i++)
+			req.setPathParameter(pathPattern.getVars()[i], patternVals[i]);
+
+		req.init(method, remainder, createRequestProperties(properties, req), defaultRequestHeaders, defaultEncoding, serializers, parsers, urlEncodingParser, encoders);
+		res.init(req.getProperties(), defaultEncoding, serializers, urlEncodingSerializer, encoders);
+
+		// Class-level guards
+		for (RestGuard guard : context.getGuards())
+			if (! guard.guard(req, res))
+				return SC_UNAUTHORIZED;
+
+		// If the method implements matchers, test them.
+		for (RestMatcher m : requiredMatchers)
+			if (! m.matches(req))
+				return SC_PRECONDITION_FAILED;
+		if (optionalMatchers.length > 0) {
+			boolean matches = false;
+			for (RestMatcher m : optionalMatchers)
+				matches |= m.matches(req);
+			if (! matches)
+				return SC_PRECONDITION_FAILED;
+		}
+
+		context.getCallHandler().onPreCall(req);
+
+		Object[] args = new Object[params.length];
+		for (int i = 0; i < params.length; i++) {
+			try {
+				args[i] = params[i].getValue(req, res);
+			} catch (RestException e) {
+				throw e;
+			} catch (Exception e) {
+				throw new RestException(SC_BAD_REQUEST,
+					"Invalid data conversion.  Could not convert {0} ''{1}'' to type ''{2}'' on method ''{3}.{4}''.",
+					params[i].paramType.name(), params[i].name, params[i].type, method.getDeclaringClass().getName(), method.getName()
+				).initCause(e);
+			}
+		}
+
+		try {
+
+			for (RestGuard guard : guards)
+				if (! guard.guard(req, res))
+					return SC_OK;
+
+			Object output = method.invoke(context.getResource(), args);
+			if (! method.getReturnType().equals(Void.TYPE))
+				if (output != null || ! res.getOutputStreamCalled())
+					res.setOutput(output);
+
+			context.getCallHandler().onPostCall(req, res);
+
+			if (res.hasOutput()) {
+				output = res.getOutput();
+				for (RestConverter converter : converters)
+					output = converter.convert(req, output, context.getBeanContext().getClassMetaForObject(output));
+				res.setOutput(output);
+			}
+		} catch (IllegalArgumentException e) {
+			throw new RestException(SC_BAD_REQUEST,
+				"Invalid argument type passed to the following method: ''{0}''.\n\tArgument types: {1}",
+				method.toString(), ClassUtils.getReadableClassNames(args)
+			);
+		} catch (InvocationTargetException e) {
+			Throwable e2 = e.getTargetException();		// Get the throwable thrown from the doX() method.
+			if (e2 instanceof RestException)
+				throw (RestException)e2;
+			if (e2 instanceof ParseException)
+				throw new RestException(SC_BAD_REQUEST, e2);
+			if (e2 instanceof InvalidDataConversionException)
+				throw new RestException(SC_BAD_REQUEST, e2);
+			throw new RestException(SC_INTERNAL_SERVER_ERROR, e2);
+		} catch (RestException e) {
+			throw e;
+		} catch (Exception e) {
+			throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
+		}
+		return SC_OK;
+	}
+
+	/**
+	 * This method creates all the request-time properties.
+	 */
+	static ObjectMap createRequestProperties(final ObjectMap methodProperties, final RestRequest req) {
+		@SuppressWarnings("serial")
+		ObjectMap m = new ObjectMap() {
+			@Override /* Map */
+			public Object get(Object key) {
+				Object o = super.get(key);
+				if (o == null) {
+					String k = key.toString();
+					if (k.indexOf('.') != -1) {
+						String prefix = k.substring(0, k.indexOf('.'));
+						String remainder = k.substring(k.indexOf('.')+1);
+						if ("path".equals(prefix))
+							return req.getPathParameter(remainder);
+						if ("query".equals(prefix))
+							return req.getQueryParameter(remainder);
+						if ("formData".equals(prefix))
+							return req.getFormDataParameter(remainder);
+						if ("header".equals(prefix))
+							return req.getHeader(remainder);
+					}
+					if (k.equals(SERIALIZER_absolutePathUriBase)) {
+						int serverPort = req.getServerPort();
+						String serverName = req.getServerName();
+						return req.getScheme() + "://" + serverName + (serverPort == 80 || serverPort == 443 ? "" : ":" + serverPort);
+					}
+					if (k.equals(REST_servletPath))
+						return req.getServletPath();
+					if (k.equals(REST_servletURI))
+						return req.getServletURI();
+					if (k.equals(REST_relativeServletURI))
+						return req.getRelativeServletURI();
+					if (k.equals(REST_pathInfo))
+						return req.getPathInfo();
+					if (k.equals(REST_requestURI))
+						return req.getRequestURI();
+					if (k.equals(REST_method))
+						return req.getMethod();
+					if (k.equals(REST_servletTitle))
+						return req.getServletTitle();
+					if (k.equals(REST_servletDescription))
+						return req.getServletDescription();
+					if (k.equals(REST_methodSummary))
+						return req.getMethodSummary();
+					if (k.equals(REST_methodDescription))
+						return req.getMethodDescription();
+					o = req.getPathParameter(k);
+					if (o == null)
+						o = req.getHeader(k);
+				}
+				if (o instanceof String)
+					o = req.getVarResolverSession().resolve(o.toString());
+				return o;
+			}
+		};
+		m.setInner(methodProperties);
+		return m;
+	}
+
+	@Override /* Object */
+	public String toString() {
+		return "SimpleMethod: name=" + httpMethod + ", path=" + pathPattern.getPatternString();
+	}
+
+	/*
+	 * compareTo() method is used to keep SimpleMethods ordered in the CallRouter list.
+	 * It maintains the order in which matches are made during requests.
+	 */
+	@Override /* Comparable */
+	public int compareTo(CallMethod o) {
+		int c;
+
+		c = priority.compareTo(o.priority);
+		if (c != 0)
+			return c;
+
+		c = pathPattern.compareTo(o.pathPattern);
+		if (c != 0)
+			return c;
+
+		c = Utils.compare(o.requiredMatchers.length, requiredMatchers.length);
+		if (c != 0)
+			return c;
+
+		c = Utils.compare(o.optionalMatchers.length, optionalMatchers.length);
+		if (c != 0)
+			return c;
+
+		c = Utils.compare(o.guards.length, guards.length);
+		if (c != 0)
+			return c;
+
+		return 0;
+	}
+
+	@Override /* Object */
+	public boolean equals(Object o) {
+		if (! (o instanceof CallMethod))
+			return false;
+		return (compareTo((CallMethod)o) == 0);
+	}
+
+	@Override /* Object */
+	public int hashCode() {
+		return super.hashCode();
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/CallRouter.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/CallRouter.java b/juneau-rest/src/main/java/org/apache/juneau/rest/CallRouter.java
new file mode 100644
index 0000000..bbac14c
--- /dev/null
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/CallRouter.java
@@ -0,0 +1,98 @@
+// ***************************************************************************************************************************
+// * 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 javax.servlet.http.HttpServletResponse.*;
+
+import java.util.*;
+
+import javax.servlet.http.*;
+
+/**
+ * Represents a group of CallMethods on a REST resource that handle the same HTTP Method name but
+ * with different paths/matchers/guards/etc...
+ * <p>
+ * Incoming requests for a particular HTTP method type (e.g. <js>"GET"</js>) are handed off to this class
+ * and then dispatched to the appropriate CallMethod.
+ */
+class CallRouter {
+	private final CallMethod[] callMethods;
+
+	private CallRouter(CallMethod[] callMethods) {
+		this.callMethods = callMethods;
+	}
+
+	/**
+	 * Builder class.
+	 */
+	static class Builder {
+		private List<CallMethod> childMethods = new ArrayList<CallMethod>();
+		private Set<String> collisions = new HashSet<String>();
+		private String httpMethodName;
+
+		Builder(String httpMethodName) {
+			this.httpMethodName = httpMethodName;
+		}
+
+		String getHttpMethodName() {
+			return httpMethodName;
+		}
+
+		Builder add(CallMethod m) throws RestServletException {
+			if (! m.hasGuardsOrMatchers()) {
+				String p = m.getHttpMethod() + ":" + m.getPathPattern();
+				if (collisions.contains(p))
+					throw new RestServletException("Duplicate Java methods assigned to the same method/pattern:  ''{0}''", p);
+				collisions.add(p);
+			}
+			childMethods.add(m);
+			return this;
+		}
+
+		CallRouter build() {
+			Collections.sort(childMethods);
+			return new CallRouter(childMethods.toArray(new CallMethod[childMethods.size()]));
+		}
+	}
+
+	/**
+	 * Workhorse method.
+	 * <p>
+	 * Routes this request to one of the CallMethods.
+	 *
+	 * @param pathInfo The value of {@link HttpServletRequest#getPathInfo()} (sorta)
+	 * @return The HTTP response code.
+	 */
+	int invoke(String pathInfo, RestRequest req, RestResponse res) throws RestException {
+		if (callMethods.length == 1)
+			return callMethods[0].invoke(pathInfo, req, res);
+
+		int maxRc = 0;
+		for (CallMethod m : callMethods) {
+			int rc = m.invoke(pathInfo, req, res);
+			if (rc == SC_OK)
+				return SC_OK;
+			maxRc = Math.max(maxRc, rc);
+		}
+		return maxRc;
+	}
+
+	@Override /* Object */
+	public String toString() {
+		StringBuilder sb = new StringBuilder("CallRouter: [\n");
+		for (CallMethod sm : callMethods)
+			sb.append("\t" + sm + "\n");
+		sb.append("]");
+		return sb.toString();
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/ClientVersionMatcher.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/ClientVersionMatcher.java b/juneau-rest/src/main/java/org/apache/juneau/rest/ClientVersionMatcher.java
index 4f65615..ccf4dd1 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/ClientVersionMatcher.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/ClientVersionMatcher.java
@@ -20,7 +20,7 @@ import org.apache.juneau.rest.annotation.*;
  * <p>
  * See {@link RestResource#clientVersionHeader} and {@link RestMethod#clientVersion} for more info.
  */
-public class ClientVersionMatcher extends RestMatcherReflecting {
+public class ClientVersionMatcher extends RestMatcher {
 
 	private final String clientVersionHeader;
 	private final VersionRange range;
@@ -28,12 +28,12 @@ public class ClientVersionMatcher extends RestMatcherReflecting {
 	/**
 	 * Constructor.
 	 *
-	 * @param servlet The servlet.
+	 * @param clientVersionHeader The HTTP request header name containing the client version.
+	 * If <jk>null</jk> or an empty string, uses <js>"X-Client-Version"</js>
 	 * @param javaMethod The version string that the client version must match.
 	 */
-	protected ClientVersionMatcher(RestServlet servlet, java.lang.reflect.Method javaMethod) {
-		super(servlet, javaMethod);
-		this.clientVersionHeader = servlet.getClientVersionHeader();
+	protected ClientVersionMatcher(String clientVersionHeader, java.lang.reflect.Method javaMethod) {
+		this.clientVersionHeader = StringUtils.isEmpty(clientVersionHeader) ? "X-Client-Version" : clientVersionHeader;
 		RestMethod m = javaMethod.getAnnotation(RestMethod.class);
 		range = new VersionRange(m.clientVersion());
 	}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/Redirect.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/Redirect.java b/juneau-rest/src/main/java/org/apache/juneau/rest/Redirect.java
index 348346a..f08d679 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/Redirect.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/Redirect.java
@@ -15,7 +15,6 @@ package org.apache.juneau.rest;
 import java.net.*;
 import java.text.*;
 
-import org.apache.juneau.*;
 import org.apache.juneau.urlencoding.*;
 
 /**
@@ -62,7 +61,7 @@ import org.apache.juneau.urlencoding.*;
  * </p>
  * <p>
  * This class is handled by {@link org.apache.juneau.rest.response.RedirectHandler}, a built-in default
- * 	response handler created by {@link RestServlet#createResponseHandlers(ObjectMap)}.
+ * 	response handler created in {@link RestConfig}.
  */
 public final class Redirect {
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/ResponseHandler.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/ResponseHandler.java b/juneau-rest/src/main/java/org/apache/juneau/rest/ResponseHandler.java
index 6607e64..da7c3e9 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/ResponseHandler.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/ResponseHandler.java
@@ -30,7 +30,7 @@ import org.apache.juneau.rest.response.*;
  * 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 overriding {@link RestServlet#createResponseHandlers(ObjectMap)} and augmenting or creating your
+ * 	<li>By calling the {@link RestConfig#addResponseHandlers(Class...)} and augmenting or creating your
  * 		own list of handlers.
  * </ul>
  * <p>
@@ -40,6 +40,8 @@ import org.apache.juneau.rest.response.*;
  * 	<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

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/RestCallHandler.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestCallHandler.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestCallHandler.java
new file mode 100644
index 0000000..5d6a780
--- /dev/null
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestCallHandler.java
@@ -0,0 +1,348 @@
+// ***************************************************************************************************************************
+// * 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 java.util.logging.Level.*;
+import static javax.servlet.http.HttpServletResponse.*;
+
+import java.io.*;
+import java.util.*;
+
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+import org.apache.juneau.internal.*;
+import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.rest.vars.*;
+
+/**
+ * Class that handles the basic lifecycle of an HTTP REST call.
+ * <p>
+ * Subclasses can override these methods to tailor how HTTP REST calls are handled.
+ * Subclasses MUST implement a public constructor that takes in a {@link RestContext} object.
+ * <p>
+ * RestCallHandlers are associated with servlets/resources in one of the following ways:
+ * <ul>
+ * 	<li>The {@link RestResource#callHandler @RestResource.callHandler()} annotation.
+ * 	<li>The {@link RestConfig#setCallHandler(Class)} method.
+ * </ul>
+ */
+public class RestCallHandler {
+
+	private final RestContext context;
+	private final RestLogger logger;
+	private final RestServlet restServlet;
+	private final Map<String,CallRouter> callRouters;
+
+	/**
+	 * Constructor.
+	 * @param context The resource context.
+	 */
+	public RestCallHandler(RestContext context) {
+		this.context = context;
+		this.logger = context.getLogger();
+		this.callRouters = context.getCallRouters();
+		this.restServlet = context.getRestServlet();  // Null if this isn't a RestServlet!
+	}
+
+	/**
+	 * Creates a {@link RestRequest} object based on the specified incoming {@link HttpServletRequest} object.
+	 * <p>
+	 * Subclasses may choose to override this method to provide a specialized request object.
+	 *
+	 * @param req The request object from the {@link #service(HttpServletRequest, HttpServletResponse)} method.
+	 * @return The wrapped request object.
+	 * @throws ServletException If any errors occur trying to interpret the request.
+	 */
+	protected RestRequest createRequest(HttpServletRequest req) throws ServletException {
+		return new RestRequest(context, req);
+	}
+
+	/**
+	 * Creates a {@link RestResponse} object based on the specified incoming {@link HttpServletResponse} object
+	 * 	and the request returned by {@link #createRequest(HttpServletRequest)}.
+	 * <p>
+	 * Subclasses may choose to override this method to provide a specialized response object.
+	 *
+	 * @param req The request object returned by {@link #createRequest(HttpServletRequest)}.
+	 * @param res The response object from the {@link #service(HttpServletRequest, HttpServletResponse)} method.
+	 * @return The wrapped response object.
+	 * @throws ServletException If any errors occur trying to interpret the request or response.
+	 */
+	protected RestResponse createResponse(RestRequest req, HttpServletResponse res) throws ServletException {
+		return new RestResponse(context, req, res);
+	}
+
+	/**
+	 * The main service method.
+	 * <p>
+	 * Subclasses can optionally override this method if they want to tailor the behavior of requests.
+	 *
+	 * @param r1 The incoming HTTP servlet request object.
+	 * @param r2 The incoming HTTP servlet response object.
+	 * @throws ServletException
+	 * @throws IOException
+	 */
+	protected void service(HttpServletRequest r1, HttpServletResponse r2) throws ServletException, IOException {
+
+		logger.log(FINE, "HTTP: {0} {1}", r1.getMethod(), r1.getRequestURI());
+		long startTime = System.currentTimeMillis();
+
+		try {
+			context.checkForInitException();
+
+			String pathInfo = RestUtils.getPathInfoUndecoded(r1);  // Can't use r1.getPathInfo() because we don't want '%2F' resolved.
+
+			// If this resource has child resources, try to recursively call them.
+			if (pathInfo != null && context.hasChildResources() && (! pathInfo.equals("/"))) {
+				int i = pathInfo.indexOf('/', 1);
+				String pathInfoPart = i == -1 ? pathInfo.substring(1) : pathInfo.substring(1, i);
+				RestContext childResource = context.getChildResource(pathInfoPart);
+				if (childResource != null) {
+					final String pathInfoRemainder = (i == -1 ? null : pathInfo.substring(i));
+					final String servletPath = r1.getServletPath() + "/" + pathInfoPart;
+					final HttpServletRequest childRequest = new HttpServletRequestWrapper(r1) {
+						@Override /* ServletRequest */
+						public String getPathInfo() {
+							return RestUtils.decode(pathInfoRemainder);
+						}
+						@Override /* ServletRequest */
+						public String getServletPath() {
+							return servletPath;
+						}
+					};
+					childResource.getCallHandler().service(childRequest, r2);
+					return;
+				}
+			}
+
+			RestRequest req = createRequest(r1);
+			RestResponse res = createResponse(req, r2);
+			String method = req.getMethod();
+			String methodUC = method.toUpperCase(Locale.ENGLISH);
+
+			StreamResource r = null;
+			if (pathInfo != null) {
+				String p = pathInfo.substring(1);
+				if (p.equals("favicon.ico"))
+					r = context.getFavIcon();
+				else if (p.equals("style.css"))
+					r = context.getStyleSheet();
+				else if (context.isStaticFile(p))
+					r = context.resolveStaticFile(p);
+			}
+
+			if (r != null) {
+				res.setStatus(SC_OK);
+				res.setOutput(r);
+			} else {
+				// If the specified method has been defined in a subclass, invoke it.
+				int rc = SC_METHOD_NOT_ALLOWED;
+				if (callRouters.containsKey(methodUC)) {
+					rc = callRouters.get(methodUC).invoke(pathInfo, req, res);
+				} else if (callRouters.containsKey("*")) {
+					rc = callRouters.get("*").invoke(pathInfo, req, res);
+				}
+
+				// If not invoked above, see if it's an OPTIONs request
+				if (rc != SC_OK)
+					handleNotFound(rc, req, res);
+			}
+
+			if (res.hasOutput()) {
+				Object output = res.getOutput();
+
+				// Do any class-level transforming.
+				for (RestConverter converter : context.getConverters())
+					output = converter.convert(req, output, context.getBeanContext().getClassMetaForObject(output));
+
+				res.setOutput(output);
+
+				// Now serialize the output if there was any.
+				// Some subclasses may write to the OutputStream or Writer directly.
+				handleResponse(req, res, output);
+			}
+
+			onSuccess(req, res, System.currentTimeMillis() - startTime);
+
+		} catch (RestException e) {
+			handleError(r1, r2, e);
+		} catch (Throwable e) {
+			handleError(r1, r2, new RestException(SC_INTERNAL_SERVER_ERROR, e));
+		}
+		logger.log(FINE, "HTTP: [{0} {1}] finished in {2}ms", r1.getMethod(), r1.getRequestURI(), System.currentTimeMillis()-startTime);
+	}
+
+	/**
+	 * The main method for serializing POJOs passed in through the {@link RestResponse#setOutput(Object)} method or returned by
+	 * the Java method.
+	 * <p>
+	 * Subclasses may override this method if they wish to modify the way the output is rendered or support
+	 * 	other output formats.
+	 * <p>
+	 * The default implementation simply iterates through the response handlers on this resource
+	 * looking for the first one whose {@link ResponseHandler#handle(RestRequest, RestResponse, Object)} method returns <jk>true</jk>.
+	 *
+	 * @param req The HTTP request.
+	 * @param res The HTTP response.
+	 * @param output The output to serialize in the response.
+	 * @throws IOException
+	 * @throws RestException
+	 */
+	protected void handleResponse(RestRequest req, RestResponse res, Object output) throws IOException, RestException {
+		// Loop until we find the correct handler for the POJO.
+		for (ResponseHandler h : context.getResponseHandlers())
+			if (h.handle(req, res, output))
+				return;
+		throw new RestException(SC_NOT_IMPLEMENTED, "No response handlers found to process output of type '"+(output == null ? null : output.getClass().getName())+"'");
+	}
+
+	/**
+	 * Handle the case where a matching method was not found.
+	 * <p>
+	 * Subclasses can override this method to provide a 2nd-chance for specifying a response.
+	 * The default implementation will simply throw an exception with an appropriate message.
+	 *
+	 * @param rc The HTTP response code.
+	 * @param req The HTTP request.
+	 * @param res The HTTP response.
+	 * @throws Exception
+	 */
+	protected void handleNotFound(int rc, RestRequest req, RestResponse res) throws Exception {
+		String pathInfo = req.getPathInfo();
+		String methodUC = req.getMethod();
+		String onPath = pathInfo == null ? " on no pathInfo"  : String.format(" on path '%s'", pathInfo);
+		if (rc == SC_NOT_FOUND)
+			throw new RestException(rc, "Method ''{0}'' not found on resource with matching pattern{1}.", methodUC, onPath);
+		else if (rc == SC_PRECONDITION_FAILED)
+			throw new RestException(rc, "Method ''{0}'' not found on resource{1} with matching matcher.", methodUC, onPath);
+		else if (rc == SC_METHOD_NOT_ALLOWED)
+			throw new RestException(rc, "Method ''{0}'' not found on resource.", methodUC);
+		else
+			throw new ServletException("Invalid method response: " + rc);
+	}
+
+	/**
+	 * Method for handling response errors.
+	 * <p>
+	 * The default implementation logs the error and calls {@link #renderError(HttpServletRequest,HttpServletResponse,RestException)}.
+	 * <p>
+	 * Subclasses can override this method to provide their own custom error response handling.
+	 *
+	 * @param req The servlet request.
+	 * @param res The servlet response.
+	 * @param e The exception that occurred.
+	 * @throws IOException Can be thrown if a problem occurred trying to write to the output stream.
+	 */
+	protected synchronized void handleError(HttpServletRequest req, HttpServletResponse res, RestException e) throws IOException {
+		e.setOccurrence(context == null ? 0 : context.getStackTraceOccurrence(e));
+		logger.onError(req, res, e);
+		renderError(req, res, e);
+	}
+
+	/**
+	 * Method for rendering response errors.
+	 * <p>
+	 * The default implementation renders a plain text English message, optionally with a stack trace
+	 * 	if {@link RestContext#REST_renderResponseStackTraces} is enabled.
+	 * <p>
+	 * Subclasses can override this method to provide their own custom error response handling.
+	 *
+	 * @param req The servlet request.
+	 * @param res The servlet response.
+	 * @param e The exception that occurred.
+	 * @throws IOException Can be thrown if a problem occurred trying to write to the output stream.
+	 */
+	protected void renderError(HttpServletRequest req, HttpServletResponse res, RestException e) throws IOException {
+
+		int status = e.getStatus();
+		res.setStatus(status);
+		res.setContentType("text/plain");
+		res.setHeader("Content-Encoding", "identity");
+		PrintWriter w = null;
+		try {
+			w = res.getWriter();
+		} catch (IllegalStateException e2) {
+			w = new PrintWriter(new OutputStreamWriter(res.getOutputStream(), IOUtils.UTF8));
+		}
+		String httpMessage = RestUtils.getHttpResponseText(status);
+		if (httpMessage != null)
+			w.append("HTTP ").append(String.valueOf(status)).append(": ").append(httpMessage).append("\n\n");
+		if (context != null && context.isRenderResponseStackTraces())
+			e.printStackTrace(w);
+		else
+			w.append(e.getFullStackMessage(true));
+		w.flush();
+		w.close();
+	}
+
+	/**
+	 * Callback method for listening for successful completion of requests.
+	 * <p>
+	 * Subclasses can override this method for gathering performance statistics.
+	 * <p>
+	 * The default implementation does nothing.
+	 *
+	 * @param req The HTTP request.
+	 * @param res The HTTP response.
+	 * @param time The time in milliseconds it took to process the request.
+	 */
+	protected void onSuccess(RestRequest req, RestResponse res, long time) {
+		if (restServlet != null)
+			restServlet.onSuccess(req, res, time);
+	}
+
+	/**
+	 * Callback method that gets invoked right before the REST Java method is invoked.
+	 * <p>
+	 * Subclasses can override this method to override request headers or set request-duration properties
+	 * 	before the Java method is invoked.
+	 *
+	 * @param req The HTTP servlet request object.
+	 * @throws RestException If any error occurs.
+	 */
+	protected void onPreCall(RestRequest req) throws RestException {
+		if (restServlet != null)
+			restServlet.onPreCall(req);
+	}
+
+	/**
+	 * Callback method that gets invoked right after the REST Java method is invoked, but before
+	 * 	the serializer is invoked.
+	 * <p>
+	 * Subclasses can override this method to override request and response headers, or
+	 * 	set/override properties used by the serializer.
+	 *
+	 * @param req The HTTP servlet request object.
+	 * @param res The HTTP servlet response object.
+	 * @throws RestException If any error occurs.
+	 */
+	protected void onPostCall(RestRequest req, RestResponse res) throws RestException {
+		if (restServlet != null)
+			restServlet.onPostCall(req, res);
+	}
+
+	/**
+	 * Returns the session objects for the specified request.
+	 * <p>
+	 * The default implementation simply returns a single map containing <code>{'req':req}</code>.
+	 *
+	 * @param req The REST request.
+	 * @return The session objects for that request.
+	 */
+	public Map<String,Object> getSessionObjects(RestRequest req) {
+		Map<String,Object> m = new HashMap<String,Object>();
+		m.put(RequestVar.SESSION_req, req);
+		return m;
+	}
+}


[3/7] incubator-juneau git commit: Allow @RestResource/@RestMethod annotations to be used on any classes.

Posted by ja...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/RestLogger.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestLogger.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestLogger.java
new file mode 100644
index 0000000..2b55b47
--- /dev/null
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestLogger.java
@@ -0,0 +1,233 @@
+// ***************************************************************************************************************************
+// * 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 javax.servlet.http.HttpServletResponse.*;
+
+import java.text.*;
+import java.util.logging.*;
+
+import javax.servlet.http.*;
+
+import org.apache.juneau.internal.*;
+import org.apache.juneau.json.*;
+import org.apache.juneau.rest.annotation.*;
+
+/**
+ * Logging utility class.
+ * <p>
+ * Subclasses can override these methods to tailor logging of HTTP requests.
+ * Subclasses MUST implement a no-arg public constructor.
+ * <p>
+ * RestLoggers are associated with servlets/resources in one of the following ways:
+ * <ul>
+ * 	<li>The {@link RestResource#logger @RestResource.logger()} annotation.
+ * 	<li>The {@link RestConfig#setLogger(Class)}/{@link RestConfig#setLogger(RestLogger)} methods.
+ * </ul>
+ */
+public abstract class RestLogger {
+
+	/**
+	 * Returns the Java logger used for logging.
+	 * <p>
+	 * Subclasses can provide their own logger.
+	 * The default implementation returns the logger created using <code>Logger.getLogger(getClass())</code>.
+	 *
+	 * @return The logger used for logging.
+	 */
+	protected abstract Logger getLogger();
+
+	/**
+	 * Log a message to the logger.
+	 * <p>
+	 * Subclasses can override this method if they wish to log messages using a library other than
+	 * 	Java Logging (e.g. Apache Commons Logging).
+	 *
+	 * @param level The log level.
+	 * @param cause The cause.
+	 * @param msg The message to log.
+	 * @param args Optional {@link MessageFormat}-style arguments.
+	 */
+	protected abstract void log(Level level, Throwable cause, String msg, Object...args);
+
+	/**
+	 * Log a message.
+	 * <p>
+	 * Equivalent to calling <code>log(level, <jk>null</jk>, msg, args);</code>
+	 *
+	 * @param level The log level.
+	 * @param msg The message to log.
+	 * @param args Optional {@link MessageFormat}-style arguments.
+	 */
+	protected void log(Level level, String msg, Object...args) {
+		log(level, null, msg, args);
+	}
+
+	/**
+	 * Same as {@link #log(Level, String, Object...)} excepts runs the arguments through {@link JsonSerializer#DEFAULT_LAX_READABLE}.
+	 * <p>
+	 * Serialization of arguments do not occur if message is not logged, so it's safe to use this method from within debug log statements.
+	 *
+	 * <h5 class='section'>Example:</h5>
+	 * <p class='bcode'>
+	 * 	logObjects(<jsf>DEBUG</jsf>, <js>"Pojo contents:\n{0}"</js>, myPojo);
+	 * </p>
+	 *
+	 * @param level The log level.
+	 * @param msg The message to log.
+	 * @param args Optional {@link MessageFormat}-style arguments.
+	 */
+	protected void logObjects(Level level, String msg, Object...args) {
+		for (int i = 0; i < args.length; i++)
+			args[i] = JsonSerializer.DEFAULT_LAX_READABLE.toStringObject(args[i]);
+		log(level, null, msg, args);
+	}
+
+	/**
+	 * Callback method for logging errors during HTTP requests.
+	 * <p>
+	 * Typically, subclasses will override this method and log errors themselves.
+	 * <p>
+	 * The default implementation simply logs errors to the <code>RestServlet</code> logger.
+	 * <p>
+	 * Here's a typical implementation showing how stack trace hashing can be used to reduce log file sizes...
+	 * </p>
+	 * <p class='bcode'>
+	 * 	<jk>protected void</jk> onError(HttpServletRequest req, HttpServletResponse res, RestException e, <jk>boolean</jk> noTrace) {
+	 * 		String qs = req.getQueryString();
+	 * 		String msg = <js>"HTTP "</js> + req.getMethod() + <js>" "</js> + e.getStatus() + <js>" "</js> + req.getRequestURI() + (qs == <jk>null</jk> ? <js>""</js> : <js>"?"</js> + qs);
+	 * 		<jk>int</jk> c = e.getOccurrence();
+	 *
+	 * 		<jc>// REST_useStackTraceHashes is disabled, so we have to log the exception every time.</jc>
+	 * 		<jk>if</jk> (c == 0)
+	 * 			myLogger.log(Level.<jsf>WARNING</jsf>, <jsm>format</jsm>(<js>"[%s] %s"</js>, e.getStatus(), msg), e);
+	 *
+	 * 		<jc>// This is the first time we've countered this error, so log a stack trace
+	 * 		// unless ?noTrace was passed in as a URL parameter.</jc>
+	 * 		<jk>else if</jk> (c == 1 &amp;&amp; ! noTrace)
+	 * 			myLogger.log(Level.<jsf>WARNING</jsf>, <jsm>format</jsm>(<js>"[%h.%s.%s] %s"</js>, e.hashCode(), e.getStatus(), c, msg), e);
+	 *
+	 * 		<jc>// This error occurred before.
+	 * 		// Only log the message, not the stack trace.</jc>
+	 * 		<jk>else</jk>
+	 * 			myLogger.log(Level.<jsf>WARNING</jsf>, <jsm>format</jsm>(<js>"[%h.%s.%s] %s, %s"</js>, e.hashCode(), e.getStatus(), c, msg, e.getLocalizedMessage()));
+	 * 	}
+	 * </p>
+	 *
+	 * @param req The servlet request object.
+	 * @param res The servlet response object.
+	 * @param e Exception indicating what error occurred.
+	 */
+	protected void onError(HttpServletRequest req, HttpServletResponse res, RestException e) {
+		if (shouldLog(req, res, e)) {
+			String qs = req.getQueryString();
+			String msg = "HTTP " + req.getMethod() + " " + e.getStatus() + " " + req.getRequestURI() + (qs == null ? "" : "?" + qs);
+			int c = e.getOccurrence();
+			if (shouldLogStackTrace(req, res, e)) {
+				msg = '[' + Integer.toHexString(e.hashCode()) + '.' + e.getStatus() + '.' + c + "] " + msg;
+				log(Level.WARNING, e, msg);
+			} else {
+				msg = '[' + Integer.toHexString(e.hashCode()) + '.' + e.getStatus() + '.' + c + "] " + msg + ", " + e.getLocalizedMessage();
+				log(Level.WARNING, msg);
+			}
+		}
+	}
+
+	/**
+	 * Returns <jk>true</jk> if the specified exception should be logged.
+	 * <p>
+	 * Subclasses can override this method to provide their own logic for determining when exceptions are logged.
+	 * <p>
+	 * The default implementation will return <jk>false</jk> if <js>"noTrace=true"</js> is passed in the query string.
+	 *
+	 * @param req The HTTP request.
+	 * @param res The HTTP response.
+	 * @param e The exception.
+	 * @return <jk>true</jk> if exception should be logged.
+	 */
+	protected boolean shouldLog(HttpServletRequest req, HttpServletResponse res, RestException e) {
+		String q = req.getQueryString();
+		return (q == null ? true : q.indexOf("noTrace=true") == -1);
+	}
+
+	/**
+	 * Returns <jk>true</jk> if a stack trace should be logged for this exception.
+	 * <p>
+	 * Subclasses can override this method to provide their own logic for determining when stack traces are logged.
+	 * <p>
+	 * The default implementation will only log a stack trace if {@link RestException#getOccurrence()} returns <code>1</code>
+	 * 	and the exception is not one of the following:
+	 * </p>
+	 * <ul>
+	 * 	<li>{@link HttpServletResponse#SC_UNAUTHORIZED}
+	 * 	<li>{@link HttpServletResponse#SC_FORBIDDEN}
+	 * 	<li>{@link HttpServletResponse#SC_NOT_FOUND}
+	 * </ul>
+	 *
+	 * @param req The HTTP request.
+	 * @param res The HTTP response.
+	 * @param e The exception.
+	 * @return <jk>true</jk> if stack trace should be logged.
+	 */
+	protected boolean shouldLogStackTrace(HttpServletRequest req, HttpServletResponse res, RestException e) {
+		if (e.getOccurrence() == 1) {
+			switch (e.getStatus()) {
+				case SC_UNAUTHORIZED:
+				case SC_FORBIDDEN:
+				case SC_NOT_FOUND:  return false;
+				default:            return true;
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * NO-OP logger.
+	 * <p>
+	 * Disables all logging.
+	 *
+	 * @author James Bognar (james.bognar@salesforce.com)
+	 */
+	public static class NoOp extends RestLogger {
+
+		@Override /* RestLogger */
+		protected Logger getLogger() {
+			return null;
+		}
+
+		@Override /* RestLogger */
+		protected void log(Level level, Throwable cause, String msg, Object...args) {}
+	}
+
+	/**
+	 * Default logger.
+	 * <p>
+	 * Logs all messages to the logger returned by <code>Logger.<jsm>getLogger</jsm>(getClass().getName())</code>
+	 */
+	public static class Normal extends RestLogger {
+
+		private final JuneauLogger logger = JuneauLogger.getLogger(getClass());
+
+		@Override /* RestLogger */
+		protected Logger getLogger() {
+			return logger;
+		}
+
+		@Override /* RestLogger */
+		protected void log(Level level, Throwable cause, String msg, Object...args) {
+			if (args.length > 0)
+				msg = MessageFormat.format(msg, args);
+			getLogger().log(level, msg, cause);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/RestMatcherReflecting.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestMatcherReflecting.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestMatcherReflecting.java
index 4a2619d..2a1cc16 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/RestMatcherReflecting.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestMatcherReflecting.java
@@ -15,7 +15,7 @@ package org.apache.juneau.rest;
 import java.lang.reflect.*;
 
 /**
- * Subclass of {@link RestMatcher} that gives access to the servlet and Java method it's applied to.
+ * Subclass of {@link RestMatcher} that gives access to the servlet/resource 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
@@ -26,8 +26,8 @@ public abstract class RestMatcherReflecting extends RestMatcher {
 	/**
 	 * Constructor.
 	 *
-	 * @param servlet The REST servlet.
+	 * @param resource The REST servlet.
 	 * @param javaMethod The Java method that this rest matcher is defined on.
 	 */
-	protected RestMatcherReflecting(RestServlet servlet, Method javaMethod) {}
+	protected RestMatcherReflecting(Object resource, Method javaMethod) {}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/RestRequest.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestRequest.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestRequest.java
index cc60feb..98aba93 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/RestRequest.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestRequest.java
@@ -19,6 +19,7 @@ import static javax.servlet.http.HttpServletResponse.*;
 import java.io.*;
 import java.lang.reflect.*;
 import java.net.*;
+import java.nio.charset.*;
 import java.text.*;
 import java.util.*;
 import java.util.logging.*;
@@ -63,13 +64,15 @@ import org.apache.juneau.utils.*;
 @SuppressWarnings("unchecked")
 public final class RestRequest extends HttpServletRequestWrapper {
 
-	private final RestServlet servlet;
+	private final RestContext context;
+
 	private final String method;
 	private String pathRemainder, body;
 	private Method javaMethod;
 	private ObjectMap properties;
 	private SerializerGroup serializerGroup;
 	private ParserGroup parserGroup;
+	private EncoderGroup encoders;
 	private Encoder encoder;
 	private int contentLength;
 	private final boolean debug;
@@ -89,17 +92,17 @@ public final class RestRequest extends HttpServletRequestWrapper {
 	/**
 	 * Constructor.
 	 */
-	RestRequest(RestServlet servlet, HttpServletRequest req) throws ServletException {
+	RestRequest(RestContext context, HttpServletRequest req) throws ServletException {
 		super(req);
+		this.context = context;
 
 		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());
+				queryParams = context.getUrlEncodingParser().parseIntoSimpleMap(getQueryString());
 			else {
 				queryParams = req.getParameterMap();
 			}
@@ -109,23 +112,23 @@ public final class RestRequest extends HttpServletRequestWrapper {
 			String _method = super.getMethod();
 
 			String m = getQueryParameter("method");
-			if (! StringUtils.isEmpty(m) && (servlet.context.allowMethodParams.contains(m) || servlet.context.allowMethodParams.contains("*")))
+			if (context.allowMethodParam(m))
 				_method = m;
 
 			method = _method;
 
-			if (servlet.context.allowBodyParam) {
+			if (context.isAllowBodyParam()) {
 				body = getQueryParameter("body");
 				if (body != null)
 					setHeader("Content-Type", UonSerializer.DEFAULT.getResponseContentType());
 			}
 
-			defaultServletHeaders = servlet.getDefaultRequestHeaders();
+			defaultServletHeaders = context.getDefaultRequestHeaders();
 
 			debug = "true".equals(getQueryParameter("debug", "false"));
 
 			if (debug) {
-				servlet.log(Level.INFO, toString());
+				context.getLogger().log(Level.INFO, toString());
 			}
 
 		} catch (RestException e) {
@@ -139,7 +142,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
 	 * Called from RestServlet after a match has been made but before the guard or method invocation.
 	 */
 	@SuppressWarnings("hiding")
-	final void init(Method javaMethod, String pathRemainder, ObjectMap properties, Map<String,String> mDefaultRequestHeaders, String defaultCharset, SerializerGroup mSerializers, ParserGroup mParsers, UrlEncodingParser mUrlEncodingParser) {
+	final void init(Method javaMethod, String pathRemainder, ObjectMap properties, Map<String,String> mDefaultRequestHeaders, String defaultCharset, SerializerGroup mSerializers, ParserGroup mParsers, UrlEncodingParser mUrlEncodingParser, EncoderGroup encoders) {
 		this.javaMethod = javaMethod;
 		this.pathRemainder = pathRemainder;
 		this.properties = properties;
@@ -149,6 +152,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
 		this.urlEncodingParser = mUrlEncodingParser;
 		this.beanSession = urlEncodingParser.getBeanContext().createSession();
 		this.defaultCharset = defaultCharset;
+		this.encoders = encoders;
 	}
 
 	/**
@@ -403,7 +407,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
 			}
 			if (charset == null)
 				charset = defaultCharset;
-			if (! RestServlet.availableCharsets.containsKey(charset))
+			if (! Charset.isSupported(charset))
 				throw new RestException(SC_UNSUPPORTED_MEDIA_TYPE, "Unsupported charset in header ''Content-Type'': ''{0}''", h);
 		}
 		return charset;
@@ -1299,11 +1303,11 @@ public final class RestRequest extends HttpServletRequestWrapper {
 					properties.append("mediaType", mediaType).append("characterEncoding", getCharacterEncoding());
 					if (! p.isReaderParser()) {
 						InputStreamParser p2 = (InputStreamParser)p;
-						ParserSession session = p2.createSession(getInputStream(), properties, getJavaMethod(), getServlet(), locale, timeZone, mediaType);
+						ParserSession session = p2.createSession(getInputStream(), properties, getJavaMethod(), context.getResource(), locale, timeZone, mediaType);
 						return p2.parseSession(session, cm);
 					}
 					ReaderParser p2 = (ReaderParser)p;
-					ParserSession session = p2.createSession(getUnbufferedReader(), properties, getJavaMethod(), getServlet(), locale, timeZone, mediaType);
+					ParserSession session = p2.createSession(getUnbufferedReader(), properties, getJavaMethod(), context.getResource(), locale, timeZone, mediaType);
 					return p2.parseSession(session, cm);
 				} catch (ParseException e) {
 					throw new RestException(SC_BAD_REQUEST,
@@ -1576,45 +1580,45 @@ public final class RestRequest extends HttpServletRequestWrapper {
 	/**
 	 * Returns the localized servlet title.
 	 * <p>
-	 * Equivalent to calling {@link RestServlet#getTitle(RestRequest)} with this object.
+	 * Equivalent to calling {@link RestInfoProvider#getTitle(RestRequest)} with this object.
 	 *
 	 * @return The localized servlet label.
 	 */
 	public String getServletTitle() {
-		return servlet.getTitle(this);
+		return context.getInfoProvider().getTitle(this);
 	}
 
 	/**
 	 * Returns the localized servlet description.
 	 * <p>
-	 * Equivalent to calling {@link RestServlet#getDescription(RestRequest)} with this object.
+	 * Equivalent to calling {@link RestInfoProvider#getDescription(RestRequest)} with this object.
 	 *
 	 * @return The localized servlet description.
 	 */
 	public String getServletDescription() {
-		return servlet.getDescription(this);
+		return context.getInfoProvider().getDescription(this);
 	}
 
 	/**
 	 * Returns the localized method summary.
 	 * <p>
-	 * Equivalent to calling {@link RestServlet#getMethodSummary(String, RestRequest)} with this object.
+	 * Equivalent to calling {@link RestInfoProvider#getMethodSummary(String, RestRequest)} with this object.
 	 *
 	 * @return The localized method description.
 	 */
 	public String getMethodSummary() {
-		return servlet.getMethodSummary(javaMethod.getName(), this);
+		return context.getInfoProvider().getMethodSummary(javaMethod.getName(), this);
 	}
 
 	/**
 	 * Returns the localized method description.
 	 * <p>
-	 * Equivalent to calling {@link RestServlet#getMethodDescription(String, RestRequest)} with this object.
+	 * Equivalent to calling {@link RestInfoProvider#getMethodDescription(String, RestRequest)} with this object.
 	 *
 	 * @return The localized method description.
 	 */
 	public String getMethodDescription() {
-		return servlet.getMethodDescription(javaMethod.getName(), this);
+		return context.getInfoProvider().getMethodDescription(javaMethod.getName(), this);
 	}
 
 
@@ -1713,14 +1717,14 @@ public final class RestRequest extends HttpServletRequestWrapper {
 	}
 
 	/**
-	 * Shortcut method for calling {@link RestServlet#getMessage(Locale, String, Object...)} based on the request locale.
+	 * Shortcut method for calling {@link MessageBundle#getString(Locale, String, Object...)} based on the request locale.
 	 *
 	 * @param key The message key.
 	 * @param args Optional {@link MessageFormat}-style arguments.
 	 * @return The localized message.
 	 */
 	public String getMessage(String key, Object...args) {
-		return servlet.getMessage(getLocale(), key, args);
+		return context.getMessages().getString(getLocale(), key, args);
 	}
 
 	/**
@@ -1729,7 +1733,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
 	 * @return The resource bundle.  Never <jk>null</jk>.
 	 */
 	public MessageBundle getResourceBundle() {
-		return servlet.getMessages(getLocale());
+		return context.getMessages().getBundle(getLocale());
 	}
 
 	/**
@@ -1740,8 +1744,8 @@ public final class RestRequest extends HttpServletRequestWrapper {
 	 *
 	 * @return The servlet handling the request.
 	 */
-	public RestServlet getServlet() {
-		return servlet;
+	public RestContext getContext() {
+		return context;
 	}
 
 	/**
@@ -1772,13 +1776,13 @@ public final class RestRequest extends HttpServletRequestWrapper {
 	}
 
 	/**
-	 * Returns the variable resolver session for this request using session objects created by {@link RestServlet#getSessionObjects(RestRequest)}.
+	 * Returns the variable resolver session for this request using session objects created by {@link RestCallHandler#getSessionObjects(RestRequest)}.
 	 *
 	 * @return The variable resolver for this request.
 	 */
 	public VarResolverSession getVarResolverSession() {
 		if (varSession == null)
-			varSession = servlet.getVarResolver().createSession(servlet.getSessionObjects(this));
+			varSession = context.getVarResolver().createSession(context.getCallHandler().getSessionObjects(this));
 		return varSession;
 	}
 
@@ -1803,7 +1807,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
 	 * @throws IOException
 	 */
 	public ReaderResource getReaderResource(String name, boolean resolveVars, MediaType mediaType) throws IOException {
-		String s = servlet.getResourceAsString(name, getLocale());
+		String s = context.getResourceAsString(name, getLocale());
 		if (s == null)
 			return null;
 		ReaderResource.Builder b = new ReaderResource.Builder().mediaType(mediaType).contents(s);
@@ -1813,8 +1817,8 @@ public final class RestRequest extends HttpServletRequestWrapper {
 	}
 
 	/**
-	 * Same as {@link #getReaderResource(String, boolean, MediaType)} except uses {@link RestServlet#getMimetypesFileTypeMap()}
-	 * to determine the media type.
+	 * Same as {@link #getReaderResource(String, boolean, MediaType)} except uses the resource mime-type map
+	 * constructed using {@link RestConfig#addMimeTypes(String...)} 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 org.apache.juneau.rest.annotation.Parameter} variables will be resolved by the variable resolver returned
@@ -1823,7 +1827,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
 	 * @throws IOException
 	 */
 	public ReaderResource getReaderResource(String name, boolean resolveVars) throws IOException {
-		return getReaderResource(name, resolveVars, MediaType.forString(servlet.getMimetypesFileTypeMap().getContentType(name)));
+		return getReaderResource(name, resolveVars, MediaType.forString(context.getMediaTypeForName(name)));
 	}
 
 	/**
@@ -1834,7 +1838,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
 	 * @throws IOException
 	 */
 	public ReaderResource getReaderResource(String name) throws IOException {
-		return getReaderResource(name, false, MediaType.forString(servlet.getMimetypesFileTypeMap().getContentType(name)));
+		return getReaderResource(name, false, MediaType.forString(context.getMediaTypeForName(name)));
 	}
 
 	/**
@@ -1842,9 +1846,9 @@ public final class RestRequest extends HttpServletRequestWrapper {
 	 *
 	 * @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() {
+	public ConfigFile getConfigFile() {
 		if (cf == null)
-			cf = servlet.getConfig().getResolving(getVarResolverSession());
+			cf = context.getConfigFile().getResolving(getVarResolverSession());
 		return cf;
 	}
 
@@ -1855,7 +1859,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
 	 */
 	public Swagger getSwagger() {
 		if (swagger == null)
-			swagger = servlet.getSwagger(this);
+			swagger = context.getInfoProvider().getSwagger(this);
 		return swagger;
 	}
 
@@ -1871,7 +1875,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
 	 */
 	protected Swagger getSwaggerFromFile() {
 		if (fileSwagger == null)
-			fileSwagger = servlet.getSwaggerFromFile(this.getLocale());
+			fileSwagger = context.getInfoProvider().getSwaggerFromFile(this.getLocale());
 		if (fileSwagger == null)
 			fileSwagger = Swagger.NULL;
 		return fileSwagger == Swagger.NULL ? null : fileSwagger;
@@ -1895,7 +1899,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
 				sb.append(getBodyAsString()).append("\n");
 			} catch (Exception e1) {
 				sb.append(e1.getLocalizedMessage());
-				servlet.log(WARNING, e1, "Error occurred while trying to read debug input.");
+				context.getLogger().log(WARNING, e1, "Error occurred while trying to read debug input.");
 			}
 		}
 		return sb.toString();
@@ -1942,11 +1946,11 @@ public final class RestRequest extends HttpServletRequestWrapper {
 			String ce = getHeader("content-encoding");
 			if (! (ce == null || ce.isEmpty())) {
 				ce = ce.trim();
-				encoder = servlet.getEncoders().getEncoder(ce);
+				encoder = encoders.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()
+						getHeader("content-encoding"), encoders.getSupportedEncodings()
 					);
 			}
 
@@ -1966,7 +1970,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
 	 */
 	private String getOverriddenHeader(String name) {
 		String h = null;
-		if (servlet.context.allowHeaderParams)
+		if (context.isAllowHeaderParams())
 			h = getQueryParameter(name);
 		if (h != null)
 			return h;

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/RestResourceResolver.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestResourceResolver.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestResourceResolver.java
new file mode 100644
index 0000000..7989eb9
--- /dev/null
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestResourceResolver.java
@@ -0,0 +1,80 @@
+// ***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                                                              *
+// *                                                                                                                         *
+// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
+// *                                                                                                                         *
+// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the License.                                              *
+// ***************************************************************************************************************************
+package org.apache.juneau.rest;
+
+import java.lang.reflect.*;
+
+import org.apache.juneau.internal.*;
+import org.apache.juneau.rest.annotation.*;
+
+/**
+ * Class used to resolve {@link Class} objects to instances.
+ * <p>
+ * Used to convert classes defined via {@link RestResource#children() @RestResource.children()} into child instances.
+ * <p>
+ * Subclasses can be created to provide customized resource resolution.
+ * These can be associated with REST resources in one of the following ways:
+ * <ul>
+ * 	<li>{@link RestResource#resourceResolver() @RestResource.resourceResolver()} annotation.
+ * 	<li>{@link RestConfig#setResourceResolver(Class)}/{@link RestConfig#setResourceResolver(RestResourceResolver)} methods.
+ * </ul>
+ * <p>
+ * The default implementation simply instantiates the class using one of the following constructors:
+ * <ul>
+ * 	<li><code><jk>public</jk> T(RestConfig)</code>
+ * 	<li><code><jk>public</jk> T()</code>
+ * </ul>
+ * The former constructor can be used to get access to the {@link RestConfig} object to get access to the
+ * config file and initialization information or make programmatic modifications to the resource before
+ * full initialization.
+ * <p>
+ * Non-<code>RestServlet</code> classes can also add the following two methods to get access to the
+ * {@link RestConfig} and {@link RestContext} objects:
+ * <ul>
+ * 	<li><jk>public void</jk> init(RestConfig);</code>
+ * 	<li><jk>public void</jk> init(RestContext);</code>
+ * </ul>
+ */
+public class RestResourceResolver {
+
+	/**
+	 * Denotes the default resolver.
+	 */
+	public static final class DEFAULT extends RestResourceResolver {}
+
+	/**
+	 * Resolves the specified class to a resource object.
+	 * <p>
+	 * Subclasses can override this method to provide their own custom resolution.
+	 * <p>
+	 * The default implementation simply creates a new class instance using {@link Class#newInstance()}.
+	 *
+	 * @param c The class to resolve.
+	 * @param config The initialization configuration for the resource.
+	 * @return The instance of that class.
+	 * @throws RestServletException If class could not be resolved.
+	 */
+	public Object resolve(Class<?> c, RestConfig config) throws RestServletException {
+		try {
+			Constructor<?> c1 = ClassUtils.findPublicConstructor(c, RestConfig.class);
+			if (c1 != null)
+				return c1.newInstance(config);
+			c1 = ClassUtils.findPublicConstructor(c);
+			if (c1 != null)
+				return c1.newInstance();
+		} catch (Exception e) {
+			throw new RestServletException("Could not instantiate resource class ''{0}''", c.getName()).initCause(e);
+		}
+		throw new RestServletException("Could not find public constructor for class ''{0}''.", c);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/RestResponse.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestResponse.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestResponse.java
index 3fa5398..637dc7e 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/RestResponse.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestResponse.java
@@ -13,6 +13,7 @@
 package org.apache.juneau.rest;
 
 import java.io.*;
+import java.nio.charset.*;
 import java.util.*;
 
 import javax.servlet.*;
@@ -55,24 +56,22 @@ public final class RestResponse extends HttpServletResponseWrapper {
 	SerializerGroup serializerGroup;
 	UrlEncodingSerializer urlEncodingSerializer;         // The serializer used to convert arguments passed into Redirect objects.
 	private EncoderGroup encoders;
-	private final RestServlet servlet;
 	private ServletOutputStream os;
 
 	/**
 	 * Constructor.
 	 */
-	RestResponse(RestServlet servlet, RestRequest req, HttpServletResponse res) {
+	RestResponse(RestContext context, RestRequest req, HttpServletResponse res) {
 		super(res);
 		this.request = req;
-		this.servlet = servlet;
 
-		for (Map.Entry<String,Object> e : servlet.getDefaultResponseHeaders().entrySet())
+		for (Map.Entry<String,Object> e : context.getDefaultResponseHeaders().entrySet())
 			setHeader(e.getKey(), e.getValue().toString());
 
 		try {
 			String passThroughHeaders = req.getHeader("x-response-headers");
 			if (passThroughHeaders != null) {
-				ObjectMap m = servlet.getUrlEncodingParser().parseParameter(passThroughHeaders, ObjectMap.class);
+				ObjectMap m = context.getUrlEncodingParser().parseParameter(passThroughHeaders, ObjectMap.class);
 				for (Map.Entry<String,Object> e : m.entrySet())
 					setHeader(e.getKey(), e.getValue().toString());
 			}
@@ -101,7 +100,7 @@ public final class RestResponse extends HttpServletResponseWrapper {
 				MediaType mt = r.getMediaType();
 				if (mt.getType().equals("*"))
 					charset = defaultCharset;
-				else if (RestServlet.availableCharsets.containsKey(mt.getType()))
+				else if (Charset.isSupported(mt.getType()))
 					charset = mt.getType();
 				if (charset != null)
 					break;
@@ -138,7 +137,7 @@ public final class RestResponse extends HttpServletResponseWrapper {
 	 * @throws RestServletException
 	 */
 	public List<String> getSupportedEncodings() throws RestServletException {
-		return servlet.getEncoders().getSupportedEncodings();
+		return encoders.getSupportedEncodings();
 	}
 
 	/**


[2/7] incubator-juneau git commit: Allow @RestResource/@RestMethod annotations to be used on any classes.

Posted by ja...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/RestServlet.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestServlet.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestServlet.java
index f876360..d7288d3 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/RestServlet.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestServlet.java
@@ -12,48 +12,17 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.rest;
 
-import static java.lang.String.*;
 import static java.util.logging.Level.*;
 import static javax.servlet.http.HttpServletResponse.*;
-import static org.apache.juneau.dto.swagger.SwaggerBuilder.*;
-import static org.apache.juneau.internal.ArrayUtils.*;
-import static org.apache.juneau.internal.ClassUtils.*;
-import static org.apache.juneau.rest.RestServlet.ParamType.*;
-import static org.apache.juneau.rest.RestServletContext.*;
-import static org.apache.juneau.rest.annotation.Inherit.*;
-import static org.apache.juneau.serializer.SerializerContext.*;
 
 import java.io.*;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.*;
-import java.lang.reflect.Method;
-import java.nio.charset.*;
 import java.text.*;
-import java.util.*;
-import java.util.concurrent.*;
 import java.util.logging.*;
 
-import javax.activation.*;
 import javax.servlet.*;
 import javax.servlet.http.*;
 
 import org.apache.juneau.*;
-import org.apache.juneau.dto.swagger.*;
-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.json.*;
-import org.apache.juneau.parser.*;
-import org.apache.juneau.parser.ParseException;
-import org.apache.juneau.rest.annotation.*;
-import org.apache.juneau.rest.annotation.Properties;
-import org.apache.juneau.rest.response.*;
-import org.apache.juneau.rest.vars.*;
-import org.apache.juneau.serializer.*;
-import org.apache.juneau.svl.*;
-import org.apache.juneau.svl.vars.*;
-import org.apache.juneau.urlencoding.*;
 import org.apache.juneau.utils.*;
 
 /**
@@ -61,3265 +30,253 @@ import org.apache.juneau.utils.*;
  * <p>
  * Refer to <a class="doclink" href="package-summary.html#TOC">REST Servlet API</a> for information about using this class.
  */
-@SuppressWarnings({"rawtypes","hiding"})
+@SuppressWarnings("hiding")
 public abstract class RestServlet extends HttpServlet {
 
 	private static final long serialVersionUID = 1L;
 
-	static final SortedMap<String,Charset> availableCharsets = new TreeMap<String,Charset>(String.CASE_INSENSITIVE_ORDER);
-	static {
-		availableCharsets.putAll(Charset.availableCharsets());
-	}
-	// Map of HTTP method names (e.g. GET/PUT/...) to ResourceMethod implementations for it.  Populated during resource initialization.
-	private final Map<String,ResourceMethod> restMethods = new LinkedHashMap<String,ResourceMethod>();
-
-	// The list of all @RestMethod annotated methods in the order they appear in the class.
-	private final Map<String,MethodMeta> javaRestMethods = new LinkedHashMap<String,MethodMeta>();
-
-	// Child resources of this resource defined through getX() methods on this class.
-	private final Map<String,RestServlet> childResources = new LinkedHashMap<String,RestServlet>();
-
-	private RestServlet parentResource;
-
-	private ServletConfig servletConfig;
-	private volatile boolean isInitialized = false;
-	private Exception initException;                       // Exception thrown by init() method (cached so it can be thrown on all subsequent requests).
-	private JuneauLogger logger;
-	private MessageBundle msgs;                           // NLS messages.
-
-	private Map<Integer,Integer> stackTraceHashes = new HashMap<Integer,Integer>();
-	private String path;
-
-	private LinkedHashMap<Class<?>,RestResource> restResourceAnnotationsChildFirst, restResourceAnnotationsParentFirst;
-
-	private UrlEncodingSerializer urlEncodingSerializer;
-	private UrlEncodingParser urlEncodingParser;
-	private ObjectMap properties;
-	private RestGuard[] guards;
-	private Class<?>[] beanFilters, pojoSwaps;
-	private RestConverter[] converters;
-	private TreeMap<String,String> defaultRequestHeaders;
-	private Map<String,Object> defaultResponseHeaders;
-	private EncoderGroup encoders;
-	private SerializerGroup serializers;
-	private ParserGroup parsers;
-	private MimetypesFileTypeMap mimetypesFileTypeMap;
-	private BeanContext beanContext;
-	private VarResolver varResolver;
-	private String title, description, termsOfService, contact, license, version, tags, externalDocs;
-	private Map<String,byte[]> resourceStreams = new ConcurrentHashMap<String,byte[]>();
-	private Map<String,String> resourceStrings = new ConcurrentHashMap<String,String>();
-	private ConfigFile configFile, resolvingConfigFile;
-	private String configPath;
-	private StreamResource styleSheet, favIcon;
-	private Map<String,String> staticFilesMap;
-	private String[] staticFilesPrefixes;
-	private ResponseHandler[] responseHandlers;
-	private String clientVersionHeader = "";
-	private ConcurrentHashMap<Locale,Swagger> swaggers = new ConcurrentHashMap<Locale,Swagger>();
-
-	RestServletContext context;
-
-	// In-memory cache of images and stylesheets in the org.apache.juneau.rest.htdocs package.
-	private Map<String,StreamResource> staticFilesCache = new ConcurrentHashMap<String,StreamResource>();
-
-	// The following code block is executed before the constructor is called to
-	// allow the config file to be accessed during object creation.
-	// e.g. private String myConfig = getConfig().getString("myConfig");
-	{
-		VarResolverBuilder vrb = createVarResolver();
-
-		// @RestResource annotations from bottom to top.
-		restResourceAnnotationsChildFirst = ReflectionUtils.findAnnotationsMap(RestResource.class, getClass());
-
-		// @RestResource annotations from top to bottom.
-		restResourceAnnotationsParentFirst = CollectionUtils.reverse(restResourceAnnotationsChildFirst);
-
-		for (RestResource r : restResourceAnnotationsParentFirst.values()) {
-			if (! r.config().isEmpty())
-				configPath = r.config();
-		}
-
-		try {
-			configFile = createConfigFile(vrb);
-			vrb.contextObject(ConfigFileVar.SESSION_config, configFile);
-		} catch (IOException e) {
-			this.initException = e;
-		}
+	private RestConfig config;
+	private RestContext context;
+	private boolean isInitialized = false;
+	private Exception initException;
 
-		varResolver = vrb.build();
-	}
 
 	@Override /* Servlet */
-	public synchronized void init(ServletConfig servletConfig) throws ServletException {
+	public final synchronized void init(ServletConfig servletConfig) throws ServletException {
 		try {
-			log(FINE, "Servlet {0} init called.", getClass().getName());
-			this.servletConfig = servletConfig;
-
-			if (isInitialized)
-				return;
-
-			super.init(servletConfig);
-
-			// Find resource resource bundle location.
-			for (Map.Entry<Class<?>,RestResource> e : restResourceAnnotationsChildFirst.entrySet()) {
-				Class<?> c = e.getKey();
-				RestResource r = e.getValue();
-				if (! r.messages().isEmpty()) {
-					if (msgs == null)
-						msgs = new MessageBundle(c, r.messages());
-					else
-						msgs.addSearchPath(c, r.messages());
-				}
-			}
-			for (RestResource r : restResourceAnnotationsParentFirst.values()) {
-				if (! r.title().isEmpty())
-					title = r.title();
-				if (! r.description().isEmpty())
-					description = r.description();
-				if (! r.clientVersionHeader().isEmpty())
-					clientVersionHeader = r.clientVersionHeader();
-				if (! r.termsOfService().isEmpty())
-					termsOfService = r.termsOfService();
-				if (! r.contact().isEmpty())
-					contact = r.contact();
-				if (! r.license().isEmpty())
-					license = r.license();
-				if (! r.version().isEmpty())
-					version = r.version();
-				if (! r.tags().isEmpty())
-					tags = r.tags();
-				if (! r.externalDocs().isEmpty())
-					externalDocs = r.externalDocs();
-			}
-
-			if (msgs == null)
-				msgs = new MessageBundle(this.getClass(), "");
-			if (clientVersionHeader.isEmpty())
-				clientVersionHeader = "X-Client-Version";
-
-			styleSheet = createStyleSheet();
-			favIcon = createFavIcon();
-			staticFilesMap = Collections.unmodifiableMap(createStaticFilesMap());
-			staticFilesPrefixes = staticFilesMap.keySet().toArray(new String[0]);
-
-			properties = createProperties();
-			beanFilters = createBeanFilters();
-			pojoSwaps = createPojoSwaps();
-			context = PropertyStore.create().setProperties(properties).getContext(RestServletContext.class);
-			beanContext = createBeanContext(properties, beanFilters, pojoSwaps);
-			urlEncodingSerializer = createUrlEncodingSerializer(properties, beanFilters, pojoSwaps).build();
-			urlEncodingParser = createUrlEncodingParser(properties, beanFilters, pojoSwaps).build();
-			serializers = createSerializers(properties, beanFilters, pojoSwaps).build();
-			parsers = createParsers(properties, beanFilters, pojoSwaps).build();
-			converters = createConverters(properties);
-			encoders = createEncoders(properties).build();
-			guards = createGuards(properties);
-			mimetypesFileTypeMap = createMimetypesFileTypeMap(properties);
-			defaultRequestHeaders = new TreeMap<String,String>(String.CASE_INSENSITIVE_ORDER);
-			defaultRequestHeaders.putAll(createDefaultRequestHeaders(properties));
-			defaultResponseHeaders = createDefaultResponseHeaders(properties);
-			responseHandlers = createResponseHandlers(properties);
-
-			// Discover the @RestMethod methods available on the resource.
-			List<String> methodsFound = new LinkedList<String>();   // Temporary to help debug transient duplicate method issue.
-			for (java.lang.reflect.Method method : this.getClass().getMethods()) {
-				if (method.isAnnotationPresent(RestMethod.class)) {
-					RestMethod a = method.getAnnotation(RestMethod.class);
-					methodsFound.add(method.getName() + "," + a.name() + "," + a.path());
-					try {
-						if (! Modifier.isPublic(method.getModifiers()))
-							throw new RestServletException("@RestMethod method {0}.{1} must be defined as public.", this.getClass().getName(), method.getName());
-
-						MethodMeta sm = new MethodMeta(method);
-						javaRestMethods.put(method.getName(), sm);
-						ResourceMethod rm = restMethods.get(sm.httpMethod);
-						if (rm == null)
-							restMethods.put(sm.httpMethod, sm);
-						else if (rm instanceof MultiMethod)
-							((MultiMethod)rm).addSimpleMethod(sm);
-						else
-							restMethods.put(sm.httpMethod, new MultiMethod((MethodMeta)rm, sm));
-					} catch (RestServletException e) {
-						throw new RestServletException("Problem occurred trying to serialize methods on class {0}, methods={1}", this.getClass().getName(), JsonSerializer.DEFAULT_LAX.serialize(methodsFound)).initCause(e);
-					}
-				}
+			RestConfig rsc = new RestConfig(servletConfig, this.getClass(), null);
+			init(rsc);
+			if (! isInitialized) {
+				// Subclass may not have called super.init(RestServletConfig), so initialize here.
+				createContext(rsc);
+				super.init(servletConfig);
 			}
-
-			for (ResourceMethod m : restMethods.values())
-				m.complete();
-
-			// Discover the child resources.
-			childResources.putAll(createChildrenMap());
-
-			for (RestServlet child : childResources.values())
-				child.init(servletConfig);
-
-			varResolver = varResolver
-				.builder()
-				.vars(LocalizationVar.class, RequestVar.class, SerializedRequestAttrVar.class, ServletInitParamVar.class, UrlEncodeVar.class)
-				.build()
-			;
-
 		} catch (RestException e) {
 			// Thrown RestExceptions are simply caught and rethrown on subsequent calls to service().
 			initException = e;
 			log(SEVERE, e, "Servlet init error on class ''{0}''", getClass().getName());
-			title = String.valueOf(initException.getLocalizedMessage());
 		} catch (ServletException e) {
 			initException = e;
 			log(SEVERE, e, "Servlet init error on class ''{0}''", getClass().getName());
-			title = String.valueOf(initException.getLocalizedMessage());
 			throw e;
 		} catch (Exception e) {
 			initException = e;
 			log(SEVERE, e, "Servlet init error on class ''{0}''", getClass().getName());
-			title = String.valueOf(initException.getLocalizedMessage());
 			throw new ServletException(e);
 		} catch (Throwable e) {
 			initException = new Exception(e);
 			log(SEVERE, e, "Servlet init error on class ''{0}''", getClass().getName());
-			title = String.valueOf(initException.getLocalizedMessage());
 			throw new ServletException(e);
 		} finally {
 			isInitialized = true;
 		}
 	}
 
-
-	//--------------------------------------------------------------------------------
-	// Initialization methods
-	//--------------------------------------------------------------------------------
-
-	/**
-	 * Creates the child resources of this resource.
-	 * <p>
-	 * Default implementation calls {@link #createChildren()} and uses the {@link RestResource#path() @RestResource.path()} annotation
-	 * 	on each child to identify the subpath for the resource which become the keys in this map.
-	 * It then calls the {@link #setParent(RestServlet)} method on the child resource.
-	 * <p>
-	 * Subclasses can override this method to programatically create child resources
-	 * 	without using the {@link RestResource#children() @RestResource.children()} annotation.
-	 * When overridding this method, you are responsible for calling {@link #setParent(RestServlet)} on the
-	 * 	child resources.
-	 *
-	 * @return The new mutable list of child resource instances.
-	 * @throws Exception If an error occurred during servlet instantiation.
-	 */
-	protected Map<String,RestServlet> createChildrenMap() throws Exception {
-		Map<String,RestServlet> m = new LinkedHashMap<String,RestServlet>();
-		for (RestServlet r : createChildren()) {
-			r.setParent(this);
-			String p = r.findPath();
-			if (p == null)
-				throw new RestServletException("Child resource ''{0}'' does not define a ''@RestResource.path'' attribute.", r.getClass().getName());
-			m.put(p, r);
-		}
-		return m;
-	}
-
-	/**
-	 * Creates instances of child resources for this servlet.
-	 * <p>
-	 * Default implementation uses the {@link RestResource#children() @RestResource.children()} annotation to identify and
-	 * 	instantiate children.
-	 * <p>
-	 * Subclasses can override this method to programatically create child resources
-	 * 	without using the {@link RestResource#children() @RestResource.children()} annotation.
-	 *
-	 * @return The new mutable list of child resource instances.
-	 * @throws Exception If an error occurred during servlet instantiation.
-	 */
-	protected List<RestServlet> createChildren() throws Exception {
-		List<RestServlet> l = new LinkedList<RestServlet>();
-		for (Class<?> c : getChildClasses()) {
-			if (isParentClass(RestServlet.class, c))
-				l.add((RestServlet)c.newInstance());
-			else
-				l.add(resolveChild(c));
-		}
-		return l;
-	}
-
-	/**
-	 * Programmatic equivalent to the {@link RestResource#children() @RestResource.children()} annotation.
-	 * <p>
-	 * Subclasses can override this method to provide customized list of child resources.
-	 * 	(e.g. different children based on values specified in the config file).
-	 * <p>
-	 * Default implementation simply returns the value from the {@link RestResource#children() @RestResource.children()} annotation.
-	 *
-	 * @return The new mutable list of child resource instances.
-	 * @throws Exception If an error occurred during servlet instantiation.
-	 */
-	protected Class<?>[] getChildClasses() throws Exception {
-		List<Class<?>> l = new ArrayList<Class<?>>();
-		List<RestResource> rr = ReflectionUtils.findAnnotations(RestResource.class, getClass());
-		for (RestResource r : rr)
-			l.addAll(Arrays.asList(r.children()));
-		return l.toArray(new Class<?>[l.size()]);
-	}
-
-	/**
-	 * Creates the class-level properties associated with this servlet.
-	 * <p>
-	 * Subclasses can override this method to provide their own class-level properties for this servlet, typically
-	 * 	by calling <code><jk>super</jk>.createProperties()</code> and appending to the map.
-	 * However, in most cases, the existing set of properties can be added to by overridding {@link #getProperties()}
-	 * 	and appending to the map returned by <code><jk>super</jk>.getProperties()</code>
-	 * <p>
-	 * By default, the map returned by this method contains the following:
-	 * </p>
-	 * <ul class='spaced-list'>
-	 * 	<li>Servlet-init parameters.
-	 * 	<li>{@link RestResource#properties()} annotations in parent-to-child order.
-	 * 	<li>{@link SerializerContext#SERIALIZER_relativeUriBase} from {@link ServletConfig#getServletContext()}.
-	 * </ul>
-	 *
-	 * @return The resource properties as an {@link ObjectMap}.
-	 */
-	protected ObjectMap createProperties() {
-		ObjectMap m = new ObjectMap();
-
-		ServletContext ctx = servletConfig.getServletContext();
-
-		// Workaround for bug in Jetty that causes context path to always end in "null".
-		String ctxPath = ctx.getContextPath();
-		if (ctxPath.endsWith("null"))
-			ctxPath = ctxPath.substring(0, ctxPath.length()-4);
-		m.put(SERIALIZER_relativeUriBase, ctxPath);
-
-		// Get the initialization parameters.
-		for (Enumeration ep = servletConfig.getInitParameterNames(); ep.hasMoreElements();) {
-			String p = (String)ep.nextElement();
-			String initParam = servletConfig.getInitParameter(p);
-			m.put(p, initParam);
-		}
-
-		// Properties are loaded in parent-to-child order to allow overrides.
-		for (RestResource r : restResourceAnnotationsParentFirst.values())
-			for (Property p : r.properties())
-				m.append(getVarResolver().resolve(p.name()), getVarResolver().resolve(p.value()));
-
-		return m;
-	}
-
-	/**
-	 * Creates the class-level bean filters associated with this servlet.
-	 * <p>
-	 * Subclasses can override this method to provide their own class-level bean filters for this servlet.
-	 * <p>
-	 * By default, returns the bean filters specified through the {@link RestResource#beanFilters() @RestResource.beanFilters()} annotation in child-to-parent order.
-	 * 	(i.e. bean filters will be applied in child-to-parent order with child annotations overriding parent annotations when
-	 * 	the same filters are applied).
-	 *
-	 * @return The new set of transforms associated with this servet.
-	 */
-	protected Class<?>[] createBeanFilters() {
-		List<Class<?>> l = new LinkedList<Class<?>>();
-
-		// Bean filters are loaded in parent-to-child order to allow overrides.
-		for (RestResource r : restResourceAnnotationsChildFirst.values())
-			for (Class c : r.beanFilters())
-				l.add(c);
-
-		return l.toArray(new Class<?>[l.size()]);
-	}
-
-	/**
-	 * Creates the class-level POJO swaps associated with this servlet.
-	 * <p>
-	 * Subclasses can override this method to provide their own class-level POJO swaps for this servlet.
-	 * <p>
-	 * By default, returns the transforms specified through the {@link RestResource#pojoSwaps() @RestResource.pojoSwaps()} annotation in child-to-parent order.
-	 * 	(i.e. POJO swaps will be applied in child-to-parent order with child annotations overriding parent annotations when
-	 * 	the same swaps are applied).
-	 *
-	 * @return The new set of transforms associated with this servet.
-	 */
-	protected Class<?>[] createPojoSwaps() {
-		List<Class<?>> l = new LinkedList<Class<?>>();
-
-		// Swaps are loaded in parent-to-child order to allow overrides.
-		for (RestResource r : restResourceAnnotationsChildFirst.values())
-			for (Class c : r.pojoSwaps())
-				l.add(c);
-
-		return l.toArray(new Class<?>[l.size()]);
-	}
-
-	/**
-	 * Creates the {@link BeanContext} object used for parsing path variables and header values.
-	 * <p>
-	 * Subclasses can override this method to provide their own specialized bean context.
-	 *
-	 * @param properties Servlet-level properties returned by {@link #createProperties()}.
-	 * @param beanFilters Servlet-level bean filters returned by {@link #createBeanFilters()}.
-	 * @param pojoSwaps Servlet-level POJO swaps returned by {@link #createPojoSwaps()}.
-	 * @return The new bean context.
-	 * @throws Exception If bean context not be constructed for any reason.
-	 */
-	protected BeanContext createBeanContext(ObjectMap properties, Class<?>[] beanFilters, Class<?>[] pojoSwaps) throws Exception {
-		return PropertyStore.create().addBeanFilters(beanFilters).addPojoSwaps(pojoSwaps).setProperties(properties).getBeanContext();
-	}
-
-	/**
-	 * Creates the URL-encoding serializer used for serializing object passed to {@link Redirect}.
-	 * <p>
-	 * Subclasses can override this method to provide their own specialized serializer.
-	 *
-	 * @param properties Servlet-level properties returned by {@link #createProperties()}.
-	 * @param beanFilters Servlet-level bean filters returned by {@link #createBeanFilters()}.
-	 * @param pojoSwaps Servlet-level POJO swaps returned by {@link #createPojoSwaps()}.
-	 * @return The new URL-Encoding serializer.
-	 * @throws Exception If the serializer could not be constructed for any reason.
-	 */
-	protected UrlEncodingSerializerBuilder createUrlEncodingSerializer(ObjectMap properties, Class<?>[] beanFilters, Class<?>[] pojoSwaps) throws Exception {
-		return new UrlEncodingSerializerBuilder().properties(properties).beanFilters(beanFilters).pojoSwaps(pojoSwaps);
-	}
-
-	/**
-	 * Creates the URL-encoding parser used for parsing URL query parameters.
-	 * <p>
-	 * Subclasses can override this method to provide their own specialized parser.
-	 *
-	 * @param properties Servlet-level properties returned by {@link #createProperties()}.
-	 * @param beanFilters Servlet-level bean filters returned by {@link #createBeanFilters()}.
-	 * @param pojoSwaps Servlet-level POJO swaps returned by {@link #createPojoSwaps()}.
-	 * @return The new URL-Encoding parser.
-	 * @throws Exception If the parser could not be constructed for any reason.
-	 */
-	protected UrlEncodingParserBuilder createUrlEncodingParser(ObjectMap properties, Class<?>[] beanFilters, Class<?>[] pojoSwaps) throws Exception {
-		return new UrlEncodingParserBuilder().properties(properties).beanFilters(beanFilters).pojoSwaps(pojoSwaps);
-	}
-
 	/**
-	 * Creates the serializer group containing serializers used for serializing output POJOs in HTTP responses.
+	 * Resource initialization method.
 	 * <p>
-	 * Subclasses can override this method to provide their own set of serializers for this servlet.
-	 * They can do this by either creating a new {@link SerializerGroup} from scratch, or appending to the
-	 * 	group returned by <code><jk>super</jk>.createSerializers()</code>.
+	 * Identical to {@link Servlet#init(ServletConfig)} except the config object provides
+	 * access to the external config file, configuration properties, and variable resolver
+	 * defined for this resource.
 	 * <p>
-	 * By default, returns the serializers defined through {@link RestResource#serializers() @RestResource.serializers()} on this class
-	 * 	and all parent classes.
-	 *
-	 * @param properties Servlet-level properties returned by {@link #createProperties()}.
-	 * @param beanFilters Servlet-level bean filters returned by {@link #createBeanFilters()}.
-	 * @param pojoSwaps Servlet-level POJO swaps returned by {@link #createPojoSwaps()}.
-	 * @return The group of serializers.
-	 * @throws Exception If serializer group could not be constructed for any reason.
-	 */
-	protected SerializerGroupBuilder createSerializers(ObjectMap properties, Class<?>[] beanFilters, Class<?>[] pojoSwaps) throws Exception {
-		SerializerGroupBuilder g = new SerializerGroupBuilder();
-
-		// Serializers are loaded in parent-to-child order to allow overrides.
-		for (RestResource r : restResourceAnnotationsParentFirst.values())
-			g.append(reverse(r.serializers()));
-
-		return g.properties(properties).beanFilters(beanFilters).pojoSwaps(pojoSwaps);
-	}
-
-	/**
-	 * Creates the parser group containing parsers used for parsing input into POJOs from HTTP requests.
+	 * Classes can also use {@link HttpServlet#init()} and {@link RestServlet#getServletConfig()}
+	 * as well to perform initialization.
 	 * <p>
-	 * Subclasses can override this method to provide their own set of parsers for this servlet.
-	 * They can do this by either creating a new {@link ParserGroup} from scratch, or appending to the
-	 * 	group returned by <code><jk>super</jk>.createParsers()</code>.
+	 * Note that if you override this method, you must first call <code><jk>super</jk>.init(servletConfig)</code>!
 	 * <p>
-	 * By default, returns the parsers defined through {@link RestResource#parsers() @RestResource.parsers()} on this class
-	 * 	and all parent classes.
+	 * Resource classes that don't extend from {@link RestServlet} can add this method to their class
+	 * to get access to the config object.
 	 *
-	 * @param properties Servlet-level properties returned by {@link #createProperties()}.
-	 * @param beanFilters Servlet-level bean filters returned by {@link #createBeanFilters()}.
-	 * @param pojoSwaps Servlet-level POJO swaps returned by {@link #createPojoSwaps()}.
-	 * @return The group of parsers.
-	 * @throws Exception If parser group could not be constructed for any reason.
+	 * @param config The servlet configuration.
+	 * @throws Exception Any exception can be thrown to signal an initialization failure.
 	 */
-	protected ParserGroupBuilder createParsers(ObjectMap properties, Class<?>[] beanFilters, Class<?>[] pojoSwaps) throws Exception {
-		ParserGroupBuilder g = new ParserGroupBuilder();
-
-		// Parsers are loaded in parent-to-child order to allow overrides.
-		for (RestResource r : restResourceAnnotationsParentFirst.values())
-			g.append(reverse(r.parsers()));
-
-		return g.properties(properties).beanFilters(beanFilters).pojoSwaps(pojoSwaps);
+	public synchronized void init(RestConfig config) throws Exception {
+		if (isInitialized)
+			return;
+		createContext(config);
+		super.init(config);
+		init(context);
 	}
 
 	/**
-	 * Creates the class-level converters associated with this servlet.
+	 * Convenience method if you want to perform initialization on your resource after all configuration settings
+	 * have been made.
 	 * <p>
-	 * Subclasses can override this method to provide their own class-level converters for this servlet.
+	 * This allows you to get access to the {@link RestContext} object during initialization.
 	 * <p>
-	 * By default, returns the converters specified through the {@link RestResource#converters() @RestResource.converters()} annotation in child-to-parent order.
-	 * 	(e.g. converters on children will be called before converters on parents).
+	 * The default implementation does nothing.
 	 *
-	 * @param properties Servlet-level properties returned by {@link #createProperties()}.
-	 * @return The new set of transforms associated with this servet.
-	 * @throws RestServletException
+	 * @param context The servlet context containing all the set-in-stone configurations for this resource.
+	 * @throws Exception Any exception can be thrown to signal an initialization failure.
 	 */
-	protected RestConverter[] createConverters(ObjectMap properties) throws RestServletException {
-		List<RestConverter> l = new LinkedList<RestConverter>();
+	public synchronized void init(RestContext context) throws Exception {}
 
-		// Converters are loaded in child-to-parent order.
-		for (RestResource r : restResourceAnnotationsChildFirst.values())
-			for (Class<? extends RestConverter> c : r.converters())
-				try {
-					l.add(c.newInstance());
-				} catch (Exception e) {
-					throw new RestServletException("Exception occurred while trying to instantiate RestConverter ''{0}''", c.getSimpleName()).initCause(e);
-				}
 
-		return l.toArray(new RestConverter[l.size()]);
+	private synchronized void createContext(RestConfig config) throws Exception {
+		if (isInitialized)
+			return;
+		this.config = config;
+		this.context = new RestContext(this, config);
+		this.isInitialized = true;
 	}
 
-	/**
-	 * Creates the {@link EncoderGroup} for this servlet for handling various encoding schemes.
-	 * <p>
-	 * Subclasses can override this method to provide their own encoder group, typically by
-	 * 	appending to the group returned by <code><jk>super</jk>.createEncoders()</code>.
-	 * <p>
-	 * By default, returns a group containing {@link IdentityEncoder#INSTANCE} and all encoders
-	 * 	specified through {@link RestResource#encoders() @RestResource.encoders()} annotations in parent-to-child order.
-	 *
-	 * @param properties Servlet-level properties returned by {@link #createProperties()}.
-	 * @return The new encoder group associated with this servet.
-	 * @throws RestServletException
-	 */
-	protected EncoderGroupBuilder createEncoders(ObjectMap properties) throws RestServletException {
-		EncoderGroupBuilder g = new EncoderGroupBuilder().append(IdentityEncoder.INSTANCE);
-
-		// Encoders are loaded in parent-to-child order to allow overrides.
-		for (RestResource r : restResourceAnnotationsParentFirst.values())
-			for (Class<? extends Encoder> c : reverse(r.encoders()))
-				try {
-					g.append(c);
-				} catch (Exception e) {
-					throw new RestServletException("Exception occurred while trying to instantiate Encoder ''{0}''", c.getSimpleName()).initCause(e);
-				}
 
-		return g;
-	}
+	//--------------------------------------------------------------------------------
+	// Other methods
+	//--------------------------------------------------------------------------------
 
 	/**
-	 * Creates the class-level guards associated with this servlet.
-	 * <p>
-	 * Subclasses can override this method to provide their own class-level guards for this servlet.
+	 * The main service method.
 	 * <p>
-	 * By default, returns the guards specified through the {@link RestResource#guards() @RestResource.guards()} annotation in child-to-parent order.
-	 * 	(i.e. guards on children will be called before guards on parents).
-	 *
-	 * @param properties Servlet-level properties returned by {@link #createProperties()}.
-	 * @return The new set of guards associated with this servet.
-	 * @throws RestServletException
+	 * Subclasses can optionally override this method if they want to tailor the behavior of requests.
 	 */
-	protected RestGuard[] createGuards(ObjectMap properties) throws RestServletException {
-		List<RestGuard> l = new LinkedList<RestGuard>();
+	@Override /* Servlet */
+	public void service(HttpServletRequest r1, HttpServletResponse r2) throws ServletException, IOException {
+		try {
 
-		// Guards are loaded in child-to-parent order.
-		for (RestResource r : restResourceAnnotationsChildFirst.values())
-			for (Class<? extends RestGuard> c : reverse(r.guards()))
-				try {
-					l.add(c.newInstance());
-				} catch (Exception e) {
-					throw new RestServletException("Exception occurred while trying to instantiate RestGuard ''{0}''", c.getSimpleName()).initCause(e);
-				}
+			if (initException != null) {
+				if (initException instanceof RestException)
+					throw (RestException)initException;
+				throw new RestException(SC_INTERNAL_SERVER_ERROR, initException);
+			}
+			if (context == null)
+				throw new RestException(SC_INTERNAL_SERVER_ERROR, "Servlet not initialized.  init(RestServletConfig) was not called.");
+			if (! isInitialized)
+				throw new RestException(SC_INTERNAL_SERVER_ERROR, "Servlet has not been initialized");
 
-		return l.toArray(new RestGuard[l.size()]);
-	}
+			context.getCallHandler().service(r1, r2);
 
-	/**
-	 * Creates an instance of {@link MimetypesFileTypeMap} that is used to determine
-	 * 	the media types of static files.
-	 * <p>
-	 * Subclasses can override this method to provide their own mappings, or augment the existing
-	 * 	map by appending to <code><jk>super</jk>.createMimetypesFileTypeMap()</code>
-	 *
-	 * @param properties Servlet-level properties returned by {@link #createProperties()}.
-	 * @return A new reusable MIME-types map.
-	 */
-	protected MimetypesFileTypeMap createMimetypesFileTypeMap(ObjectMap properties) {
-		MimetypesFileTypeMap m = new MimetypesFileTypeMap();
-		m.addMimeTypes("text/css css CSS");
-		m.addMimeTypes("text/html html htm HTML");
-		m.addMimeTypes("text/plain txt text TXT");
-		m.addMimeTypes("application/javascript js");
-		m.addMimeTypes("image/png png");
-		m.addMimeTypes("image/gif gif");
-		m.addMimeTypes("application/xml xml XML");
-		m.addMimeTypes("application/json json JSON");
-		return m;
+		} catch (RestException e) {
+			context.getCallHandler().handleError(r1, r2, e);
+		} catch (Throwable e) {
+			context.getCallHandler().handleError(r1, r2, new RestException(SC_INTERNAL_SERVER_ERROR, e));
+		}
 	}
 
 	/**
-	 * Creates the set of default request headers for this servlet.
+	 * Returns the read-only context object that contains all the configuration information about this resource.
 	 * <p>
-	 * Default request headers are default values for when HTTP requests do not specify a header value.
-	 * For example, you can specify a default value for <code>Accept</code> if a request does not specify that header value.
+	 * This object is <jk>null</jk> during the call to {@link #init(RestConfig)} but is populated
+	 * by the time {@link #init()} is called.
 	 * <p>
-	 * Subclasses can override this method to provide their own class-level default request headers for this servlet.
-	 * <p>
-	 * By default, returns the default request headers specified through the {@link RestResource#defaultRequestHeaders() @RestResource.defaultRequestHeaders()}
-	 * 	annotation in parent-to-child order.
-	 * (e.g. headers defined on children will override the same headers defined on parents).
+	 * Resource classes that don't extend from {@link RestServlet} can add the following method to their
+	 * class to get access to this context object:
+	 * <p class='bcode'>
+	 * 	<jk>public void</jk> init(RestServletContext context) <jk>throws</jk> Exception;
+	 * </p>
 	 *
-	 * @param properties Servlet-level properties returned by {@link #createProperties()}.
-	 * @return The new set of default request headers associated with this servet.
-	 * @throws RestServletException
+	 * @return The context information on this servlet.
 	 */
-	protected Map<String,String> createDefaultRequestHeaders(ObjectMap properties) throws RestServletException {
-		Map<String,String> m = new HashMap<String,String>();
-
-		// Headers are loaded in parent-to-child order to allow overrides.
-		for (RestResource r : restResourceAnnotationsParentFirst.values()) {
-			for (String s : r.defaultRequestHeaders()) {
-				String[] h = parseHeader(s);
-				if (h == null)
-					throw new RestServletException("Invalid default request header specified: ''{0}''.  Must be in the format: ''Header-Name: header-value''", s);
-				m.put(h[0], h[1]);
-			}
-		}
-
-		return m;
+	protected RestContext getContext() {
+		return context;
 	}
 
 	/**
-	 * Creates the set of default response headers for this servlet.
+	 * Callback method for listening for successful completion of requests.
 	 * <p>
-	 * Default response headers are headers that will be appended to all responses if those headers have not already been
-	 * 	set on the response object.
+	 * Subclasses can override this method for gathering performance statistics.
 	 * <p>
-	 * Subclasses can override this method to provide their own class-level default response headers for this servlet.
+	 * The default implementation does nothing.
 	 * <p>
-	 * By default, returns the default response headers specified through the {@link RestResource#defaultResponseHeaders() @RestResource.defaultResponseHeaders()}
-	 * 	annotation in parent-to-child order.
-	 * (e.g. headers defined on children will override the same headers defined on parents).
+	 * Resources that don't extend from {@link RestServlet} can implement an equivalent method by
+	 * 	overriding the {@link RestCallHandler#onSuccess(RestRequest, RestResponse, long)} method.
 	 *
-	 * @param properties Servlet-level properties returned by {@link #createProperties()}.
-	 * @return The new set of default response headers associated with this servet.
-	 * @throws RestServletException
+	 * @param req The HTTP request.
+	 * @param res The HTTP response.
+	 * @param time The time in milliseconds it took to process the request.
 	 */
-	protected Map<String,Object> createDefaultResponseHeaders(ObjectMap properties) throws RestServletException {
-		Map<String,Object> m = new LinkedHashMap<String,Object>();
-
-		// Headers are loaded in parent-to-child order to allow overrides.
-		for (RestResource r : restResourceAnnotationsParentFirst.values()) {
-			for (String s : r.defaultResponseHeaders()) {
-				String[] h = parseHeader(s);
-				if (h == null)
-					throw new RestServletException("Invalid default response header specified: ''{0}''.  Must be in the format: ''Header-Name: header-value''", s);
-				m.put(h[0], h[1]);
-			}
-		}
-
-		return m;
-	}
+	protected void onSuccess(RestRequest req, RestResponse res, long time) {}
 
 	/**
-	 * Creates the class-level response handlers associated with this servlet.
+	 * Callback method that gets invoked right before the REST Java method is invoked.
 	 * <p>
-	 * Subclasses can override this method to provide their own class-level response handlers for this servlet.
+	 * Subclasses can override this method to override request headers or set request-duration properties
+	 * 	before the Java method is invoked.
 	 * <p>
-	 * By default, returns the handlers specified through the {@link RestResource#responseHandlers() @RestResource.responseHandlers()}
-	 * 	annotation in parent-to-child order.
-	 * (e.g. handlers on children will be called before handlers on parents).
+	 * Resources that don't extend from {@link RestServlet} can implement an equivalent method by
+	 * 	overriding the {@link RestCallHandler#onPreCall(RestRequest)} method.
 	 *
-	 * @param properties Servlet-level properties returned by {@link #createProperties()}.
-	 * @return The new set of response handlers associated with this servet.
-	 * @throws RestException
+	 * @param req The HTTP servlet request object.
+	 * @throws RestException If any error occurs.
 	 */
-	protected ResponseHandler[] createResponseHandlers(ObjectMap properties) throws RestException {
-		List<ResponseHandler> l = new LinkedList<ResponseHandler>();
-
-		// Loaded in parent-to-child order to allow overrides.
-		for (RestResource r : restResourceAnnotationsParentFirst.values())
-			for (Class<? extends ResponseHandler> c : r.responseHandlers())
-				try {
-					l.add(c.newInstance());
-				} catch (Exception e) {
-					throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
-				}
-
-		// Add the default handlers.
-		l.add(new StreamableHandler());
-		l.add(new WritableHandler());
-		l.add(new ReaderHandler());
-		l.add(new InputStreamHandler());
-		l.add(new RedirectHandler());
-		l.add(new DefaultHandler());
-
-		return l.toArray(new ResponseHandler[l.size()]);
-	}
-
-
-	//--------------------------------------------------------------------------------
-	// Other methods
-	//--------------------------------------------------------------------------------
+	protected void onPreCall(RestRequest req) throws RestException {}
 
 	/**
-	 * Returns the localized Swagger from the file system.
+	 * Callback method that gets invoked right after the REST Java method is invoked, but before
+	 * 	the serializer is invoked.
 	 * <p>
-	 * Looks for a file called <js>"{ServletClass}_{locale}.json"</js> in the same package
-	 * as this servlet and returns it as a parsed {@link Swagger} object.
+	 * Subclasses can override this method to override request and response headers, or
+	 * 	set/override properties used by the serializer.
 	 * <p>
-	 * Returned objects are cached for later quick-lookup.
+	 * Resources that don't extend from {@link RestServlet} can implement an equivalent method by
+	 * 	overriding the {@link RestCallHandler#onPostCall(RestRequest,RestResponse)} method.
 	 *
-	 * @param locale The locale of the swagger.
-	 * @return The parsed swagger object, or <jk>null</jk> if the swagger file could not be found.
-	 * @throws RestException
-	 */
-	protected Swagger getSwaggerFromFile(Locale locale) throws RestException {
-		Swagger s = swaggers.get(locale);
-		if (s == null) {
-			try {
-				s = getResource(Swagger.class, MediaType.JSON, getClass().getSimpleName() + ".json", locale);
-				swaggers.putIfAbsent(locale, s == null ? Swagger.NULL : s);
-			} catch (Exception e) {
-				throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
-			}
-		}
-		return s == Swagger.NULL ? null : s;
-	}
-
-
-	/**
-	 * Returns the localized swagger for this REST resource.
-	 *
-	 * @param req The incoming HTTP request.
-	 * @return A new Swagger instance.
-	 * @throws RestException
+	 * @param req The HTTP servlet request object.
+	 * @param res The HTTP servlet response object.
+	 * @throws RestException If any error occurs.
 	 */
-	protected Swagger getSwagger(RestRequest req) throws RestException {
-		try {
-			// If a file is defined, use that.
-			Swagger s = req.getSwaggerFromFile();
-			if (s != null)
-				return s;
-
-			s = swagger(
-				info(getTitle(req), getVersion(req))
-					.contact(getContact(req))
-					.license(getLicense(req))
-					.description(getDescription(req))
-					.termsOfService(getTermsOfService(req))
-				)
-				.consumes(getSupportedAcceptTypes())
-				.produces(getSupportedContentTypes())
-				.tags(getTags(req))
-				.externalDocs(getExternalDocs(req));
-
-			for (MethodMeta sm : javaRestMethods.values()) {
-				if (sm.isRequestAllowed(req)) {
-					Operation o = sm.getSwaggerOperation(req);
-					s.path(
-						sm.pathPattern.getPatternString(),
-						sm.httpMethod.toLowerCase(),
-						o
-					);
-				}
-			}
-			return s;
-		} catch (RestException e) {
-			throw e;
-		} catch (Exception e) {
-			throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
-		}
-	}
+	protected void onPostCall(RestRequest req, RestResponse res) throws RestException {}
 
 	/**
-	 * Sets the parent of this resource.
+	 * Convenience method for calling <code>getContext().getLogger().log(level, msg, args);</code>
 	 *
-	 * @param parent The parent of this resource.
+	 * @param level The log level.
+	 * @param msg The message to log.
+	 * @param args Optional {@link MessageFormat}-style arguments.
 	 */
-	protected void setParent(RestServlet parent) {
-		this.parentResource = parent;
+	protected void log(Level level, String msg, Object...args) {
+		if (context != null)
+			context.getLogger().log(level, msg, args);
 	}
 
 	/**
-	 * Returns the parent of this resource.
+	 * Convenience method for calling <code>getContext().getLogger().log(level, cause, msg, args);</code>
 	 *
-	 * @return The parent of this resource, or <jk>null</jk> if resource has no parent.
+	 * @param level The log level.
+	 * @param cause The cause.
+	 * @param msg The message to log.
+	 * @param args Optional {@link MessageFormat}-style arguments.
 	 */
-	public RestServlet getParent() {
-		return this.parentResource;
-	}
-
-	private static String[] parseHeader(String s) {
-		int i = s.indexOf(':');
-		if (i == -1)
-			return null;
-		String name = s.substring(0, i).trim().toLowerCase(Locale.ENGLISH);
-		String val = s.substring(i+1).trim();
-		return new String[]{name,val};
+	protected void log(Level level, Throwable cause, String msg, Object...args) {
+		if (context != null)
+			context.getLogger().log(level, cause, msg, args);
 	}
 
-	/**
-	 * Creates a {@link RestRequest} object based on the specified incoming {@link HttpServletRequest} object.
-	 * <p>
-	 * Subclasses may choose to override this method to provide a specialized request object.
-	 *
-	 * @param req The request object from the {@link #service(HttpServletRequest, HttpServletResponse)} method.
-	 * @return The wrapped request object.
-	 * @throws ServletException If any errors occur trying to interpret the request.
-	 */
-	protected RestRequest createRequest(HttpServletRequest req) throws ServletException {
-		return new RestRequest(this, req);
+	@Override /* GenericServlet */
+	public RestConfig getServletConfig() {
+		return config;
 	}
 
-	/**
-	 * Creates a {@link RestResponse} object based on the specified incoming {@link HttpServletResponse} object
-	 * 	and the request returned by {@link #createRequest(HttpServletRequest)}.
-	 * <p>
-	 * Subclasses may choose to override this method to provide a specialized response object.
-	 *
-	 * @param req The request object returned by {@link #createRequest(HttpServletRequest)}.
-	 * @param res The response object from the {@link #service(HttpServletRequest, HttpServletResponse)} method.
-	 * @return The wrapped response object.
-	 * @throws ServletException If any erros occur trying to interpret the request or response.
-	 */
-	protected RestResponse createResponse(RestRequest req, HttpServletResponse res) throws ServletException {
-		return new RestResponse(this, req, res);
+	@Override /* GenericServlet */
+	public void destroy() {
+		context.destroy();
+		super.destroy();
 	}
 
 	/**
-	 * Returns whether this resource class can provide an OPTIONS page.
-	 * <p>
-	 * By default, returns <jk>false</jk>.
-	 * <p>
-	 * Subclasses can override this method to cause the <code>options</code> link to show up in the HTML serialized output.
-	 *
-	 * @return <jk>true</jk> if this resource has implemented a {@code getOptions()} method.
+	 * Convenience method for calling <code>getContext().getMessages();</code>
+	 * @return The resource bundle for this resource.  Never <jk>null</jk>.
+	 * @see RestContext#getProperties()
 	 */
-	public boolean hasOptionsPage() {
-		return false;
+	public MessageBundle getMessages() {
+		return context.getMessages();
 	}
 
 	/**
-	 * Specify a class-level property.
-	 * <p>
-	 * Typically, properties in {@link RestServletContext} can be set in the {@link Servlet#init(ServletConfig)} method.
-	 *
-	 * @param key The property name.
-	 * @param value The property value.
-	 * @return This object (for method chaining).
+	 * Convenience method for calling <code>getContext().getProperties();</code>
+	 * @return The resource properties as an {@link ObjectMap}.
+	 * @see RestContext#getProperties()
 	 */
-	public synchronized RestServlet setProperty(String key, Object value) {
-		getProperties().put(key, value);
-		return this;
+	public ObjectMap getProperties() {
+		return getContext().getProperties();
 	}
 
 	/**
-	 * The main service method.
-	 * <p>
-	 * Subclasses can optionally override this method if they want to tailor the behavior of requests.
-	 */
-	@Override /* Servlet */
-	public void service(HttpServletRequest r1, HttpServletResponse r2) throws ServletException, IOException {
-
-		log(FINE, "HTTP: {0} {1}", r1.getMethod(), r1.getRequestURI());
-		long startTime = System.currentTimeMillis();
-
-		try {
-
-			if (initException != null) {
-				if (initException instanceof RestException)
-					throw (RestException)initException;
-				throw new RestException(SC_INTERNAL_SERVER_ERROR, initException);
-			}
-
-			if (! isInitialized)
-				throw new RestException(SC_INTERNAL_SERVER_ERROR, "Servlet has not been initialized");
-
-			String pathInfo = RestUtils.getPathInfoUndecoded(r1);  // Can't use r1.getPathInfo() because we don't want '%2F' resolved.
-
-			// If this resource has child resources, try to recursively call them.
-			if (pathInfo != null && (! childResources.isEmpty()) && (! pathInfo.equals("/"))) {
-				int i = pathInfo.indexOf('/', 1);
-				String pathInfoPart = i == -1 ? pathInfo.substring(1) : pathInfo.substring(1, i);
-				RestServlet childResource = childResources.get(pathInfoPart);
-				if (childResource != null) {
-					final String pathInfoRemainder = (i == -1 ? null : pathInfo.substring(i));
-					final String servletPath = r1.getServletPath() + "/" + pathInfoPart;
-					final HttpServletRequest childRequest = new HttpServletRequestWrapper(r1) {
-						@Override /* ServletRequest */
-						public String getPathInfo() {
-							return RestUtils.decode(pathInfoRemainder);
-						}
-						@Override /* ServletRequest */
-						public String getServletPath() {
-							return servletPath;
-						}
-					};
-					childResource.service(childRequest, r2);
-					return;
-				}
-			}
-
-			RestRequest req = createRequest(r1);
-			RestResponse res = createResponse(req, r2);
-			String method = req.getMethod();
-			String methodUC = method.toUpperCase(Locale.ENGLISH);
-
-			StreamResource r = null;
-			if (pathInfo != null) {
-				String p = pathInfo.substring(1);
-				if (p.equals("favicon.ico"))
-					r = favIcon;
-				else if (p.equals("style.css"))
-					r = styleSheet;
-				else if (StringUtils.pathStartsWith(p, staticFilesPrefixes))
-					r = resolveStaticFile(p);
-			}
-
-			if (r != null) {
-				res.setStatus(SC_OK);
-				res.setOutput(r);
-			} else {
-				// If the specified method has been defined in a subclass, invoke it.
-				int rc = SC_METHOD_NOT_ALLOWED;
-				if (restMethods.containsKey(methodUC)) {
-					rc = restMethods.get(methodUC).invoke(method, pathInfo, this, req, res);
-				} else if (restMethods.containsKey("*")) {
-					rc = restMethods.get("*").invoke(method, pathInfo, this, req, res);
-				}
-
-				// If not invoked above, see if it's an OPTIONs request
-				if (rc != SC_OK)
-					handleNotFound(rc, req, res);
-			}
-
-			if (res.hasOutput()) {
-				Object output = res.getOutput();
-
-				// Do any class-level transforming.
-				for (RestConverter converter : getConverters())
-					output = converter.convert(req, output, getBeanContext().getClassMetaForObject(output));
-
-				res.setOutput(output);
-
-				// Now serialize the output if there was any.
-				// Some subclasses may write to the OutputStream or Writer directly.
-				handleResponse(req, res, output);
-			}
-
-			onSuccess(req, res, System.currentTimeMillis() - startTime);
-
-		} catch (RestException e) {
-			handleError(r1, r2, e);
-		} catch (Throwable e) {
-			handleError(r1, r2, new RestException(SC_INTERNAL_SERVER_ERROR, e));
-		}
-		log(FINE, "HTTP: [{0} {1}] finished in {2}ms", r1.getMethod(), r1.getRequestURI(), System.currentTimeMillis()-startTime);
-	}
-
-	/**
-	 * Handle the case where a matching method was not found.
-	 * <p>
-	 * Subclasses can override this method to provide a 2nd-chance for specifying a response.
-	 * The default implementation will simply throw an exception with an appropriate message.
-	 *
-	 * @param rc The HTTP response code.
-	 * @param req The HTTP request.
-	 * @param res The HTTP response.
-	 * @throws Exception
-	 */
-	protected void handleNotFound(int rc, RestRequest req, RestResponse res) throws Exception {
-		String pathInfo = req.getPathInfo();
-		String methodUC = req.getMethod();
-		String onPath = pathInfo == null ? " on no pathInfo"  : format(" on path '%s'", pathInfo);
-		if (rc == SC_NOT_FOUND)
-			throw new RestException(rc, "Method ''{0}'' not found on resource with matching pattern{1}.", methodUC, onPath);
-		else if (rc == SC_PRECONDITION_FAILED)
-			throw new RestException(rc, "Method ''{0}'' not found on resource{1} with matching matcher.", methodUC, onPath);
-		else if (rc == SC_METHOD_NOT_ALLOWED)
-			throw new RestException(rc, "Method ''{0}'' not found on resource.", methodUC);
-		else
-			throw new ServletException("Invalid method response: " + rc);
-	}
-
-	private synchronized void handleError(HttpServletRequest req, HttpServletResponse res, RestException e) throws IOException {
-		Integer c = 1;
-		if (context.useStackTraceHashes) {
-			int h = e.hashCode();
-			c = stackTraceHashes.get(h);
-			if (c == null)
-				c = 1;
-			else
-				c++;
-			stackTraceHashes.put(h, c);
-			e.setOccurrence(c);
-		}
-		onError(req, res, e);
-		renderError(req, res, e);
-	}
-
-	/**
-	 * Method for rendering response errors.
-	 * <p>
-	 * The default implementation renders a plain text English message, optionally with a stack trace
-	 * 	if {@link RestServletContext#REST_renderResponseStackTraces} is enabled.
-	 * <p>
-	 * Subclasses can override this method to provide their own custom error response handling.
-	 *
-	 * @param req The servlet request.
-	 * @param res The servlet response.
-	 * @param e The exception that occurred.
-	 * @throws IOException Can be thrown if a problem occurred trying to write to the output stream.
-	 */
-	protected void renderError(HttpServletRequest req, HttpServletResponse res, RestException e) throws IOException {
-
-		int status = e.getStatus();
-		res.setStatus(status);
-		res.setContentType("text/plain");
-		res.setHeader("Content-Encoding", "identity");
-		PrintWriter w = null;
-		try {
-			w = res.getWriter();
-		} catch (IllegalStateException e2) {
-			w = new PrintWriter(new OutputStreamWriter(res.getOutputStream(), IOUtils.UTF8));
-		}
-		String httpMessage = RestUtils.getHttpResponseText(status);
-		if (httpMessage != null)
-			w.append("HTTP ").append(String.valueOf(status)).append(": ").append(httpMessage).append("\n\n");
-		if (context.renderResponseStackTraces)
-			e.printStackTrace(w);
-		else
-			w.append(e.getFullStackMessage(true));
-		w.flush();
-		w.close();
-	}
-
-	/**
-	 * Callback method for logging errors during HTTP requests.
-	 * <p>
-	 * 	ypically, subclasses will override this method and log errors themselves.
-	 * <p>
-	 * The default implementation simply logs errors to the <code>RestServlet</code> logger.
-	 * <p>
-	 * Here's a typical implementation showing how stack trace hashing can be used to reduce log file sizes...
-	 * </p>
-	 * <p class='bcode'>
-	 * 	<jk>protected void</jk> onError(HttpServletRequest req, HttpServletResponse res, RestException e, <jk>boolean</jk> noTrace) {
-	 * 		String qs = req.getQueryString();
-	 * 		String msg = <js>"HTTP "</js> + req.getMethod() + <js>" "</js> + e.getStatus() + <js>" "</js> + req.getRequestURI() + (qs == <jk>null</jk> ? <js>""</js> : <js>"?"</js> + qs);
-	 * 		<jk>int</jk> c = e.getOccurrence();
-	 *
-	 * 		<jc>// REST_useStackTraceHashes is disabled, so we have to log the exception every time.</jc>
-	 * 		<jk>if</jk> (c == 0)
-	 * 			myLogger.log(Level.<jsf>WARNING</jsf>, <jsm>format</jsm>(<js>"[%s] %s"</js>, e.getStatus(), msg), e);
-	 *
-	 * 		<jc>// This is the first time we've countered this error, so log a stack trace
-	 * 		// unless ?noTrace was passed in as a URL parameter.</jc>
-	 * 		<jk>else if</jk> (c == 1 &amp;&amp; ! noTrace)
-	 * 			myLogger.log(Level.<jsf>WARNING</jsf>, <jsm>format</jsm>(<js>"[%h.%s.%s] %s"</js>, e.hashCode(), e.getStatus(), c, msg), e);
-	 *
-	 * 		<jc>// This error occurred before.
-	 * 		// Only log the message, not the stack trace.</jc>
-	 * 		<jk>else</jk>
-	 * 			myLogger.log(Level.<jsf>WARNING</jsf>, <jsm>format</jsm>(<js>"[%h.%s.%s] %s, %s"</js>, e.hashCode(), e.getStatus(), c, msg, e.getLocalizedMessage()));
-	 * 	}
-	 * </p>
-	 *
-	 * @param req The servlet request object.
-	 * @param res The servlet response object.
-	 * @param e Exception indicating what error occurred.
-	 */
-	protected void onError(HttpServletRequest req, HttpServletResponse res, RestException e) {
-		if (shouldLog(req, res, e)) {
-			String qs = req.getQueryString();
-			String msg = "HTTP " + req.getMethod() + " " + e.getStatus() + " " + req.getRequestURI() + (qs == null ? "" : "?" + qs);
-			int c = e.getOccurrence();
-			if (shouldLogStackTrace(req, res, e)) {
-				msg = '[' + Integer.toHexString(e.hashCode()) + '.' + e.getStatus() + '.' + c + "] " + msg;
-				log(Level.WARNING, e, msg);
-			} else {
-				msg = '[' + Integer.toHexString(e.hashCode()) + '.' + e.getStatus() + '.' + c + "] " + msg + ", " + e.getLocalizedMessage();
-				log(Level.WARNING, msg);
-			}
-		}
-	}
-
-	/**
-	 * Returns <jk>true</jk> if the specified exception should be logged.
-	 * <p>
-	 * Subclasses can override this method to provide their own logic for determining when exceptions are logged.
-	 * <p>
-	 * The default implementation will return <jk>false</jk> if <js>"noTrace=true"</js> is passed in the query string.
-	 *
-	 * @param req The HTTP request.
-	 * @param res The HTTP response.
-	 * @param e The exception.
-	 * @return <jk>true</jk> if exception should be logged.
-	 */
-	protected boolean shouldLog(HttpServletRequest req, HttpServletResponse res, RestException e) {
-		String q = req.getQueryString();
-		return (q == null ? true : q.indexOf("noTrace=true") == -1);
-	}
-
-	/**
-	 * Returns <jk>true</jk> if a stack trace should be logged for this exception.
-	 * <p>
-	 * Subclasses can override this method to provide their own logic for determining when stack traces are logged.
-	 * <p>
-	 * The default implementation will only log a stack trace if {@link RestException#getOccurrence()} returns <code>1</code>
-	 * 	and the exception is not one of the following:
-	 * </p>
-	 * <ul>
-	 * 	<li>{@link HttpServletResponse#SC_UNAUTHORIZED}
-	 * 	<li>{@link HttpServletResponse#SC_FORBIDDEN}
-	 * 	<li>{@link HttpServletResponse#SC_NOT_FOUND}
-	 * </ul>
-	 *
-	 * @param req The HTTP request.
-	 * @param res The HTTP response.
-	 * @param e The exception.
-	 * @return <jk>true</jk> if stack trace should be logged.
-	 */
-	protected boolean shouldLogStackTrace(HttpServletRequest req, HttpServletResponse res, RestException e) {
-		if (e.getOccurrence() == 1) {
-			switch (e.getStatus()) {
-				case SC_UNAUTHORIZED:
-				case SC_FORBIDDEN:
-				case SC_NOT_FOUND:  return false;
-				default:            return true;
-			}
-		}
-		return false;
-	}
-
-	/**
-	 * Log a message.
-	 * <p>
-	 * Equivalent to calling <code>log(level, <jk>null</jk>, msg, args);</code>
-	 *
-	 * @param level The log level.
-	 * @param msg The message to log.
-	 * @param args Optional {@link MessageFormat}-style arguments.
-	 */
-	protected void log(Level level, String msg, Object...args) {
-		log(level, null, msg, args);
-	}
-
-	/**
-	 * Same as {@link #log(Level, String, Object...)} excepts runs the
-	 * arguments through {@link JsonSerializer#DEFAULT_LAX_READABLE}.
-	 * <p>
-	 * Serialization of arguments do not occur if message is not logged, so
-	 * 	it's safe to use this method from within debug log statements.
-	 *
-	 * <h5 class='section'>Example:</h5>
-	 * <p class='bcode'>
-	 * 	logObjects(<jsf>DEBUG</jsf>, <js>"Pojo contents:\n{0}"</js>, myPojo);
-	 * </p>
-	 *
-	 * @param level The log level.
-	 * @param msg The message to log.
-	 * @param args Optional {@link MessageFormat}-style arguments.
-	 */
-	protected void logObjects(Level level, String msg, Object...args) {
-		for (int i = 0; i < args.length; i++)
-			args[i] = JsonSerializer.DEFAULT_LAX_READABLE.toStringObject(args[i]);
-		log(level, null, msg, args);
-	}
-
-	/**
-	 * Log a message to the logger returned by {@link #getLogger()}.
-	 * <p>
-	 * Subclasses can override this method if they wish to log messages using a library other than
-	 * 	Java Logging (e.g. Apache Commons Logging).
-	 *
-	 * @param level The log level.
-	 * @param cause The cause.
-	 * @param msg The message to log.
-	 * @param args Optional {@link MessageFormat}-style arguments.
-	 */
-	protected void log(Level level, Throwable cause, String msg, Object...args) {
-		JuneauLogger log = getLogger();
-		if (args.length > 0)
-			msg = MessageFormat.format(msg, args);
-		log.log(level, msg, cause);
-	}
-
-	/**
-	 * Callback method for listening for successful completion of requests.
-	 * <p>
-	 * Subclasses can override this method for gathering performance statistics.
-	 * <p>
-	 * The default implementation does nothing.
-	 *
-	 * @param req The HTTP request.
-	 * @param res The HTTP response.
-	 * @param time The time in milliseconds it took to process the request.
-	 */
-	protected void onSuccess(RestRequest req, RestResponse res, long time) {}
-
-	/**
-	 * Callback method that gets invoked right before the REST Java method is invoked.
-	 * <p>
-	 * Subclasses can override this method to override request headers or set request-duration properties
-	 * 	before the Java method is invoked.
-	 *
-	 * @param req The HTTP servlet request object.
-	 * @throws RestException If any error occurs.
-	 */
-	protected void onPreCall(RestRequest req) throws RestException {}
-
-	/**
-	 * Callback method that gets invoked right after the REST Java method is invoked, but before
-	 * 	the serializer is invoked.
-	 * <p>
-	 * Subclasses can override this method to override request and response headers, or
-	 * 	set/override properties used by the serializer.
-	 *
-	 * @param req The HTTP servlet request object.
-	 * @param res The HTTP servlet response object.
-	 * @throws RestException If any error occurs.
-	 */
-	protected void onPostCall(RestRequest req, RestResponse res) throws RestException {}
-
-	/**
-	 * The main method for serializing POJOs passed in through the {@link RestResponse#setOutput(Object)} method.
-	 * <p>
-	 * Subclasses may override this method if they wish to modify the way the output is rendered, or support
-	 * 	other output formats.
-	 *
-	 * @param req The HTTP request.
-	 * @param res The HTTP response.
-	 * @param output The output to serialize in the response.
-	 * @throws IOException
-	 * @throws RestException
-	 */
-	protected void handleResponse(RestRequest req, RestResponse res, Object output) throws IOException, RestException {
-		// Loop until we find the correct handler for the POJO.
-		for (ResponseHandler h : getResponseHandlers())
-			if (h.handle(req, res, output))
-				return;
-		throw new RestException(SC_NOT_IMPLEMENTED, "No response handlers found to process output of type '"+(output == null ? null : output.getClass().getName())+"'");
-	}
-
-	@Override /* GenericServlet */
-	public ServletConfig getServletConfig() {
-		return servletConfig;
-	}
-
-	@Override /* GenericServlet */
-	public void destroy() {
-		for (RestServlet r : childResources.values())
-			r.destroy();
-		super.destroy();
-	}
-
-	/**
-	 * Resolve a static resource file.
-	 * <p>
-	 * Subclasses can override this method to provide their own way to resolve files.
-	 *
-	 * @param pathInfo The unencoded path info.
-	 * @return The resource, or <jk>null</jk> if the resource could not be resolved.
-	 * @throws IOException
-	 */
-	protected StreamResource resolveStaticFile(String pathInfo) throws IOException {
-		if (! staticFilesCache.containsKey(pathInfo)) {
-			String p = RestUtils.decode(RestUtils.trimSlashes(pathInfo));
-			if (p.indexOf("..") != -1)
-				throw new RestException(SC_NOT_FOUND, "Invalid path");
-			for (Map.Entry<String,String> e : staticFilesMap.entrySet()) {
-				String key = RestUtils.trimSlashes(e.getKey());
-				if (p.startsWith(key)) {
-					String remainder = (p.equals(key) ? "" : p.substring(key.length()));
-					if (remainder.isEmpty() || remainder.startsWith("/")) {
-						String p2 = RestUtils.trimSlashes(e.getValue()) + remainder;
-						InputStream is = getResource(p2, null);
-						if (is != null) {
-							try {
-								int i = p2.lastIndexOf('/');
-								String name = (i == -1 ? p2 : p2.substring(i+1));
-								String mediaType = getMimetypesFileTypeMap().getContentType(name);
-								ObjectMap headers = new ObjectMap().append("Cache-Control", "max-age=86400, public");
-								staticFilesCache.put(pathInfo, new StreamResource(MediaType.forString(mediaType), headers, is));
-								return staticFilesCache.get(pathInfo);
-							} finally {
-								is.close();
-							}
-						}
-					}
-				}
-			}
-		}
-		return staticFilesCache.get(pathInfo);
-	}
-
-	/**
-	 * Returns a list of valid {@code Accept} content types for this resource.
-	 * <p>
-	 * Typically used by subclasses during {@code OPTIONS} requests.
-	 * <p>
-	 * The default implementation resturns the list from {@link ParserGroup#getSupportedMediaTypes()}
-	 * 	from the parser group returned by {@link #getParsers()}.
-	 * <p>
-	 * Subclasses can override or expand this list as they see fit.
-	 *
-	 * @return The list of valid {@code Accept} content types for this resource.
-	 * @throws RestServletException
-	 */
-	public Collection<MediaType> getSupportedAcceptTypes() throws RestServletException {
-		return getParsers().getSupportedMediaTypes();
-	}
-
-	/**
-	 * Returns a list of valid {@code Content-Types} for input for this resource.
-	 * <p>
-	 * Typically used by subclasses during {@code OPTIONS} requests.
-	 * <p>
-	 * The default implementation resturns the list from {@link SerializerGroup#getSupportedMediaTypes()}
-	 * 	from the parser group returned by {@link #getSerializers()}.
-	 * <p>
-	 * Subclasses can override or expand this list as they see fit.
-	 *
-	 * @return The list of valid {@code Content-Type} header values for this resource.
-	 * @throws RestServletException
-	 */
-	public Collection<MediaType> getSupportedContentTypes() throws RestServletException {
-		return getSerializers().getSupportedMediaTypes();
-	}
-
-	/**
-	 * Returns the localized summary of the specified java method on this servlet.
-	 * <p>
-	 * Subclasses can override this method to provide their own summary.
-	 * <p>
-	 * The default implementation returns the summary from the following locations (whichever matches first):
-	 * </p>
-	 * <ol>
-	 * 	<li>{@link RestMethod#summary() @RestMethod.summary()} annotation on the method.
-	 * 	<li><ck>[ClassName].[javaMethodName].summary</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
-	 * 		annotation for this class, then any parent classes.
-	 * 	<li><ck>[javaMethodName].summary</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
-	 * 		annotation for this class, then any parent classes.
-	 * </ol>
-	 *
-	 * @param javaMethodName The name of the Java method whose description we're retrieving.
-	 * @param req The current request.
-	 * @return The localized summary of the method, or a blank string if no summary was found.
-	 */
-	public String getMethodSummary(String javaMethodName, RestRequest req) {
-		MethodMeta m = javaRestMethods.get(javaMethodName);
-		if (m != null)
-			return m.getSummary(req);
-		return "";
-	}
-
-	/**
-	 * Returns the localized description of the specified java method on this servlet.
-	 * <p>
-	 * Subclasses can override this method to provide their own description.
-	 * <p>
-	 * The default implementation returns the description from the following locations (whichever matches first):
-	 * </p>
-	 * <ol>
-	 * 	<li>{@link RestMethod#description() @RestMethod.description()} annotation on the method.
-	 * 	<li><ck>[ClassName].[javaMethodName].description</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
-	 * 		annotation for this class, then any parent classes.
-	 * 	<li><ck>[javaMethodName].description</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
-	 * 		annotation for this class, then any parent classes.
-	 * </ol>
-	 *
-	 * @param javaMethodName The name of the Java method whose description we're retrieving.
-	 * @param req The current request.
-	 * @return The localized description of the method, or a blank string if no description was found.
-	 */
-	public String getMethodDescription(String javaMethodName, RestRequest req) {
-		MethodMeta m = javaRestMethods.get(javaMethodName);
-		if (m != null)
-			return m.getDescription(req);
-		return "";
-	}
-
-	/**
-	 * Returns the localized title of this REST resource.
-	 * <p>
-	 * Subclasses can override this method to provide their own title.
-	 * <p>
-	 * The default implementation returns the description from the following locations (whichever matches first):
-	 * <p>
-	 * <ol>
-	 * 	<li>{@link RestResource#title() @RestResourcel.title()} annotation on this class, and then any parent classes.
-	 * 	<li><ck>[ClassName].title</ck> property in resource bundle identified by {@link RestResource#messages() @ResourceBundle.messages()}
-	 * 		annotation for this class, then any parent classes.
-	 * 	<li><ck>title</ck> in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
-	 * 		annotation for this class, then any parent classes.
-	 * 	<li><ck>/info/title</ck> entry in swagger file.
-	 * </ol>
-	 *
-	 * @param req The current request.
-	 * @return The localized description of this REST resource, or <jk>null</jk> if no resource description was found.
-	 */
-	public String getTitle(RestRequest req) {
-		VarResolverSession vr = req.getVarResolverSession();
-		if (title != null)
-			return vr.resolve(title);
-		String title = msgs.findFirstString(req.getLocale(), "title");
-		if (title != null)
-			return vr.resolve(title);
-		Swagger s = req.getSwaggerFromFile();
-		if (s != null && s.getInfo() != null)
-			return s.getInfo().getTitle();
-		return null;
-	}
-
-	/**
-	 * Returns the localized description of this REST resource.
-	 * <p>
-	 * Subclasses can override this method to provide their own description.
-	 * <p>
-	 * The default implementation returns the description from the following locations (whichever matches first):
-	 * <ol>
-	 * 	<li>{@link RestResource#description() @RestResource.description()} annotation on this class, and then any parent classes.
-	 * 	<li><ck>[ClassName].description</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
-	 * 		annotation for this class, then any parent classes.
-	 * 	<li><ck>description</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
-	 * 		annotation for this class, then any parent classes.
-	 * 	<li><ck>/info/description</ck> entry in swagger file.
-	 * </ol>
-	 *
-	 * @param req The current request.
-	 * @return The localized description of this REST resource, or <jk>null</jk> if no resource description was found.
-	 */
-	public String getDescription(RestRequest req) {
-		VarResolverSession vr = req.getVarResolverSession();
-		if (description != null)
-			return vr.resolve(description);
-		String description = msgs.findFirstString(req.getLocale(), "description");
-		if (description != null)
-			return vr.resolve(description);
-		Swagger s = req.getSwaggerFromFile();
-		if (s != null && s.getInfo() != null)
-			return s.getInfo().getDescription();
-		return null;
-	}
-
-	/**
-	 * Returns the localized contact information of this REST resource.
-	 * <p>
-	 * Subclasses can override this method to provide their own contact information.
-	 * <p>
-	 * The default implementation returns the contact information from the following locations (whichever matches first):
-	 * <ol>
-	 * 	<li>{@link RestResource#contact() @RestResource.contact()} annotation on this class, and then any parent classes.
-	 * 	<li><ck>[ClassName].contact</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
-	 * 		annotation for this class, then any parent classes.
-	 * 	<li><ck>contact</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
-	 * 		annotation for this class, then any parent classes.
-	 * 	<li><ck>/info/contact</ck> entry in swagger file.
-	 * </ol>
-	 *
-	 * @param req The current request.
-	 * @return The localized contact information of this REST resource, or <jk>null</jk> if no contact information was found.
-	 */
-	public Contact getContact(RestRequest req) {
-		VarResolverSession vr = req.getVarResolverSession();
-		JsonParser jp = JsonParser.DEFAULT;
-		try {
-			if (contact != null)
-				return jp.parse(vr.resolve(contact), Contact.class);
-			String contact = msgs.findFirstString(req.getLocale(), "contact");
-			if (contact != null)
-				return jp.parse(vr.resolve(contact), Contact.class);
-			Swagger s = req.getSwaggerFromFile();
-			if (s != null && s.getInfo() != null)
-				return s.getInfo().getContact();
-			return null;
-		} catch (ParseException e) {
-			throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
-		}
-	}
-
-	/**
-	 * Returns the localized license information of this REST resource.
-	 * <p>
-	 * Subclasses can override this method to provide their own license information.
-	 * <p>
-	 * The default implementation returns the license information from the following locations (whichever matches first):
-	 * <ol>
-	 * 	<li>{@link RestResource#license() @RestResource.license()} annotation on this class, and then any parent classes.
-	 * 	<li><ck>[ClassName].license</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
-	 * 		annotation for this class, then any parent classes.
-	 * 	<li><ck>license</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
-	 * 		annotation for this class, then any parent classes.
-	 * 	<li><ck>/info/license</ck> entry in swagger file.
-	 * </ol>
-	 *
-	 * @param req The current request.
-	 * @return The localized contact information of this REST resource, or <jk>null</jk> if no contact information was found.
-	 */
-	public License getLicense(RestRequest req) {
-		VarResolverSession vr = req.getVarResolverSession();
-		JsonParser jp = JsonParser.DEFAULT;
-		try {
-			if (license != null)
-				return jp.parse(vr.resolve(license), License.class);
-			String license = msgs.findFirstString(req.getLocale(), "license");
-			if (license != null)
-				return jp.parse(vr.resolve(license), License.class);
-			Swagger s = req.getSwaggerFromFile();
-			if (s != null && s.getInfo() != null)
-				return s.getInfo().getLicense();
-			return null;
-		} catch (ParseException e) {
-			throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
-		}
-	}
-
-	/**
-	 * Returns the terms-of-service information of this REST resource.
-	 * <p>
-	 * Subclasses can override this method to provide their own terms-of-service information.
-	 * <p>
-	 * The default implementation returns the terms-of-service information from the following locations (whichever matches first):
-	 * <ol>
-	 * 	<li>{@link RestResource#termsOfService() @RestResource.termsOfService()} annotation on this class, and then any parent classes.
-	 * 	<li><ck>[ClassName].termsOfService</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
-	 * 		annotation for this class, then any parent classes.
-	 * 	<li><ck>termsOfService</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
-	 * 		annotation for this class, then any parent classes.
-	 * 	<li><ck>/info/termsOfService</ck> entry in swagger file.
-	 * </ol>
-	 *
-	 * @param req The current request.
-	 * @return The localized contact information of this REST resource, or <jk>null</jk> if no contact information was found.
-	 */
-	public String getTermsOfService(RestRequest req) {
-		VarResolverSession vr = req.getVarResolverSession();
-		if (termsOfService != null)
-			return vr.resolve(termsOfService);
-		String termsOfService = msgs.findFirstString(req.getLocale(), "termsOfService");
-		if (termsOfService != null)
-			return vr.resolve(termsOfService);
-		Swagger s = req.getSwaggerFromFile();
-		if (s != null && s.getInfo() != null)
-			return s.getInfo().getTermsOfService();
-		return null;
-	}
-
-	/**
-	 * Returns the version information of this REST resource.
-	 * <p>
-	 * Subclasses can override this method to provide their own version information.
-	 * <p>
-	 * The default implementation returns the version information from the following locations (whichever matches first):
-	 * <ol>
-	 * 	<li>{@link RestResource#version() @RestResource.version()} annotation on this class, and then any parent classes.
-	 * 	<li><ck>[ClassName].version</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
-	 * 		annotation for this class, then any parent classes.
-	 * 	<li><ck>version</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
-	 * 		annotation for this class, then any parent classes.
-	 * 	<li><ck>/info/version</ck> entry in swagger file.
-	 * </ol>
-	 *
-	 * @param req The current request.
-	 * @return The localized contact information of this REST resource, or <jk>null</jk> if no contact information was found.
-	 */
-	public String getVersion(RestRequest req) {
-		VarResolverSession vr = req.getVarResolverSession();
-		if (version != null)
-			return vr.resolve(version);
-		String version = msgs.findFirstString(req.getLocale(), "version");
-		if (version != null)
-			return vr.resolve(version);
-		Swagger s = req.getSwaggerFromFile();
-		if (s != null && s.getInfo() != null)
-			return s.getInfo().getVersion();
-		return null;
-	}
-
-	/**
-	 * Returns the version information of this REST resource.
-	 * <p>
-	 * Subclasses can override this method to provide their own version information.
-	 * <p>
-	 * The default implementation returns the version information from the following locations (whichever matches first):
-	 * <ol>
-	 * 	<li>{@link RestResource#version() @RestResource.version()} annotation on this class, and then any parent classes.
-	 * 	<li><ck>[ClassName].version</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
-	 * 		annotation for this class, then any parent classes.
-	 * 	<li><ck>version</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
-	 * 		annotation for this class, then any parent classes.
-	 * 	<li><ck>/info/version</ck> entry in swagger file.
-	 * </ol>
-	 *
-	 * @param req The current request.
-	 * @return The localized contact information of this REST resource, or <jk>null</jk> if no contact information was found.
-	 */
-	public List<Tag> getTags(RestRequest req) {
-		VarResolverSession vr = req.getVarResolverSession();
-		JsonParser jp = JsonParser.DEFAULT;
-		try {
-			if (tags != null)
-				return jp.parse(vr.resolve(tags), ArrayList.class, Tag.class);
-			String tags = msgs.findFirstString(req.getLocale(), "tags");
-			if (tags != null)
-				return jp.parse(vr.resolve(tags), ArrayList.class, Tag.class);
-			Swagger s = req.getSwaggerFromFile();
-			if (s != null)
-				return s.getTags();
-			return null;
-		} catch (Exception e) {
-			throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
-		}
-	}
-
-	/**
-	 * Returns the version information of this REST resource.
-	 * <p>
-	 * Subclasses can override this method to provide their own version information.
-	 * <p>
-	 * The default implementation returns the version information from the following locations (whichever matches first):
-	 * <ol>
-	 * 	<li>{@link RestResource#version() @RestResource.version()} annotation on this class, and then any parent classes.
-	 * 	<li><ck>[ClassName].version</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
-	 * 		annotation for this class, then any parent classes.
-	 * 	<li><ck>version</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
-	 * 		annotation for this class, then any parent classes.
-	 * 	<li><ck>/info/version</ck> entry in swagger file.
-	 * </ol>
-	 *
-	 * @param req The current request.
-	 * @return The localized contact information of this REST resource, or <jk>null</jk> if no contact information was found.
-	 */
-	public ExternalDocumentation getExternalDocs(RestRequest req) {
-		VarResolverSession vr = req.getVarResolverSession();
-		JsonParser jp = JsonParser.DEFAULT;
-		try {
-			if (externalDocs != null)
-				return jp.parse(vr.resolve(externalDocs), ExternalDocumentation.class);
-			String externalDocs = msgs.findFirstString(req.getLocale(), "externalDocs");
-			if (externalDocs != null)
-				return jp.parse(vr.resolve(externalDocs), ExternalDocumentation.class);
-			Swagger s = req.getSwaggerFromFile();
-			if (s != null)
-				return s.getExternalDocs();
-			return null;
-		} catch (Exception e) {
-			throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
-		}
-	}
-
-	/**
-	 * Returns the resource bundle identified by the {@link RestResource#messages() @RestResource.messages()} annotation for the default locale.
-	 *
-	 * @return The resource bundle.  Never <jk>null</jk>.
-	 */
-	public MessageBundle getMessages() {
-		return msgs;
-	}
-
-	/**
-	 * Returns the resource bundle identified by the {@link RestResource#messages() @RestResource.messages()} annotation for the specified locale.
-	 *
-	 * @param locale The resource bundle locale.
-	 * @return The resource bundle.  Never <jk>null</jk>.
-	 */
-	public MessageBundle getMessages(Locale locale) {
-		return msgs.getBundle(locale);
-	}
-
-	/**
-	 * Gets a localized message from the resource bundle identified by the {@link RestResource#messages() @RestResource.messages()} annotation.
-	 * <p>
-	 * If resource bundle location was not specified, or the resource bundle was not found,
-	 * 	returns the string <js>"{!!key}"</js>.
-	 * <p>
-	 * If message was not found in the resource bundle, returns the string <js>"{!key}"</js>.
-	 *
-	 * @param locale The client locale.
-	 * @param key The resource bundle key.
-	 * @param args Optional {@link MessageFormat}-style arguments.
-	 * @return The localized message.
-	 */
-	public String getMessage(Locale locale, String key, Object...args) {
-		return msgs.getString(locale, key, args);
-	}
-
-	/**
-	 * Programmatically adds the specified resource as a child to this resource.
-	 * <p>
-	 * This method can be used in a resources {@link #init()} method to define child resources
-	 * 	accessible through a child URL.
-	 * <p>
-	 * Typically, child methods are defined via {@link RestResource#children() @RestResource.children()}.  However, this
-	 * 	method is provided to handle child resources determined at runtime.
-	 *
-	 * @param name The sub-URL under which this resource is accessible.<br>
-	 * For example, if the parent resource URL is <js>"/foo"</js>, and this name is <js>"bar"</js>, then
-	 * 	the child resource will be accessible via the URL <js>"/foo/bar"</js>.
-	 * @param resource The child resource.
-	 * @throws ServletException Thrown by the child init() method.
-	 */
-	protected void addChildResource(String name, RestServlet resource) throws ServletException {
-		resource.init(getServletConfig());
-		childResources.put(name, resource);
-	}
-
-	/**
-	 * Returns the child resources associated with this servlet.
-	 *
-	 * @return An unmodifiable map of child resources.
-	 * Keys are the {@link RestResource#path() @RestResource.path()} annotation defined on the child resource.
-	 */
-	public Map<String,RestServlet> getChildResources() {
-		return Collections.unmodifiableMap(childResources);
-	}
-
-	/**
-	 * Returns the path for this servlet as defined by the {@link RestResource#path()} annotation
-	 * on this class concatenated with those on all parent classes.
-	 * <p>
-	 * If path is not specified, returns <js>"/"</js>.
-	 * <p>
-	 * Path always starts with <js>"/"</js>.
-	 *
-	 * @return The servlet path.
-	 */
-	public String getPath() {
-		if (path == null) {
-			LinkedList<String> l = new LinkedList<String>();
-			RestServlet r = this;
-			while (r != null) {
-				String p = r.findPath();
-				if (p == null)
-					break;
-				l.addFirst(p);
-				r = r.parentResource;
-			}
-			StringBuilder sb = new StringBuilder();
-			for (String p : l)
-				sb.append('/').append(p);
-			path = sb.toString();
-		}
-		return path;
-	}
-
-	private String findPath() {
-		List<RestResource> rrc = ReflectionUtils.findAnnotations(RestResource.class, getClass());
-		for (RestResource rc : rrc) {
-			String p = rc.path();
-			if (StringUtils.startsWith(p, '/'))
-				p = p.substring(1);
-			if (! p.isEmpty())
-				return p;
-		}
-		return null;
-	}
-
-	/**
-	 * Returns the config file for this servlet.
-	 * <p>
-	 * Subclasses can override this method to provide their own config file.
-	 * <p>
-	 * The default implementation uses the path defined by the {@link RestResource#config() @RestResource.config()} property resolved
-	 * 	by {@link ConfigMgr#DEFAULT}.
-	 * @param vrb
-	 *
-	 * @return The config file for this servlet.
-	 * @throws IOException
-	 */
-	protected ConfigFile createConfigFile(VarResolverBuilder vrb) throws IOException {
-		String cf = vrb.build().resolve(configPath);
-		if (cf.isEmpty())
-			return getConfigMgr().create();
-		return getConfigMgr().get(cf);
-	}
-
-	/**
-	 * Creates the stylesheet for this servlet.
-	 * <p>
-	 * The stylesheet is made available on the path <js>"/servlet-path/style.css"</js>.
-	 * <p>
-	 * Subclasses can override this method to provide their own stylesheet.
-	 * <p>
-	 * The default implementation uses the {@link RestResource#stylesheet() @RestResource.stylesheet()} annotation
-	 * 	to determine the stylesheet name and then searches the classpath then working directory
-	 * 	for that stylesheet.
-	 *
-	 * @return The stylesheet to use for this servlet, or <jk>null</jk> if the stylesheet could not be found.
-	 * @throws IOException If stylesheet could not be loaded.
-	 */
-	protected StreamResource createStyleSheet() throws IOException {
-		for (RestResource r : restResourceAnnotationsChildFirst.values())
-			if (! r.stylesheet().isEmpty()) {
-				List<InputStream> contents = new ArrayList<InputStream>();
-
-				for (String path : StringUtils.split(getVarResolver().resolve(r.stylesheet()), ','))
-					contents.add(getResource(path, null));
-
-				return new StreamResource(MediaType.forString("text/css"), contents.toArray());
-			}
-		return null;
-	}
-
-	/**
-	 * Creates the favicon for this servlet.
-	 * <p>
-	 * The favicon is made available on the path <js>"/servlet-path/favicon.ico"</js>.
-	 * <p>
-	 * Subclasses can override this method to provide their own favorites icon.
-	 * <p>
-	 * The default implementation uses the {@link RestResource#favicon() @RestResource.favicon()} annotation
-	 * 	to determine the file name and then searches the classpath then working directory
-	 * 	for that file.
-	 *
-	 * @return The icon file to use for this servlet.
-	 * @throws IOException If icon file could not be loaded.
-	 */
-	protected StreamResource createFavIcon() throws IOException {
-		for (RestResource r : restResourceAnnotationsChildFirst.values()) {
-			if (! r.favicon().isEmpty()) {
-				String path = getVarResolver().resolve(r.favicon());
-				InputStream is = getResource(path, null);
-				if (is != null) {
-					try {
-						return new StreamResource(MediaType.forString("image/x-icon"), is);
-					} finally {
-						is.close();
-					}
-				}
-			}
-		}
-		return null;
-	}
-
-	/**
-	 * Creates the static files map for this servlet.
-	 * <p>
-	 * This map defines static files that can be served up through subpaths on this servlet.
-	 * The map keys are subpaths (e.g. <js>"htdocs"</js>) and the values are locations to look in
-	 * 	the classpath and working directory for those files.
-	 * <p>
-	 * Subclasses can override this method to provide their own mappings.
-	 * <p>
-	 * The default implementation uses the {@link RestResource#staticFiles() @RestResource.staticFiles()} annotation
-	 * 	to determine the mappings.
-	 *
-	 * @return The list of static file mappings.
-	 * @throws ParseException
-	 */
-	@SuppressWarnings("unchecked")
-	protected Map<String,String> createStaticFilesMap() throws ParseException {
-		Map<String,String> m = new LinkedHashMap<String,String>();
-	

<TRUNCATED>

[5/7] incubator-juneau git commit: Allow @RestResource/@RestMethod annotations to be used on any classes.

Posted by ja...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/RestConfig.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestConfig.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestConfig.java
new file mode 100644
index 0000000..8e10182
--- /dev/null
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestConfig.java
@@ -0,0 +1,1153 @@
+// ***************************************************************************************************************************
+// * 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.serializer.SerializerContext.*;
+
+import java.io.*;
+import java.util.*;
+
+import javax.activation.*;
+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.rest.annotation.*;
+import org.apache.juneau.rest.response.*;
+import org.apache.juneau.rest.vars.*;
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.svl.*;
+import org.apache.juneau.svl.vars.*;
+
+/**
+ * Defines the initial configuration of a <code>RestServlet</code> or <code>@RestResource</code> annotated object.
+ * <p>
+ * An extension of the {@link ServletConfig} object used during servlet initialization.
+ * <p>
+ * Provides access to the following initialized resources:
+ * <ul>
+ * 	<li>{@link #getConfigFile()} - The external configuration file for this resource.
+ * 	<li>{@link #getProperties()} - The modifiable configuration properties for this resource.
+ * 	<li>{@link #getVarResolverBuilder()} - The variable resolver for this resource.
+ * </ul>
+ * <p>
+ * Methods are provided for overriding or augmenting the information provided by the <ja>@RestResource</ja> annotation.
+ * In general, most information provided in the <ja>@RestResource</ja> annotation can be specified programmatically
+ * through calls on this object.
+ * <p>
+ * To interact with this object, simply implement the following init method in your resource class:
+ * <p class='bcode'>
+ * 	<jk>public synchronized void</jk> init(RestConfig config) <jk>throws</jk> Exception {
+ * 		config.addPojoSwaps(CalendarSwap.<jsf>RFC2822DTZ</jsf>.<jk>class</jk>);
+ * 		config.setProperty(<jsf>PARSER_debug</jsf>, <jk>true</jk>);
+ * 		<jk>super</jk>.init(config); <jc>// Make sure this is the last line! (or just leave it out entirely)</jc>
+ * 	}
+ * </p>
+ * <p>
+ * Note that this method is identical to {@link HttpServlet#init(ServletConfig)} except you get access to
+ * this object instead.  Also, this method can throw any exception, not just a {@link ServletException}.
+ * <p>
+ * The parent <code>init(RestServletConfig)</code> method will construct a read-only {@link RestContext} object
+ * that contains a snapshot of these settings.  If you call <code><jk>super</jk>.init(RestServletConfig)<code> before
+ * you modify this config object, you won't see the changes!
+ */
+@SuppressWarnings("hiding")
+public class RestConfig implements ServletConfig {
+
+	final ServletConfig inner;
+
+	//---------------------------------------------------------------------------
+	// The following fields are meant to be modifiable.
+	// They should not be declared final.
+	// Read-only snapshots of these will be made in RestServletContext.
+	//---------------------------------------------------------------------------
+
+	ObjectMap properties;
+	ConfigFile configFile;
+	VarResolverBuilder varResolverBuilder;
+
+	List<Class<?>>
+		beanFilters = new ArrayList<Class<?>>(),
+		pojoSwaps = new ArrayList<Class<?>>();
+	SerializerGroupBuilder serializers = new SerializerGroupBuilder();
+	ParserGroupBuilder parsers = new ParserGroupBuilder();
+	EncoderGroupBuilder encoders = new EncoderGroupBuilder().append(IdentityEncoder.INSTANCE);
+	List<Object> converters = new ArrayList<Object>();
+	List<Object> guards = new ArrayList<Object>();
+	MimetypesFileTypeMap mimeTypes = new MimetypesFileTypeMap();
+	Map<String,String> defaultRequestHeaders = new TreeMap<String,String>(String.CASE_INSENSITIVE_ORDER);
+	Map<String,Object> defaultResponseHeaders = new LinkedHashMap<String,Object>();
+	List<Object> responseHandlers = new ArrayList<Object>();
+	List<Object> childResources = new ArrayList<Object>();
+	List<MediaType> supportedContentTypes, supportedAcceptTypes;
+	List<Object> styleSheets;
+	Object favIcon;
+	List<Object> staticFiles;
+	RestContext parentContext;
+	String path;
+	String clientVersionHeader = "X-Client-Version";
+
+	Object resourceResolver = RestResourceResolver.class;
+	Object logger = RestLogger.Normal.class;
+	Object callHandler = RestCallHandler.class;
+	Object infoProvider = RestInfoProvider.class;
+
+	/**
+	 * Constructor.
+	 * @param config The servlet config passed into the servlet by the servlet container.
+	 * @param resource The class annotated with <ja>@RestResource</ja>.
+	 * @throws ServletException Something bad happened.
+	 */
+	RestConfig(ServletConfig config, Class<?> resourceClass, RestContext parentContext) throws ServletException {
+		this.inner = config;
+		this.parentContext = parentContext;
+		try {
+
+			properties = new ObjectMap();
+			configFile = ConfigMgr.DEFAULT.create();
+			varResolverBuilder = new VarResolverBuilder()
+				.vars(
+					SystemPropertiesVar.class,
+					EnvVariablesVar.class,
+					ConfigFileVar.class,
+					IfVar.class,
+					SwitchVar.class
+				);
+
+			VarResolver vr = varResolverBuilder.build();
+
+			Map<Class<?>,RestResource> restResourceAnnotationsParentFirst = ReflectionUtils.findAnnotationsMapParentFirst(RestResource.class, resourceClass);
+
+			// Find our config file.  It's the last non-empty @RestResource.config().
+			String configPath = "";
+			for (RestResource r : restResourceAnnotationsParentFirst.values())
+				if (! r.config().isEmpty())
+					configPath = r.config();
+			String cf = vr.resolve(configPath);
+			if (! cf.isEmpty())
+				configFile = ConfigMgr.DEFAULT.get(cf);
+			configFile = configFile.getResolving(vr);
+
+			// Add our config file to the variable resolver.
+			varResolverBuilder.contextObject(ConfigFileVar.SESSION_config, configFile);
+			vr = varResolverBuilder.build();
+
+			// Add our servlet URI to our properties.
+			ServletContext ctx = config.getServletContext();
+			String ctxPath = ctx.getContextPath();
+			// Workaround for bug in Jetty that causes context path to always end in "null".
+			if (ctxPath.endsWith("null"))
+				ctxPath = ctxPath.substring(0, ctxPath.length()-4);
+			properties.put(SERIALIZER_relativeUriBase, ctxPath);
+
+			// Add the servlet init parameters to our properties.
+			for (Enumeration<String> ep = config.getInitParameterNames(); ep.hasMoreElements();) {
+				String p = ep.nextElement();
+				String initParam = config.getInitParameter(p);
+				properties.put(vr.resolve(p), vr.resolve(initParam));
+			}
+
+			// Load stuff from parent-to-child order.
+			// This allows child settings to overwrite parent settings.
+			for (Map.Entry<Class<?>,RestResource> e : restResourceAnnotationsParentFirst.entrySet()) {
+				Class<?> c = e.getKey();
+				RestResource r = e.getValue();
+				for (Property p : r.properties())
+					properties.append(vr.resolve(p.name()), vr.resolve(p.value()));
+				addSerializers(reverse(r.serializers()));  // TODO - why reverse?
+				addParsers(reverse(r.parsers()));  // TODO - why reverse?
+				addEncoders(reverse(r.encoders()));  // TODO - why reverse?
+				addDefaultRequestHeaders(r.defaultRequestHeaders());
+				addDefaultResponseHeaders(r.defaultResponseHeaders());
+				addResponseHandlers(r.responseHandlers());
+				addConverters(r.converters());
+				addGuards(reverse(r.guards()));
+				addChildResources(r.children());
+				addBeanFilters(r.beanFilters());
+				addPojoSwaps(r.pojoSwaps());
+				if (! r.stylesheet().isEmpty())
+					setStyleSheet(c, r.stylesheet());
+				if (! r.favicon().isEmpty())
+					setFavIcon(c, r.favicon());
+				if (! r.staticFiles().isEmpty())
+					addStaticFiles(c, r.staticFiles());
+				if (! r.path().isEmpty())
+					setPath(r.path());
+				if (! r.clientVersionHeader().isEmpty())
+					setClientVersionHeader(r.clientVersionHeader());
+				if (r.resourceResolver() != RestResourceResolver.class)
+					setResourceResolver(r.resourceResolver());
+				if (r.logger() != RestLogger.Normal.class)
+					setLogger(r.logger());
+				if (r.callHandler() != RestCallHandler.class)
+					setCallHandler(r.callHandler());
+				if (r.infoProvider() != RestInfoProvider.class)
+					setInfoProvider(r.infoProvider());
+			}
+
+			addResponseHandlers(
+				StreamableHandler.class,
+				WritableHandler.class,
+				ReaderHandler.class,
+				InputStreamHandler.class,
+				RedirectHandler.class,
+				DefaultHandler.class
+			);
+
+			addMimeTypes(
+				"text/css css CSS",
+				"text/html html htm HTML",
+				"text/plain txt text TXT",
+				"application/javascript js",
+				"image/png png",
+				"image/gif gif",
+				"application/xml xml XML",
+				"application/json json JSON"
+			);
+		} catch (Exception e) {
+			throw new ServletException(e);
+		}
+	}
+
+	/**
+	 * Adds the specified {@link Var} classes to this config.
+	 * <p>
+	 * These variables affect the variable resolver returned by {@link RestRequest#getVarResolverSession()} which is
+	 * used to resolve string variables of the form <js>"$X{...}"</js>.
+	 * <p>
+	 * By default, this config includes the following variables:
+	 * <ul class='spaced-list'>
+	 * 	<li>{@link SystemPropertiesVar}
+	 * 	<li>{@link EnvVariablesVar}
+	 * 	<li>{@link ConfigFileVar}
+	 * 	<li>{@link IfVar}
+	 * 	<li>{@link SwitchVar}
+	 * </ul>
+	 * <p>
+	 * Later during the construction of {@link RestContext}, we add the following variables:
+	 * <ul>
+	 * 	<li>{@link LocalizationVar}
+	 * 	<li>{@link RequestVar}
+	 * 	<li>{@link SerializedRequestAttrVar}
+	 * 	<li>{@link ServletInitParamVar}
+	 * 	<li>{@link UrlEncodeVar}
+	 * </ul>
+	 *
+	 * @param vars The {@link Var} classes to add to this config.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig addVars(Class<?>...vars) {
+		this.varResolverBuilder.vars(vars);
+		return this;
+	}
+
+	/**
+	 * Adds a var context object to this config.
+	 * <p>
+	 * Var context objects are read-only objects associated with the variable resolver for
+	 * vars that require external information.
+	 * <p>
+	 * For example, the {@link ConfigFileVar} needs access to this resource's {@link ConfigFile} through the {@link ConfigFileVar#SESSION_config}
+	 * object that can be specified as either a session object (temporary) or context object (permanent).
+	 * In this case, we call the following code to add it to the context map:
+	 * <p class='bcode'>
+	 * 	config.addVarContextObject(<jsf>SESSION_config</jsf>, configFile);
+	 * </p>
+	 *
+	 * @param name The context object key (i.e. the name that the Var class looks for).
+	 * @param object The context object.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig addVarContextObject(String name, Object object) {
+		this.varResolverBuilder.contextObject(name, object);
+		return this;
+	}
+
+	/**
+	 * Overwrites the default config file with a custom config file.
+	 * <p>
+	 * By default, the config file is determined using the {@link RestResource#config() @RestResource.config()} annotation.
+	 * This method allows you to programmatically override it with your own custom config file.
+	 *
+	 * @param configFile The new config file.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig setConfigFile(ConfigFile configFile) {
+		this.configFile = configFile;
+		return this;
+	}
+
+	/**
+	 * Sets a property on this resource.
+	 * <p>
+	 * This is the programmatic equivalent to the {@link RestResource#properties()} annotation.
+	 *
+	 * @param key The property name.
+	 * @param value The property value.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig setProperty(String key, Object value) {
+		this.properties.put(key, value);
+		return this;
+	}
+
+	/**
+	 * Sets multiple properties on this resource.
+	 * <p>
+	 * This is the programmatic equivalent to the {@link RestResource#properties() @RestResource.properties()} annotation.
+	 * <p>
+	 * Values in the map are added to the existing properties and are overwritten if duplicates are found.
+	 *
+	 * @param properties The new properties to add to this config.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig setProperties(Map<String,Object> properties) {
+		this.properties.putAll(properties);
+		return this;
+	}
+
+	/**
+	 * Adds class-level bean filters to this resource.
+	 * <p>
+	 * This is the programmatic equivalent to the {@link RestResource#beanFilters() @RestResource.beanFilters()} annotation.
+	 * <p>
+	 * Values are added AFTER those found in the annotation and therefore take precedence over those defined via the annotation.
+	 *
+	 * @param beanFilters The bean filters to add to this config.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig addBeanFilters(Class<?>...beanFilters) {
+		this.beanFilters.addAll(Arrays.asList(beanFilters));
+		return this;
+	}
+
+	/**
+	 * Adds class-level pojo swaps to this resource.
+	 * <p>
+	 * This is the programmatic equivalent to the {@link RestResource#pojoSwaps() @RestResource.pojoSwaps()} annotation.
+	 * <p>
+	 * Values are added AFTER those found in the annotation and therefore take precedence over those defined via the annotation.
+	 *
+	 * @param pojoSwaps The pojo swaps to add to this config.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig addPojoSwaps(Class<?>...pojoSwaps) {
+		this.pojoSwaps.addAll(Arrays.asList(pojoSwaps));
+		return this;
+	}
+
+	/**
+	 * Adds class-level serializers to this resource.
+	 * <p>
+	 * This is the programmatic equivalent to the {@link RestResource#serializers() @RestResource.serializers()} annotation.
+	 * <p>
+	 * Values are added AFTER those found in the annotation and therefore take precedence over those defined via the annotation.
+	 *
+	 * @param serializers The serializer classes to add to this config.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig addSerializers(Class<?>...serializers) {
+		this.serializers.append(serializers);
+		return this;
+	}
+
+	/**
+	 * Adds class-level serializers to this resource.
+	 * <p>
+	 * Same as {@link #addSerializers(Class...)} except allows you to pass in serializer instances.
+	 * The actual serializer ends up being the result of this operation using the bean filters, pojo swaps, and properties on this config:
+	 * <p class='bcode'>
+	 * 	serializer = serializer.builder().beanFilters(beanFilters).pojoSwaps(pojoSwaps).properties(properties).build();
+	 * </p>
+	 * <p>
+	 * Values are added AFTER those found in the annotation and therefore take precedence over those defined via the annotation.
+	 *
+	 * @param serializers The serializers to add to this config.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig addSerializers(Serializer...serializers) {
+		this.serializers.append(serializers);
+		return this;
+	}
+
+	/**
+	 * Adds class-level parsers to this resource.
+	 * <p>
+	 * This is the programmatic equivalent to the {@link RestResource#parsers() @RestResource.parsers()} annotation.
+	 * <p>
+	 * Values are added AFTER those found in the annotation and therefore take precedence over those defined via the annotation.
+	 *
+	 * @param parsers The parser classes to add to this config.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig addParsers(Class<?>...parsers) {
+		this.parsers.append(parsers);
+		return this;
+	}
+
+	/**
+	 * Adds class-level parsers to this resource.
+	 * <p>
+	 * Same as {@link #addParsers(Class...)} except allows you to pass in parser instances.
+	 * The actual parser ends up being the result of this operation using the bean filters, pojo swaps, and properties on this config:
+	 * <p class='bcode'>
+	 * 	parser = parser.builder().beanFilters(beanFilters).pojoSwaps(pojoSwaps).properties(properties).build();
+	 * </p>
+	 * <p>
+	 * Values are added AFTER those found in the annotation and therefore take precedence over those defined via the annotation.
+	 *
+	 * @param parsers The parsers to add to this config.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig addParsers(Parser...parsers) {
+		this.parsers.append(parsers);
+		return this;
+	}
+
+	/**
+	 * Adds class-level encoders to this resource.
+	 * <p>
+	 * This is the programmatic equivalent to the {@link RestResource#encoders() @RestResource.encoders()} annotation.
+	 * <p>
+	 * Values are added AFTER those found in the annotation and therefore take precedence over those defined via the annotation.
+	 * <p>
+	 * By default, only the {@link IdentityEncoder} is included in this list.
+	 *
+	 * @param encoders The parser classes to add to this config.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig addEncoders(Class<?>...encoders) {
+		this.encoders.append(encoders);
+		return this;
+	}
+
+	/**
+	 * Adds class-level encoders to this resource.
+	 * <p>
+	 * Same as {@link #addEncoders(Class...)} except allows you to pass in encoder instances.
+	 *
+	 * @param encoders The encoders to add to this config.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig addEncoders(Encoder...encoders) {
+		this.encoders.append(encoders);
+		return this;
+	}
+
+	/**
+	 * Adds class-level converters to this resource.
+	 * <p>
+	 * This is the programmatic equivalent to the {@link RestResource#converters() @RestResource.converters()} annotation.
+	 * <p>
+	 * Values are added AFTER those found in the annotation and therefore take precedence over those defined via the annotation.
+	 * <p>
+	 * By default, this config includes the following converters:
+	 * <ul class='spaced-list'>
+	 * 	<li>{@link StreamableHandler}
+	 * 	<li>{@link WritableHandler}
+	 * 	<li>{@link ReaderHandler}
+	 * 	<li>{@link InputStreamHandler}
+	 * 	<li>{@link RedirectHandler}
+	 * 	<li>{@link DefaultHandler}
+	 * </ul>
+	 *
+	 * @param converters The converter classes to add to this config.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig addConverters(Class<?>...converters) {
+		this.converters.addAll(Arrays.asList(converters));
+		return this;
+	}
+
+	/**
+	 * Adds class-level encoders to this resource.
+	 * <p>
+	 * Same as {@link #addConverters(Class...)} except allows you to pass in converter instances.
+	 *
+	 * @param converters The converters to add to this config.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig addConverters(RestConverter...converters) {
+		this.converters.addAll(Arrays.asList(converters));
+		return this;
+	}
+
+	/**
+	 * Adds class-level guards to this resource.
+	 * <p>
+	 * This is the programmatic equivalent to the {@link RestResource#guards() @RestResource.guards()} annotation.
+	 * <p>
+	 * Values are added AFTER those found in the annotation and therefore take precedence over those defined via the annotation.
+	 *
+	 * @param guards The guard classes to add to this config.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig addGuards(Class<?>...guards) {
+		this.guards.addAll(Arrays.asList(guards));
+		return this;
+	}
+
+	/**
+	 * Adds class-level guards to this resource.
+	 * <p>
+	 * Same as {@link #addGuards(Class...)} except allows you to pass in guard instances.
+	 *
+	 * @param guards The guards to add to this config.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig addGuards(RestGuard...guards) {
+		this.guards.addAll(Arrays.asList(guards));
+		return this;
+	}
+
+	/**
+	 * Adds MIME-type definitions.
+	 * <p>
+	 * These definitions are used in the following locations for setting the media type on responses:
+	 * <ul>
+	 * 	<li>{@link RestRequest#getReaderResource(String)}
+	 * 	<li>Static files resolved through {@link RestResource#staticFiles()}
+	 * </ul>
+	 * <p>
+	 * Refer to {@link MimetypesFileTypeMap#addMimeTypes(String)} for an explanation of the format.
+	 * <p>
+	 * By default, this config includes the following mime-type definitions:
+	 * <ul class='spaced-list'>
+	 * 	<li><js>"text/css css CSS"</js>
+	 * 	<li><js>"text/html html htm HTML"</js>
+	 * 	<li><js>"text/plain txt text TXT"</js>
+	 * 	<li><js>"application/javascript js"</js>
+	 * 	<li><js>"image/png png"</js>
+	 * 	<li><js>"image/gif gif"</js>
+	 * 	<li><js>"application/xml xml XML"</js>
+	 * 	<li><js>"application/json json JSON"</js>
+	 * </ul>
+	 *
+	 * @param mimeTypes The MIME-types to add to this config.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig addMimeTypes(String...mimeTypes) {
+		for (String mimeType : mimeTypes)
+			this.mimeTypes.addMimeTypes(mimeType);
+		return this;
+	}
+
+	/**
+	 * Adds class-level default HTTP request headers to this resource.
+	 * <p>
+	 * Default request headers are default values for when HTTP requests do not specify a header value.
+	 * For example, you can specify a default value for <code>Accept</code> if a request does not specify that header value.
+	 * <p>
+	 * This is the programmatic equivalent to the {@link RestResource#defaultRequestHeaders() @RestResource.defaultRequestHeaders()} annotation.
+	 *
+	 * @param name The HTTP header name.
+	 * @param value The HTTP header value.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig addDefaultRequestHeader(String name, Object value) {
+		this.defaultRequestHeaders.put(name, StringUtils.toString(value));
+		return this;
+	}
+
+	/**
+	 * Adds class-level default HTTP request headers to this resource.
+	 * <p>
+	 * Default request headers are default values for when HTTP requests do not specify a header value.
+	 * For example, you can specify a default value for <code>Accept</code> if a request does not specify that header value.
+	 * <p>
+	 * This is the programmatic equivalent to the {@link RestResource#defaultRequestHeaders() @RestResource.defaultRequestHeaders()} annotation.
+	 *
+	 * @param headers HTTP headers of the form <js>"Name: Value"</js>.
+	 * @return This object (for method chaining).
+	 * @throws RestServletException If header string is not correctly formatted.
+	 */
+	public RestConfig addDefaultRequestHeaders(String...headers) throws RestServletException {
+		for (String header : headers) {
+			String[] h = RestUtils.parseHeader(header);
+			if (h == null)
+				throw new RestServletException("Invalid default request header specified: ''{0}''.  Must be in the format: ''Header-Name: header-value''", header);
+			addDefaultRequestHeader(h[0], h[1]);
+		}
+		return this;
+	}
+
+	/**
+	 * Adds class-level default HTTP response headers to this resource.
+	 * <p>
+	 * Default response headers are headers that will be appended to all responses if those headers have not already been
+	 * 	set on the response object.
+	 * <p>
+	 * This is the programmatic equivalent to the {@link RestResource#defaultResponseHeaders() @RestResource.defaultResponseHeaders()} annotation.
+	 * <p>
+	 * Values are added AFTER those found in the annotation and therefore take precedence over those defined via the annotation.
+	 *
+	 * @param name The HTTP header name.
+	 * @param value The HTTP header value.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig addDefaultResponseHeader(String name, Object value) {
+		this.defaultResponseHeaders.put(name, value);
+		return this;
+	}
+
+	/**
+	 * Adds class-level default HTTP response headers to this resource.
+	 * <p>
+	 * Default response headers are headers that will be appended to all responses if those headers have not already been
+	 * 	set on the response object.
+	 * <p>
+	 * This is the programmatic equivalent to the {@link RestResource#defaultResponseHeaders() @RestResource.defaultResponseHeaders()} annotation.
+	 *
+	 * @param headers HTTP headers of the form <js>"Name: Value"</js>.
+	 * @return This object (for method chaining).
+	 * @throws RestServletException If header string is not correctly formatted.
+	 */
+	public RestConfig addDefaultResponseHeaders(String...headers) throws RestServletException {
+		for (String header : headers) {
+			String[] h = RestUtils.parseHeader(header);
+			if (h == null)
+				throw new RestServletException("Invalid default response header specified: ''{0}''.  Must be in the format: ''Header-Name: header-value''", header);
+			addDefaultResponseHeader(h[0], h[1]);
+		}
+		return this;
+	}
+
+	/**
+	 * Adds class-level response handler classes to this resource.
+	 * <p>
+	 * Response handlers are responsible for converting various POJOs returned by REST methods into actual HTTP responses.
+	 * <p>
+	 * By default, this config includes the following response handlers:
+	 * <ul class='spaced-list'>
+	 * 	<li>{@link StreamableHandler}
+	 * 	<li>{@link WritableHandler}
+	 * 	<li>{@link ReaderHandler}
+	 * 	<li>{@link InputStreamHandler}
+	 * 	<li>{@link RedirectHandler}
+	 * 	<li>{@link DefaultHandler}
+	 * </ul>
+	 * <p>
+	 * This is the programmatic equivalent to the {@link RestResource#responseHandlers() @RestResource.responseHandlers()} annotation.
+	 *
+	 * @param responseHandlers The response handlers to add to this config.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig addResponseHandlers(Class<?>...responseHandlers) {
+		this.responseHandlers.addAll(Arrays.asList(responseHandlers));
+		return this;
+	}
+
+	/**
+	 * Adds class-level response handlers to this resource.
+	 * <p>
+	 * Same as {@link #addResponseHandlers(Class...)} except allows you to pass in response handler instances.
+	 *
+	 * @param responseHandlers The response handlers to add to this config.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig addResponseHandlers(ResponseHandler...responseHandlers) {
+		this.responseHandlers.addAll(Arrays.asList(responseHandlers));
+		return this;
+	}
+
+	/**
+	 * Adds a child resource to this resource.
+	 * <p>
+	 * Child resources are resources that are accessed under the path of the parent resource.
+	 * <p>
+	 * This is the programmatic equivalent to the {@link RestResource#children() @RestResource.children()} annotation.
+	 *
+	 * @param path The child path of the resource.  Must conform to {@link RestResource#path()} format.
+	 * @param child The child resource.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig addChildResource(String path, Object child) {
+		this.childResources.add(new Pair<String,Object>(path, child));
+		return this;
+	}
+
+	/**
+	 * Add child resources to this resource.
+	 * <p>
+	 * Child resources are resources that are accessed under the path of the parent resource.
+	 * <p>
+	 * This is the programmatic equivalent to the {@link RestResource#children() @RestResource.children()} annotation.
+	 *
+	 * @param children The child resources to add to this resource.
+	 * Children must be annotated with {@link RestResource#path()} to identify the child path.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig addChildResources(Object...children) {
+		this.childResources.addAll(Arrays.asList(children));
+		return this;
+	}
+
+	/**
+	 * Add child resources to this resource.
+	 * <p>
+	 * Child resources are resources that are accessed under the path of the parent resource.
+	 * <p>
+	 * This is the programmatic equivalent to the {@link RestResource#children() @RestResource.children()} annotation.
+	 *
+	 * @param children The child resources to add to this resource.
+	 * Children must be annotated with {@link RestResource#path()} to identify the child path.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig addChildResources(Class<?>...children) {
+		this.childResources.addAll(Arrays.asList(children));
+		return this;
+	}
+
+	/**
+	 * Specifies the list of supported <code>Accept</code> media types for this resource.
+	 * <p>
+	 * This overrides the media types inferred from the parsers on this resource.
+	 * <p>
+	 * There is no annotation equivalent to this method call.
+	 *
+	 * @param mediaTypes The new list of media types supported by this resource.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig setSupportedAcceptTypes(String...mediaTypes) {
+		supportedAcceptTypes = new ArrayList<MediaType>();
+		for (String mediaType : mediaTypes)
+			supportedAcceptTypes.add(MediaType.forString(mediaType));
+		return this;
+	}
+
+	/**
+	 * Specifies the list of supported <code>Accept</code> media types for this resource.
+	 * <p>
+	 * This overrides the media types inferred from the parsers on this resource.
+	 * <p>
+	 * There is no annotation equivalent to this method call.
+	 *
+	 * @param mediaTypes The new list of media types supported by this resource.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig setSupportedAcceptTypes(MediaType...mediaTypes) {
+		supportedAcceptTypes = Arrays.asList(mediaTypes);
+		return this;
+	}
+
+	/**
+	 * Specifies the list of supported <code>Content-Type</code> media types for this resource.
+	 * <p>
+	 * This overrides the media types inferred from the serializers on this resource.
+	 * <p>
+	 * There is no annotation equivalent to this method call.
+	 *
+	 * @param mediaTypes The new list of media types supported by this resource.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig setSupportedContentTypes(String...mediaTypes) {
+		supportedContentTypes = new ArrayList<MediaType>();
+		for (String mediaType : mediaTypes)
+			supportedContentTypes.add(MediaType.forString(mediaType));
+		return this;
+	}
+
+	/**
+	 * Specifies the list of supported <code>Content-Type</code> media types for this resource.
+	 * <p>
+	 * This overrides the media types inferred from the serializers on this resource.
+	 * <p>
+	 * There is no annotation equivalent to this method call.
+	 *
+	 * @param mediaTypes The new list of media types supported by this resource.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig setSupportedContentTypes(MediaType...mediaTypes) {
+		supportedContentTypes = Arrays.asList(mediaTypes);
+		return this;
+	}
+
+	/**
+	 * Specifies the stylesheets that make up the contents of the page <js>"/resource-path/styles.css"</js>.
+	 * <p>
+	 * This is the programmatic equivalent to the {@link RestResource#stylesheet() @RestResource.stylesheet()} annotation.
+	 * <p>
+	 * The object types can be any of the following:
+	 * <ul>
+	 * 	<li>{@link InputStream}
+	 * 	<li>{@link Reader}
+	 * 	<li>{@link File}
+	 * 	<li>{@link CharSequence}
+	 * 	<li><code><jk>byte</jk>[]</code>
+	 * </ul>
+	 * The contents of all these stylesheets will be aggregated into a single page in the order they are specified in this list.
+	 *
+	 * @param styleSheets The new list of style sheets that make up the <code>styles.css</code> page.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig setStyleSheet(Object...styleSheets) {
+		this.styleSheets = new ArrayList<Object>(Arrays.asList(styleSheets));
+		return this;
+	}
+
+	/**
+	 * Specifies the stylesheet that make up the contents of the page <js>"/resource-path/styles.css"</js>.
+	 * <p>
+	 * This is the programmatic equivalent to the {@link RestResource#stylesheet() @RestResource.stylesheet()} annotation.
+	 * <p>
+	 * Use this method to specify a resource located in the classpath.
+	 * This call uses the {@link Class#getResourceAsStream(String)} method to retrieve the stylesheet contents.
+	 *
+	 * @param resourceClass The resource class used to resolve the resource stream.
+	 * @param resourcePath The path passed to the {@link Class#getResourceAsStream(String)} method.
+	 * Can also be a path starting with <js>"file://"</js> denoting a location to pull from the file system.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig setStyleSheet(Class<?> resourceClass, String resourcePath) {
+		this.styleSheets = new ArrayList<Object>();
+		this.styleSheets.add(new Pair<Class<?>,String>(resourceClass, resourcePath));
+		return this;
+	}
+
+	/**
+	 * Adds to the stylesheet that make up the contents of the page <js>"/resource-path/styles.css"</js>.
+	 * <p>
+	 * Same as {@link #setStyleSheet(Object...)} except appends to the existing list instead of replacing.
+	 *
+	 * @param styleSheets The list of style sheets to add that make up the <code>styles.css</code> page.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig addStyleSheet(Object...styleSheets) {
+		if (this.styleSheets == null)
+			this.styleSheets = new ArrayList<Object>();
+		this.styleSheets.addAll(Arrays.asList(styleSheets));
+		return this;
+	}
+
+	/**
+	 * Adds to the stylesheet that make up the contents of the page <js>"/resource-path/styles.css"</js>.
+	 * <p>
+	 * Same as {@link #setStyleSheet(Class,String)} except appends to the existing list instead of replacing.
+	 *
+	 * @param resourceClass The resource class used to resolve the resource stream.
+	 * @param resourcePath The path passed to the {@link Class#getResourceAsStream(String)} method.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig addStyleSheet(Class<?> resourceClass, String resourcePath) {
+		if (this.styleSheets == null)
+			this.styleSheets = new ArrayList<Object>();
+		this.styleSheets.add(new Pair<Class<?>,String>(resourceClass, resourcePath));
+		return this;
+	}
+
+	/**
+	 * Specifies the icon contents that make up the contents of the page <js>"/resource-path/favicon.ico"</js>.
+	 * <p>
+	 * This is the programmatic equivalent to the {@link RestResource#favicon() @RestResource.favicon()} annotation.
+	 * <p>
+	 * The object type can be any of the following:
+	 * <ul>
+	 * 	<li>{@link InputStream}
+	 * 	<li>{@link File}
+	 * 	<li><code><jk>byte</jk>[]</code>
+	 * </ul>
+	 *
+	 * @param favIcon The contents that make up the <code>favicon.ico</code> page.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig setFavIcon(Object favIcon) {
+		this.favIcon = favIcon;
+		return this;
+	}
+
+	/**
+	 * Specifies the icon contents that make up the contents of the page <js>"/resource-path/favicon.ico"</js>.
+	 * <p>
+	 * This is the programmatic equivalent to the {@link RestResource#favicon() @RestResource.favicon()} annotation.
+	 * <p>
+	 * Use this method to specify a resource located in the classpath.
+	 * This call uses the {@link Class#getResourceAsStream(String)} method to retrieve the stylesheet contents.
+	 *
+	 * @param resourceClass The resource class used to resolve the resource stream.
+	 * @param resourcePath The path passed to the {@link Class#getResourceAsStream(String)} method.
+	 * Can also be a path starting with <js>"file://"</js> denoting a location to pull from the file system.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig setFavIcon(Class<?> resourceClass, String resourcePath) {
+		this.favIcon = new Pair<Class<?>,String>(resourceClass, resourcePath);
+		return this;
+	}
+
+	/**
+	 * Appends to the static files resource map.
+	 * <p>
+	 * Use this method to specify resources located in the classpath to be served up as static files.
+	 * <p>
+	 * This is the programmatic equivalent to the {@link RestResource#staticFiles() @RestResource.staticFiles()} annotation.
+	 *
+	 * @param resourceClass The resource class used to resolve the resource streams.
+	 * @param staticFilesString A JSON string denoting a map of child URLs to classpath subdirectories.
+	 * For example, if this string is <js>"{htdocs:'docs'}"</js> with class <code>com.foo.MyResource</code>,
+	 * then URLs of the form <js>"/resource-path/htdocs/..."</js> will resolve to files located in the <code>com.foo.docs</code> package.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig addStaticFiles(Class<?> resourceClass, String staticFilesString) {
+		if (staticFiles == null)
+			staticFiles = new ArrayList<Object>();
+		staticFiles.add(new Pair<Class<?>,Object>(resourceClass, staticFilesString));
+		return this;
+	}
+
+	/**
+	 * Overrides the default REST resource resolver.
+	 * <p>
+	 * The resource resolver is used to resolve instances from {@link Class} objects defined in the {@link RestResource#children()} annotation.
+	 * The default value is the base class {@link RestResourceResolver}.
+	 * <p>
+	 * This is the programmatic equivalent to the {@link RestResource#resourceResolver() @RestResource.resourceResolver()} annotation.
+	 *
+	 * @param resourceResolver The new resource resolver.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig setResourceResolver(Class<? extends RestResourceResolver> resourceResolver) {
+		this.resourceResolver = resourceResolver;
+		return this;
+	}
+
+	/**
+	 * Overrides the default REST resource resolver.
+	 * <p>
+	 * Same as {@link #setResourceResolver(Class)} except allows you to specify an instance instead of a class.
+	 *
+	 * @param resourceResolver The new resource resolver.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig setResourceResolver(RestResourceResolver resourceResolver) {
+		this.resourceResolver = resourceResolver;
+		return this;
+	}
+
+	/**
+	 * Sets the URL path of the resource <js>"/foobar"</js>.
+	 * <p>
+	 * This is the programmatic equivalent to the {@link RestResource#path() @RestResource.path()} annotation.
+	 *
+	 * @param path The URL path of this resource.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig setPath(String path) {
+		if (StringUtils.startsWith(path, '/'))
+			path = path.substring(1);
+		this.path = path;
+		return this;
+	}
+
+	/**
+	 * Sets name of the header used to denote the client version on HTTP requests.
+	 * <p>
+	 * This is the programmatic equivalent to the {@link RestResource#clientVersionHeader() @RestResource.clientVersionHeader()} annotation.
+	 *
+	 * @param clientVersionHeader The name of the HTTP header that denotes the client version.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig setClientVersionHeader(String clientVersionHeader) {
+		this.clientVersionHeader = clientVersionHeader;
+		return this;
+	}
+
+	/**
+	 * Overrides the logger for the resource.
+	 * <p>
+	 * This is the programmatic equivalent to the {@link RestResource#logger() @RestResource.logger()} annotation.
+	 *
+	 * @param logger The new logger for this resource.  Can be <jk>null</jk> to disable logging.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig setLogger(Class<? extends RestLogger> logger) {
+		this.logger = logger;
+		return this;
+	}
+
+	/**
+	 * Overrides the logger for the resource.
+	 * <p>
+	 * This is the programmatic equivalent to the {@link RestResource#logger() @RestResource.logger()} annotation.
+	 *
+	 * @param logger The new logger for this resource.  Can be <jk>null</jk> to disable logging.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig setLogger(RestLogger logger) {
+		this.logger = logger;
+		return this;
+	}
+
+	/**
+	 * Overrides the call handler for the resource.
+	 * <p>
+	 * The call handler is the object that handles execution of REST HTTP calls.
+	 * Subclasses can be created that customize the behavior of how REST calls are handled.
+	 * <p>
+	 * This is the programmatic equivalent to the {@link RestResource#callHandler() @RestResource.callHandler()} annotation.
+	 *
+	 * @param restHandler The new call handler for this resource.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig setCallHandler(Class<? extends RestCallHandler> restHandler) {
+		this.callHandler = restHandler;
+		return this;
+	}
+
+	/**
+	 * Overrides the call handler for the resource.
+	 * <p>
+	 * The call handler is the object that handles execution of REST HTTP calls.
+	 * Subclasses can be created that customize the behavior of how REST calls are handled.
+	 * <p>
+	 * This is the programmatic equivalent to the {@link RestResource#callHandler() @RestResource.callHandler()} annotation.
+	 *
+	 * @param restHandler The new call handler for this resource.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig setCallHandler(RestCallHandler restHandler) {
+		this.callHandler = restHandler;
+		return this;
+	}
+
+	/**
+	 * Overrides the info provider for the resource.
+	 * <p>
+	 * The info provider provides all the various information about a resource such as the Swagger documentation.
+	 * Subclasses can be created that customize the information.
+	 * <p>
+	 * This is the programmatic equivalent to the {@link RestResource#infoProvider() @RestResource.infoProvider()} annotation.
+	 *
+	 * @param infoProvider The new info provider for this resource.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig setInfoProvider(Class<? extends RestInfoProvider> infoProvider) {
+		this.infoProvider = infoProvider;
+		return this;
+	}
+
+	/**
+	 * Overrides the info provider for the resource.
+	 * <p>
+	 * The info provider provides all the various information about a resource such as the Swagger documentation.
+	 * Subclasses can be created that customize the information.
+	 * <p>
+	 * This is the programmatic equivalent to the {@link RestResource#infoProvider() @RestResource.infoProvider()} annotation.
+	 *
+	 * @param infoProvider The new info provider for this resource.
+	 * @return This object (for method chaining).
+	 */
+	public RestConfig setInfoProvider(RestInfoProvider infoProvider) {
+		this.infoProvider = infoProvider;
+		return this;
+	}
+
+	/**
+	 * Creates a new {@link PropertyStore} object initialized with the properties defined in this config.
+	 * @return A new property store.
+	 */
+	protected PropertyStore createPropertyStore() {
+		return PropertyStore.create().addProperties(properties);
+	}
+
+
+	//----------------------------------------------------------------------------------------------------
+	// Methods that give access to the config file, var resolver, and properties.
+	//----------------------------------------------------------------------------------------------------
+
+	/**
+	 * Returns the external configuration file for this resource.
+	 * <p>
+	 * The configuration file location is determined via the {@link RestResource#config() @RestResource.config()} annotation on the resource.
+	 * <p>
+	 * The config file can be programmatically overridden by adding the following method to your resource:
+	 * <p class='bcode'>
+	 * 	<jk>public</jk> ConfigFile createConfigFile(ServletConfig servletConfig) <jk>throws</jk> ServletException;
+	 * </p>
+	 * <p>
+	 * If a config file is not set up, then an empty config file will be returned that is not backed by any file.
+	 *
+	 * @return The external config file for this resource.  Never <jk>null</jk>.
+	 */
+	public ConfigFile getConfigFile() {
+		return configFile;
+	}
+
+	/**
+	 * Returns the configuration properties for this resource.
+	 * <p>
+	 * The configuration properties are determined via the {@link RestResource#properties()} annotation on the resource.
+	 * <p>
+	 * The configuration properties can be augmented programmatically by adding the following method to your resource:
+	 * <p class='bcode'>
+	 * 	<jk>public</jk> ObjectMap createProperties(ServletConfig servletConfig) <jk>throws</jk> ServletException;
+	 * </p>
+	 * <p>
+	 * These properties can be modified during servlet initialization.
+	 * However, any modifications made after {@link RestServlet#init(RestConfig)} has been called will have no effect.
+	 *
+	 * @return The configuration properties for this resource.  Never <jk>null</jk>.
+	 */
+	public ObjectMap getProperties() {
+		return properties;
+	}
+
+	/**
+	 * Creates the variable resolver for this resource.
+	 * <p>
+	 * The variable resolver returned by this method can resolve the following variables:
+	 * <ul>
+	 * 	<li>{@link SystemPropertiesVar}
+	 * 	<li>{@link EnvVariablesVar}
+	 * 	<li>{@link ConfigFileVar}
+	 * 	<li>{@link IfVar}
+	 * 	<li>{@link SwitchVar}
+	 * </ul>
+	 * <p>
+	 * Note that the variables supported here are only a subset of those returned by {@link RestRequest#getVarResolverSession()}.
+	 *
+	 * @return The variable resolver for this resource.  Never <jk>null</jk>.
+	 */
+	public VarResolverBuilder getVarResolverBuilder() {
+		return varResolverBuilder;
+	}
+
+
+	//----------------------------------------------------------------------------------------------------
+	// Methods inherited from ServletConfig
+	//----------------------------------------------------------------------------------------------------
+
+	@Override /* ServletConfig */
+	public String getInitParameter(String name) {
+		return inner.getInitParameter(name);
+	}
+
+	@Override /* ServletConfig */
+	public Enumeration<String> getInitParameterNames() {
+		return inner.getInitParameterNames();
+	}
+
+	@Override /* ServletConfig */
+	public ServletContext getServletContext() {
+		return inner.getServletContext();
+	}
+
+	@Override /* ServletConfig */
+	public String getServletName() {
+		return inner.getServletName();
+	}
+}



[7/7] incubator-juneau git commit: Allow @RestResource/@RestMethod annotations to be used on any classes.

Posted by ja...@apache.org.
Allow @RestResource/@RestMethod annotations to be used on any classes.

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

Branch: refs/heads/master
Commit: 07843d641d9911843e3c489bd96b5999de7faa84
Parents: db0cf72
Author: JamesBognar <ja...@apache.org>
Authored: Thu Mar 16 22:16:57 2017 -0400
Committer: JamesBognar <ja...@apache.org>
Committed: Thu Mar 16 22:16:57 2017 -0400

----------------------------------------------------------------------
 .../java/org/apache/juneau/jena/package.html    |    2 +-
 .../org/apache/juneau/utils/ClassUtilsTest.java |   36 +
 .../org/apache/juneau/internal/ArrayUtils.java  |   21 +-
 .../org/apache/juneau/internal/ClassUtils.java  |   90 +
 .../apache/juneau/internal/CollectionUtils.java |    2 +-
 .../org/apache/juneau/internal/IOUtils.java     |   32 +
 .../org/apache/juneau/internal/ObjectUtils.java |   36 +
 .../java/org/apache/juneau/internal/Pair.java   |   64 +
 .../apache/juneau/internal/ReflectionUtils.java |   18 +-
 .../java/org/apache/juneau/json/package.html    |    2 +-
 .../java/org/apache/juneau/uon/package.html     |    2 +-
 .../org/apache/juneau/urlencoding/package.html  |    2 +-
 .../java/org/apache/juneau/xml/package.html     |    2 +-
 juneau-core/src/main/javadoc/overview.html      |  226 +-
 .../juneau/examples/rest/DirectoryResource.java |   11 +-
 .../examples/rest/DockerRegistryResource.java   |   11 +-
 .../examples/rest/SampleRemoteableServlet.java  |    2 +-
 .../juneau/examples/rest/SqlQueryResource.java  |   23 +-
 .../rest/addressbook/AddressBookResource.java   |    4 +-
 .../apache/juneau/microservice/Resource.java    |   19 +-
 .../juneau/microservice/ResourceGroup.java      |   19 +-
 .../org/apache/juneau/microservice/package.html |   12 +-
 .../microservice/resources/ConfigResource.java  |   14 +-
 .../resources/DirectoryResource.java            |    2 +-
 .../microservice/resources/LogsResource.java    |   25 +-
 .../juneau/rest/jaxrs/JuneauProvider.java       |    2 +-
 .../juneau/rest/test/AcceptCharsetResource.java |    2 +-
 .../apache/juneau/rest/test/ConfigResource.java |    4 +-
 .../juneau/rest/test/ContentResource.java       |    2 +-
 .../apache/juneau/rest/test/GroupsResource.java |   17 +-
 .../apache/juneau/rest/test/ParamsResource.java |   10 +-
 .../apache/juneau/rest/test/PathResource.java   |    6 +-
 .../java/org/apache/juneau/rest/test/Root.java  |    2 +-
 .../juneau/rest/test/ErrorConditionsTest.java   |    2 +-
 .../org/apache/juneau/rest/test/TestUtils.java  |   14 +-
 .../java/org/apache/juneau/rest/CallMethod.java |  996 ++++++
 .../java/org/apache/juneau/rest/CallRouter.java |   98 +
 .../juneau/rest/ClientVersionMatcher.java       |   10 +-
 .../java/org/apache/juneau/rest/Redirect.java   |    3 +-
 .../org/apache/juneau/rest/ResponseHandler.java |    4 +-
 .../org/apache/juneau/rest/RestCallHandler.java |  348 ++
 .../java/org/apache/juneau/rest/RestConfig.java | 1153 ++++++
 .../org/apache/juneau/rest/RestContext.java     | 1418 ++++++++
 .../org/apache/juneau/rest/RestException.java   |    4 +-
 .../apache/juneau/rest/RestInfoProvider.java    |  499 +++
 .../java/org/apache/juneau/rest/RestLogger.java |  233 ++
 .../juneau/rest/RestMatcherReflecting.java      |    6 +-
 .../org/apache/juneau/rest/RestRequest.java     |   84 +-
 .../juneau/rest/RestResourceResolver.java       |   80 +
 .../org/apache/juneau/rest/RestResponse.java    |   13 +-
 .../org/apache/juneau/rest/RestServlet.java     | 3321 +-----------------
 .../apache/juneau/rest/RestServletContext.java  |  306 --
 .../apache/juneau/rest/RestServletDefault.java  |    7 +-
 .../juneau/rest/RestServletGroupDefault.java    |    2 +-
 .../java/org/apache/juneau/rest/RestUtils.java  |    9 +
 .../apache/juneau/rest/annotation/FormData.java |    2 +-
 .../apache/juneau/rest/annotation/Query.java    |    2 +-
 .../juneau/rest/annotation/RestMethod.java      |    5 +
 .../juneau/rest/annotation/RestResource.java    |  152 +-
 .../rest/jena/RestServletJenaDefault.java       |    7 +-
 .../rest/jena/RestServletJenaGroupDefault.java  |    2 +-
 .../rest/labels/ChildResourceDescriptions.java  |   14 +-
 .../java/org/apache/juneau/rest/package.html    |  125 +-
 .../rest/remoteable/RemoteableServlet.java      |    4 +-
 .../juneau/rest/response/DefaultHandler.java    |    3 -
 .../rest/response/InputStreamHandler.java       |    3 -
 .../juneau/rest/response/ReaderHandler.java     |    3 -
 .../juneau/rest/response/RedirectHandler.java   |    3 -
 .../juneau/rest/response/StreamableHandler.java |    3 -
 .../juneau/rest/response/WritableHandler.java   |    3 -
 .../juneau/rest/vars/ServletInitParamVar.java   |    2 +-
 71 files changed, 5775 insertions(+), 3890 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-core-rdf/src/main/java/org/apache/juneau/jena/package.html
----------------------------------------------------------------------
diff --git a/juneau-core-rdf/src/main/java/org/apache/juneau/jena/package.html b/juneau-core-rdf/src/main/java/org/apache/juneau/jena/package.html
index c29250c..5f1d7aa 100644
--- a/juneau-core-rdf/src/main/java/org/apache/juneau/jena/package.html
+++ b/juneau-core-rdf/src/main/java/org/apache/juneau/jena/package.html
@@ -1083,7 +1083,7 @@
  				The <code>$L{...}</code> variable represent localized strings pulled from the resource bundle identified by the <code>messages</code> annotation.
  				These variables are replaced at runtime based on the HTTP request locale.
 				Several built-in runtime variable types are defined, and the API can be extended to include user-defined variables.
-				See {@link org.apache.juneau.rest.RestServlet#getVarResolver()} for more information.
+				See {@link org.apache.juneau.rest.RestContext#getVarResolver()} for more information.
 			</p>
 			<p>
 				This document won't go into all the details of the Juneau <code>RestServlet</code> class.<br>

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-core-test/src/test/java/org/apache/juneau/utils/ClassUtilsTest.java
----------------------------------------------------------------------
diff --git a/juneau-core-test/src/test/java/org/apache/juneau/utils/ClassUtilsTest.java b/juneau-core-test/src/test/java/org/apache/juneau/utils/ClassUtilsTest.java
index ce2d30e..688c2bc 100755
--- a/juneau-core-test/src/test/java/org/apache/juneau/utils/ClassUtilsTest.java
+++ b/juneau-core-test/src/test/java/org/apache/juneau/utils/ClassUtilsTest.java
@@ -112,4 +112,40 @@ public class ClassUtilsTest {
 	public void getClassFromReadableName() throws Exception {
 		fail("Not implemented");
 	}
+	
+	//====================================================================================================
+	// findPublicMethod
+	//====================================================================================================
+	@Test
+	public void testFindPublicMethod() {
+		
+		assertNotNull(findPublicMethod(B.class, "m1", void.class));
+		assertNull(findPublicMethod(B.class, "m1", int.class));
+
+		assertNull(findPublicMethod(B.class, "m2", void.class));
+
+		assertNull(findPublicMethod(B.class, "m3", void.class));
+		assertNotNull(findPublicMethod(B.class, "m3", int.class));
+		
+		assertNotNull(findPublicMethod(B.class, "m4", CharSequence.class));
+		assertNotNull(findPublicMethod(B.class, "m4", Object.class));
+		assertNull(findPublicMethod(B.class, "m4", String.class));
+		
+		assertNotNull(findPublicMethod(B.class, "m5", void.class, int.class, CharSequence.class));
+		assertNotNull(findPublicMethod(B.class, "m5", void.class, int.class, String.class));
+		assertNull(findPublicMethod(B.class, "m5", void.class, int.class, Object.class));
+		
+		assertNull(findPublicMethod(B.class, "m5", void.class, int.class));
+		assertNull(findPublicMethod(B.class, "m5", void.class, int.class, CharSequence.class, CharSequence.class));
+	}
+	
+	public static class B {
+		
+		public void m1() {};
+		protected void m2() {};
+		public int m3() { return 0; }
+		public CharSequence m4() { return ""; }
+
+		public void m5(int f1, CharSequence f2) {}
+	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-core/src/main/java/org/apache/juneau/internal/ArrayUtils.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/internal/ArrayUtils.java b/juneau-core/src/main/java/org/apache/juneau/internal/ArrayUtils.java
index 12c7614..effd363 100644
--- a/juneau-core/src/main/java/org/apache/juneau/internal/ArrayUtils.java
+++ b/juneau-core/src/main/java/org/apache/juneau/internal/ArrayUtils.java
@@ -195,9 +195,9 @@ public final class ArrayUtils {
 	 * @param componentType The component type of the collection.
 	 * @return A new array.
 	 */
-	public static <T> Object toArray(Collection<T> c, Class<T> componentType) {
+	public static <T> Object toArray(Collection<?> c, Class<T> componentType) {
 		Object a = Array.newInstance(componentType, c.size());
-		Iterator<T> it = c.iterator();
+		Iterator<?> it = c.iterator();
 		int i = 0;
 		while (it.hasNext())
 			Array.set(a, i++, it.next());
@@ -205,6 +205,23 @@ public final class ArrayUtils {
 	}
 
 	/**
+	 * Shortcut for calling <code>myList.toArray(new T[myList.size()]);</code>
+	 *
+	 * @param c The collection being converted to an array.
+	 * @param componentType The component type of the array.
+	 * @return The collection converted to an array.
+	 */
+	@SuppressWarnings("unchecked")
+	public static <T> T[] toObjectArray(Collection<?> c, Class<T> componentType) {
+		Object a = Array.newInstance(componentType, c.size());
+		Iterator<?> it = c.iterator();
+		int i = 0;
+		while (it.hasNext())
+			Array.set(a, i++, it.next());
+		return (T[])a;
+	}
+
+	/**
 	 * Copies the specified array into the specified list.
 	 * Works on both object and primitive arrays.
 	 *

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-core/src/main/java/org/apache/juneau/internal/ClassUtils.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/internal/ClassUtils.java b/juneau-core/src/main/java/org/apache/juneau/internal/ClassUtils.java
index 088d70a..4ba239f 100644
--- a/juneau-core/src/main/java/org/apache/juneau/internal/ClassUtils.java
+++ b/juneau-core/src/main/java/org/apache/juneau/internal/ClassUtils.java
@@ -426,6 +426,96 @@ public final class ClassUtils {
 		}
 	}
 
+	/**
+	 * Finds a public method with the specified parameters.
+	 *
+	 * @param c The class to look for the method.
+	 * @param name The method name.
+	 * @param returnType The return type of the method.
+	 * Can be a super type of the actual return type.
+	 * For example, if the actual return type is <code>CharSequence</code>, then <code>Object</code> will match but <code>String</code> will not.
+	 * @param parameterTypes The parameter types of the method.
+	 * Can be subtypes of the actual parameter types.
+	 * For example, if the parameter type is <code>CharSequence</code>, then <code>String</code> will match but <code>Object</code> will not.
+	 * @return The matched method, or <jk>null</jk> if no match was found.
+	 */
+	public static Method findPublicMethod(Class<?> c, String name, Class<?> returnType, Class<?>...parameterTypes) {
+		for (Method m : c.getMethods()) {
+			if (isPublic(m) && m.getName().equals(name)) {
+				Class<?> rt = m.getReturnType();
+				if (isParentClass(returnType, rt)) {
+					Class<?>[] pt = m.getParameterTypes();
+					if (pt.length == parameterTypes.length) {
+						boolean matches = true;
+						for (int i = 0; i < pt.length; i++) {
+							if (! isParentClass(pt[i], parameterTypes[i])) {
+								matches = false;
+								break;
+							}
+						}
+						if (matches)
+							return m;
+					}
+				}
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * Finds a public constructor with the specified parameters without throwing an exception.
+	 *
+	 * @param c The class to search for a constructor.
+	 * @param parameterTypes The parameter types in the constructor.
+	 * Can be subtypes of the actual constructor argument types.
+	 * @return The matching constructor, or <jk>null</jk> if constructor could not be found.
+	 */
+	@SuppressWarnings("unchecked")
+	public static <T> Constructor<T> findPublicConstructor(Class<T> c, Class<?>...parameterTypes) {
+		for (Constructor<?> n : c.getConstructors()) {
+			if (isPublic(n)) {
+				Class<?>[] pt = n.getParameterTypes();
+				if (pt.length == parameterTypes.length) {
+					boolean matches = true;
+					for (int i = 0; i < pt.length; i++) {
+						if (! isParentClass(pt[i], parameterTypes[i])) {
+							matches = false;
+							break;
+						}
+					}
+					if (matches)
+						return (Constructor<T>)n;
+				}
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * Finds the public constructor that can take in the specified arguments.
+	 *
+	 * @param c The class we're trying to construct.
+	 * @param args The arguments we want to pass into the constructor.
+	 * @return The constructor, or <jk>null</jk> if a public constructor could not be found that takes
+	 * in the specified arguments.
+	 */
+	public static <T> Constructor<T> findPublicConstructor(Class<T> c, Object...args) {
+		return findPublicConstructor(c, getClasses(args));
+	}
+
+	/**
+	 * Returns the class types for the specified arguments.
+	 *
+	 * @param args The objects we're getting the classes of.
+	 * @return The classes of the arguments.
+	 */
+	public static Class<?>[] getClasses(Object...args) {
+		Class<?>[] pt = new Class<?>[args.length];
+		for (int i = 0; i < args.length; i++)
+			pt[i] = args[i] == null ? null : args[i].getClass();
+		return pt;
+	}
+
 // This code is inherently unsafe (but still potentially useful?)
 //
 //	/**

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-core/src/main/java/org/apache/juneau/internal/CollectionUtils.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/internal/CollectionUtils.java b/juneau-core/src/main/java/org/apache/juneau/internal/CollectionUtils.java
index 7c11d22..c413427 100644
--- a/juneau-core/src/main/java/org/apache/juneau/internal/CollectionUtils.java
+++ b/juneau-core/src/main/java/org/apache/juneau/internal/CollectionUtils.java
@@ -25,7 +25,7 @@ public class CollectionUtils {
 	 * @param in The map to reverse the order on.
 	 * @return A new {@link LinkedHashMap} with keys in reverse order.
 	 */
-	public static <K,V> LinkedHashMap<K,V> reverse(LinkedHashMap<K,V> in) {
+	public static <K,V> LinkedHashMap<K,V> reverse(Map<K,V> in) {
 		if (in == null)
 			return null;
 		LinkedHashMap<K,V> m = new LinkedHashMap<K,V>();

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-core/src/main/java/org/apache/juneau/internal/IOUtils.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/internal/IOUtils.java b/juneau-core/src/main/java/org/apache/juneau/internal/IOUtils.java
index 72ece2c..77caa63 100644
--- a/juneau-core/src/main/java/org/apache/juneau/internal/IOUtils.java
+++ b/juneau-core/src/main/java/org/apache/juneau/internal/IOUtils.java
@@ -361,4 +361,36 @@ public final class IOUtils {
 				closeQuietly((Writer)o2);
 		}
 	}
+
+	/**
+	 * Converts an object to an <code>InputStream</code>.
+	 *
+	 * @param o The object to convert to an input stream.
+	 * Can be any of the following:
+	 * <ul>
+	 * 	<li>{@link InputStream}
+	 * 	<li>{@link Reader}
+	 * 	<li>{@link File}
+	 * 	<li>{@link CharSequence} - Converted to UTF-8 stream.
+	 * 	<li><code><jk>byte</jk>[]</code>
+	 * 	<li><code><jk>null</jk></code> - Returns null.
+	 * </ul>
+	 * @return The object converted to an input stream.
+	 * @throws IOException If invalid object passed in or file could not be read.
+	 */
+	public static InputStream toInputStream(Object o) throws IOException {
+		if (o == null)
+			return null;
+		if (o instanceof InputStream)
+			return (InputStream)o;
+		if (o instanceof File)
+			return new FileInputStream((File)o);
+		if (o instanceof byte[])
+			return new ByteArrayInputStream((byte[])o);
+		if (o instanceof CharSequence)
+			return new ByteArrayInputStream(((CharSequence)o).toString().getBytes(UTF8));
+		if (o instanceof Reader)
+			return new ByteArrayInputStream(IOUtils.read((Reader)o).getBytes(UTF8));
+		throw new IOException("Invalid object type passed to IOUtils.toInputStream(Object): " + o.getClass().getName());
+	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-core/src/main/java/org/apache/juneau/internal/ObjectUtils.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/internal/ObjectUtils.java b/juneau-core/src/main/java/org/apache/juneau/internal/ObjectUtils.java
new file mode 100644
index 0000000..fac4337
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/internal/ObjectUtils.java
@@ -0,0 +1,36 @@
+// ***************************************************************************************************************************
+// * 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.internal;
+
+/**
+ * Object-related utility methods.
+ */
+public class ObjectUtils {
+
+	/**
+	 * Returns <jk>true</jk> if the specified objects are equal.
+	 * <p>
+	 * Gracefully handles <jk>null</jk>s.
+	 *
+	 * @param o1 Object #1
+	 * @param o2 Object #2
+	 * @return <jk>true</jk> if the objects are equal or both <jk>null</jk>.
+	 */
+	public static boolean equals(Object o1, Object o2) {
+		if (o1 == null && o2 == null)
+			return true;
+		if (o1 == null || o2 == null)
+			return false;
+		return o1.equals(o2);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-core/src/main/java/org/apache/juneau/internal/Pair.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/internal/Pair.java b/juneau-core/src/main/java/org/apache/juneau/internal/Pair.java
new file mode 100644
index 0000000..93cda29
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/internal/Pair.java
@@ -0,0 +1,64 @@
+// ***************************************************************************************************************************
+// * 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.internal;
+
+/**
+ * Represents a simple object pair.
+ * @param <F> The first object type.
+ * @param <S> The second object type.
+ */
+public class Pair<F,S> {
+	private final F first;
+	private final S second;
+
+	/**
+	 * Constructor.
+	 *
+	 * @param first The first object in the pair.
+	 * @param second The second object in the pair.
+	 */
+	public Pair(F first, S second) {
+		this.first = first;
+		this.second = second;
+	}
+
+	/**
+	 * Returns the first object in the pair.
+	 * @return The first object in the pair.
+	 */
+	public F first() {
+		return first;
+	}
+
+	/**
+	 * Returns the second object in the pair.
+	 * @return The second object in the pair.
+	 */
+	public S second() {
+		return second;
+	}
+
+	@Override /* Object */
+	public boolean equals(Object o) {
+		if (o instanceof Pair) {
+			Pair<?,?> p = (Pair<?,?>)o;
+			return ObjectUtils.equals(first, p.first) && ObjectUtils.equals(second, p.second);
+		}
+		return false;
+	}
+
+	@Override /* Object */
+	public int hashCode() {
+		return (first == null ? 0 : first.hashCode()) ^ (second == null ? 0 : second.hashCode());
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-core/src/main/java/org/apache/juneau/internal/ReflectionUtils.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/internal/ReflectionUtils.java b/juneau-core/src/main/java/org/apache/juneau/internal/ReflectionUtils.java
index 0a0c602..acb2043 100644
--- a/juneau-core/src/main/java/org/apache/juneau/internal/ReflectionUtils.java
+++ b/juneau-core/src/main/java/org/apache/juneau/internal/ReflectionUtils.java
@@ -102,7 +102,7 @@ public final class ReflectionUtils {
 	}
 
 	/**
-	 * Sames as {@link #findAnnotations(Class, Class)} except returns the annotations as a map
+	 * Same as {@link #findAnnotations(Class, Class)} except returns the annotations as a map
 	 * with the keys being the class on which the annotation was found.
 	 * <p>
 	 * Results are ordered child-to-parent.
@@ -110,7 +110,7 @@ public final class ReflectionUtils {
 	 * @param <T> The annotation class type.
 	 * @param a The annotation class type.
 	 * @param c The class being searched.
-	 * @return The found matches, or an empty array if annotation was not found.
+	 * @return The found matches, or an empty map if annotation was not found.
 	 */
 	public static <T extends Annotation> LinkedHashMap<Class<?>,T> findAnnotationsMap(Class<T> a, Class<?> c) {
 		LinkedHashMap<Class<?>,T> m = new LinkedHashMap<Class<?>,T>();
@@ -118,6 +118,18 @@ public final class ReflectionUtils {
 		return m;
 	}
 
+	/**
+	 * Same as {@link #findAnnotationsMap(Class, Class)} except returns results in parent-to-child order.
+	 *
+	 * @param <T> The annotation class type.
+	 * @param a The annotation class type.
+	 * @param c The class being searched.
+	 * @return The found matches, or an empty map if annotation was not found.
+	 */
+	public static <T extends Annotation> LinkedHashMap<Class<?>,T> findAnnotationsMapParentFirst(Class<T> a, Class<?> c) {
+		return CollectionUtils.reverse(findAnnotationsMap(a, c));
+	}
+
 	private static <T extends Annotation> void findAnnotationsMap(Class<T> a, Class<?> c, Map<Class<?>,T> m) {
 		if (c == null)
 			return;
@@ -163,6 +175,8 @@ public final class ReflectionUtils {
 	 * @return An input stream on the specified resource, or <jk>null</jk> if the resource could not be found.
 	 */
 	public static InputStream getResource(Class<?> c, String name) {
+		if (name == null)
+			return null;
 		while (c != null) {
 			InputStream is = c.getResourceAsStream(name);
 			if (is != null)

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-core/src/main/java/org/apache/juneau/json/package.html
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/json/package.html b/juneau-core/src/main/java/org/apache/juneau/json/package.html
index 06e6db6..f302ec9 100644
--- a/juneau-core/src/main/java/org/apache/juneau/json/package.html
+++ b/juneau-core/src/main/java/org/apache/juneau/json/package.html
@@ -1022,7 +1022,7 @@
  				The <code>$L{...}</code> variable represent localized strings pulled from the resource bundle identified by the <code>messages</code> annotation.
  				These variables are replaced at runtime based on the HTTP request locale.
 				Several built-in runtime variable types are defined, and the API can be extended to include user-defined variables.
-				See {@link org.apache.juneau.rest.RestServlet#getVarResolver()} for more information.
+				See {@link org.apache.juneau.rest.RestContext#getVarResolver()} for more information.
 			</p>
 			<p>
 				This document won't go into all the details of the Juneau <code>RestServlet</code> class.<br>

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-core/src/main/java/org/apache/juneau/uon/package.html
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/uon/package.html b/juneau-core/src/main/java/org/apache/juneau/uon/package.html
index e6b1d10..6c2c6ca 100644
--- a/juneau-core/src/main/java/org/apache/juneau/uon/package.html
+++ b/juneau-core/src/main/java/org/apache/juneau/uon/package.html
@@ -1005,7 +1005,7 @@
  				The <code>$L{...}</code> variable represent localized strings pulled from the resource bundle identified by the <code>messages</code> annotation.
  				These variables are replaced at runtime based on the HTTP request locale.
 				Several built-in runtime variable types are defined, and the API can be extended to include user-defined variables.
-				See {@link org.apache.juneau.rest.RestServlet#getVarResolver()} for more information.
+				See {@link org.apache.juneau.rest.RestContext#getVarResolver()} for more information.
 			</p>
 			<p>
 				This document won't go into all the details of the Juneau <code>RestServlet</code> class.<br>

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-core/src/main/java/org/apache/juneau/urlencoding/package.html
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/urlencoding/package.html b/juneau-core/src/main/java/org/apache/juneau/urlencoding/package.html
index 4d623d1..ddb09b2 100644
--- a/juneau-core/src/main/java/org/apache/juneau/urlencoding/package.html
+++ b/juneau-core/src/main/java/org/apache/juneau/urlencoding/package.html
@@ -1005,7 +1005,7 @@
  				The <code>$L{...}</code> variable represent localized strings pulled from the resource bundle identified by the <code>messages</code> annotation.
  				These variables are replaced at runtime based on the HTTP request locale.
 				Several built-in runtime variable types are defined, and the API can be extended to include user-defined variables.
-				See {@link org.apache.juneau.rest.RestServlet#getVarResolver()} for more information.
+				See {@link org.apache.juneau.rest.RestContext#getVarResolver()} for more information.
 			</p>
 			<p>
 				This document won't go into all the details of the Juneau <code>RestServlet</code> class.<br>

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-core/src/main/java/org/apache/juneau/xml/package.html
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/package.html b/juneau-core/src/main/java/org/apache/juneau/xml/package.html
index 4d9d595..4c0fbeb 100644
--- a/juneau-core/src/main/java/org/apache/juneau/xml/package.html
+++ b/juneau-core/src/main/java/org/apache/juneau/xml/package.html
@@ -2941,7 +2941,7 @@
  				The <code>$L{...}</code> variable represent localized strings pulled from the resource bundle identified by the <code>messages</code> annotation.
  				These variables are replaced at runtime based on the HTTP request locale.
 				Several built-in runtime variable types are defined, and the API can be extended to include user-defined variables.
-				See {@link org.apache.juneau.rest.RestServlet#getVarResolver()} for more information.
+				See {@link org.apache.juneau.rest.RestContext#getVarResolver()} for more information.
 			</p>
 			<p>
 				This document won't go into all the details of the Juneau <code>RestServlet</code> class.<br>

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-core/src/main/javadoc/overview.html
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/javadoc/overview.html b/juneau-core/src/main/javadoc/overview.html
index bab4158..9a959d0 100644
--- a/juneau-core/src/main/javadoc/overview.html
+++ b/juneau-core/src/main/javadoc/overview.html
@@ -633,7 +633,7 @@
 					<li class='c'><a class='doclink' href='org/apache/juneau/xml/XmlParserContext.html#ConfigProperties'>XmlParserContext</a> - Configurable properties on the XML parser.
 				</ul>
 			</ul>
-			<li class='c'><a class='doclink' href='org/apache/juneau/server/RestServletContext.html#ConfigProperties'>RestServletContext</a> - Configurable properties on the REST servlet.
+			<li class='c'><a class='doclink' href='org/apache/juneau/server/RestContext.html#ConfigProperties'>RestContext</a> - Configurable properties on the REST servlet.
 		</ul>
 	</div>
 		
@@ -2625,7 +2625,7 @@
 		</p>
 		<p>
 			The <l>title</l> and <l>description</l> keys identify the localized values
-			return by the {@link org.apache.juneau.rest.RestServlet#getTitle(RestRequest)} and {@link org.apache.juneau.rest.RestServlet#getDescription(RestRequest)} methods.
+			return by the {@link org.apache.juneau.rest.RestRequest#getServletTitle()} and {@link org.apache.juneau.rest.RestRequest#getServletDescription()} methods.
 		</p>
 		<p>
 			The <l>children</l> annotation defines the child resources of this router resource.
@@ -2646,7 +2646,7 @@
 			It should be noted that child resources do not need to be defined this way.  
 			They could also be defined as servlets in the same way as the root resource.  
 			The <l>children</l> annotation approach simply makes it easier to define them without having to touch the <l>web.xml</l> file again.
-			Child resources can also be defined programmatically by overriding the {@link org.apache.juneau.rest.RestServlet#createChildren()} method.
+			Child resources can also be defined programmatically by using the {@link org.apache.juneau.rest.RestConfig#addChildResources(Class[])} method.
 		</p>
 		<p>
 			Note that these router pages can be arbitrarily nested deep.  
@@ -3131,8 +3131,8 @@
 		
 		<h6 class='topic'>Additional Information</h6>
 		<ul class='javahierarchy'>
-			<li class='m'>{@link org.apache.juneau.rest.RestServlet#createVarResolver()} - Servlet and request variables.
-			<li class='m'>{@link org.apache.juneau.rest.RestServlet#getSessionObjects(RestRequest)} - Var resolver session objects.
+			<li class='m'>{@link org.apache.juneau.rest.RestConfig#addVars(Class[])} - Servlet and request variables.
+			<li class='m'>{@link org.apache.juneau.rest.RestCallHandler#getSessionObjects(RestRequest)} - Var resolver session objects.
 		</ul>
 	</div>	
 	
@@ -3208,31 +3208,27 @@
 				{@link org.apache.juneau.rest.annotation.RestResource#pojoSwaps() @RestResopurce.pojoSwaps()} annotations are used 
 				to set behavior properties on the resource's underlying bean context, serializers, and parsers.  
 			You're using them here to modify the behavior of serialization for all content types.  
-			The annotations are functionally equivalent to overriding the {@link org.apache.juneau.rest.RestServlet#createSerializers(ObjectMap,Class[],Class[])} method, as follows:
+			The annotations are functionally equivalent to using the {@link org.apache.juneau.rest.RestConfig} class, as follows:
 		</p>
 		<h6 class='figure'>Hypothetical RequestEchoResource.createSerializers() method</h6>
 		<p class='bcode'>
 	<jd>/** Override the default rest serializers to add some transforms */</jd>
 	<ja>@Override</ja>
-	<jk>protected</jk> SerializerGroupBuilder createSerializers(ObjectMap properties, Class[] beanFilters, Class[] pojoSwaps) {
-
-		<jc>// You'll just reuse the parent serializer group</jc>
-		SerializerGroupBuilder b = <jk>super</jk>.createSerializers(properties, beanFilters, pojoSwaps);
-		
+	<jk>public synchronize void</jk> init(RestConfig config) throws Exception {
 		<jc>// Add bean filters for the HttpServletRequest, HttpSession, and ServletContext objects
 		//		so that you don't show vendor-specific properties on subclasses.
 		// Add Enumeration POJO swap to be able to render the contents of Enumeration properties.
 		// The max depth and detect recursion options prevent any possible runaway serializations.  
 		// This shouldn't happen, but future JEE APIs may introduce deep hierarchies or loops.</jc>
-		b
-			.beanFilters(HttpServletRequest.<jk>class</jk>, HttpSession.<jk>class</jk>, ServletContext.<jk>class</jk>)
-			.pojoSwaps(EnumerationSwap.<jk>class</jk>)
-			.maxDepth(10)
-			.detectRecursions(<jk>true</jk>);
+		config
+			.addBeanFilters(HttpServletRequest.<jk>class</jk>, HttpSession.<jk>class</jk>, ServletContext.<jk>class</jk>)
+			.addPojoSwaps(EnumerationSwap.<jk>class</jk>)
+			.setProperty(<jsf>SERIALIZER_maxDepth</jsf>, 10)
+			.setProperty(<jsf>SERIALIZER_detectRecursions</jsf>, <jk>true</jk>)
 			.property(<jsf>HTMLDOC_links</jsf>, <js>"{...}"</js>);
 		
-		<jc>// Return the updated group</jc>
-		<jk>return</jk> b;
+		<jc>// Don't forget to call this!</jc>
+		<jk>super</jk>.init(config);
 	}
 		</p>
 		<p>
@@ -4248,7 +4244,7 @@
 	http://localhost:10000/addressBook/people/3/name?method=PUT&amp;content="'Barack Hussein Obama'"	
 			</p>
 			<p>
-				The ability to overload methods is enabled through the {@link org.apache.juneau.rest.RestServletContext#REST_allowMethodParam} property.
+				The ability to overload methods is enabled through the {@link org.apache.juneau.rest.RestContext#REST_allowMethodParam} property.
 			</p>
 		</div>
 	</div>	
@@ -4590,7 +4586,7 @@
 		<ul class='spaced-list'>
 			<li>Accessing a docker registry REST API as POJOs using {@link org.apache.juneau.rest.client.RestClient}.
 			<li>Using the {@link org.apache.juneau.rest.labels.ResourceDescription} class to implement a top-level 'router' page.
-			<li>Using the {@link org.apache.juneau.rest.RestServlet#getConfig()} method to access external configuration file values.
+			<li>Using the {@link org.apache.juneau.rest.RestContext#getConfigFile()} method to access external configuration file values.
 		</ul>
 		<p>
 			Pointing a browser to the resource shows the following:
@@ -4663,7 +4659,7 @@
 		<h6 class='topic'>Additional Information</h6>
 		<ul class='javahierarchy'>
 			<li class='c'>{@link org.apache.juneau.rest.labels.ResourceDescription}
-			<li class='m'>{@link org.apache.juneau.rest.RestServlet#getConfig()}
+			<li class='m'>{@link org.apache.juneau.rest.RestContext#getConfigFile()}
 		</ul>
 	</div>	
 	
@@ -4975,7 +4971,7 @@
 		</p>
 		<ul class='spaced-list'>
 			<li>Using the {@link org.apache.juneau.dto.ResultSetList} to serialize database result sets.
-			<li>Using {@link org.apache.juneau.rest.RestServlet#getConfig()} to access config properties.
+			<li>Using {@link org.apache.juneau.rest.RestContext#getConfigFile()} to access config properties.
 			<li>Using form entry beans.
 		</ul>
 		<p>
@@ -5449,8 +5445,8 @@
 		<h3 class='topic' onclick='toggle(this)'>9.2.2 - Apply transforms to a subset of serializers or parsers</h3>
 		<div class='topic'>
 			<p>
-				The {@link org.apache.juneau.rest.RestServlet#createSerializers(ObjectMap,Class[],Class[])} and {@link org.apache.juneau.rest.RestServlet#createParsers(ObjectMap,Class[],Class[])}
-				methods are the servlet methods that get called during servlet initialization to create the serializer and
+				The {@link org.apache.juneau.rest.RestConfig#addSerializers(Class[])} and {@link org.apache.juneau.rest.RestConfig#addParsers(Class[])}
+				methods are the methods that get called during servlet initialization to create the serializer and
 				parser groups.
 				These methods can be overridden to customize individual serializers and parsers in a way that can't be done using annotations.
 			</p>
@@ -5460,10 +5456,9 @@
 			</p>
 			<p class='bcode'>
 	<ja>@Override</ja>
-	<jk>protected</jk> SerializerGroup createSerializers(ObjectMap properties, Class[] beanFilters, Class[] pojoSwaps) <jk>throws</jk> Exception {
-		SerializerGroup g = <jk>super</jk>.createSerializers(properties, beanFilters, pojoSwaps);
-		g.getSerializer(<js>"text/html"</js>).pojoSwaps(DoubleSwap.<jk>class</jk>); 
-		<jk>return</jk> g;
+	<jk>public synchronized void</jk> init(RestServletConfig config) <jk>throws</jk> Exception {
+		config.addSerializer(<jk>new</jk> HtmlSerializerBuilder().pojoSwaps(DoubleSwap.<jk>class</jk>).build());
+		super.init(config);
 	}
 			</p>
 		</div>
@@ -5593,7 +5588,7 @@
 	
 	<h5 class='toc'>What's new in each release</h5>
 	<ul class='toc'>
-		<li><p><a class='doclink' href='#6.1.1'>6.1.1 (TBD)</a></p>
+		<li><p><a class='doclink' href='#6.2.0'>6.2.0 (TBD)</a></p>
 		<li><p><a class='doclink' href='#6.1.0'>6.1.0 (Feb 25, 2017)</a></p>
 		<li><p><a class='doclink' href='#6.0.1'>6.0.1 (Jan 3, 2017)</a></p>
 		<li><p><a class='doclink' href='#6.0.0'>6.0.0 (Oct 3, 2016)</a></p>
@@ -5661,11 +5656,11 @@
 
 
 	<!-- ======================================================================================================== -->
-	<a id="6.1.1"></a>
-	<h3 class='topic' onclick='toggle(this)'>6.1.1 (TBD)</h3>
+	<a id="6.2.0"></a>
+	<h3 class='topic' onclick='toggle(this)'>6.2.0 (TBD)</h3>
 	<div class='topic'>
 		<p>
-			Juneau 6.1.1 is ...
+			Juneau 6.2.0 is a major update.
 		</p>
 
 		<h6 class='topic'>org.apache.juneau</h6>
@@ -5732,6 +5727,48 @@
 		
 		<h6 class='topic'>org.apache.juneau.rest</h6>
 		<ul class='spaced-list'>
+			<li>{@link org.apache.juneau.rest.annotation.RestResource @RestResource} annotation can now be applied to 
+				any class!  You're no longer restricted to subclassing your resources from {@link org.apache.juneau.rest.RestServlet}.
+				<br>This is a major enhancement in the API.  Anything you could do by subclassing from <code>RestServlet</code>
+				should have an equivalent for non-<code>RestServlet</code> classes.
+				<br>The only restriction is that the top-level resource must subclass from <code>RestServlet</code>.
+				Child resources do not.
+				<br><br>
+				The majority of code has been split up into two separate classes:
+				<ul>
+					<li>{@link org.apache.juneau.rest.RestConfig} - A modifiable configuration of a resource.  Subclasses from {@link javax.servlet.ServletConfig}.
+					<li>{@link org.apache.juneau.rest.RestContext} - A read-only configuration that's the result of a snapshot of the config.
+				</ul>
+				<br><br>
+				The {@link org.apache.juneau.rest.RestServlet} class now has the following initialization method that allows you to override
+				the config settings define via annotations:
+				<ul>
+					<li>{@link org.apache.juneau.rest.RestServlet#init(RestConfig)} - A modifiable configuration of a resource.
+				</ul>
+				Non-<code>RestServlet</code> classes must have one of the following to allow it to be instantiated:
+				<ul>
+					<li>A <code><jk>public</jk> T(RestConfig)</code> constructor.
+					<li>A <code><jk>public</jk> T()</code> constructor.
+					<li>The parent resource must have a customized {@link org.apache.juneau.rest.RestResourceResolver} for instantiating it.
+				</ul>
+				<br>
+				Non-<code>RestServlet</code> classes can optionally include the following init methods to gain access to the config and context:
+				<ul>
+					<li><code><jk>public</jk> init(RestConfig)</code>
+					<li><code><jk>public</jk> init(RestContext)</code>
+				</ul>
+			<li>New annotations added to {@link org.apache.juneau.rest.annotation.RestResource @RestResource} to allow non-<code>RestServlet</code>
+				resources to do the same as subclassing directly from <code>RestServlet</code>:
+			<ul>
+				<li>{@link org.apache.juneau.rest.annotation.RestResource#resourceResolver() resourceResolver()} 
+					- Specify a {@link org.apache.juneau.rest.RestResourceResolver} class for resolving child resources.
+				<li>{@link org.apache.juneau.rest.annotation.RestResource#callHandler() callHandler()} 
+					- Specify a {@link org.apache.juneau.rest.RestCallHandler} class for handling the lifecycle of a REST call.
+				<li>{@link org.apache.juneau.rest.annotation.RestResource#infoProvider() infoProvider()} 
+					- Specify a {@link org.apache.juneau.rest.RestInfoProvider} class for customizing title/description/Swagger information on a REST resource.
+				<li>{@link org.apache.juneau.rest.annotation.RestResource#logger() logger()} 
+					- Specify a {@link org.apache.juneau.rest.RestLogger} class for handling logging.
+			</ul>
 			<li>{@link org.apache.juneau.rest.annotation.RestResource#stylesheet()} can now take in a comma-delimited list of stylesheet paths.
 			<li>{@link org.apache.juneau.rest.StreamResource} can now contain multiple sources from a variety of source types (e.g. <code><jk>byte</jk>[]</code> arrays, <code>InputStreams</code>, <code>Files</code>, etc...)
 				and is now immutable.  It also includes a new {@link org.apache.juneau.rest.StreamResource.Builder} class.
@@ -6126,16 +6163,16 @@
 					<li>Eliminated <code>ResourceOptions</code> and related code.
 					<li>New annotations and related methods:
 						<ul>
-							<li>{@link org.apache.juneau.rest.annotation.RestResource#title() @RestResource.title()} / {@link org.apache.juneau.rest.RestServlet#getTitle(RestRequest)}
-							<li>{@link org.apache.juneau.rest.annotation.RestResource#description() @RestResource.description()} / {@link org.apache.juneau.rest.RestServlet#getDescription(RestRequest)}
-							<li>{@link org.apache.juneau.rest.annotation.RestResource#termsOfService() @RestResource.termsOfService()} / {@link org.apache.juneau.rest.RestServlet#getTermsOfService(RestRequest)}
-							<li>{@link org.apache.juneau.rest.annotation.RestResource#contact() @RestResource.contact()} / {@link org.apache.juneau.rest.RestServlet#getContact(RestRequest)}
-							<li>{@link org.apache.juneau.rest.annotation.RestResource#license() @RestResource.license()} / {@link org.apache.juneau.rest.RestServlet#getLicense(RestRequest)}
-							<li>{@link org.apache.juneau.rest.annotation.RestResource#version() @RestResource.version()} / {@link org.apache.juneau.rest.RestServlet#getVersion(RestRequest)}
-							<li>{@link org.apache.juneau.rest.annotation.RestResource#tags() @RestResource.tags()} / {@link org.apache.juneau.rest.RestServlet#getTags(RestRequest)}
-							<li>{@link org.apache.juneau.rest.annotation.RestResource#externalDocs() @RestResource.externalDocs()} / {@link org.apache.juneau.rest.RestServlet#getExternalDocs(RestRequest)}
-							<li>{@link org.apache.juneau.rest.annotation.RestMethod#summary() @RestMethod.summary()} / {@link org.apache.juneau.rest.RestServlet#getMethodSummary(String,RestRequest)}
-							<li>{@link org.apache.juneau.rest.annotation.RestMethod#description() @RestMethod.description()} / {@link org.apache.juneau.rest.RestServlet#getMethodDescription(String,RestRequest)}
+							<li>{@link org.apache.juneau.rest.annotation.RestResource#title() @RestResource.title()} / {@link org.apache.juneau.rest.RestInfoProvider#getTitle(RestRequest)}
+							<li>{@link org.apache.juneau.rest.annotation.RestResource#description() @RestResource.description()} / {@link org.apache.juneau.rest.RestInfoProvider#getDescription(RestRequest)}
+							<li>{@link org.apache.juneau.rest.annotation.RestResource#termsOfService() @RestResource.termsOfService()} / {@link org.apache.juneau.rest.RestInfoProvider#getTermsOfService(RestRequest)}
+							<li>{@link org.apache.juneau.rest.annotation.RestResource#contact() @RestResource.contact()} / {@link org.apache.juneau.rest.RestInfoProvider#getContact(RestRequest)}
+							<li>{@link org.apache.juneau.rest.annotation.RestResource#license() @RestResource.license()} / {@link org.apache.juneau.rest.RestInfoProvider#getLicense(RestRequest)}
+							<li>{@link org.apache.juneau.rest.annotation.RestResource#version() @RestResource.version()} / {@link org.apache.juneau.rest.RestInfoProvider#getVersion(RestRequest)}
+							<li>{@link org.apache.juneau.rest.annotation.RestResource#tags() @RestResource.tags()} / {@link org.apache.juneau.rest.RestInfoProvider#getTags(RestRequest)}
+							<li>{@link org.apache.juneau.rest.annotation.RestResource#externalDocs() @RestResource.externalDocs()} / {@link org.apache.juneau.rest.RestInfoProvider#getExternalDocs(RestRequest)}
+							<li>{@link org.apache.juneau.rest.annotation.RestMethod#summary() @RestMethod.summary()} / {@link org.apache.juneau.rest.RestInfoProvider#getMethodSummary(String,RestRequest)}
+							<li>{@link org.apache.juneau.rest.annotation.RestMethod#description() @RestMethod.description()} / {@link org.apache.juneau.rest.RestInfoProvider#getMethodDescription(String,RestRequest)}
 							<li>{@link org.apache.juneau.rest.annotation.RestMethod#externalDocs() @RestMethod.externalDocs()} 
 							<li>{@link org.apache.juneau.rest.annotation.RestMethod#tags() @RestMethod.tags()} 
 							<li>{@link org.apache.juneau.rest.annotation.RestMethod#deprecated() @RestMethod.deprecated()} 
@@ -6144,23 +6181,23 @@
 						</ul>
 					</li>					
 				</ul>
-			<li>New {@link org.apache.juneau.rest.RestServletContext#paramFormat} context property.
+			<li>New <code><del>RestServletContext.paramFormat</del></code> context property.
 			<li>New/updated methods on {@link org.apache.juneau.rest.RestServlet}:
 				<ul>
-					<li>{@link org.apache.juneau.rest.RestServlet#createProperties()}
-					<li>{@link org.apache.juneau.rest.RestServlet#createBeanContext(ObjectMap,Class[],Class[])}
-					<li>{@link org.apache.juneau.rest.RestServlet#createBeanFilters()}
-					<li>{@link org.apache.juneau.rest.RestServlet#createPojoSwaps()}
-					<li>{@link org.apache.juneau.rest.RestServlet#createParsers(ObjectMap,Class[],Class[])}
-					<li>{@link org.apache.juneau.rest.RestServlet#createUrlEncodingSerializer(ObjectMap,Class[],Class[])}
-					<li>{@link org.apache.juneau.rest.RestServlet#createUrlEncodingParser(ObjectMap,Class[],Class[])}
-					<li>{@link org.apache.juneau.rest.RestServlet#createConverters(ObjectMap)}
-					<li>{@link org.apache.juneau.rest.RestServlet#createDefaultRequestHeaders(ObjectMap)}
-					<li>{@link org.apache.juneau.rest.RestServlet#createDefaultResponseHeaders(ObjectMap)}
-					<li>{@link org.apache.juneau.rest.RestServlet#createEncoders(ObjectMap)}
-					<li>{@link org.apache.juneau.rest.RestServlet#createGuards(ObjectMap)}
-					<li>{@link org.apache.juneau.rest.RestServlet#createMimetypesFileTypeMap(ObjectMap)}
-					<li>{@link org.apache.juneau.rest.RestServlet#createResponseHandlers(ObjectMap)}
+					<li><code><del>RestServlet.createProperties()</del></code>
+					<li><code><del>RestServlet.createBeanContext(ObjectMap,Class[],Class[])</del></code>
+					<li><code><del>RestServlet.createBeanFilters()</del></code>
+					<li><code><del>RestServlet.createPojoSwaps()</del></code>
+					<li><code><del>RestServlet.createParsers(ObjectMap,Class[],Class[])</del></code>
+					<li><code><del>RestServlet.createUrlEncodingSerializer(ObjectMap,Class[],Class[])</del></code>
+					<li><code><del>RestServlet.createUrlEncodingParser(ObjectMap,Class[],Class[])</del></code>
+					<li><code><del>RestServlet.createConverters(ObjectMap)</del></code>
+					<li><code><del>RestServlet.createDefaultRequestHeaders(ObjectMap)</del></code>
+					<li><code><del>RestServlet.createDefaultResponseHeaders(ObjectMap)</del></code>
+					<li><code><del>RestServlet.createEncoders(ObjectMap)</del></code>
+					<li><code><del>RestServlet.createGuards(ObjectMap)</del></code>
+					<li><code><del>RestServlet.createMimetypesFileTypeMap(ObjectMap)</del></code>
+					<li><code><del>RestServlet.createResponseHandlers(ObjectMap)</del></code>
 				</ul>
 			</li>
 			<li>New client-version annotations:
@@ -6223,10 +6260,9 @@
 
 		<h6 class='topic'>Server</h6>
 		<ul class='spaced-list'>
-			<li>New methods on {@link org.apache.juneau.rest.RestServlet}:
+			<li>New methods on {@link org.apache.juneau.rest.RestContext}:
 				<ul>
-					<li>{@link org.apache.juneau.rest.RestServlet#getMessages()}
-					<li>{@link org.apache.juneau.rest.RestServlet#getMessages(Locale)}
+					<li>{@link org.apache.juneau.rest.RestContext#getMessages()}
 				</ul>
 		</ul>
 
@@ -6569,15 +6605,15 @@
 				</ul>
 			<li>New methods in {@link org.apache.juneau.rest.RestServlet}:
 				<ul>
-					<li>{@link org.apache.juneau.rest.RestServlet#getChildClasses()} - Programmatic equivalent to {@link org.apache.juneau.rest.annotation.RestResource#children()} annotation.
-					<li>{@link org.apache.juneau.rest.RestServlet#shouldLog(HttpServletRequest,HttpServletResponse,RestException)} 
-					<li>{@link org.apache.juneau.rest.RestServlet#shouldLogStackTrace(HttpServletRequest,HttpServletResponse,RestException)} 
-					<li>{@link org.apache.juneau.rest.RestServlet#logObjects(Level,String,Object[])} 
-					<li>{@link org.apache.juneau.rest.RestServlet#resolveStaticFile(String)} 
-					<li>{@link org.apache.juneau.rest.RestServlet#createStyleSheet()} 
-					<li>{@link org.apache.juneau.rest.RestServlet#createFavIcon()} 
-					<li>{@link org.apache.juneau.rest.RestServlet#createStaticFilesMap()} 
-					<li>{@link org.apache.juneau.rest.RestServlet#getConfigMgr()} 
+					<li><code><del>RestServlet.getChildClasses()</del></code>  - Programmatic equivalent to {@link org.apache.juneau.rest.annotation.RestResource#children()} annotation.
+					<li><code><del>RestServlet.shouldLog(HttpServletRequest,HttpServletResponse,RestException)</del></code> 
+					<li><code><del>RestServlet.shouldLogStackTrace(HttpServletRequest,HttpServletResponse,RestException)</del></code> 
+					<li><code><del>RestServlet.logObjects(Level,String,Object[])</del></code> 
+					<li><code><del>RestServlet.resolveStaticFile(String)</del></code> 
+					<li><code><del>RestServlet.createStyleSheet()</del></code> 
+					<li><code><del>RestServlet.createFavIcon()</del></code> 
+					<li><code><del>RestServlet.createStaticFilesMap()</del></code> 
+					<li><code><del>RestServlet.getConfigMgr()</del></code>
 				</ul>
 			<li>Removed {@link org.apache.juneau.jso.JsoParser}
 				from {@link org.apache.juneau.rest.RestServletDefault} and {@link org.apache.juneau.rest.jena.RestServletJenaDefault}.  
@@ -6709,7 +6745,7 @@
 			<li>Changes to {@link org.apache.juneau.rest.RestServlet}:
 				<ul>
 					<li>New methods for accessing external INI config files:<br>  
-						{@link org.apache.juneau.rest.RestServlet#getConfig()}<br>
+						<code><del>RestServlet.getConfig()</del></code><br>
 						<code><del>RestServlet.createConfigFile()</del></code>
 					<li>New <js>"$C{...}"</js> variable that resolve to INI config file values.
 					<li>New <js>"$UE{...}"</js> variable that  URL-encodes the value inside the variable.
@@ -6718,16 +6754,16 @@
 						<del><code>RestServlet.getResourceAsString(String)</code></del><br>
 						<del><code>RestServlet.getResource(Class,String,String)</code></del>.
 							Useful if you want to load predefined POJOs from JSON files in your classpath.
-					<li>New {@link org.apache.juneau.rest.RestServlet#handleNotFound(int,RestRequest,RestResponse)} method for customized handling
+					<li>New <code><del>RestServlet.handleNotFound(int,RestRequest,RestResponse)</del></code> method for customized handling
 						of when a resource or method was not found.  
 				</ul>
 			<li>{@link org.apache.juneau.rest.RestServletDefault}  now automatically processes <js>"/favicon.ico"</js> requests by 
-				overriding the new {@link org.apache.juneau.rest.RestServlet#handleNotFound(int,RestRequest,RestResponse)} method.
+				overriding the new <code><del>RestServlet.handleNotFound(int,RestRequest,RestResponse)</del></code> method.
 			<li>New {@link org.apache.juneau.rest.RestRequest} methods: 
 				<ul>
 					<li>{@link org.apache.juneau.rest.RestRequest#resolveVars(String)}
 					<li><code>RestRequest.getVarResource(String)</code>
-					<li>{@link org.apache.juneau.rest.RestRequest#getConfig()}
+					<li><code><del>RestRequest.getConfig()</del></code>
 				</ul>
 			<li>New {@link org.apache.juneau.rest.RestResponse} methods: 
 				<ul>
@@ -6816,11 +6852,11 @@
 				These replace the various <code>description</code> annotations added 2 days ago with a simpler design.
 			<li>New methods on {@link org.apache.juneau.rest.RestServlet}:
 				<ul>
-					<li>{@link org.apache.juneau.rest.RestServlet#getMethodDescription(String,RestRequest)} so that subclasses
+					<li><code><del>RestServlet.getMethodDescription(String,RestRequest)</del></code> so that subclasses
 						can override the method description in the OPTIONS page.
 					<li><del><code>RestServlet.createRequestVarResolver(RestRequest)</code></del> so that subclasses
 						can override and augment the variable resolver.
-					<li>{@link org.apache.juneau.rest.RestServlet#resolveChild(Class)} and {@link org.apache.juneau.rest.RestServlet#replaceChild(RestServlet)}
+					<li><code><del>RestServlet.resolveChild(Class)</del></code> and <code><del>RestServlet.replaceChild(RestServlet)</del></code>
 						classes that allows customized resolution of servlet instances (e.g. if services are defined in OSGi).
 				</ul> 
 			<li>Reverted the <del><code>MethodDescription</code></del> back to 5.1.0.16 since it was being used by someone.
@@ -6904,7 +6940,7 @@
 					<li><code>$R{trimmedRequestURI}</code> - Returns value from {@link org.apache.juneau.rest.RestRequest#getTrimmedRequestURI()}
 					<li><code>$E{var}</code> - Environment variables.
 				</ul>
-			<li>Added methods {@link org.apache.juneau.rest.RestServlet#getDescription(RestRequest)} and <del><code>RestServlet.getLabel(RestRequest)</code></del>.
+			<li>Added methods <code><del>RestServlet.getDescription(RestRequest)</del></code> and <del><code>RestServlet.getLabel(RestRequest)</code></del>.
 			<li>{@link org.apache.juneau.rest.RestServletDefault} and {@link org.apache.juneau.rest.jena.RestServletJenaDefault} now provide default HTML titles
 				and descriptions:
 				<p class='bcode'>
@@ -7456,7 +7492,7 @@
 		</p>
 		
 		<ul class='spaced-list'>
-			<li>New {@link org.apache.juneau.rest.RestServlet#getPath()} method.
+			<li>New <code><del>RestServlet.getPath()</del></code> method.
 			<li>New <code>SerializerContext.getJavaMethod()</code> and <code>ParserContext.getJavaMethod()</code>
 					to allow access to REST methods that invoked the serializers or parsers.
 				For example, can be used to access additional annotations on REST methods to perform special handing
@@ -7691,20 +7727,20 @@
 			<li>
 				New methods on {@link org.apache.juneau.rest.RestServlet} that allow easier customization by subclasses:
 				<ul>
-					<li><code>RestServlet.createConfigFactory()</code></li>
-					<li><code>RestServlet.createConverters()</code></li>
-					<li><code>RestServlet.createDefaultRequestHeaders()</code></li>
-					<li><code>RestServlet.createDefaultResponseHeaders()</code></li>
-					<li><code>RestServlet.createEncoders()</code></li>
-					<li><code>RestServlet.createFilters()</code></li>
-					<li><code>RestServlet.createGuards()</code></li>
-					<li><code>RestServlet.createMimetypesFileTypeMap()</code></li>
-					<li><code>RestServlet.createParsers()</code></li>
-					<li>{@link org.apache.juneau.rest.RestServlet#createProperties()}</li>
-					<li>{@link org.apache.juneau.rest.RestServlet#createRequestProperties(ObjectMap,RestRequest)}</li>
-					<li><code>RestServlet.createRequestVarResolver(RestRequest)</code></li>
-					<li><code>RestServlet.createSerializers()</code></li>
-					<li><code>RestServlet.createUrlEncodingParser()</code></li>
+					<li><code><del>RestServlet.createConfigFactory()</del></code></li>
+					<li><code><del>RestServlet.createConverters()</del></code></li>
+					<li><code><del>RestServlet.createDefaultRequestHeaders()</del></code></li>
+					<li><code><del>RestServlet.createDefaultResponseHeaders()</del></code></li>
+					<li><code><del>RestServlet.createEncoders()</del></code></li>
+					<li><code><del>RestServlet.createFilters()</del></code></li>
+					<li><code><del>RestServlet.createGuards()</del></code></li>
+					<li><code><del>RestServlet.createMimetypesFileTypeMap()</del></code></li>
+					<li><code><del>RestServlet.createParsers()</del></code></li>
+					<li><code><del>RestServlet.createProperties()</del></code></li>
+					<li><code><del>RestServlet.createRequestProperties(ObjectMap,RestRequest)</del></code></li>
+					<li><code><del>RestServlet.createRequestVarResolver(RestRequest)</del></code></li>
+					<li><code><del>RestServlet.createSerializers()</del></code></li>
+					<li><code><del>RestServlet.createUrlEncodingParser()</del></code></li>
 				</ul>
 			</li>
 			<li>
@@ -8537,7 +8573,7 @@
 				Support for <code>Accept</code> and <code>Content-Type</code> <js>"application/x-www-form-urlencoded"</js> added by default on {@link org.apache.juneau.rest.RestServletDefault}.
 			</li>
 			<li>
-				New {@link org.apache.juneau.rest.RestServlet#renderError(HttpServletRequest,HttpServletResponse,RestException)} method to allow customized handling of response errors.
+				New <code><del>RestServlet.renderError(HttpServletRequest,HttpServletResponse,RestException)</del></code> method to allow customized handling of response errors.
 			</li>
 		</ul>
 	</div>
@@ -8557,7 +8593,7 @@
 		<h6 class='topic'>REST server API changes</h6>
 		<ul class='spaced-list'>
 			<li>New <code>RestServletProperties</code> class that defines all the class-level properties that can be set on the servlet.</li>
-			<li>Properties can be set through {@link org.apache.juneau.rest.annotation.RestResource#properties() @RestResource.properties} annotation, or new {@link org.apache.juneau.rest.RestServlet#setProperty(String,Object)} method.</li>
+			<li>Properties can be set through {@link org.apache.juneau.rest.annotation.RestResource#properties() @RestResource.properties} annotation, or new <code><del>RestServlet.setProperty(String,Object)</del></code> method.</li>
 			<li>New <js>"?noTrace"</js> URL parameter to prevent stack traces from being logged (for JUnit testing of error conditions).</li>
 			<li>New <code>RestServletProperties.REST_useStackTraceHashes</code> property to prevent the same stack trace from being logged multiple times.</li>
 			<li>New <code>RestServletProperties.REST_renderResponseStackTraces</code> property for preventing stack traces in responses for security reasons.</li>
@@ -9041,6 +9077,4 @@
 	</div>
 
 </div>
-</body>
-		
-		
+</body>								

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DirectoryResource.java
----------------------------------------------------------------------
diff --git a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DirectoryResource.java b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DirectoryResource.java
index 6a8512c..f0b6a3d 100644
--- a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DirectoryResource.java
+++ b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DirectoryResource.java
@@ -15,15 +15,13 @@ package org.apache.juneau.examples.rest;
 import static java.util.logging.Level.*;
 import static javax.servlet.http.HttpServletResponse.*;
 import static org.apache.juneau.html.HtmlDocSerializerContext.*;
-import static org.apache.juneau.rest.RestServletContext.*;
+import static org.apache.juneau.rest.RestContext.*;
 
 import java.io.*;
 import java.net.*;
 import java.util.*;
 import java.util.logging.*;
 
-import javax.servlet.*;
-
 import org.apache.juneau.*;
 import org.apache.juneau.microservice.*;
 import org.apache.juneau.rest.*;
@@ -58,9 +56,10 @@ public class DirectoryResource extends Resource {
 
 	private static Logger logger = Logger.getLogger(DirectoryResource.class.getName());
 
-	@Override /* Servlet */
-	public void init() throws ServletException {
-		ObjectMap p = getProperties();
+	@Override /* RestServlet */
+	public synchronized void init(RestConfig config) throws Exception {
+		super.init(config);
+		ObjectMap p = config.getProperties();
 		rootDir = new File(p.getString("rootDir"));
 		allowViews = p.getBoolean("allowViews", false);
 		allowDeletes = p.getBoolean("allowDeletes", false);

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DockerRegistryResource.java
----------------------------------------------------------------------
diff --git a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DockerRegistryResource.java b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DockerRegistryResource.java
index eab51fa..00bc907 100644
--- a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DockerRegistryResource.java
+++ b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DockerRegistryResource.java
@@ -16,8 +16,7 @@ import static org.apache.juneau.html.HtmlDocSerializerContext.*;
 
 import java.util.*;
 
-import javax.servlet.*;
-
+import org.apache.juneau.ini.*;
 import org.apache.juneau.microservice.*;
 import org.apache.juneau.rest.*;
 import org.apache.juneau.rest.annotation.*;
@@ -38,13 +37,15 @@ public class DockerRegistryResource extends Resource {
 	private static final long serialVersionUID = 1L;
 
 	// Get registry URL from examples.cfg file.
-	private String registryUrl = getConfig().getString("DockerRegistry/url");
+	private String registryUrl;
 
 	RestClient rc;
 
 	@Override /* Servlet */
-	public void init() throws ServletException {
-		super.init();
+	public synchronized void init(RestConfig servletConfig) throws Exception {
+		super.init(servletConfig);
+		ConfigFile cf = servletConfig.getConfigFile();
+		registryUrl = cf.getString("DockerRegistry/url");
 		rc = new RestClientBuilder().build();
 	}
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SampleRemoteableServlet.java
----------------------------------------------------------------------
diff --git a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SampleRemoteableServlet.java b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SampleRemoteableServlet.java
index 68f457b..4173623 100644
--- a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SampleRemoteableServlet.java
+++ b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SampleRemoteableServlet.java
@@ -13,7 +13,7 @@
 package org.apache.juneau.examples.rest;
 
 import static org.apache.juneau.html.HtmlDocSerializerContext.*;
-import static org.apache.juneau.rest.RestServletContext.*;
+import static org.apache.juneau.rest.RestContext.*;
 
 import java.util.*;
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SqlQueryResource.java
----------------------------------------------------------------------
diff --git a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SqlQueryResource.java b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SqlQueryResource.java
index bd270d9..e32d302 100644
--- a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SqlQueryResource.java
+++ b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SqlQueryResource.java
@@ -43,17 +43,20 @@ import org.apache.juneau.rest.annotation.Body;
 public class SqlQueryResource extends Resource {
 	private static final long serialVersionUID = 1L;
 
-	private ConfigFile cf = getConfig();
-
-	private String driver = cf.getString("SqlQueryResource/driver");
-	private String connectionUrl = cf.getString("SqlQueryResource/connectionUrl");
-	private boolean
-		allowUpdates = cf.getBoolean("SqlQueryResource/allowUpdates", false),
-		allowTempUpdates = cf.getBoolean("SqlQueryResource/allowTempUpdates", false),
+	private String driver, connectionUrl;
+	private boolean allowUpdates, allowTempUpdates, includeRowNums;
+
+	@Override /* RestServlet */
+	public synchronized void init(RestConfig servletConfig) throws Exception {
+		super.init(servletConfig);
+		ConfigFile cf = servletConfig.getConfigFile();
+
+		driver = cf.getString("SqlQueryResource/driver");
+		connectionUrl = cf.getString("SqlQueryResource/connectionUrl");
+		allowUpdates = cf.getBoolean("SqlQueryResource/allowUpdates", false);
+		allowTempUpdates = cf.getBoolean("SqlQueryResource/allowTempUpdates", false);
 		includeRowNums = cf.getBoolean("SqlQueryResource/includeRowNums", false);
-
-	@Override /* Servlet */
-	public void init() {
+		
 		try {
 			Class.forName(driver).newInstance();
 		} catch (Exception e) {

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/addressbook/AddressBookResource.java
----------------------------------------------------------------------
diff --git a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/addressbook/AddressBookResource.java b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/addressbook/AddressBookResource.java
index e077abf..b8a7a50 100644
--- a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/addressbook/AddressBookResource.java
+++ b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/addressbook/AddressBookResource.java
@@ -17,7 +17,7 @@ import static org.apache.juneau.examples.addressbook.AddressBook.*;
 import static org.apache.juneau.html.HtmlDocSerializerContext.*;
 import static org.apache.juneau.jena.RdfCommonContext.*;
 import static org.apache.juneau.jena.RdfSerializerContext.*;
-import static org.apache.juneau.rest.RestServletContext.*;
+import static org.apache.juneau.rest.RestContext.*;
 
 import java.util.*;
 
@@ -288,7 +288,7 @@ public class AddressBookResource extends ResourceJena {
 				)
 		};
 
-		return new DataSet(items, addressBook, this.getBeanContext().createSession());
+		return new DataSet(items, addressBook, this.getContext().getBeanContext().createSession());
 	}
 
 	/**

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-microservice/src/main/java/org/apache/juneau/microservice/Resource.java
----------------------------------------------------------------------
diff --git a/juneau-microservice/src/main/java/org/apache/juneau/microservice/Resource.java b/juneau-microservice/src/main/java/org/apache/juneau/microservice/Resource.java
index c0b9326..6102193 100755
--- a/juneau-microservice/src/main/java/org/apache/juneau/microservice/Resource.java
+++ b/juneau-microservice/src/main/java/org/apache/juneau/microservice/Resource.java
@@ -16,14 +16,13 @@ import static org.apache.juneau.html.HtmlDocSerializerContext.*;
 
 import org.apache.juneau.rest.*;
 import org.apache.juneau.rest.annotation.*;
-import org.apache.juneau.svl.*;
 import org.apache.juneau.svl.vars.*;
 
 /**
  * Superclass for all REST resources.
  * <p>
  * In additional to the functionality of the {@link RestServletDefault} group,
- * augments the {@link #createVarResolver()} method with the following additional variable types:
+ * augments the {@link RestContext#getVarResolver()} method with the following additional variable types:
  * <ul class='spaced-list'>
  * 	<li><code class='snippet'>$ARG{...}</code> - Command line arguments pulled from {@link Microservice#getArgs()}.<br>
  * 		<h6 class='figure'>Example:</h6>
@@ -48,14 +47,12 @@ import org.apache.juneau.svl.vars.*;
 )
 public abstract class Resource extends RestServletDefault {
 
-	/**
-	 * Adds $ARG and $MF variables to variable resolver defined on {@link RestServlet#createVarResolver()}.
-	 */
-	@Override
-	protected VarResolverBuilder createVarResolver() {
-		return super.createVarResolver()
-			.vars(ArgsVar.class, ManifestFileVar.class)
-			.contextObject(ArgsVar.SESSION_args, Microservice.getArgs())
-			.contextObject(ManifestFileVar.SESSION_manifest, Microservice.getManifest());
+	@Override /* RestServlet */
+	public synchronized void init(RestConfig config) throws Exception {
+		config
+			.addVars(ArgsVar.class, ManifestFileVar.class)
+			.addVarContextObject(ArgsVar.SESSION_args, Microservice.getArgs())
+			.addVarContextObject(ManifestFileVar.SESSION_manifest, Microservice.getManifest());
+		super.init(config);
 	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-microservice/src/main/java/org/apache/juneau/microservice/ResourceGroup.java
----------------------------------------------------------------------
diff --git a/juneau-microservice/src/main/java/org/apache/juneau/microservice/ResourceGroup.java b/juneau-microservice/src/main/java/org/apache/juneau/microservice/ResourceGroup.java
index 610575d..440bd28 100755
--- a/juneau-microservice/src/main/java/org/apache/juneau/microservice/ResourceGroup.java
+++ b/juneau-microservice/src/main/java/org/apache/juneau/microservice/ResourceGroup.java
@@ -16,14 +16,13 @@ import static org.apache.juneau.html.HtmlDocSerializerContext.*;
 
 import org.apache.juneau.rest.*;
 import org.apache.juneau.rest.annotation.*;
-import org.apache.juneau.svl.*;
 import org.apache.juneau.svl.vars.*;
 
 /**
  * Superclass for all REST resource groups.
  * <p>
  * In additional to the functionality of the {@link RestServletGroupDefault} group,
- * augments the {@link #createVarResolver()} method with the following additional variable types:
+ * augments the {@link RestContext#getVarResolver()} method with the following additional variable types:
  * <ul class='spaced-list'>
  * 	<li><jk>$ARG{...}</jk> - Command line arguments.<br>
  * 		Resolves values from {@link Microservice#getArgs()}.<br>
@@ -49,14 +48,12 @@ import org.apache.juneau.svl.vars.*;
 )
 public abstract class ResourceGroup extends RestServletGroupDefault {
 
-	/**
-	 * Adds $ARG and $MF variables to variable resolver defined on {@link RestServlet#createVarResolver()}.
-	 */
-	@Override
-	protected VarResolverBuilder createVarResolver() {
-		return super.createVarResolver()
-			.vars(ArgsVar.class, ManifestFileVar.class)
-			.contextObject(ArgsVar.SESSION_args, Microservice.getArgs())
-			.contextObject(ManifestFileVar.SESSION_manifest, Microservice.getManifest());
+	@Override /* RestServlet */
+	public synchronized void init(RestConfig config) throws Exception {
+		config
+			.addVars(ArgsVar.class, ManifestFileVar.class)
+			.addVarContextObject(ArgsVar.SESSION_args, Microservice.getArgs())
+			.addVarContextObject(ManifestFileVar.SESSION_manifest, Microservice.getManifest());
+		super.init(config);
 	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-microservice/src/main/java/org/apache/juneau/microservice/package.html
----------------------------------------------------------------------
diff --git a/juneau-microservice/src/main/java/org/apache/juneau/microservice/package.html b/juneau-microservice/src/main/java/org/apache/juneau/microservice/package.html
index b546032..f77d119 100755
--- a/juneau-microservice/src/main/java/org/apache/juneau/microservice/package.html
+++ b/juneau-microservice/src/main/java/org/apache/juneau/microservice/package.html
@@ -612,8 +612,9 @@
 					<li><l>$MF{key}, $MF{key,default}</l> - Manifest file entries.
 					<li><l>$ARG{key}, $ARG{key,default}</l> - Command-line arguments.
 				</ul>
-				Additional user-defined variables can be defined by overriding the {@link org.apache.juneau.microservice.Microservice#createVarResolver()} method.
-			<li class='m'>{@link org.apache.juneau.rest.RestServlet#getConfig()} - An instance method to access it from inside a REST servlet.<br>
+				Additional user-defined variables can be defined by overriding the {@link org.apache.juneau.microservice.Microservice#createVarResolver()} method
+				and using the {@link org.apache.juneau.rest.RestConfig#addVars(Class...)} method.
+			<li class='m'>{@link org.apache.juneau.rest.RestContext#getConfigFile()} - An instance method to access it from inside a REST servlet.<br>
 				The following variables are available in addition to the variables defined above:
 				<ul>
 					<li><l>$I{key}, $I{key,default}</l> - Servlet initialization parameters.
@@ -640,9 +641,10 @@
 	}		
 				</p>
 				<p>
-					Additional user-defined variables can be defined at this level by overriding the {@link org.apache.juneau.microservice.Resource#createVarResolver()} method.
+					Additional user-defined variables can be defined at this level by overriding the {@link org.apache.juneau.microservice.Resource#init(RestConfig)} method
+					and using the {@link org.apache.juneau.rest.RestConfig#addVars(Class...)} method.
 				</p>
-			<li class='m'>{@link org.apache.juneau.rest.RestRequest#getConfig()} - An instance method to access it from inside a REST method.<br>
+			<li class='m'>{@link org.apache.juneau.rest.RestRequest#getConfigFile()} - An instance method to access it from inside a REST method.<br>
 				The following variables are available in addition to the variables defined above:
 				<ul>
 					<li><l>$L{key}, $L{key,args}</l> - Localized variables pulled from {@link org.apache.juneau.rest.RestRequest#getMessage(String, Object...)}.
@@ -709,7 +711,7 @@
 	<ck>MyHelloResource.HelloMessage</ck> = <cv>Hello {0}!</cv> 
 				</p>
 				<p>
-					Additional user-defined variables can be defined at this level by overriding the {@link org.apache.juneau.rest.RestServlet#createVarResolver()} method.
+					Additional user-defined variables can be defined at this level by overriding the {@link org.apache.juneau.rest.RestConfig#addVars(Class...)} method.
 				</p>
 		</ul>
 		<p>

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-microservice/src/main/java/org/apache/juneau/microservice/resources/ConfigResource.java
----------------------------------------------------------------------
diff --git a/juneau-microservice/src/main/java/org/apache/juneau/microservice/resources/ConfigResource.java b/juneau-microservice/src/main/java/org/apache/juneau/microservice/resources/ConfigResource.java
index c7f1b08..f258ec8 100755
--- a/juneau-microservice/src/main/java/org/apache/juneau/microservice/resources/ConfigResource.java
+++ b/juneau-microservice/src/main/java/org/apache/juneau/microservice/resources/ConfigResource.java
@@ -49,7 +49,7 @@ public class ConfigResource extends Resource {
 	 */
 	@RestMethod(name="GET", path="/", description="Show contents of config file.")
 	public ConfigFile getConfigContents() throws Exception {
-		return getConfig();
+		return getServletConfig().getConfigFile();
 	}
 
 	/**
@@ -68,7 +68,7 @@ public class ConfigResource extends Resource {
 					tr(th().child("Contents")),
 					tr(th().child(
 						textarea().name("contents").rows(40).cols(120).style("white-space:pre;word-wrap:normal;overflow-x:scroll;font-family:monospace;")
-							.text(getConfig().toString()))
+							.text(getConfigContents().toString()))
 					)
 				)
 			)
@@ -143,7 +143,7 @@ public class ConfigResource extends Resource {
 	)
 	public ConfigFile setConfigContents(@Body Reader contents) throws Exception {
 		ConfigFile cf2 = ConfigMgr.DEFAULT.create().load(contents);
-		return getConfig().merge(cf2).save();
+		return getConfigContents().merge(cf2).save();
 	}
 
 	/**
@@ -162,7 +162,7 @@ public class ConfigResource extends Resource {
 		}
 	)
 	public ObjectMap setConfigSection(@Path("section") String section, @Body Map<String,String> contents) throws Exception {
-		getConfig().setSection(section, contents);
+		getConfigContents().setSection(section, contents);
 		return getSection(section);
 	}
 
@@ -184,12 +184,12 @@ public class ConfigResource extends Resource {
 		}
 	)
 	public String setConfigSection(@Path("section") String section, @Path("key") String key, @Body String value) throws Exception {
-		getConfig().put(section, key, value, false);
+		getConfigContents().put(section, key, value, false);
 		return getSection(section).getString(key);
 	}
 
-	private ObjectMap getSection(String name) {
-		ObjectMap m = getConfig().getSectionMap(name);
+	private ObjectMap getSection(String name) throws Exception {
+		ObjectMap m = getConfigContents().getSectionMap(name);
 		if (m == null)
 			throw new RestException(SC_NOT_FOUND, "Section not found.");
 		return m;

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-microservice/src/main/java/org/apache/juneau/microservice/resources/DirectoryResource.java
----------------------------------------------------------------------
diff --git a/juneau-microservice/src/main/java/org/apache/juneau/microservice/resources/DirectoryResource.java b/juneau-microservice/src/main/java/org/apache/juneau/microservice/resources/DirectoryResource.java
index da1cae9..0922221 100755
--- a/juneau-microservice/src/main/java/org/apache/juneau/microservice/resources/DirectoryResource.java
+++ b/juneau-microservice/src/main/java/org/apache/juneau/microservice/resources/DirectoryResource.java
@@ -15,7 +15,7 @@ package org.apache.juneau.microservice.resources;
 import static java.util.logging.Level.*;
 import static javax.servlet.http.HttpServletResponse.*;
 import static org.apache.juneau.html.HtmlDocSerializerContext.*;
-import static org.apache.juneau.rest.RestServletContext.*;
+import static org.apache.juneau.rest.RestContext.*;
 
 import java.io.*;
 import java.net.*;

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-microservice/src/main/java/org/apache/juneau/microservice/resources/LogsResource.java
----------------------------------------------------------------------
diff --git a/juneau-microservice/src/main/java/org/apache/juneau/microservice/resources/LogsResource.java b/juneau-microservice/src/main/java/org/apache/juneau/microservice/resources/LogsResource.java
index ea1bc2f..b2011a4 100755
--- a/juneau-microservice/src/main/java/org/apache/juneau/microservice/resources/LogsResource.java
+++ b/juneau-microservice/src/main/java/org/apache/juneau/microservice/resources/LogsResource.java
@@ -14,7 +14,7 @@ package org.apache.juneau.microservice.resources;
 
 import static javax.servlet.http.HttpServletResponse.*;
 import static org.apache.juneau.html.HtmlDocSerializerContext.*;
-import static org.apache.juneau.rest.RestServletContext.*;
+import static org.apache.juneau.rest.RestContext.*;
 
 import java.io.*;
 import java.net.*;
@@ -53,14 +53,8 @@ import org.apache.juneau.transforms.*;
 public class LogsResource extends Resource {
 	private static final long serialVersionUID = 1L;
 
-	private ConfigFile cf = getConfig();
-
-	private File logDir = new File(cf.getString("Logging/logDir", "."));
-	private LogEntryFormatter leFormatter = new LogEntryFormatter(
-		cf.getString("Logging/format", "[{date} {level}] {msg}%n"),
-		cf.getString("Logging/dateFormat", "yyyy.MM.dd hh:mm:ss"),
-		cf.getBoolean("Logging/useStackTraceHashes")
-	);
+	private File logDir;
+	private LogEntryFormatter leFormatter;
 
 	private final FileFilter filter = new FileFilter() {
 		@Override /* FileFilter */
@@ -68,6 +62,19 @@ public class LogsResource extends Resource {
 			return f.isDirectory() || f.getName().endsWith(".log");
 		}
 	};
+	
+	@Override /* RestServlet */
+	public synchronized void init(RestConfig config) throws Exception {
+		super.init(config);
+		ConfigFile cf = config.getConfigFile();
+		
+		logDir = new File(cf.getString("Logging/logDir", "."));
+		leFormatter = new LogEntryFormatter(
+			cf.getString("Logging/format", "[{date} {level}] {msg}%n"),
+			cf.getString("Logging/dateFormat", "yyyy.MM.dd hh:mm:ss"),
+			cf.getBoolean("Logging/useStackTraceHashes")
+		);
+	}
 
 	/**
 	 * [GET /*] - Get file details or directory listing.

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest-jaxrs/src/main/java/org/apache/juneau/rest/jaxrs/JuneauProvider.java
----------------------------------------------------------------------
diff --git a/juneau-rest-jaxrs/src/main/java/org/apache/juneau/rest/jaxrs/JuneauProvider.java b/juneau-rest-jaxrs/src/main/java/org/apache/juneau/rest/jaxrs/JuneauProvider.java
index 6ded12d..e2a6571 100644
--- a/juneau-rest-jaxrs/src/main/java/org/apache/juneau/rest/jaxrs/JuneauProvider.java
+++ b/juneau-rest-jaxrs/src/main/java/org/apache/juneau/rest/jaxrs/JuneauProvider.java
@@ -65,7 +65,7 @@ public @interface JuneauProvider {
 	 * <p>
 	 * Any of the following property names can be specified:
 	 * <ul>
-	 * 	<li>{@link RestServletContext}
+	 * 	<li>{@link RestContext}
 	 * 	<li>{@link BeanContext}
 	 * 	<li>{@link SerializerContext}
 	 * 	<li>{@link ParserContext}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/AcceptCharsetResource.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/AcceptCharsetResource.java b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/AcceptCharsetResource.java
index ee3a172..9ab29c6 100644
--- a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/AcceptCharsetResource.java
+++ b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/AcceptCharsetResource.java
@@ -12,7 +12,7 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.rest.test;
 
-import static org.apache.juneau.rest.RestServletContext.*;
+import static org.apache.juneau.rest.RestContext.*;
 
 import java.io.*;
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ConfigResource.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ConfigResource.java b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ConfigResource.java
index 5be60cb..e8f84cc 100644
--- a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ConfigResource.java
+++ b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ConfigResource.java
@@ -28,11 +28,11 @@ public class ConfigResource extends Resource {
 
 	@RestMethod(name="GET", path="/")
 	public ConfigFile test1(RestRequest req) {
-		return req.getConfig();
+		return req.getConfigFile();
 	}
 
 	@RestMethod(name="GET", path="/{key}/{class}")
 	public Object test2(RestRequest req, @Path("key") String key, @Path("class") Class<?> c) throws Exception {
-		return req.getConfig().getObject(c, key);
+		return req.getConfigFile().getObject(c, key);
 	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ContentResource.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ContentResource.java b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ContentResource.java
index 3da65d6..6575ec9 100644
--- a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ContentResource.java
+++ b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ContentResource.java
@@ -12,7 +12,7 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.rest.test;
 
-import static org.apache.juneau.rest.RestServletContext.*;
+import static org.apache.juneau.rest.RestContext.*;
 
 import java.util.*;
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/GroupsResource.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/GroupsResource.java b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/GroupsResource.java
index 0ca0557..adad8d2 100644
--- a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/GroupsResource.java
+++ b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/GroupsResource.java
@@ -29,6 +29,12 @@ import org.apache.juneau.serializer.*;
 public class GroupsResource extends RestServlet {
 	private static final long serialVersionUID = 1L;
 
+	@Override /* RestServlet */
+	public synchronized void init(RestConfig config) throws Exception {
+		config.addSerializers(SSerializer.class).addParsers(PParser.class);
+		super.init(config);
+	}
+
 	@Produces("text/s1,text/s2")
 	public static class SSerializer extends WriterSerializer {
 
@@ -56,17 +62,6 @@ public class GroupsResource extends RestServlet {
 		}
 	}
 
-
-	@Override /* RestServlet */
-	public SerializerGroupBuilder createSerializers(ObjectMap properties, Class<?>[] beanFilters, Class<?>[] pojoSwaps) throws Exception {
-		return super.createSerializers(properties, beanFilters, pojoSwaps).append(SSerializer.class);
-	}
-
-	@Override /* RestServlet */
-	public ParserGroupBuilder createParsers(ObjectMap properties, Class<?>[] beanFilters, Class<?>[] pojoSwaps) throws Exception {
-		return super.createParsers(properties, beanFilters, pojoSwaps).append(PParser.class);
-	}
-
 	//====================================================================================================
 	// Serializer defined on class.
 	//====================================================================================================


[4/7] incubator-juneau git commit: Allow @RestResource/@RestMethod annotations to be used on any classes.

Posted by ja...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/RestContext.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestContext.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestContext.java
new file mode 100644
index 0000000..243afee
--- /dev/null
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestContext.java
@@ -0,0 +1,1418 @@
+// ***************************************************************************************************************************
+// * 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 javax.servlet.http.HttpServletResponse.*;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.*;
+
+import javax.activation.*;
+import javax.servlet.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.encoders.*;
+import org.apache.juneau.ini.*;
+import org.apache.juneau.internal.*;
+import org.apache.juneau.json.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.rest.vars.*;
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.svl.*;
+import org.apache.juneau.urlencoding.*;
+import org.apache.juneau.utils.*;
+
+/**
+ * Contains all the configuration on a REST resource and the entry points for handling REST calls.
+ * <p>
+ * See {@link PropertyStore} for more information about context properties.
+ */
+public final class RestContext extends Context {
+
+	/**
+	 * <b>Configuration property:</b>  Enable header URL parameters.
+	 * <p>
+	 * <ul>
+	 * 	<li><b>Name:</b> <js>"RestServlet.allowHeaderParams"</js>
+	 * 	<li><b>Data type:</b> <code>Boolean</code>
+	 * 	<li><b>Default:</b> <jk>true</jk>
+	 * </ul>
+	 * <p>
+	 * When enabled, headers such as <js>"Accept"</js> and <js>"Content-Type"</js> to be passed in as URL query parameters.
+	 * For example:  <js>"?Accept=text/json&amp;Content-Type=text/json"</js>
+	 * <p>
+	 * Parameter names are case-insensitive.
+	 * <p>
+	 * Useful for debugging REST interface using only a browser.
+	 * <p>
+	 * Applicable to servlet class only.
+	 */
+	public static final String REST_allowHeaderParams = "RestServlet.allowHeaderParams";
+
+	/**
+	 * <b>Configuration property:</b>  Enable <js>"method"</js> URL parameter for specific HTTP methods.
+	 * <p>
+	 * <ul>
+	 * 	<li><b>Name:</b> <js>"RestServlet.allowMethodParam"</js>
+	 * 	<li><b>Data type:</b> <code>String</code>
+	 * 	<li><b>Default:</b> <js>""</js>
+	 * </ul>
+	 * <p>
+	 * When specified, the HTTP method can be overridden by passing in a <js>"method"</js> URL parameter on a regular GET request.
+	 * For example:  <js>"?method=OPTIONS"</js>
+	 * <p>
+	 * Format is a comma-delimited list of HTTP method names that can be passed in as a method parameter.
+	 * Parameter name is case-insensitive.
+	 * Use "*" to represent all methods.
+	 * For backwards compatibility, "true" also means "*".
+	 * <p>
+	 * Note that per the <a class="doclink" href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html">HTTP specification</a>, special care should
+	 * 	be taken when allowing non-safe (POST, PUT, DELETE) methods to be invoked through GET requests.
+	 * <p>
+	 * Applicable to servlet class only.
+	 * <p>
+	 * Example: <js>"HEAD,OPTIONS"</js>
+	 */
+	public static final String REST_allowMethodParam = "RestServlet.allowMethodParam";
+
+	/**
+	 * <b>Configuration property:</b>  Enable <js>"body"</js> URL parameter.
+	 * <p>
+	 * <ul>
+	 * 	<li><b>Name:</b> <js>"RestServlet.allowBodyParam"</js>
+	 * 	<li><b>Data type:</b> <code>Boolean</code>
+	 * 	<li><b>Default:</b> <jk>true</jk>
+	 * </ul>
+	 * <p>
+	 * When enabled, the HTTP body content on PUT and POST requests can be passed in as text using the <js>"body"</js> URL parameter.
+	 * For example:  <js>"?body={name:'John%20Smith',age:45}"</js>
+	 * <p>
+	 * Parameter name is case-insensitive.
+	 * <p>
+	 * Useful for debugging PUT and POST methods using only a browser.
+	 * <p>
+	 * Applicable to servlet class only.
+	 */
+	public static final String REST_allowBodyParam = "RestServlet.allowBodyParam";
+
+	/**
+	 * <b>Configuration property:</b>  Render stack traces.
+	 * <p>
+	 * <ul>
+	 * 	<li><b>Name:</b> <js>"RestServlet.renderResponseStackTraces"</js>
+	 * 	<li><b>Data type:</b> <code>Boolean</code>
+	 * 	<li><b>Default:</b> <jk>false</jk>
+	 * </ul>
+	 * <p>
+	 * Render stack traces in HTTP response bodies when errors occur.
+	 * <p>
+	 * When enabled, Java stack traces will be rendered in the output response.
+	 * Useful for debugging, although allowing stack traces to be rendered may cause security concerns.
+	 * <p>
+	 * Applicable to servlet class only.
+	 */
+	public static final String REST_renderResponseStackTraces = "RestServlet.renderResponseStackTraces";
+
+	/**
+	 * <b>Configuration property:</b>  Use stack trace hashes.
+	 * <p>
+	 * <ul>
+	 * 	<li><b>Name:</b> <js>"RestServlet.useStackTraceHashes"</js>
+	 * 	<li><b>Data type:</b> <code>Boolean</code>
+	 * 	<li><b>Default:</b> <jk>true</jk>
+	 * </ul>
+	 * <p>
+	 * When enabled, the number of times an exception has occurred will be determined based on stack trace hashsums,
+	 * 	made available through the {@link RestException#getOccurrence()} method.
+	 * <p>
+	 * Applicable to servlet class only.
+	 */
+	public static final String REST_useStackTraceHashes = "RestServlet.useStackTraceHashes";
+
+	/**
+	 * <b>Configuration property:</b>  Default character encoding.
+	 * <p>
+	 * <ul>
+	 * 	<li><b>Name:</b> <js>"RestServlet.defaultCharset"</js>
+	 * 	<li><b>Data type:</b> <code>String</code>
+	 * 	<li><b>Default:</b> <js>"utf-8"</js>
+	 * </ul>
+	 * <p>
+	 * The default character encoding for the request and response if not specified on the request.
+	 * <p>
+	 * Applicable to servlet class and methods.
+	 */
+	public static final String REST_defaultCharset = "RestServlet.defaultCharset";
+
+	/**
+	 * <b>Configuration property:</b>  Expected format of request parameters.
+	 * <p>
+	 * <ul>
+	 * 	<li><b>Name:</b> <js>"RestServlet.paramFormat"</js>
+	 * 	<li><b>Data type:</b> <code>String</code>
+	 * 	<li><b>Default:</b> <js>"UON"</js>
+	 * </ul>
+	 * <p>
+	 * Possible values:
+	 * <ul class='spaced-list'>
+	 * 	<li><js>"UON"</js> - URL-Encoded Object Notation.<br>
+	 * 		This notation allows for request parameters to contain arbitrarily complex POJOs.
+	 * 	<li><js>"PLAIN"</js> - Plain text.<br>
+	 * 		This treats request parameters as plain text.<br>
+	 * 		Only POJOs directly convertable from <l>Strings</l> can be represented in parameters when using this mode.
+	 * </ul>
+	 * <p>
+	 * Note that the parameter value <js>"(foo)"</js> is interpreted as <js>"(foo)"</js> when using plain mode, but
+	 * 	<js>"foo"</js> when using UON mode.
+	 * <p>
+	 * The format can also be specified per-parameter using the {@link FormData#format() @FormData.format()} and {@link Query#format() @Query.format()}
+	 * 	annotations.
+	 * <p>
+	 * Applicable to servlet class and methods.
+	 */
+	public static final String REST_paramFormat = "RestServlet.paramFormat";
+
+
+	//--------------------------------------------------------------------------------
+	// Automatically added properties.
+	//--------------------------------------------------------------------------------
+
+	/**
+	 * The request servlet path.
+	 * <p>
+	 * Automatically added to properties returned by {@link SerializerSession#getProperties()} and {@link ParserSession#getProperties()}.
+	 * <p>
+	 * Equivalent to the value returned by {@link RestRequest#getServletPath()}
+	 */
+	public static final String REST_servletPath = "RestServlet.servletPath";
+
+	/**
+	 * The request servlet URI.
+	 * <p>
+	 * Equivalent to the value returned by {@link RestRequest#getServletURI()}
+	 */
+	public static final String REST_servletURI = "RestServlet.servletURI";
+
+	/**
+	 * The request servlet URI.
+	 * <p>
+	 * Equivalent to the value returned by {@link RestRequest#getRelativeServletURI()}
+	 */
+	public static final String REST_relativeServletURI = "RestServlet.relativeServletURI";
+
+	/**
+	 * The request URI path info.
+	 * <p>
+	 * Automatically added to properties returned by {@link SerializerSession#getProperties()} and {@link ParserSession#getProperties()}.
+	 * <p>
+	 * Equivalent to the value returned by {@link RestRequest#getPathInfo()}
+	 */
+	public static final String REST_pathInfo = "RestServlet.pathInfo";
+
+	/**
+	 * The request URI.
+	 * <p>
+	 * Automatically added to properties returned by {@link SerializerSession#getProperties()} and {@link ParserSession#getProperties()}.
+	 * <p>
+	 * Equivalent to the value returned by {@link RestRequest#getRequestURI()}
+	 */
+	public static final String REST_requestURI = "RestServlet.requestURI";
+
+	/**
+	 * The request method.
+	 * <p>
+	 * Automatically added to properties returned by {@link SerializerSession#getProperties()} and {@link ParserSession#getProperties()}.
+	 * <p>
+	 * Equivalent to the value returned by {@link RestRequest#getMethod()}
+	 */
+	public static final String REST_method = "RestServlet.method";
+
+	/**
+	 * The localized servlet title.
+	 * <p>
+	 * Automatically added to properties returned by {@link SerializerSession#getProperties()} and {@link ParserSession#getProperties()}.
+	 * <p>
+	 * Equivalent to the value returned by {@link RestRequest#getServletTitle()}
+	 */
+	public static final String REST_servletTitle = "RestServlet.servletTitle";
+
+	/**
+	 * The localized servlet description.
+	 * <p>
+	 * Automatically added to properties returned by {@link SerializerSession#getProperties()} and {@link ParserSession#getProperties()}.
+	 * <p>
+	 * Equivalent to the value returned by {@link RestRequest#getServletDescription()}
+	 */
+	public static final String REST_servletDescription = "RestServlet.servletDescription";
+
+	/**
+	 * The localized method summary.
+	 * <p>
+	 * Automatically added to properties returned by {@link SerializerSession#getProperties()} and {@link ParserSession#getProperties()}.
+	 * <p>
+	 * Equivalent to the value returned by {@link RestRequest#getMethodSummary()}
+	 */
+	public static final String REST_methodSummary = "RestServlet.methodSummary";
+
+	/**
+	 * The localized method description.
+	 * <p>
+	 * Automatically added to properties returned by {@link SerializerSession#getProperties()} and {@link ParserSession#getProperties()}.
+	 * <p>
+	 * Equivalent to the value returned by {@link RestRequest#getMethodDescription()}
+	 */
+	public static final String REST_methodDescription = "RestServlet.methodDescription";
+
+	private final Object resource;
+	private final RestConfig config;
+	private final boolean
+		allowHeaderParams,
+		allowBodyParam,
+		renderResponseStackTraces,
+		useStackTraceHashes;
+	private final String
+		defaultCharset,
+		paramFormat,
+		clientVersionHeader,
+		fullPath;
+	private final Set<String> allowMethodParams;
+
+	private final ObjectMap properties;
+	private final Class<?>[]
+		beanFilters,
+		pojoSwaps;
+	private final SerializerGroup serializers;
+	private final ParserGroup parsers;
+	private final UrlEncodingSerializer urlEncodingSerializer;
+	private final UrlEncodingParser urlEncodingParser;
+	private final EncoderGroup encoders;
+	private final MediaType[]
+		supportedContentTypes,
+		supportedAcceptTypes;
+	private final Map<String,String> defaultRequestHeaders;
+	private final Map<String,Object> defaultResponseHeaders;
+	private final BeanContext beanContext;
+	private final RestConverter[] converters;
+	private final RestGuard[] guards;
+	private final ResponseHandler[] responseHandlers;
+	private final MimetypesFileTypeMap mimetypesFileTypeMap;
+	private final StreamResource styleSheet, favIcon;
+	private final Map<String,String> staticFilesMap;
+	private final String[] staticFilesPrefixes;
+	private final MessageBundle msgs;
+	private final ConfigFile configFile;
+	private final VarResolver varResolver;
+	private final Map<String,CallRouter> callRouters;
+	private final Map<String,CallMethod> callMethods;
+	private final Map<String,RestContext> childResources;
+	private final RestLogger logger;
+	private final RestCallHandler callHandler;
+	private final RestInfoProvider infoProvider;
+	private final RestException initException;
+
+	// In-memory cache of images and stylesheets in the org.apache.juneau.rest.htdocs package.
+	private final Map<String,StreamResource> staticFilesCache = new ConcurrentHashMap<String,StreamResource>();
+	private final Map<String,byte[]> resourceStreams = new ConcurrentHashMap<String,byte[]>();
+	private final Map<String,String> resourceStrings = new ConcurrentHashMap<String,String>();
+	private final ConcurrentHashMap<Integer,AtomicInteger> stackTraceHashes = new ConcurrentHashMap<Integer,AtomicInteger>();
+
+
+	/**
+	 * Constructor.
+	 *
+	 * @param resource The resource class (a class annotated with {@link RestResource @RestResource}).
+	 * @param config The servlet configuration object.
+	 * @throws Exception If any initialization problems were encountered.
+	 */
+	@SuppressWarnings("unchecked")
+	public RestContext(Object resource, RestConfig config) throws Exception {
+		super(null);
+		try {
+			this.resource = resource;
+			this.config = config;
+
+			Builder b = new Builder(resource, config);
+			this.allowHeaderParams = b.allowHeaderParams;
+			this.allowBodyParam = b.allowBodyParam;
+			this.renderResponseStackTraces = b.renderResponseStackTraces;
+			this.useStackTraceHashes = b.useStackTraceHashes;
+			this.allowMethodParams = Collections.unmodifiableSet(b.allowMethodParams);
+			this.defaultCharset = b.defaultCharset;
+			this.paramFormat = b.paramFormat;
+			this.varResolver = b.varResolver;
+			this.configFile = b.configFile;
+			this.properties = b.properties;
+			this.beanFilters = b.beanFilters;
+			this.pojoSwaps = b.pojoSwaps;
+			this.serializers = b.serializers;
+			this.parsers = b.parsers;
+			this.urlEncodingSerializer = b.urlEncodingSerializer;
+			this.urlEncodingParser = b.urlEncodingParser;
+			this.encoders = b.encoders;
+			this.supportedContentTypes = ArrayUtils.toObjectArray(b.supportedContentTypes, MediaType.class);
+			this.supportedAcceptTypes = ArrayUtils.toObjectArray(b.supportedAcceptTypes, MediaType.class);
+			this.clientVersionHeader = b.clientVersionHeader;
+			this.defaultRequestHeaders = Collections.unmodifiableMap(b.defaultRequestHeaders);
+			this.defaultResponseHeaders = Collections.unmodifiableMap(b.defaultResponseHeaders);
+			this.beanContext = b.beanContext;
+			this.converters = b.converters.toArray(new RestConverter[b.converters.size()]);
+			this.guards = b.guards.toArray(new RestGuard[b.guards.size()]);
+			this.responseHandlers = ArrayUtils.toObjectArray(b.responseHandlers, ResponseHandler.class);
+			this.mimetypesFileTypeMap = b.mimetypesFileTypeMap;
+			this.styleSheet = b.styleSheet;
+			this.favIcon = b.favIcon;
+			this.staticFilesMap = Collections.unmodifiableMap(b.staticFilesMap);
+			this.staticFilesPrefixes = b.staticFilesPrefixes;
+			this.msgs = b.messageBundle;
+			this.childResources = Collections.synchronizedMap(new LinkedHashMap<String,RestContext>());  // Not unmodifiable on purpose so that children can be replaced.
+			this.logger = b.logger;
+			this.fullPath = b.fullPath;
+
+			//----------------------------------------------------------------------------------------------------
+			// Initialize the child resources.
+			// Done after initializing fields above since we pass this object to the child resources.
+			//----------------------------------------------------------------------------------------------------
+			List<String> methodsFound = new LinkedList<String>();   // Temporary to help debug transient duplicate method issue.
+			Map<String,CallRouter.Builder> routers = new LinkedHashMap<String,CallRouter.Builder>();
+			Map<String,CallMethod> _javaRestMethods = new LinkedHashMap<String,CallMethod>();
+			for (java.lang.reflect.Method method : resource.getClass().getMethods()) {
+				if (method.isAnnotationPresent(RestMethod.class)) {
+					RestMethod a = method.getAnnotation(RestMethod.class);
+					methodsFound.add(method.getName() + "," + a.name() + "," + a.path());
+					try {
+						if (! Modifier.isPublic(method.getModifiers()))
+							throw new RestServletException("@RestMethod method {0}.{1} must be defined as public.", this.getClass().getName(), method.getName());
+
+						CallMethod sm = new CallMethod(resource, method, this);
+						_javaRestMethods.put(method.getName(), sm);
+
+						String httpMethod = sm.getHttpMethod();
+						if (! routers.containsKey(httpMethod))
+							routers.put(httpMethod, new CallRouter.Builder(httpMethod));
+
+						routers.get(httpMethod).add(sm);
+
+					} catch (RestServletException e) {
+						throw new RestServletException("Problem occurred trying to serialize methods on class {0}, methods={1}", this.getClass().getName(), JsonSerializer.DEFAULT_LAX.serialize(methodsFound)).initCause(e);
+					}
+				}
+			}
+			this.callMethods = Collections.unmodifiableMap(_javaRestMethods);
+
+			Map<String,CallRouter> _callRouters = new LinkedHashMap<String,CallRouter>();
+			for (CallRouter.Builder crb : routers.values())
+				_callRouters.put(crb.getHttpMethodName(), crb.build());
+			this.callRouters = Collections.unmodifiableMap(_callRouters);
+
+			// Initialize our child resources.
+			RestResourceResolver rrr = resolve(RestResourceResolver.class, config.resourceResolver);
+			for (Object o : config.childResources) {
+				String path = null;
+				Object r = null;
+				if (o instanceof Pair) {
+					Pair<String,Object> p = (Pair<String,Object>)o;
+					path = p.first();
+					r = p.second();
+				} else if (o instanceof Class<?>) {
+					Class<?> c = (Class<?>)o;
+					r = c;
+				} else {
+					r = o;
+				}
+
+				RestConfig childConfig = null;
+
+				if (o instanceof Class) {
+					Class<?> oc = (Class<?>)o;
+					childConfig = new RestConfig(config.inner, oc, this);
+					r = rrr.resolve(oc, childConfig);
+				} else {
+					r = o;
+					childConfig = new RestConfig(config.inner, o.getClass(), this);
+				}
+
+				if (r instanceof RestServlet) {
+					RestServlet rs = (RestServlet)r;
+					rs.init(childConfig);
+					path = childConfig.path;
+					childResources.put(path, rs.getContext());
+				} else {
+
+					// Call the init(RestConfig) method.
+					java.lang.reflect.Method m2 = ClassUtils.findPublicMethod(r.getClass(), "init", Void.class, RestConfig.class);
+					if (m2 != null)
+						m2.invoke(r, childConfig);
+
+					RestContext rc2 = new RestContext(r, childConfig);
+
+					// Call the init(RestContext) method.
+					m2 = ClassUtils.findPublicMethod(r.getClass(), "init", Void.class, RestContext.class);
+					if (m2 != null)
+						m2.invoke(r, rc2);
+
+					path = childConfig.path;
+					childResources.put(path, rc2);
+				}
+			}
+
+			callHandler = config.callHandler == null ? new RestCallHandler(this) : resolve(RestCallHandler.class, config.callHandler, this);
+			infoProvider = config.infoProvider == null ? new RestInfoProvider(this) : resolve(RestInfoProvider.class, config.infoProvider, this);
+
+		} catch (RestException e) {
+			initException = e;
+			throw e;
+		} catch (Exception e) {
+			initException = new RestException(SC_INTERNAL_SERVER_ERROR, e);
+			throw e;
+		}
+		initException = null;
+	}
+
+	private static class Builder {
+
+		boolean allowHeaderParams, allowBodyParam, renderResponseStackTraces, useStackTraceHashes;
+		VarResolver varResolver;
+		ConfigFile configFile;
+		ObjectMap properties;
+		Class<?>[] beanFilters;
+		Class<?>[] pojoSwaps;
+		SerializerGroup serializers;
+		ParserGroup parsers;
+		UrlEncodingSerializer urlEncodingSerializer;
+		UrlEncodingParser urlEncodingParser;
+		EncoderGroup encoders;
+		String clientVersionHeader = "", defaultCharset, paramFormat;
+		List<MediaType> supportedContentTypes, supportedAcceptTypes;
+		Map<String,String> defaultRequestHeaders = new TreeMap<String,String>(String.CASE_INSENSITIVE_ORDER);
+		Map<String,Object> defaultResponseHeaders;
+		BeanContext beanContext;
+		List<RestConverter> converters = new ArrayList<RestConverter>();
+		List<RestGuard> guards = new ArrayList<RestGuard>();
+		List<ResponseHandler> responseHandlers = new ArrayList<ResponseHandler>();
+		MimetypesFileTypeMap mimetypesFileTypeMap;
+		StreamResource styleSheet, favIcon;
+		Map<String,String> staticFilesMap;
+		String[] staticFilesPrefixes;
+		MessageBundle messageBundle;
+		Set<String> allowMethodParams = new LinkedHashSet<String>();
+		RestLogger logger;
+		String fullPath;
+
+		@SuppressWarnings("unchecked")
+		private Builder(Object resource, RestConfig sc) throws Exception {
+
+			PropertyStore ps = sc.createPropertyStore();
+
+			LinkedHashMap<Class<?>,RestResource> restResourceAnnotationsChildFirst = ReflectionUtils.findAnnotationsMap(RestResource.class, resource.getClass());
+
+			allowHeaderParams = ps.getProperty(REST_allowHeaderParams, boolean.class, true);
+			allowBodyParam = ps.getProperty(REST_allowBodyParam, boolean.class, true);
+			renderResponseStackTraces = ps.getProperty(REST_renderResponseStackTraces, boolean.class, false);
+			useStackTraceHashes = ps.getProperty(REST_useStackTraceHashes, boolean.class, true);
+			defaultCharset = ps.getProperty(REST_defaultCharset, String.class, "utf-8");
+			paramFormat = ps.getProperty(REST_paramFormat, String.class, "");
+
+			for (String m : StringUtils.split(ps.getProperty(REST_allowMethodParam, String.class, ""), ','))
+				if (m.equals("true"))  // For backwards compatibility when this was a boolean field.
+					allowMethodParams.add("*");
+				else
+					allowMethodParams.add(m.toUpperCase());
+
+			varResolver = sc.varResolverBuilder
+				.vars(LocalizationVar.class, RequestVar.class, SerializedRequestAttrVar.class, ServletInitParamVar.class, UrlEncodeVar.class)
+				.build()
+			;
+			configFile = sc.configFile.getResolving(this.varResolver);
+			properties = sc.properties;
+			Collections.reverse(sc.beanFilters);
+			Collections.reverse(sc.pojoSwaps);
+			beanFilters = ArrayUtils.toObjectArray(sc.beanFilters, Class.class);
+			pojoSwaps = ArrayUtils.toObjectArray(sc.pojoSwaps, Class.class);
+			clientVersionHeader = sc.clientVersionHeader;
+
+			// Find resource resource bundle location.
+			for (Map.Entry<Class<?>,RestResource> e : restResourceAnnotationsChildFirst.entrySet()) {
+				Class<?> c = e.getKey();
+				RestResource r = e.getValue();
+				if (! r.messages().isEmpty()) {
+					if (messageBundle == null)
+						messageBundle = new MessageBundle(c, r.messages());
+					else
+						messageBundle.addSearchPath(c, r.messages());
+				}
+			}
+
+			if (messageBundle == null)
+				messageBundle = new MessageBundle(resource.getClass(), "");
+
+			ps.addBeanFilters(beanFilters).addPojoSwaps(pojoSwaps).setProperties(properties);
+
+			serializers = sc.serializers.beanFilters(beanFilters).pojoSwaps(pojoSwaps).properties(properties).build();
+			parsers = sc.parsers.beanFilters(beanFilters).pojoSwaps(pojoSwaps).properties(properties).build();
+			urlEncodingSerializer = new UrlEncodingSerializer(ps);
+			urlEncodingParser = new UrlEncodingParser(ps);
+			encoders = sc.encoders.build();
+			supportedContentTypes = sc.supportedContentTypes != null ? sc.supportedContentTypes : serializers.getSupportedMediaTypes();
+			supportedAcceptTypes = sc.supportedAcceptTypes != null ? sc.supportedAcceptTypes : parsers.getSupportedMediaTypes();
+			defaultRequestHeaders.putAll(sc.defaultRequestHeaders);
+			defaultResponseHeaders = Collections.unmodifiableMap(new LinkedHashMap<String,Object>(sc.defaultResponseHeaders));
+			beanContext = ps.getBeanContext();
+
+			for (Object o : sc.converters)
+				converters.add(resolve(RestConverter.class, o));
+
+			for (Object o : sc.guards)
+				guards.add(resolve(RestGuard.class, o));
+
+			for (Object o : sc.responseHandlers)
+				responseHandlers.add(resolve(ResponseHandler.class, o));
+
+			mimetypesFileTypeMap = sc.mimeTypes;
+
+			VarResolver vr = sc.getVarResolverBuilder().build();
+
+			if (sc.styleSheets != null) {
+				List<InputStream> contents = new ArrayList<InputStream>();
+				for (Object o : sc.styleSheets) {
+					if (o instanceof Pair) {
+						Pair<Class<?>,String> p = (Pair<Class<?>,String>)o;
+						for (String path : StringUtils.split(vr.resolve(StringUtils.toString(p.second())), ','))
+							if (path.startsWith("file://"))
+								contents.add(new FileInputStream(path));
+							else
+								contents.add(ReflectionUtils.getResource(p.first(), path));
+					} else {
+						contents.add(IOUtils.toInputStream(o));
+					}
+				}
+				styleSheet = new StreamResource(MediaType.forString("text/css"), contents.toArray());
+			}
+
+			if (sc.favIcon != null) {
+				Object o = sc.favIcon;
+				InputStream is = null;
+				if (o instanceof Pair) {
+					Pair<Class<?>,String> p = (Pair<Class<?>,String>)o;
+					is = ReflectionUtils.getResource(p.first(), vr.resolve(p.second()));
+				} else {
+					is = IOUtils.toInputStream(o);
+				}
+				if (is != null)
+					favIcon = new StreamResource(MediaType.forString("image/x-icon"), is);
+			}
+
+			staticFilesMap = new LinkedHashMap<String,String>();
+			if (sc.staticFiles != null) {
+				for (Object o : sc.staticFiles) {
+					if (o instanceof Pair) {
+						Pair<Class<?>,String> p = (Pair<Class<?>,String>)o;
+						// TODO - Currently doesn't take parent class location into account.
+						staticFilesMap.putAll(JsonParser.DEFAULT.parse(vr.resolve(p.second()), LinkedHashMap.class));
+					} else {
+						throw new RuntimeException("TODO");
+					}
+				}
+			}
+			staticFilesPrefixes = staticFilesMap.keySet().toArray(new String[0]);
+
+			logger = sc.logger == null ? new RestLogger.NoOp() : resolve(RestLogger.class, sc.logger);
+
+			fullPath = (sc.parentContext == null ? "" : (sc.parentContext.fullPath + '/')) + sc.path;
+		}
+	}
+
+	/**
+	 * Returns the variable resolver for this servlet.
+	 * <p>
+	 * Variable resolvers are used to replace variables in property values.
+	 * </p>
+	 * <h6 class='figure'>Example:</h6>
+	 * <p class='bcode'>
+	 * 	<ja>@RestResource</ja>(
+	 * 		messages=<js>"nls/Messages"</js>,
+	 * 		properties={
+	 * 			<ja>@Property</ja>(name=<js>"title"</js>,value=<js>"$L{title}"</js>),  <jc>// Localized variable in Messages.properties</jc>
+	 * 			<ja>@Property</ja>(name=<js>"javaVendor"</js>,value=<js>"$S{java.vendor,Oracle}"</js>),  <jc>// System property with default value</jc>
+	 * 			<ja>@Property</ja>(name=<js>"foo"</js>,value=<js>"bar"</js>),
+	 * 			<ja>@Property</ja>(name=<js>"bar"</js>,value=<js>"baz"</js>),
+	 * 			<ja>@Property</ja>(name=<js>"v1"</js>,value=<js>"$R{foo}"</js>),  <jc>// Request variable. value="bar"</jc>
+	 * 			<ja>@Property</ja>(name=<js>"v2"</js>,value=<js>"$R{$R{foo}}"</js>)  <jc>// Nested request variable. value="baz"</jc>
+	 * 		}
+	 * 	)
+	 * 	<jk>public class</jk> MyRestResource <jk>extends</jk> RestServletDefault {
+	 * </p>
+	 * <p>
+	 * A typical usage pattern is using variables for resolving URL links when rendering HTML:
+	 * </p>
+	 * <p class='bcode'>
+	 * 	<ja>@RestMethod</ja>(
+	 * 		name=<js>"GET"</js>, path=<js>"/{name}/*"</js>,
+	 * 		properties={
+	 * 			<ja>@Property</ja>(
+	 * 				name=<jsf>HTMLDOC_links</jsf>,
+	 * 				value=<js>"{up:'$R{requestParentURI}', options:'?method=OPTIONS', editLevel:'$R{servletURI}/editLevel?logger=$R{attribute.name}'}"</js>
+	 * 			)
+	 * 		}
+	 * 	)
+	 * 	<jk>public</jk> LoggerEntry getLogger(RestRequest req, <ja>@Path</ja> String name) <jk>throws</jk> Exception {
+	 * </p>
+	 * <p>
+	 * Calls to <code>req.getProperties().getString(<js>"key"</js>)</code> returns strings with variables resolved.
+	 *
+	 * @return The var resolver in use by this resource.
+	 */
+	public VarResolver getVarResolver() {
+		return varResolver;
+	}
+
+	/**
+	 * Returns the config file associated with this servlet.
+	 * <p>
+	 * The config file is identified via one of the following:
+	 * <ul>
+	 * 	<li>{@link RestResource#config() @RestResource.config()} annotation.
+	 * 	<li>{@link RestConfig#setConfigFile(ConfigFile)} method.
+	 * </ul>
+	 *
+	 * @return The resolving config file associated with this servlet.  Never <jk>null</jk>.
+	 */
+	public ConfigFile getConfigFile() {
+		return configFile;
+	}
+
+	/**
+	 * Resolve a static resource file.
+	 * <p>
+	 * The location of static resources are defined via one of the following:
+	 * <ul>
+	 * 	<li>{@link RestResource#staticFiles() @RestResource.staticFiles()} annotation.
+	 * 	<li>{@link RestConfig#addStaticFiles(Class, String)} method.
+	 * </ul>
+	 *
+	 * @param pathInfo The unencoded path info.
+	 * @return The resource, or <jk>null</jk> if the resource could not be resolved.
+	 * @throws IOException
+	 */
+	public StreamResource resolveStaticFile(String pathInfo) throws IOException {
+		if (! staticFilesCache.containsKey(pathInfo)) {
+			String p = RestUtils.decode(RestUtils.trimSlashes(pathInfo));
+			if (p.indexOf("..") != -1)
+				throw new RestException(SC_NOT_FOUND, "Invalid path");
+			for (Map.Entry<String,String> e : staticFilesMap.entrySet()) {
+				String key = RestUtils.trimSlashes(e.getKey());
+				if (p.startsWith(key)) {
+					String remainder = (p.equals(key) ? "" : p.substring(key.length()));
+					if (remainder.isEmpty() || remainder.startsWith("/")) {
+						String p2 = RestUtils.trimSlashes(e.getValue()) + remainder;
+						InputStream is = getResource(p2, null);
+						if (is != null) {
+							try {
+								int i = p2.lastIndexOf('/');
+								String name = (i == -1 ? p2 : p2.substring(i+1));
+								String mediaType = mimetypesFileTypeMap.getContentType(name);
+								ObjectMap headers = new ObjectMap().append("Cache-Control", "max-age=86400, public");
+								staticFilesCache.put(pathInfo, new StreamResource(MediaType.forString(mediaType), headers, is));
+								return staticFilesCache.get(pathInfo);
+							} finally {
+								is.close();
+							}
+						}
+					}
+				}
+			}
+		}
+		return staticFilesCache.get(pathInfo);
+	}
+
+	/**
+	 * Same as {@link Class#getResourceAsStream(String)} except if it doesn't find the resource
+	 * 	on this class, searches up the parent hierarchy chain.
+	 * <p>
+	 * If the resource cannot be found in the classpath, then an attempt is made to look in the
+	 * 	JVM working directory.
+	 * <p>
+	 * If the <code>locale</code> is specified, then we look for resources whose name matches that locale.
+	 * For example, if looking for the resource <js>"MyResource.txt"</js> for the Japanese locale, we will
+	 * 	look for files in the following order:
+	 * <ol>
+	 * 	<li><js>"MyResource_ja_JP.txt"</js>
+	 * 	<li><js>"MyResource_ja.txt"</js>
+	 * 	<li><js>"MyResource.txt"</js>
+	 * </ol>
+	 *
+	 * @param name The resource name.
+	 * @param locale Optional locale.
+	 * @return An input stream of the resource, or <jk>null</jk> if the resource could not be found.
+	 * @throws IOException
+	 */
+	protected InputStream getResource(String name, Locale locale) throws IOException {
+		String n = (locale == null || locale.toString().isEmpty() ? name : name + '|' + locale);
+		if (! resourceStreams.containsKey(n)) {
+			InputStream is = ReflectionUtils.getLocalizedResource(resource.getClass(), name, locale);
+			if (is == null && name.indexOf("..") == -1) {
+				for (String n2 : FileUtils.getCandidateFileNames(name, locale)) {
+					File f = new File(n2);
+					if (f.exists() && f.canRead()) {
+						is = new FileInputStream(f);
+						break;
+					}
+				}
+			}
+			if (is != null) {
+				try {
+					resourceStreams.put(n, ByteArrayCache.DEFAULT.cache(is));
+				} finally {
+					is.close();
+				}
+			}
+		}
+		byte[] b = resourceStreams.get(n);
+		return b == null ? null : new ByteArrayInputStream(b);
+	}
+
+	/**
+	 * Reads the input stream from {@link #getResource(String, Locale)} into a String.
+	 *
+	 * @param name The resource name.
+	 * @param locale Optional locale.
+	 * @return The contents of the stream as a string, or <jk>null</jk> if the resource could not be found.
+	 * @throws IOException If resource could not be found.
+	 */
+	public String getResourceAsString(String name, Locale locale) throws IOException {
+		String n = (locale == null || locale.toString().isEmpty() ? name : name + '|' + locale);
+		if (! resourceStrings.containsKey(n)) {
+			String s = IOUtils.read(getResource(name, locale));
+			if (s == null)
+				throw new IOException("Resource '"+name+"' not found.");
+			resourceStrings.put(n, s);
+		}
+		return resourceStrings.get(n);
+	}
+
+	/**
+	 * Reads the input stream from {@link #getResource(String, Locale)} and parses it into a POJO
+	 * 	using the parser matched by the specified media type.
+	 * <p>
+	 * Useful if you want to load predefined POJOs from JSON files in your classpath.
+	 *
+	 * @param c The class type of the POJO to create.
+	 * @param mediaType The media type of the data in the stream (e.g. <js>"text/json"</js>)
+	 * @param name The resource name (e.g. "htdocs/styles.css").
+	 * @param locale Optional locale.
+	 * @return The parsed resource, or <jk>null</jk> if the resource could not be found.
+	 * @throws IOException
+	 * @throws ServletException If the media type was unknown or the input could not be parsed into a POJO.
+	 */
+	public <T> T getResource(Class<T> c, MediaType mediaType, String name, Locale locale) throws IOException, ServletException {
+		InputStream is = getResource(name, locale);
+		if (is == null)
+			return null;
+		try {
+			Parser p = parsers.getParser(mediaType);
+			if (p != null) {
+				try {
+					if (p.isReaderParser())
+						return p.parse(new InputStreamReader(is, IOUtils.UTF8), c);
+					return p.parse(is, c);
+				} catch (ParseException e) {
+					throw new ServletException("Could not parse resource '' as media type '"+mediaType+"'.");
+				}
+			}
+			throw new ServletException("Unknown media type '"+mediaType+"'");
+		} catch (Exception e) {
+			throw new ServletException("Could not parse resource with name '"+name+"'", e);
+		}
+	}
+
+	/**
+	 * Returns the path for this resource as defined by the {@link RestResource#path()} annotation or {@link RestConfig#setPath(String)} method
+	 * concatenated with those on all parent classes.
+	 * <p>
+	 * If path is not specified, returns <js>"/"</js>.
+	 * <p>
+	 * Path always starts with <js>"/"</js>.
+	 *
+	 * @return The servlet path.
+	 */
+	public String getPath() {
+		return fullPath;
+	}
+
+	/**
+	 * Returns the logger to use for this resource.
+	 * <p>
+	 * The logger for a resource is defined via one of the following:
+	 * <ul>
+	 * 	<li>{@link RestResource#logger() @RestResource.logger()} annotation.
+	 * 	<li>{@link RestConfig#setLogger(Class)}/{@link RestConfig#setLogger(RestLogger)} methods.
+	 * </ul>
+	 *
+	 * @return The logger to use for this resource.  Never <jk>null</jk>.
+	 */
+	public RestLogger getLogger() {
+		return logger;
+	}
+
+	/**
+	 * Returns the resource bundle used by this resource.
+	 * <p>
+	 * The resource bundle is defined via one of the following:
+	 * <ul>
+	 * 	<li>{@link RestResource#messages() @RestResource.messages()} annotation.
+	 * </ul>
+	 *
+	 * @return The resource bundle for this resource.  Never <jk>null</jk>.
+	 */
+	public MessageBundle getMessages() {
+		return msgs;
+	}
+
+	/**
+	 * Returns the REST information provider used by this resource.
+	 * <p>
+	 * The information provider is defined via one of the following:
+	 * <ul>
+	 * 	<li>{@link RestResource#infoProvider() @RestResource.infoProvider()} annotation.
+	 * 	<li>{@link RestConfig#setInfoProvider(Class)}/{@link RestConfig#setInfoProvider(RestInfoProvider)} methods.
+	 * </ul>
+	 *
+	 * @return The information provider for this resource.  Never <jk>null</jk>.
+	 */
+	public RestInfoProvider getInfoProvider() {
+		return infoProvider;
+	}
+
+	/**
+	 * Returns the REST call handler used by this resource.
+	 * <p>
+	 * The call handler is defined via one of the following:
+	 * <ul>
+	 * 	<li>{@link RestResource#callHandler() @RestResource.callHandler()} annotation.
+	 * 	<li>{@link RestConfig#setCallHandler(Class)}/{@link RestConfig#setCallHandler(RestCallHandler)} methods.
+	 * </ul>
+	 *
+	 * @return The call handler for this resource.  Never <jk>null</jk>.
+	 */
+	protected RestCallHandler getCallHandler() {
+		return callHandler;
+	}
+
+	/**
+	 * Returns a map of HTTP method names to call routers.
+	 *
+	 * @return A map with HTTP method names uppercased as the keys, and call routers as the values.
+	 */
+	protected Map<String,CallRouter> getCallRouters() {
+		return callRouters;
+	}
+
+	/**
+	 * Returns the resource object.
+	 * <p>
+	 * This is the instance of the class annotated with the {@link RestResource @RestResource} annotation, usually
+	 * an instance of {@link RestServlet}.
+	 *
+	 * @return The resource object.  Never <jk>null</jk>.
+	 */
+	protected Object getResource() {
+		return resource;
+	}
+
+	/**
+	 * Returns the resource object as a {@link RestServlet}.
+	 *
+	 * @return The resource object cast to {@link RestServlet}, or
+	 * <jk>null</jk> if the resource doesn't subclass from {@link RestServlet}
+	 */
+	protected RestServlet getRestServlet() {
+		return resource instanceof RestServlet ? (RestServlet)resource : null;
+	}
+
+	/**
+	 * Throws a {@link RestException} if an exception occurred in the constructor of this object.
+	 *
+	 * @throws RestException The initialization exception wrapped in a {@link RestException}.
+	 */
+	protected void checkForInitException() throws RestException {
+		if (initException != null)
+			throw initException;
+	}
+
+	/**
+	 * Returns the {@link BeanContext} object used for parsing path variables and header values.
+	 *
+	 * @return The bean context used for parsing path variables and header values.
+	 */
+	public BeanContext getBeanContext() {
+		return beanContext;
+	}
+
+	/**
+	 * Returns the class-level properties associated with this servlet.
+	 * <p>
+	 * Properties at the class level are defined via one of the following:
+	 * <ul>
+	 * 	<li>{@link RestResource#properties() @RestResource.properties()} annotation.
+	 * 	<li>{@link RestConfig#setProperty(String, Object)}/{@link RestConfig#setProperties(Map)} methods.
+	 * </ul>
+	 * <p>
+	 * <h5 class='section'>Notes:</h5>
+	 * <ul>
+	 * 	<li>The returned {@code Map} is mutable.  Therefore, subclasses are free to override
+	 * 	or set additional initialization parameters in their {@code init()} method.
+	 * </ul>
+	 *
+	 * @return The resource properties as an {@link ObjectMap}.
+	 */
+	public ObjectMap getProperties() {
+		return properties;
+	}
+
+	/**
+	 * Returns the serializers registered with this resource.
+	 * <p>
+	 * Serializers at the class level are defined via one of the following:
+	 * <ul>
+	 * 	<li>{@link RestResource#serializers() @RestResource.serializers()} annotation.
+	 * 	<li>{@link RestConfig#addSerializers(Class...)}/{@link RestConfig#addSerializers(Serializer...)} methods.
+	 * </ul>
+	 *
+	 * @return The serializers registered with this resource.
+	 */
+	public SerializerGroup getSerializers() {
+		return serializers;
+	}
+
+	/**
+	 * Returns the parsers registered with this resource.
+	 * <p>
+	 * Parsers at the class level are defined via one of the following:
+	 * <ul>
+	 * 	<li>{@link RestResource#parsers() @RestResource.parsers()} annotation.
+	 * 	<li>{@link RestConfig#addParsers(Class...)}/{@link RestConfig#addParsers(Parser...)} methods.
+	 * </ul>
+	 *
+	 * @return The parsers registered with this resource.
+	 */
+	public ParserGroup getParsers() {
+		return parsers;
+	}
+
+	/**
+	 * Returns the servlet init parameter returned by {@link ServletConfig#getInitParameter(String)}.
+	 *
+	 * @param name The init parameter name.
+	 * @return The servlet init parameter, or <jk>null</jk> if not found.
+	 */
+	public String getServletInitParameter(String name) {
+		return config.getInitParameter(name);
+	}
+
+	/**
+	 * Returns the child resources associated with this servlet.
+	 *
+	 * @return An unmodifiable map of child resources.
+	 * Keys are the {@link RestResource#path() @RestResource.path()} annotation defined on the child resource.
+	 */
+	public Map<String,RestContext> getChildResources() {
+		return Collections.unmodifiableMap(childResources);
+	}
+
+	/**
+	 * Returns the number of times this exception was thrown based on a hash of its stacktrace.
+	 *
+	 * @param e The exception to check.
+	 * @return The number of times this exception was thrown, or <code>0</code> if <code>stackTraceHashes</code>
+	 * setting is not enabled.
+	 */
+	protected int getStackTraceOccurrence(Throwable e) {
+		if (! useStackTraceHashes)
+			return 0;
+		int h = e.hashCode();
+		stackTraceHashes.putIfAbsent(h, new AtomicInteger());
+		return stackTraceHashes.get(h).incrementAndGet();
+	}
+
+	/**
+	 * Returns the value of the {@link #REST_renderResponseStackTraces} setting.
+	 * @return The value of the {@link #REST_renderResponseStackTraces} setting.
+	 */
+	protected boolean isRenderResponseStackTraces() {
+		return renderResponseStackTraces;
+	}
+
+	/**
+	 * Returns the value of the {@link #REST_allowHeaderParams} setting.
+	 * @return The value of the {@link #REST_allowHeaderParams} setting.
+	 */
+	protected boolean isAllowHeaderParams() {
+		return allowHeaderParams;
+	}
+
+	/**
+	 * Returns the value of the {@link #REST_allowBodyParam} setting.
+	 * @return The value of the {@link #REST_allowBodyParam} setting.
+	 */
+	protected boolean isAllowBodyParam() {
+		return allowBodyParam;
+	}
+
+	/**
+	 * Returns the value of the {@link #REST_defaultCharset} setting.
+	 * @return The value of the {@link #REST_defaultCharset} setting.
+	 */
+	protected String getDefaultCharset() {
+		return defaultCharset;
+	}
+
+	/**
+	 * Returns the value of the {@link #REST_paramFormat} setting.
+	 * @return The value of the {@link #REST_paramFormat} setting.
+	 */
+	protected String getParamFormat() {
+		return paramFormat;
+	}
+
+	/**
+	 * Returns the name of the client version header name used by this resource.
+	 * <p>
+	 * The client version header is the name of the HTTP header on requests that identify a client version.
+	 * <p>
+	 * The client version header is defined via one of the following:
+	 * <ul>
+	 * 	<li>{@link RestResource#clientVersionHeader() @RestResource.clientVersion()} annotation.
+	 * </ul>
+	 *
+	 * @return The name of the client version header used by this resource.  Never <jk>null</jk>.
+	 */
+	protected String getClientVersionHeader() {
+		return clientVersionHeader;
+	}
+
+	/**
+	 * Returns <jk>true</jk> if the specified <code>Method</code> GET parameter value can be used to override
+	 * the method name in the HTTP header.
+	 *
+	 * @param m The method name, uppercased.
+	 * @return <jk>true</jk> if this resource allows the specified method to be overridden.
+	 */
+	protected boolean allowMethodParam(String m) {
+		return (! StringUtils.isEmpty(m) && (allowMethodParams.contains(m) || allowMethodParams.contains("*")));
+	}
+
+	/**
+	 * Returns the bean filters associated with this resource.
+	 * <p>
+	 * Bean filters at the class level are defined via one of the following:
+	 * <ul>
+	 * 	<li>{@link RestResource#beanFilters() @RestResource.beanFilters()} annotation.
+	 * 	<li>{@link RestConfig#addBeanFilters(Class...)} method.
+	 * </ul>
+	 *
+	 * @return The bean filters associated with this resource.  Never <jk>null</jk>.
+	 */
+	protected Class<?>[] getBeanFilters() {
+		return beanFilters;
+	}
+
+	/**
+	 * Returns the POJO swaps associated with this resource.
+	 * <p>
+	 * POJO swaps at the class level are defined via one of the following:
+	 * <ul>
+	 * 	<li>{@link RestResource#pojoSwaps() @RestResource.pojoSwaps()} annotation.
+	 * 	<li>{@link RestConfig#addPojoSwaps(Class...)} method.
+	 * </ul>
+	 *
+	 * @return The POJO swaps associated with this resource.  Never <jk>null</jk>.
+	 */
+	protected Class<?>[] getPojoSwaps() {
+		return pojoSwaps;
+	}
+
+	/**
+	 * Returns the URL-encoding parser associated with this resource.
+	 * @return The URL-encoding parser associated with this resource.  Never <jk>null</jk>.
+	 */
+	protected UrlEncodingParser getUrlEncodingParser() {
+		return urlEncodingParser;
+	}
+
+	/**
+	 * Returns the URL-encoding serializer associated with this resource.
+	 * @return The URL-encoding serializer associated with this resource.  Never <jk>null</jk>.
+	 */
+	protected UrlEncodingSerializer getUrlEncodingSerializer() {
+		return urlEncodingSerializer;
+	}
+
+	/**
+	 * Returns the encoders associated with this resource.
+	 * <p>
+	 * Encoders are used to provide various types of encoding such as <code>gzip</code> encoding.
+	 * <p>
+	 * Encoders at the class level are defined via one of the following:
+	 * <ul>
+	 * 	<li>{@link RestResource#encoders() @RestResource.encoders()} annotation.
+	 * 	<li>{@link RestConfig#addEncoders(Class...)}/{@link RestConfig#addEncoders(org.apache.juneau.encoders.Encoder...)} methods.
+	 * </ul>
+	 *
+	 * @return The encoders associated with this resource.  Never <jk>null</jk>.
+	 */
+	protected EncoderGroup getEncoders() {
+		return encoders;
+	}
+
+	/**
+	 * Returns the explicit list of supported accept types for this resource.
+	 * <p>
+	 * By default, this is simply the list of accept types supported by the registered parsers, but
+	 * can be overridden via the {@link RestConfig#setSupportedAcceptTypes(MediaType...)}/{@link RestConfig#setSupportedAcceptTypes(String...)} methods.
+	 *
+	 * @return The supported <code>Accept</code> header values for this resource.  Never <jk>null</jk>.
+	 */
+	protected MediaType[] getSupportedAcceptTypes() {
+		return supportedAcceptTypes;
+	}
+
+	/**
+	 * Returns the explicit list of supported content types for this resource.
+	 * <p>
+	 * By default, this is simply the list of content types supported by the registered serializers, but
+	 * can be overridden via the {@link RestConfig#setSupportedContentTypes(MediaType...)}/{@link RestConfig#setSupportedContentTypes(String...)} methods.
+	 *
+	 * @return The supported <code>Content-Type</code> header values for this resource.  Never <jk>null</jk>.
+	 */
+	protected MediaType[] getSupportedContentTypes() {
+		return supportedContentTypes;
+	}
+
+	/**
+	 * Returns the default request headers for this resource.
+	 * <p>
+	 * These are headers automatically added to requests if not present.
+	 * <p>
+	 * Default request headers are defined via one of the following:
+	 * <ul>
+	 * 	<li>{@link RestResource#defaultRequestHeaders() @RestResource.defaultRequestHeaders()} annotation.
+	 * 	<li>{@link RestConfig#addDefaultRequestHeader(String, Object)}/{@link RestConfig#addDefaultRequestHeaders(String...)} methods.
+	 * </ul>
+	 *
+	 * @return The default request headers for this resource.  Never <jk>null</jk>.
+	 */
+	protected Map<String,String> getDefaultRequestHeaders() {
+		return defaultRequestHeaders;
+	}
+
+	/**
+	 * Returns the default response headers for this resource.
+	 * <p>
+	 * These are headers automatically added to responses if not otherwise specified during the request.
+	 * <p>
+	 * Default response headers are defined via one of the following:
+	 * <ul>
+	 * 	<li>{@link RestResource#defaultResponseHeaders() @RestResource.defaultResponseHeaders()} annotation.
+	 * 	<li>{@link RestConfig#addDefaultResponseHeader(String, Object)}/{@link RestConfig#addDefaultResponseHeaders(String...)} methods.
+	 * </ul>
+	 *
+	 * @return The default response headers for this resource.  Never <jk>null</jk>.
+	 */
+	public Map<String,Object> getDefaultResponseHeaders() {
+		return defaultResponseHeaders;
+	}
+
+	/**
+	 * Returns the converters associated with this resource at the class level.
+	 * <p>
+	 * Converters are used to 'convert' POJOs from one form to another before being passed of to the response handlers.
+	 * <p>
+	 * Converters at the class level are defined via one of the following:
+	 * <ul>
+	 * 	<li>{@link RestResource#converters() @RestResource.converters()} annotation.
+	 * 	<li>{@link RestConfig#addConverters(Class...)}/{@link RestConfig#addConverters(RestConverter...)} methods.
+	 * </ul>
+	 *
+	 * @return The converters associated with this resource.  Never <jk>null</jk>.
+	 */
+	protected RestConverter[] getConverters() {
+		return converters;
+	}
+
+	/**
+	 * Returns the guards associated with this resource at the class level.
+	 * <p>
+	 * Guards are used to restrict access to resources.
+	 * <p>
+	 * Guards at the class level are defined via one of the following:
+	 * <ul>
+	 * 	<li>{@link RestResource#guards() @RestResource.guards()} annotation.
+	 * 	<li>{@link RestConfig#addGuards(Class...)}/{@link RestConfig#addGuards(RestGuard...)} methods.
+	 * </ul>
+	 *
+	 * @return The guards associated with this resource.  Never <jk>null</jk>.
+	 */
+	protected RestGuard[] getGuards() {
+		return guards;
+	}
+
+	/**
+	 * Returns the response handlers associated with this resource.
+	 * <p>
+	 * Response handlers are used to convert POJOs returned by REST Java methods into actual HTTP responses.
+	 * <p>
+	 * Response handlers are defined via one of the following:
+	 * <ul>
+	 * 	<li>{@link RestResource#responseHandlers() @RestResource.responseHandlers()} annotation.
+	 * 	<li>{@link RestConfig#addResponseHandlers(Class...)}/{@link RestConfig#addResponseHandlers(ResponseHandler...)} methods.
+	 * </ul>
+	 *
+	 * @return The response handlers associated with this resource.  Never <jk>null</jk>.
+	 */
+	protected ResponseHandler[] getResponseHandlers() {
+		return responseHandlers;
+	}
+
+	/**
+	 * Returns the media type for the specified file name.
+	 * <p>
+	 * The list of MIME-type mappings can be augmented through the {@link RestConfig#addMimeTypes(String...)} method.
+	 * See that method for a description of predefined MIME-type mappings.
+	 *
+	 * @param name The file name.
+	 * @return The MIME-type, or <jk>null</jk> if it could not be determined.
+	 */
+	protected String getMediaTypeForName(String name) {
+		return mimetypesFileTypeMap.getContentType(name);
+	}
+
+	/**
+	 * Returns the favicon of the resource.
+	 * <p>
+	 * This is the icon served up under <js>"/favicon.ico"</jk> recognized by browsers.
+	 * <p>
+	 * The favicon is defined via one of the following:
+	 * <ul>
+	 * 	<li>{@link RestResource#favicon() @RestResource.favicon()} annotation.
+	 * 	<li>{@link RestConfig#setFavIcon(Object)}/{@link RestConfig#setFavIcon(Class, String)} methods.
+	 * </ul>
+	 *
+	 * @return The favicon of this resource.  Can be <jk>null</jk>.
+	 */
+	protected StreamResource getFavIcon() {
+		return favIcon;
+	}
+
+	/**
+	 * Returns the stylesheet for use in the HTML views of the resource.
+	 * <p>
+	 * This is the contents of the page served up under <js>"/styles.css"</jk>.
+	 * <p>
+	 * The stylesheet is defined via one of the following:
+	 * <ul>
+	 * 	<li>{@link RestResource#stylesheet() @RestResource.stylesheet()} annotation.
+	 * 	<li>{@link RestConfig#setStyleSheet(Object...)}/{@link RestConfig#setStyleSheet(Class, String)} methods.
+	 * </ul>
+	 *
+	 * @return The aggregated stylesheet of this resource.  Never <jk>null</jk>.
+	 */
+	protected StreamResource getStyleSheet() {
+		return styleSheet;
+	}
+
+	/**
+	 * Returns <jk>true</jk> if the specified path refers to a static file.
+	 * <p>
+	 * Static files are files pulled from the classpath and served up directly to the browser.
+	 * <p>
+	 * Static files are defined via one of the following:
+	 * <ul>
+	 * 	<li>{@link RestResource#staticFiles() @RestResource.staticFiles()} annotation.
+	 * 	<li>{@link RestConfig#addStaticFiles(Class, String)} method.
+	 * </ul>
+	 *
+	 * @param p The URL path remainder after the servlet match.
+	 * @return <jk>true</jk> if the specified path refers to a static file.
+	 */
+	protected boolean isStaticFile(String p) {
+		return StringUtils.pathStartsWith(p, staticFilesPrefixes);
+	}
+
+	/**
+	 * Returns the REST Java methods defined in this resource.
+	 * <p>
+	 * These are the methods annotated with the {@link RestMethod @RestMethod} annotation.
+	 *
+	 * @return A map of Java method names to call method objects.
+	 */
+	protected Map<String,CallMethod> getCallMethods() {
+		return callMethods;
+	}
+
+	/**
+	 * Calls {@link Servlet#destroy()} on any child resources defined on this resource.
+	 */
+	protected void destroy() {
+		for (RestContext r : childResources.values()) {
+			r.destroy();
+			if (r.resource instanceof Servlet)
+				((Servlet)r.resource).destroy();
+		}
+	}
+
+	/**
+	 * Returns <jk>true</jk> if this resource has any child resources associated with it.
+	 * @return <jk>true</jk> if this resource has any child resources associated with it.
+	 */
+	protected boolean hasChildResources() {
+		return ! childResources.isEmpty();
+	}
+
+	/**
+	 * Returns the context of the child resource associated with the specified path.
+	 *
+	 * @param path The path of the child resource to resolve.
+	 * @return The resolved context, or <jk>null</jk> if it could not be resolved.
+	 */
+	protected RestContext getChildResource(String path) {
+		return childResources.get(path);
+	}
+
+
+	//----------------------------------------------------------------------------------------------------
+	// Utility methods
+	//----------------------------------------------------------------------------------------------------
+
+	/**
+	 * Takes in an object of type T or a Class<T> and either casts or constructs a T.
+	 */
+	@SuppressWarnings("unchecked")
+	private static <T> T resolve(Class<T> c, Object o, Object...cArgs) throws RestServletException {
+		if (c.isInstance(o))
+			return (T)o;
+		if (! (o instanceof Class))
+			throw new RestServletException("Invalid object type passed to resolve:  ''{0}''.  Must be an object of type T or a Class<? extend T>.", o.getClass());
+		Constructor<T> n = ClassUtils.findPublicConstructor((Class<T>)o, cArgs);
+		if (n == null)
+			throw new RestServletException("Could not find public constructor for class ''{0}'' that takes in args {1}", c, JsonSerializer.DEFAULT_LAX.toString(ClassUtils.getClasses(cArgs)));
+		try {
+			return n.newInstance(cArgs);
+		} catch (Exception e) {
+			throw new RestServletException("Exception occurred while constructing class ''{0}''", c).initCause(e);
+		}
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/RestException.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestException.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestException.java
index 77d695a..299883a 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/RestException.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestException.java
@@ -116,9 +116,9 @@ public class RestException extends RuntimeException {
 	/**
 	 * 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.
+	 * This only gets set if {@link RestContext#REST_useStackTraceHashes} is enabled on the servlet.
 	 *
-	 * @return The occurrence number if {@link RestServletContext#REST_useStackTraceHashes} is enabled, or <code>0</code> otherwise.
+	 * @return The occurrence number if {@link RestContext#REST_useStackTraceHashes} is enabled, or <code>0</code> otherwise.
 	 */
 	public int getOccurrence() {
 		return occurrence;

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/RestInfoProvider.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestInfoProvider.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestInfoProvider.java
new file mode 100644
index 0000000..e43f514
--- /dev/null
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestInfoProvider.java
@@ -0,0 +1,499 @@
+// ***************************************************************************************************************************
+// * 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 javax.servlet.http.HttpServletResponse.*;
+import static org.apache.juneau.dto.swagger.SwaggerBuilder.*;
+
+import java.util.*;
+import java.util.concurrent.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.dto.swagger.*;
+import org.apache.juneau.internal.*;
+import org.apache.juneau.json.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.svl.*;
+
+/**
+ * Class that provides documentation and other related information about a REST resource.
+ * <p>
+ * Subclasses can override these methods to tailor how HTTP REST resources are documented.
+ * Subclasses MUST implement a public constructor that takes in a {@link RestContext} object.
+ * <p>
+ * RestInfoProviders are associated with servlets/resources in one of the following ways:
+ * <ul>
+ * 	<li>The {@link RestResource#infoProvider @RestResource.infoProvider()} annotation.
+ * 	<li>The {@link RestConfig#setInfoProvider(Class)}/{@link RestConfig#setInfoProvider(RestInfoProvider)} methods.
+ * </ul>
+ */
+@SuppressWarnings("hiding")
+public class RestInfoProvider {
+
+	private final RestContext context;
+	private final String
+		title,
+		description,
+		termsOfService,
+		contact,
+		license,
+		version,
+		tags,
+		externalDocs;
+	private final ConcurrentHashMap<Locale,Swagger> swaggers = new ConcurrentHashMap<Locale,Swagger>();
+
+	/**
+	 * Constructor.
+	 * @param context The resource context.
+	 */
+	public RestInfoProvider(RestContext context) {
+		this.context = context;
+
+		Builder b = new Builder(context);
+		this.title = b.title;
+		this.description = b.description;
+		this.termsOfService = b.termsOfService;
+		this.contact = b.contact;
+		this.license = b.license;
+		this.version = b.version;
+		this.tags = b.tags;
+		this.externalDocs = b.externalDocs;
+	}
+
+	private static class Builder {
+		private String
+			title,
+			description,
+			termsOfService,
+			contact,
+			license,
+			version,
+			tags,
+			externalDocs;
+
+		Builder(RestContext context) {
+
+			LinkedHashMap<Class<?>,RestResource> restResourceAnnotationsParentFirst = ReflectionUtils.findAnnotationsMapParentFirst(RestResource.class, context.getResource().getClass());
+
+			for (RestResource r : restResourceAnnotationsParentFirst.values()) {
+				if (! r.title().isEmpty())
+					title = r.title();
+				if (! r.description().isEmpty())
+					description = r.description();
+				if (! r.termsOfService().isEmpty())
+					termsOfService = r.termsOfService();
+				if (! r.contact().isEmpty())
+					contact = r.contact();
+				if (! r.license().isEmpty())
+					license = r.license();
+				if (! r.version().isEmpty())
+					version = r.version();
+				if (! r.tags().isEmpty())
+					tags = r.tags();
+				if (! r.externalDocs().isEmpty())
+					externalDocs = r.externalDocs();
+			}
+		}
+	}
+
+	/**
+	 * Returns the localized swagger for this REST resource.
+	 *
+	 * @param req The incoming HTTP request.
+	 * @return A new Swagger instance.
+	 * @throws RestException
+	 */
+	protected Swagger getSwagger(RestRequest req) throws RestException {
+		try {
+			// If a file is defined, use that.
+			Swagger s = req.getSwaggerFromFile();
+			if (s != null)
+				return s;
+
+			s = swagger(
+				info(getTitle(req), getVersion(req))
+					.contact(getContact(req))
+					.license(getLicense(req))
+					.description(getDescription(req))
+					.termsOfService(getTermsOfService(req))
+				)
+				.consumes(context.getSupportedAcceptTypes())
+				.produces(context.getSupportedContentTypes())
+				.tags(getTags(req))
+				.externalDocs(getExternalDocs(req));
+
+			for (CallMethod sm : context.getCallMethods().values()) {
+				if (sm.isRequestAllowed(req)) {
+					Operation o = sm.getSwaggerOperation(req);
+					s.path(
+						sm.getPathPattern(),
+						sm.getHttpMethod().toLowerCase(),
+						o
+					);
+				}
+			}
+			return s;
+		} catch (RestException e) {
+			throw e;
+		} catch (Exception e) {
+			throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
+		}
+	}
+
+	/**
+	 * Returns the localized Swagger from the file system.
+	 * <p>
+	 * Looks for a file called <js>"{ServletClass}_{locale}.json"</js> in the same package
+	 * as this servlet and returns it as a parsed {@link Swagger} object.
+	 * <p>
+	 * Returned objects are cached for later quick-lookup.
+	 *
+	 * @param locale The locale of the swagger.
+	 * @return The parsed swagger object, or <jk>null</jk> if the swagger file could not be found.
+	 * @throws RestException
+	 */
+	protected Swagger getSwaggerFromFile(Locale locale) throws RestException {
+		Swagger s = swaggers.get(locale);
+		if (s == null) {
+			try {
+				s = context.getResource(Swagger.class, MediaType.JSON, getClass().getSimpleName() + ".json", locale);
+				swaggers.putIfAbsent(locale, s == null ? Swagger.NULL : s);
+			} catch (Exception e) {
+				throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
+			}
+		}
+		return s == Swagger.NULL ? null : s;
+	}
+
+	/**
+	 * Returns the localized summary of the specified java method on this servlet.
+	 * <p>
+	 * Subclasses can override this method to provide their own summary.
+	 * <p>
+	 * The default implementation returns the summary from the following locations (whichever matches first):
+	 * </p>
+	 * <ol>
+	 * 	<li>{@link RestMethod#summary() @RestMethod.summary()} annotation on the method.
+	 * 	<li><ck>[ClassName].[javaMethodName].summary</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+	 * 		annotation for this class, then any parent classes.
+	 * 	<li><ck>[javaMethodName].summary</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+	 * 		annotation for this class, then any parent classes.
+	 * </ol>
+	 *
+	 * @param javaMethodName The name of the Java method whose description we're retrieving.
+	 * @param req The current request.
+	 * @return The localized summary of the method, or a blank string if no summary was found.
+	 */
+	public String getMethodSummary(String javaMethodName, RestRequest req) {
+		CallMethod m = context.getCallMethods().get(javaMethodName);
+		if (m != null)
+			return m.getSummary(req);
+		return "";
+	}
+
+	/**
+	 * Returns the localized description of the specified java method on this servlet.
+	 * <p>
+	 * Subclasses can override this method to provide their own description.
+	 * <p>
+	 * The default implementation returns the description from the following locations (whichever matches first):
+	 * </p>
+	 * <ol>
+	 * 	<li>{@link RestMethod#description() @RestMethod.description()} annotation on the method.
+	 * 	<li><ck>[ClassName].[javaMethodName].description</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+	 * 		annotation for this class, then any parent classes.
+	 * 	<li><ck>[javaMethodName].description</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+	 * 		annotation for this class, then any parent classes.
+	 * </ol>
+	 *
+	 * @param javaMethodName The name of the Java method whose description we're retrieving.
+	 * @param req The current request.
+	 * @return The localized description of the method, or a blank string if no description was found.
+	 */
+	protected String getMethodDescription(String javaMethodName, RestRequest req) {
+		CallMethod m = context.getCallMethods().get(javaMethodName);
+		if (m != null)
+			return m.getDescription(req);
+		return "";
+	}
+
+	/**
+	 * Returns the localized title of this REST resource.
+	 * <p>
+	 * Subclasses can override this method to provide their own title.
+	 * <p>
+	 * The default implementation returns the description from the following locations (whichever matches first):
+	 * <p>
+	 * <ol>
+	 * 	<li>{@link RestResource#title() @RestResourcel.title()} annotation on this class, and then any parent classes.
+	 * 	<li><ck>[ClassName].title</ck> property in resource bundle identified by {@link RestResource#messages() @ResourceBundle.messages()}
+	 * 		annotation for this class, then any parent classes.
+	 * 	<li><ck>title</ck> in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+	 * 		annotation for this class, then any parent classes.
+	 * 	<li><ck>/info/title</ck> entry in swagger file.
+	 * </ol>
+	 *
+	 * @param req The current request.
+	 * @return The localized description of this REST resource, or <jk>null</jk> if no resource description was found.
+	 */
+	public String getTitle(RestRequest req) {
+		VarResolverSession vr = req.getVarResolverSession();
+		if (this.title != null)
+			return vr.resolve(this.title);
+		String title = context.getMessages().findFirstString(req.getLocale(), "title");
+		if (title != null)
+			return vr.resolve(title);
+		Swagger s = req.getSwaggerFromFile();
+		if (s != null && s.getInfo() != null)
+			return s.getInfo().getTitle();
+		return null;
+	}
+
+	/**
+	 * Returns the localized description of this REST resource.
+	 * <p>
+	 * Subclasses can override this method to provide their own description.
+	 * <p>
+	 * The default implementation returns the description from the following locations (whichever matches first):
+	 * <ol>
+	 * 	<li>{@link RestResource#description() @RestResource.description()} annotation on this class, and then any parent classes.
+	 * 	<li><ck>[ClassName].description</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+	 * 		annotation for this class, then any parent classes.
+	 * 	<li><ck>description</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+	 * 		annotation for this class, then any parent classes.
+	 * 	<li><ck>/info/description</ck> entry in swagger file.
+	 * </ol>
+	 *
+	 * @param req The current request.
+	 * @return The localized description of this REST resource, or <jk>null</jk> if no resource description was found.
+	 */
+	public String getDescription(RestRequest req) {
+		VarResolverSession vr = req.getVarResolverSession();
+		if (this.description != null)
+			return vr.resolve(this.description);
+		String description = context.getMessages().findFirstString(req.getLocale(), "description");
+		if (description != null)
+			return vr.resolve(description);
+		Swagger s = req.getSwaggerFromFile();
+		if (s != null && s.getInfo() != null)
+			return s.getInfo().getDescription();
+		return null;
+	}
+
+	/**
+	 * Returns the localized contact information of this REST resource.
+	 * <p>
+	 * Subclasses can override this method to provide their own contact information.
+	 * <p>
+	 * The default implementation returns the contact information from the following locations (whichever matches first):
+	 * <ol>
+	 * 	<li>{@link RestResource#contact() @RestResource.contact()} annotation on this class, and then any parent classes.
+	 * 	<li><ck>[ClassName].contact</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+	 * 		annotation for this class, then any parent classes.
+	 * 	<li><ck>contact</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+	 * 		annotation for this class, then any parent classes.
+	 * 	<li><ck>/info/contact</ck> entry in swagger file.
+	 * </ol>
+	 *
+	 * @param req The current request.
+	 * @return The localized contact information of this REST resource, or <jk>null</jk> if no contact information was found.
+	 */
+	public Contact getContact(RestRequest req) {
+		VarResolverSession vr = req.getVarResolverSession();
+		JsonParser jp = JsonParser.DEFAULT;
+		try {
+			if (this.contact != null)
+				return jp.parse(vr.resolve(this.contact), Contact.class);
+			String contact = context.getMessages().findFirstString(req.getLocale(), "contact");
+			if (contact != null)
+				return jp.parse(vr.resolve(contact), Contact.class);
+			Swagger s = req.getSwaggerFromFile();
+			if (s != null && s.getInfo() != null)
+				return s.getInfo().getContact();
+			return null;
+		} catch (ParseException e) {
+			throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
+		}
+	}
+
+	/**
+	 * Returns the localized license information of this REST resource.
+	 * <p>
+	 * Subclasses can override this method to provide their own license information.
+	 * <p>
+	 * The default implementation returns the license information from the following locations (whichever matches first):
+	 * <ol>
+	 * 	<li>{@link RestResource#license() @RestResource.license()} annotation on this class, and then any parent classes.
+	 * 	<li><ck>[ClassName].license</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+	 * 		annotation for this class, then any parent classes.
+	 * 	<li><ck>license</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+	 * 		annotation for this class, then any parent classes.
+	 * 	<li><ck>/info/license</ck> entry in swagger file.
+	 * </ol>
+	 *
+	 * @param req The current request.
+	 * @return The localized contact information of this REST resource, or <jk>null</jk> if no contact information was found.
+	 */
+	public License getLicense(RestRequest req) {
+		VarResolverSession vr = req.getVarResolverSession();
+		JsonParser jp = JsonParser.DEFAULT;
+		try {
+			if (this.license != null)
+				return jp.parse(vr.resolve(this.license), License.class);
+			String license = context.getMessages().findFirstString(req.getLocale(), "license");
+			if (license != null)
+				return jp.parse(vr.resolve(license), License.class);
+			Swagger s = req.getSwaggerFromFile();
+			if (s != null && s.getInfo() != null)
+				return s.getInfo().getLicense();
+			return null;
+		} catch (ParseException e) {
+			throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
+		}
+	}
+
+	/**
+	 * Returns the terms-of-service information of this REST resource.
+	 * <p>
+	 * Subclasses can override this method to provide their own terms-of-service information.
+	 * <p>
+	 * The default implementation returns the terms-of-service information from the following locations (whichever matches first):
+	 * <ol>
+	 * 	<li>{@link RestResource#termsOfService() @RestResource.termsOfService()} annotation on this class, and then any parent classes.
+	 * 	<li><ck>[ClassName].termsOfService</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+	 * 		annotation for this class, then any parent classes.
+	 * 	<li><ck>termsOfService</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+	 * 		annotation for this class, then any parent classes.
+	 * 	<li><ck>/info/termsOfService</ck> entry in swagger file.
+	 * </ol>
+	 *
+	 * @param req The current request.
+	 * @return The localized contact information of this REST resource, or <jk>null</jk> if no contact information was found.
+	 */
+	public String getTermsOfService(RestRequest req) {
+		VarResolverSession vr = req.getVarResolverSession();
+		if (this.termsOfService != null)
+			return vr.resolve(this.termsOfService);
+		String termsOfService = context.getMessages().findFirstString(req.getLocale(), "termsOfService");
+		if (termsOfService != null)
+			return vr.resolve(termsOfService);
+		Swagger s = req.getSwaggerFromFile();
+		if (s != null && s.getInfo() != null)
+			return s.getInfo().getTermsOfService();
+		return null;
+	}
+
+	/**
+	 * Returns the version information of this REST resource.
+	 * <p>
+	 * Subclasses can override this method to provide their own version information.
+	 * <p>
+	 * The default implementation returns the version information from the following locations (whichever matches first):
+	 * <ol>
+	 * 	<li>{@link RestResource#version() @RestResource.version()} annotation on this class, and then any parent classes.
+	 * 	<li><ck>[ClassName].version</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+	 * 		annotation for this class, then any parent classes.
+	 * 	<li><ck>version</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+	 * 		annotation for this class, then any parent classes.
+	 * 	<li><ck>/info/version</ck> entry in swagger file.
+	 * </ol>
+	 *
+	 * @param req The current request.
+	 * @return The localized contact information of this REST resource, or <jk>null</jk> if no contact information was found.
+	 */
+	public String getVersion(RestRequest req) {
+		VarResolverSession vr = req.getVarResolverSession();
+		if (this.version != null)
+			return vr.resolve(this.version);
+		String version = context.getMessages().findFirstString(req.getLocale(), "version");
+		if (version != null)
+			return vr.resolve(version);
+		Swagger s = req.getSwaggerFromFile();
+		if (s != null && s.getInfo() != null)
+			return s.getInfo().getVersion();
+		return null;
+	}
+
+	/**
+	 * Returns the version information of this REST resource.
+	 * <p>
+	 * Subclasses can override this method to provide their own version information.
+	 * <p>
+	 * The default implementation returns the version information from the following locations (whichever matches first):
+	 * <ol>
+	 * 	<li>{@link RestResource#version() @RestResource.version()} annotation on this class, and then any parent classes.
+	 * 	<li><ck>[ClassName].version</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+	 * 		annotation for this class, then any parent classes.
+	 * 	<li><ck>version</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+	 * 		annotation for this class, then any parent classes.
+	 * 	<li><ck>/info/version</ck> entry in swagger file.
+	 * </ol>
+	 *
+	 * @param req The current request.
+	 * @return The localized contact information of this REST resource, or <jk>null</jk> if no contact information was found.
+	 */
+	public List<Tag> getTags(RestRequest req) {
+		VarResolverSession vr = req.getVarResolverSession();
+		JsonParser jp = JsonParser.DEFAULT;
+		try {
+			if (this.tags != null)
+				return jp.parse(vr.resolve(this.tags), ArrayList.class, Tag.class);
+			String tags = context.getMessages().findFirstString(req.getLocale(), "tags");
+			if (tags != null)
+				return jp.parse(vr.resolve(tags), ArrayList.class, Tag.class);
+			Swagger s = req.getSwaggerFromFile();
+			if (s != null)
+				return s.getTags();
+			return null;
+		} catch (Exception e) {
+			throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
+		}
+	}
+
+	/**
+	 * Returns the version information of this REST resource.
+	 * <p>
+	 * Subclasses can override this method to provide their own version information.
+	 * <p>
+	 * The default implementation returns the version information from the following locations (whichever matches first):
+	 * <ol>
+	 * 	<li>{@link RestResource#version() @RestResource.version()} annotation on this class, and then any parent classes.
+	 * 	<li><ck>[ClassName].version</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+	 * 		annotation for this class, then any parent classes.
+	 * 	<li><ck>version</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+	 * 		annotation for this class, then any parent classes.
+	 * 	<li><ck>/info/version</ck> entry in swagger file.
+	 * </ol>
+	 *
+	 * @param req The current request.
+	 * @return The localized contact information of this REST resource, or <jk>null</jk> if no contact information was found.
+	 */
+	public ExternalDocumentation getExternalDocs(RestRequest req) {
+		VarResolverSession vr = req.getVarResolverSession();
+		JsonParser jp = JsonParser.DEFAULT;
+		try {
+			if (this.externalDocs != null)
+				return jp.parse(vr.resolve(this.externalDocs), ExternalDocumentation.class);
+			String externalDocs = context.getMessages().findFirstString(req.getLocale(), "externalDocs");
+			if (externalDocs != null)
+				return jp.parse(vr.resolve(externalDocs), ExternalDocumentation.class);
+			Swagger s = req.getSwaggerFromFile();
+			if (s != null)
+				return s.getExternalDocs();
+			return null;
+		} catch (Exception e) {
+			throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
+		}
+	}
+}