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

[1/4] juneau git commit: Improvements to static files support in REST.

Repository: juneau
Updated Branches:
  refs/heads/master 2da728a51 -> b820b2735


http://git-wip-us.apache.org/repos/asf/juneau/blob/b820b273/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestResource.java
----------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestResource.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestResource.java
index a823e5b..6fa4ba8 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestResource.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestResource.java
@@ -16,11 +16,11 @@ import static java.lang.annotation.ElementType.*;
 import static java.lang.annotation.RetentionPolicy.*;
 
 import java.lang.annotation.*;
-
-import javax.servlet.http.*;
+import java.util.*;
 
 import org.apache.juneau.*;
 import org.apache.juneau.encoders.Encoder;
+import org.apache.juneau.http.*;
 import org.apache.juneau.httppart.*;
 import org.apache.juneau.ini.*;
 import org.apache.juneau.parser.*;
@@ -79,9 +79,17 @@ public @interface RestResource {
 	 *
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_guards}
-	 * 	<li>Annotation:  {@link RestResource#guards()} / {@link RestMethod#guards()}
-	 * 	<li>Method: {@link RestContextBuilder#guards(Class...)} / {@link RestContextBuilder#guards(RestGuard...)}
+	 * 	<li>Property:  {@link RestContext#REST_guards}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#guards()}
+	 * 			<li>{@link RestMethod#guards()}
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#guards(Class...)}
+	 * 			<li>{@link RestContextBuilder#guards(RestGuard...)}
+	 * 		</ul>
 	 * 	<li>{@link RestGuard} classes must have either a no-arg or {@link PropertyStore} argument constructors.
 	 * 	<li>Values are added AFTER those found in the annotation and therefore take precedence over those defined via the
 	 * 		annotation.
@@ -106,9 +114,17 @@ public @interface RestResource {
 	 * 
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_converters}
-	 * 	<li>Annotation:  {@link RestResource#converters()} / {@link RestMethod#converters()}
-	 * 	<li>Method: {@link RestContextBuilder#converters(Class...)} / {@link RestContextBuilder#converters(RestConverter...)}
+	 * 	<li>Property:  {@link RestContext#REST_converters}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#converters()}
+	 * 			<li>{@link RestMethod#converters()}
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#converters(Class...)}
+	 * 			<li>{@link RestContextBuilder#converters(RestConverter...)}
+	 * 		</ul>
 	 * 	<li>{@link RestConverter} classes must have either a no-arg or {@link PropertyStore} argument constructors.
 	 *	</ul>
 	 */
@@ -130,8 +146,24 @@ public @interface RestResource {
 	 * 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 RestContextBuilder#beanFilters(Class...)} method.
+	 * <h5 class='section'>Notes:</h5>
+	 * <ul class='spaced-list'>
+	 * 	<li>Property:  {@link BeanContext#BEAN_beanFilters}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#beanFilters()}
+	 * 			<li>{@link RestMethod#beanFilters()}
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#beanFilters(Class...)}
+	 * 			<li>{@link RestContextBuilder#beanFilters(Collection)}
+	 * 			<li>{@link RestContextBuilder#beanFilters(boolean, Class...)}
+	 * 			<li>{@link RestContextBuilder#beanFilters(boolean, Collection)}
+	 * 			<li>{@link RestContextBuilder#beanFiltersRemove(Class...)}
+	 * 			<li>{@link RestContextBuilder#beanFiltersRemove(Collection)}
+	 * 		</ul>
+	 *	</ul>
 	 */
 	Class<?>[] beanFilters() default {};
 
@@ -150,8 +182,24 @@ public @interface RestResource {
 	 * 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 RestContextBuilder#pojoSwaps(Class...)} method.
+	 * <h5 class='section'>Notes:</h5>
+	 * <ul class='spaced-list'>
+	 * 	<li>Property:  {@link BeanContext#BEAN_pojoSwaps}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#pojoSwaps()}
+	 * 			<li>{@link RestMethod#pojoSwaps()}
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#pojoSwaps(Class...)}
+	 * 			<li>{@link RestContextBuilder#pojoSwaps(Collection)}
+	 * 			<li>{@link RestContextBuilder#pojoSwaps(boolean, Class...)}
+	 * 			<li>{@link RestContextBuilder#pojoSwaps(boolean, Collection)}
+	 * 			<li>{@link RestContextBuilder#pojoSwapsRemove(Class...)}
+	 * 			<li>{@link RestContextBuilder#pojoSwapsRemove(Collection)}
+	 * 		</ul>
+	 *	</ul>
 	 */
 	Class<?>[] pojoSwaps() default {};
 
@@ -185,9 +233,15 @@ public @interface RestResource {
 	 *
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_paramResolvers}
-	 * 	<li>Annotation:  {@link RestResource#paramResolvers()}
-	 * 	<li>Method: {@link RestContextBuilder#paramResolvers(Class...)}
+	 * 	<li>Property:  {@link RestContext#REST_paramResolvers}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#paramResolvers()}
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#paramResolvers(Class...)}
+	 * 		</ul>
 	 * 	<li>{@link RestParam} classes must have either a no-arg or {@link PropertyStore} argument constructors.
 	 *	</ul>
 	 */
@@ -265,6 +319,62 @@ public @interface RestResource {
 	Class<? extends HttpPartParser> partParser() default UonPartParser.class;
 
 	/**
+	 * Supported accept media types.
+	 *
+	 * <p>
+	 * Overrides the media types inferred from the serializers that identify what media types can be produced by the resource.
+	 * 
+	 * <p>
+	 * This affects the values returned by {@link RestRequest#getSupportedAcceptTypes()} and the supported accept
+	 * types shown in {@link RestInfoProvider#getSwagger(RestRequest)}.
+	 *
+	 * <p>
+	 * <h5 class='section'>Notes:</h5>
+	 * <ul class='spaced-list'>
+	 * 	<li>Property:  {@link RestContext#REST_supportedAcceptTypes}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#supportedAcceptTypes()}
+	 * 			<li>{@link RestMethod#supportedAcceptTypes()}
+	 * 		</ul> 
+	 * 	<li>Methods:  
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#supportedAcceptTypes(boolean,String...)}
+	 * 			<li>{@link RestContextBuilder#supportedAcceptTypes(boolean,MediaType...)}
+	 * 		</ul>
+	 *	</ul>
+	 */
+	String[] supportedAcceptTypes() default {};
+	
+	/**
+	 * Supported content media types.
+	 *
+	 * <p>
+	 * Overrides the media types inferred from the parsers that identify what media types can be consumed by the resource.
+	 *
+	 * <p>
+	 * This affects the values returned by {@link RestRequest#getSupportedContentTypes()} and the supported content
+	 * types shown in {@link RestInfoProvider#getSwagger(RestRequest)}.
+	 * 
+	 * <p>
+	 * <h5 class='section'>Notes:</h5>
+	 * <ul class='spaced-list'>
+	 * 	<li>Property:  {@link RestContext#REST_supportedContentTypes}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#supportedContentTypes()}
+	 * 			<li>{@link RestMethod#supportedContentTypes()}
+	 * 		</ul> 
+	 * 	<li>Methods:  
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#supportedContentTypes(boolean,String...)}
+	 * 			<li>{@link RestContextBuilder#supportedContentTypes(boolean,MediaType...)}
+	 * 		</ul>
+	 *	</ul>
+	 */
+	String[] supportedContentTypes() default {};
+	
+	/**
 	 * Response handlers.
 	 *
 	 * <p>
@@ -285,9 +395,16 @@ public @interface RestResource {
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_responseHandlers}
-	 * 	<li>Annotation:  {@link RestResource#responseHandlers()} 
-	 * 	<li>Method: {@link RestContextBuilder#responseHandlers(Class...)} / {@link RestContextBuilder#responseHandlers(ResponseHandler...)}
+	 * 	<li>Property:  {@link RestContext#REST_responseHandlers}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#responseHandlers()} 
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#responseHandlers(Class...)}
+	 * 			<li>{@link RestContextBuilder#responseHandlers(ResponseHandler...)}
+	 * 		</ul>
 	 * 	<li>{@link ResponseHandler} classes must have either a no-arg or {@link PropertyStore} argument constructors.
 	 *	</ul>
 	 */
@@ -335,9 +452,17 @@ public @interface RestResource {
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_defaultRequestHeaders}
-	 * 	<li>Annotation:  {@link RestResource#defaultRequestHeaders()} / {@link RestMethod#defaultRequestHeaders()} 
-	 * 	<li>Method: {@link RestContextBuilder#defaultRequestHeader(String,Object)} / {@link RestContextBuilder#defaultRequestHeaders(String...)}
+	 * 	<li>Property:  {@link RestContext#REST_defaultRequestHeaders}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#defaultRequestHeaders()}
+	 * 			<li>{@link RestMethod#defaultRequestHeaders()} 
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#defaultRequestHeader(String,Object)}
+	 * 			<li>{@link RestContextBuilder#defaultRequestHeaders(String...)}
+	 * 		</ul>
 	 * 	<li>Strings are of the format <js>"Header-Name: header-value"</js>.
 	 * 	<li>You can use either <js>':'</js> or <js>'='</js> as the key/value delimiter.
 	 * 	<li>Key and value is trimmed of whitespace.
@@ -367,9 +492,16 @@ public @interface RestResource {
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_defaultResponseHeaders}
-	 * 	<li>Annotation:  {@link RestResource#defaultResponseHeaders()} 
-	 * 	<li>Method: {@link RestContextBuilder#defaultResponseHeader(String,Object)} / {@link RestContextBuilder#defaultResponseHeaders(String...)}
+	 * 	<li>Property:  {@link RestContext#REST_defaultResponseHeaders}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#defaultResponseHeaders()} 
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#defaultResponseHeader(String,Object)}
+	 * 			<li>{@link RestContextBuilder#defaultResponseHeaders(String...)}
+	 * 		</ul>
 	 * 	<li>Strings are of the format <js>"Header-Name: header-value"</js>.
 	 * 	<li>You can use either <js>':'</js> or <js>'='</js> as the key/value delimiter.
 	 * 	<li>Key and value is trimmed of whitespace.
@@ -545,45 +677,135 @@ public @interface RestResource {
 	String config() default "";
 
 	/**
-	 * Defines paths and locations of statically served files.
-	 *
-	 * <p>
-	 * This is a JSON map of paths to packages/directories located on either the classpath or working directory.
+	 * Static file mappings. 
 	 *
 	 * <p>
-	 * Mappings are cumulative from parent to child.  Child resources can override mappings made on parent resources.
-	 *
+	 * Used to define paths and locations of statically-served files such as images or HTML documents.
+	 * 
 	 * <p>
-	 * If the file cannot be located, the request will return {@link HttpServletResponse#SC_NOT_FOUND}.
-	 *
+	 * Static files are found using the registered  {@link ClasspathResourceFinder} for locating files on the classpath 
+	 * (or other location).
+	 * 
 	 * <h5 class='section'>Example:</h5>
 	 * <p class='bcode'>
 	 * 	<jk>package</jk> com.foo.mypackage;
-	 *
+	 * 
 	 * 	<ja>@RestResource</ja>(
 	 * 		path=<js>"/myresource"</js>,
-	 * 		staticFiles=<js>"{htdocs:'docs'}"</js>
+	 * 		staticFiles=<js>"htdocs:docs"</js>
 	 * 	)
-	 * 	<jk>public class</jk> MyResource <jk>extends</jk> RestServletDefault {
-	 * 	}
+	 * 	<jk>public class</jk> MyResource <jk>extends</jk> RestServletDefault {...}
 	 * </p>
-	 *
+	 * 
 	 * <p>
-	 * In this example, given a GET request to <code>/myresource/htdocs/foobar.html</code>, the servlet will attempt to
-	 * find the <code>foobar.html</code> file in the following ordered locations:
+	 * In the example above, given a GET request to <l>/myresource/htdocs/foobar.html</l>, the servlet will attempt to find 
+	 * the <l>foobar.html</l> file in the following ordered locations:
 	 * <ol>
-	 * 	<li><code>com.foo.mypackage.docs</code> package.
-	 * 	<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.
+	 * 	<li><l>com.foo.mypackage.docs</l> package.
+	 * 	<li><l>org.apache.juneau.rest.docs</l> package (since <l>RestServletDefault</l> is in <l>org.apache.juneau.rest</l>).
+	 * 	<li><l>[working-dir]/docs</l> directory.
 	 * </ol>
+	 * 
+	 * <h6 class='topic'>Notes:</h6>
+	 * <ul class='spaced-list'>
+	 * 	<li>Property:  {@link RestContext#REST_staticFiles}
+	 * 	<li>Annotations: 
+	 * 		<ul>
+	 * 			<li>{@link RestResource#staticFiles()} 
+	 * 		</ul>
+	 * 	<li>Methods: 
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#staticFiles(String)},
+	 * 			<li>{@link RestContextBuilder#staticFiles(Class,String)}
+	 * 			<li>{@link RestContextBuilder#staticFiles(String,String)}
+	 * 			<li>{@link RestContextBuilder#staticFiles(Class,String,String)} 
+	 * 			<li>{@link RestContextBuilder#staticFiles(StaticFileMapping...)} 
+	 * 		</ul>
+	 * 	<li>Mappings are cumulative from parent to child.  
+	 * 	<li>Child resources can override mappings made on parent resources.
+	 * 	<li>The resource finder is configured via the {@link RestContext#REST_classpathResourceFinder} setting, and can be
+	 * 		overridden to provide customized handling of resource retrieval.
+	 * 	<li>The {@link RestContext#REST_useClasspathResourceCaching} setting can be used to cache static files in memory
+	 * 		to improve performance.
+	 * </ul>
+	 */
+	String[] staticFiles() default {};
+	
+	/**
+	 * Static file response headers. 
+	 *
+	 * <p>
+	 * Used to customize the headers on responses returned for statically-served files.
+	 * 
+	 * <h6 class='topic'>Notes:</h6>
+	 * <ul class='spaced-list'>
+	 * 	<li>Property:  {@link RestContext#REST_staticFileResponseHeaders}
+	 * 	<li>Annotations: 
+	 * 		<ul>
+	 * 			<li>{@link RestResource#staticFileResponseHeaders()} 
+	 * 		</ul>
+	 * 	<li>Methods: 
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#staticFileResponseHeaders(boolean,Map)}
+	 * 			<li>{@link RestContextBuilder#staticFileResponseHeaders(String...)}
+	 * 			<li>{@link RestContextBuilder#staticFileResponseHeader(String,String)}
+	 * 		</ul>
+	 * 	<li>The default values is <code>{<js>'Cache-Control'</js>: <js>'max-age=86400, public</js>}</code>.
+	 * </ul>
+	 */
+	String[] staticFileResponseHeaders() default {};
+	
+	/**
+	 * Classpath resource finder. 
 	 *
 	 * <p>
-	 * The programmatic equivalent to this annotation is the {@link RestContextBuilder#staticFiles(Class, String)} method.
+	 * Used to retrieve localized files from the classpath.
+	 * 
+	 * <h6 class='topic'>Notes:</h6>
+	 * <ul class='spaced-list'>
+	 * 	<li>Property:  {@link RestContext#REST_classpathResourceFinder}
+	 * 	<li>Annotations: 
+	 * 		<ul>
+	 * 			<li>{@link RestResource#classpathResourceFinder()} 
+	 * 		</ul>
+	 * 	<li>Methods: 
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#classpathResourceFinder(Class)}
+	 * 			<li>{@link RestContextBuilder#classpathResourceFinder(ClasspathResourceFinder)}
+	 * 		</ul>
+	 * 	<li>
+	 * 		The default value is {@link ClasspathResourceFinderBasic} which provides basic support for finding localized
+	 * 		resources on the classpath and JVM working directory.
+	 * 		<br>The {@link ClasspathResourceFinderRecursive} is another option that also recursively searches for resources
+	 * 		up the parent class hierarchy.
+	 * 		<br>Each of these classes can be extended to provide customized handling of resource retrieval.
+	 * </ul>
 	 */
-	String staticFiles() default "";
+	Class<? extends ClasspathResourceFinder> classpathResourceFinder() default ClasspathResourceFinder.Null.class;
 
 	/**
+	 * <b>Configuration property:</b>  Use classpath resource caching. 
+	 *
+	 * <p>
+	 * When enabled, resources retrieved via {@link RestRequest#getClasspathReaderResource(String, boolean)} (and related 
+	 * methods) will be cached in memory to speed subsequent lookups.
+	 * 
+	 * <h6 class='topic'>Notes:</h6>
+	 * <ul class='spaced-list'>
+	 * 	<li>Property:  {@link RestContext#REST_useClasspathResourceCaching}
+	 * 	<li>Annotations: 
+	 * 		<ul>
+	 * 			<li>{@link RestResource#useClasspathResourceCaching()} 
+	 * 		</ul>
+	 * 	<li>Methods: 
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#useClasspathResourceCaching(boolean)}
+	 * 		</ul>
+	 * </ul>
+	 */
+	String useClasspathResourceCaching() default "";
+	
+	/**
 	 * Client version header.
 	 *
 	 * <p>
@@ -596,9 +818,15 @@ public @interface RestResource {
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_clientVersionHeader}
-	 * 	<li>Annotation:  {@link RestResource#clientVersionHeader()} 
-	 * 	<li>Method: {@link RestContextBuilder#clientVersionHeader(String)}
+	 * 	<li>Property:  {@link RestContext#REST_clientVersionHeader}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#clientVersionHeader()} 
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#clientVersionHeader(String)}
+	 * 		</ul>
 	 * 	<li>The default value is <js>"X-Client-Version"</js>.
 	 *	</ul>
 	 */
@@ -616,9 +844,16 @@ public @interface RestResource {
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_resourceResolver}
-	 * 	<li>Annotation:  {@link RestResource#resourceResolver()} 
-	 * 	<li>Method: {@link RestContextBuilder#resourceResolver(Class)} / {@link RestContextBuilder#resourceResolver(RestResourceResolver)}
+	 * 	<li>Property:  {@link RestContext#REST_resourceResolver}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#resourceResolver()} 
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#resourceResolver(Class)}
+	 * 			<li>{@link RestContextBuilder#resourceResolver(RestResourceResolver)}
+	 * 		</ul>
 	 * 	<li>Unless overridden, resource resolvers are inherited from parent resources.
 	 *	</ul>
 	 */
@@ -633,9 +868,16 @@ public @interface RestResource {
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_logger}
-	 * 	<li>Annotation:  {@link RestResource#logger()} 
-	 * 	<li>Method: {@link RestContextBuilder#logger(Class)} / {@link RestContextBuilder#logger(RestLogger)} 
+	 * 	<li>Property:  {@link RestContext#REST_logger}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#logger()} 
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#logger(Class)}
+	 * 			<li>{@link RestContextBuilder#logger(RestLogger)} 
+	 * 		</ul>
 	 * 	<li>The {@link org.apache.juneau.rest.RestLogger.Normal} logger can be used to provide basic error logging to the Java logger.
 	 *	</ul>
 	 */
@@ -651,9 +893,16 @@ public @interface RestResource {
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_callHandler}
-	 * 	<li>Annotation:  {@link RestResource#callHandler()} 
-	 * 	<li>Method: {@link RestContextBuilder#callHandler(Class)} / {@link RestContextBuilder#callHandler(RestCallHandler)} 
+	 * 	<li>Property:  {@link RestContext#REST_callHandler}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#callHandler()} 
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#callHandler(Class)}
+	 * 			<li>{@link RestContextBuilder#callHandler(RestCallHandler)} 
+	 * 		</ul>
 	 *	</ul>
 	 */
 	Class<? extends RestCallHandler> callHandler() default RestCallHandler.class;
@@ -670,9 +919,16 @@ public @interface RestResource {
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_infoProvider}
-	 * 	<li>Annotation:  {@link RestResource#infoProvider()} 
-	 * 	<li>Method: {@link RestContextBuilder#infoProvider(Class)} / {@link RestContextBuilder#infoProvider(RestInfoProvider)} 
+	 * 	<li>Property:  {@link RestContext#REST_infoProvider}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#infoProvider()} 
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#infoProvider(Class)}
+	 * 			<li>{@link RestContextBuilder#infoProvider(RestInfoProvider)} 
+	 * 		</ul>
 	 *	</ul>
 	 */
 	Class<? extends RestInfoProvider> infoProvider() default RestInfoProvider.class;
@@ -686,9 +942,15 @@ public @interface RestResource {
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link Serializer#SERIALIZER_listener}
-	 * 	<li>Annotation:  {@link RestResource#serializerListener()} 
-	 * 	<li>Method: {@link RestContextBuilder#serializerListener(Class)} 
+	 * 	<li>Property:  {@link Serializer#SERIALIZER_listener}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#serializerListener()} 
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#serializerListener(Class)} 
+	 * 		</ul>
 	 *	</ul>
 	 */
 	Class<? extends SerializerListener> serializerListener() default SerializerListener.Null.class;
@@ -702,9 +964,15 @@ public @interface RestResource {
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link Parser#PARSER_listener}
-	 * 	<li>Annotation:  {@link RestResource#parserListener()} 
-	 * 	<li>Method: {@link RestContextBuilder#parserListener(Class)} 
+	 * 	<li>Property:  {@link Parser#PARSER_listener}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#parserListener()} 
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#parserListener(Class)} 
+	 * 		</ul>
 	 *	</ul>
 	 */
 	Class<? extends ParserListener> parserListener() default ParserListener.Null.class;
@@ -793,9 +1061,15 @@ public @interface RestResource {
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_contextPath}
-	 * 	<li>Annotation:  {@link RestResource#contextPath()} 
-	 * 	<li>Method: {@link RestContextBuilder#contextPath(String)} 
+	 * 	<li>Property:  {@link RestContext#REST_contextPath}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#contextPath()} 
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#contextPath(String)} 
+	 * 		</ul>
 	 *	</ul>
 	 */
 	String contextPath() default "";
@@ -810,9 +1084,15 @@ public @interface RestResource {
 	 * 
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_allowHeaderParams}
-	 * 	<li>Annotation:  {@link RestResource#allowHeaderParams()}
-	 * 	<li>Method: {@link RestContextBuilder#allowHeaderParams(boolean)}
+	 * 	<li>Property:  {@link RestContext#REST_allowHeaderParams}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#allowHeaderParams()}
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#allowHeaderParams(boolean)}
+	 * 		</ul>
 	 * 	<li>Boolean value.
 	 * 	<li>Can contain variables.
 	 *		<li>Parameter names are case-insensitive.
@@ -832,9 +1112,15 @@ public @interface RestResource {
 	 *
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_allowedMethodParams}
-	 * 	<li>Annotation:  {@link RestResource#allowedMethodParams()}
-	 * 	<li>Method: {@link RestContextBuilder#allowedMethodParams(String...)}
+	 * 	<li>Property:  {@link RestContext#REST_allowedMethodParams}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#allowedMethodParams()}
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#allowedMethodParams(String...)}
+	 * 		</ul>
 	 * 	<li>Format is a comma-delimited list of HTTP method names that can be passed in as a method parameter.
 	 * 	<li>Parameter name is case-insensitive.
 	 * 	<li>Can contain variables.
@@ -866,9 +1152,15 @@ public @interface RestResource {
 	 * 
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_allowBodyParam}
-	 * 	<li>Annotation:  {@link RestResource#allowBodyParam()}
-	 * 	<li>Method: {@link RestContextBuilder#allowBodyParam(boolean)}
+	 * 	<li>Property:  {@link RestContext#REST_allowBodyParam}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#allowBodyParam()}
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#allowBodyParam(boolean)}
+	 * 		</ul>
 	 * 	<li>Boolean value.
 	 * 	<li>Can contain variables.
 	 * 	<li>Parameter name is case-insensitive.
@@ -885,9 +1177,15 @@ public @interface RestResource {
 	 *
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_renderResponseStackTraces}
-	 * 	<li>Annotation:  {@link RestResource#renderResponseStackTraces()}
-	 * 	<li>Method: {@link RestContextBuilder#renderResponseStackTraces(boolean)}
+	 * 	<li>Property:  {@link RestContext#REST_renderResponseStackTraces}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#renderResponseStackTraces()}
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#renderResponseStackTraces(boolean)}
+	 * 		</ul>
 	 * 	<li>Boolean value.
 	 * 	<li>Can contain variables.
 	 * 	<li>Useful for debugging, although allowing stack traces to be rendered may cause security concerns so use
@@ -905,9 +1203,15 @@ public @interface RestResource {
 	 *
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_useStackTraceHashes}
-	 * 	<li>Annotation:  {@link RestResource#useStackTraceHashes()}
-	 * 	<li>Method: {@link RestContextBuilder#useStackTraceHashes(boolean)}
+	 * 	<li>Property:  {@link RestContext#REST_useStackTraceHashes}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#useStackTraceHashes()}
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#useStackTraceHashes(boolean)}
+	 * 		</ul>
 	 * 	<li>Boolean value.
 	 * 	<li>Can contain variables.
 	 *	</ul>
@@ -922,9 +1226,16 @@ public @interface RestResource {
 	 *
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_defaultCharset}
-	 * 	<li>Annotation:  {@link RestResource#defaultCharset()} / {@link RestMethod#defaultCharset()}
-	 * 	<li>Method: {@link RestContextBuilder#defaultCharset(String)}
+	 * 	<li>Property:  {@link RestContext#REST_defaultCharset}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#defaultCharset()}
+	 * 			<li>{@link RestMethod#defaultCharset()}
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#defaultCharset(String)}
+	 * 		</ul>
 	 * 	<li>String value.
 	 * 	<li>Can contain variables.
 	 *	</ul>
@@ -947,9 +1258,16 @@ public @interface RestResource {
 	 * 
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_maxInput}
-	 * 	<li>Annotation:  {@link RestResource#maxInput()} / {@link RestMethod#maxInput()}
-	 * 	<li>Method: {@link RestContextBuilder#maxInput(String)}
+	 * 	<li>Property:  {@link RestContext#REST_maxInput}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#maxInput()}
+	 * 			<li>{@link RestMethod#maxInput()}
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#maxInput(String)}
+	 * 		</ul>
 	 * 	<li>String value that gets resolved to a <jk>long</jk>.
 	 * 	<li>Can contain variables.
 	 * 	<li>Can be suffixed with any of the following representing kilobytes, megabytes, and gigabytes:  

http://git-wip-us.apache.org/repos/asf/juneau/blob/b820b273/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/package.html
----------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/package.html b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/package.html
index 7dc7a3d..ac9a814 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/package.html
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/package.html
@@ -2424,7 +2424,7 @@
 		</table>
 		<p>
 			In addition to being used in annotation values, string variables can also be embedded in resource files 
-			retrieved through the {@link org.apache.juneau.rest.RestRequest#getReaderResource(String,boolean)} method.
+			retrieved through the {@link org.apache.juneau.rest.RestRequest#getClasspathReaderResource(String,boolean)} method.
 			This can often be useful for embedding localized strings inside HTML form pages.  
 		</p>
 		<p>
@@ -2497,7 +2497,7 @@
 		<jd>/** GET request handler */</jd> 
 		<ja>@RestMethod</ja>(name=<jsf>GET</jsf>, path=<js>"/"</js>) 
 		<jk>public</jk> ReaderResource doGet(RestRequest req) <jk>throws</jk> IOException { 
-			<jk>return</jk> req.getReaderResource(<js>"UrlEncodedForm.html"</js>, <jk>true</jk>); 
+			<jk>return</jk> req.getClasspathReaderResource(<js>"UrlEncodedForm.html"</js>, <jk>true</jk>); 
 		} 
 		
 		<jd>/** POST request handler */</jd> 
@@ -2534,7 +2534,7 @@
 			<li class='jc'>
 				{@link org.apache.juneau.rest.ReaderResource}
 			<li class='jm'>
-				{@link org.apache.juneau.rest.RestRequest#getReaderResource(String,boolean)}
+				{@link org.apache.juneau.rest.RestRequest#getClasspathReaderResource(String,boolean)}
 			<li class='jp'>
 				<a class='doclink' href='../svl/package-summary.html#TOC'>org.apache.juneau.svl</a>
 		</ul>
@@ -2560,7 +2560,7 @@
 
 	<ja>@RestResource</ja>(
 		path=<js>"/myresource"</js>,
-		staticFiles=<js>"{htdocs:'docs'}"</js>
+		staticFiles={<js>"htdocs:docs"</js>}
 	)
 	<jk>public class</jk> MyResource <jk>extends</jk> RestServletDefault {
 	}

http://git-wip-us.apache.org/repos/asf/juneau/blob/b820b273/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/vars/FileVar.java
----------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/vars/FileVar.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/vars/FileVar.java
index a3962e2..30d2271 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/vars/FileVar.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/vars/FileVar.java
@@ -24,11 +24,11 @@ import org.apache.juneau.utils.*;
  *
  * <p>
  * File variables resolve to the contents of resource files located on the classpath or local JVM directory.
- * They use the {@link RestRequest#getReaderResource(String)} method to retrieve the contents of the file.
- * That in turn uses the {@link ResourceFinder} associated with the servlet class to find the file.
+ * They use the {@link RestRequest#getClasspathReaderResource(String)} method to retrieve the contents of the file.
+ * That in turn uses the {@link ClasspathResourceFinder} associated with the servlet class to find the file.
  *
  * <p>
- * The {@link ResourceFinder} is similar to {@link Class#getResourceAsStream(String)} except if it doesn't find the
+ * The {@link ClasspathResourceFinder} is similar to {@link Class#getResourceAsStream(String)} except if it doesn't find the
  * resource on this class, it searches up the parent hierarchy chain.
  *
  * <p>
@@ -80,7 +80,7 @@ public class FileVar extends DefaultingVar {
 	@Override /* Parameter */
 	public String resolve(VarResolverSession session, String key) throws Exception {
 		RestRequest req = session.getSessionObject(RestRequest.class, SESSION_req);
-		ReaderResource rr = req.getReaderResource(key);
+		ReaderResource rr = req.getClasspathReaderResource(key);
 		return (rr == null ? null : rr.toCommentStrippedString());
 	}
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/juneau/blob/b820b273/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/Widget.java
----------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/Widget.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/Widget.java
index bd06362..1b135b0 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/Widget.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/widget/Widget.java
@@ -113,7 +113,7 @@ import org.apache.juneau.utils.*;
  */
 public abstract class Widget {
 
-	private final ResourceFinder resourceFinder = new ResourceFinder(getClass());
+	private final ClasspathResourceManager rm = new ClasspathResourceManager(getClass());
 
 	/**
 	 * The widget key.
@@ -191,7 +191,7 @@ public abstract class Widget {
 	 * @throws IOException
 	 */
 	protected String getResourceAsString(String name) throws IOException {
-		return resourceFinder.getResourceAsString(name);
+		return rm.getString(name);
 	}
 
 	/**
@@ -214,7 +214,7 @@ public abstract class Widget {
 	 * @throws IOException
 	 */
 	protected String getResourceAsString(String name, Locale locale) throws IOException {
-		return resourceFinder.getResourceAsString(name, locale);
+		return rm.getString(name, locale);
 	}
 
 	/**


[3/4] juneau git commit: Improvements to static files support in REST.

Posted by ja...@apache.org.
http://git-wip-us.apache.org/repos/asf/juneau/blob/b820b273/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
----------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
index 54eff02..d1079b5 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
@@ -80,9 +80,15 @@ public final class RestContext extends BeanContext {
 	 * 
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_allowHeaderParams}
-	 * 	<li>Annotation:  {@link RestResource#allowHeaderParams()}
-	 * 	<li>Method: {@link RestContextBuilder#allowHeaderParams(boolean)}
+	 * 	<li>Property:  {@link RestContext#REST_allowHeaderParams}
+	 * 	<li>Annotations:  
+	 * 		<ul>
+	 * 			<li>{@link RestResource#allowHeaderParams()}
+	 * 		</ul>
+	 * 	<li>Methods:  
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#allowHeaderParams(boolean)}
+	 * 		</ul>
 	 * 	<li>Parameter name is case-insensitive.
 	 * 	<li>Useful for debugging REST interface using only a browser.
 	 *	</ul>
@@ -107,9 +113,15 @@ public final class RestContext extends BeanContext {
 	 * 
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_allowBodyParam}
-	 * 	<li>Annotation:  {@link RestResource#allowBodyParam()}
-	 * 	<li>Method: {@link RestContextBuilder#allowBodyParam(boolean)}
+	 * 	<li>Property: 	{@link RestContext#REST_allowBodyParam}
+	 * 	<li>Annotations:  
+	 * 		<ul>
+	 * 			<li>{@link RestResource#allowBodyParam()}
+	 * 		</ul>
+	 * 	<li>Methods:  
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#allowBodyParam(boolean)}
+	 * 		</ul>
 	 * 	<li>Parameter name is case-insensitive.
 	 * 	<li>Useful for debugging PUT and POST methods using only a browser.
 	 *	</ul>
@@ -134,9 +146,15 @@ public final class RestContext extends BeanContext {
 	 *
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_allowedMethodParams}
-	 * 	<li>Annotation:  {@link RestResource#allowedMethodParams()}
-	 * 	<li>Method: {@link RestContextBuilder#allowedMethodParams(String...)}
+	 * 	<li>Property:  {@link RestContext#REST_allowedMethodParams}
+	 * 	<li>Annotations:  
+	 * 		<ul>
+	 * 			<li>{@link RestResource#allowedMethodParams()}
+	 * 		</ul>
+	 * 	<li>Methods:  
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#allowedMethodParams(String...)}
+	 * 		</ul>
 	 * 	<li>Format is a comma-delimited list of HTTP method names that can be passed in as a method parameter.
 	 * 	<li>Parameter name is case-insensitive.
 	 * 	<li>Use "*" to represent all methods.
@@ -164,9 +182,15 @@ public final class RestContext extends BeanContext {
 	 *
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_renderResponseStackTraces}
-	 * 	<li>Annotation:  {@link RestResource#renderResponseStackTraces()}
-	 * 	<li>Method: {@link RestContextBuilder#renderResponseStackTraces(boolean)}
+	 * 	<li>Property:  {@link RestContext#REST_renderResponseStackTraces}
+	 * 	<li>Annotations:  
+	 * 		<ul>
+	 * 			<li>{@link RestResource#renderResponseStackTraces()}
+	 * 		</ul>
+	 * 	<li>Methods:  
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#renderResponseStackTraces(boolean)}
+	 * 		</ul>
 	 * 	<li>Useful for debugging, although allowing stack traces to be rendered may cause security concerns so use
 	 * 		caution when enabling.
 	 *	</ul>
@@ -189,9 +213,15 @@ public final class RestContext extends BeanContext {
 	 *
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_useStackTraceHashes}
-	 * 	<li>Annotation:  {@link RestResource#useStackTraceHashes()}
-	 * 	<li>Method: {@link RestContextBuilder#useStackTraceHashes(boolean)}
+	 * 	<li>Property:  {@link RestContext#REST_useStackTraceHashes}
+	 * 	<li>Annotations:  
+	 * 		<ul>
+	 * 			<li>{@link RestResource#useStackTraceHashes()}
+	 * 		</ul>
+	 * 	<li>Methods:  
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#useStackTraceHashes(boolean)}
+	 * 		</ul>
 	 *	</ul>
 	 */
 	public static final String REST_useStackTraceHashes = PREFIX + "useStackTraceHashes.b";
@@ -211,9 +241,16 @@ public final class RestContext extends BeanContext {
 	 *
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_defaultCharset}
-	 * 	<li>Annotation:  {@link RestResource#defaultCharset()} / {@link RestMethod#defaultCharset()}
-	 * 	<li>Method: {@link RestContextBuilder#defaultCharset(String)}
+	 * 	<li>Property:  {@link RestContext#REST_defaultCharset}
+	 * 	<li>Annotations:  
+	 * 		<ul>
+	 * 			<li>{@link RestResource#defaultCharset()}
+	 * 			<li>{@link RestMethod#defaultCharset()}
+	 * 		</ul>
+	 * 	<li>Methods:  
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#defaultCharset(String)}
+	 * 		</ul>
 	 *	</ul>
 	 */
 	public static final String REST_defaultCharset = PREFIX + "defaultCharset.s";
@@ -234,9 +271,16 @@ public final class RestContext extends BeanContext {
 	 * 
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_maxInput}
-	 * 	<li>Annotation:  {@link RestResource#maxInput()} / {@link RestMethod#maxInput()}
-	 * 	<li>Method: {@link RestContextBuilder#maxInput(String)}
+	 * 	<li>Property:  {@link RestContext#REST_maxInput}
+	 * 	<li>Annotations:  
+	 * 		<ul>
+	 * 			<li>{@link RestResource#maxInput()}
+	 * 			<li>{@link RestMethod#maxInput()}
+	 * 		</ul>
+	 * 	<li>Methods:  
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#maxInput(String)}
+	 * 		</ul>
 	 * 	<li>String value that gets resolved to a <jk>long</jk>.
 	 * 	<li>Can be suffixed with any of the following representing kilobytes, megabytes, and gigabytes:  
 	 * 		<js>'K'</js>, <js>'M'</js>, <js>'G'</js>.
@@ -282,9 +326,15 @@ public final class RestContext extends BeanContext {
 	 *
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_paramResolvers}
-	 * 	<li>Annotation:  {@link RestResource#paramResolvers()}
-	 * 	<li>Method: {@link RestContextBuilder#paramResolvers(Class...)} / {@link RestContextBuilder#paramResolvers(RestParam...)}
+	 * 	<li>Property:  {@link RestContext#REST_paramResolvers}
+	 * 	<li>Annotations:  
+	 * 		<ul>
+	 * 			<li>{@link RestResource#paramResolvers()}
+	 * 	<li>Methods: 
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#paramResolvers(Class...)}
+	 * 			<li>{@link RestContextBuilder#paramResolvers(RestParam...)}
+	 * 		</ul>
 	 * 	<li>{@link RestParam} classes must have either a no-arg or {@link PropertyStore} argument constructors.
 	 *	</ul>
 	 */
@@ -314,9 +364,17 @@ public final class RestContext extends BeanContext {
 	 * 
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_converters}
-	 * 	<li>Annotation:  {@link RestResource#converters()} / {@link RestMethod#converters()}
-	 * 	<li>Method: {@link RestContextBuilder#converters(Class...)} / {@link RestContextBuilder#converters(RestConverter...)}
+	 * 	<li>Property:  {@link RestContext#REST_converters}
+	 * 	<li>Annotations:  
+	 * 		<ul>
+	 * 			<li>{@link RestResource#converters()}
+	 * 			<li>{@link RestMethod#converters()}
+	 * 		</ul>
+	 * 	<li>Methods: 
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#converters(Class...)}
+	 * 			<li>{@link RestContextBuilder#converters(RestConverter...)}
+	 * 		</ul>
 	 * 	<li>{@link RestConverter} classes must have either a no-arg or {@link PropertyStore} argument constructors.
 	 *	</ul>
 	 */
@@ -342,9 +400,17 @@ public final class RestContext extends BeanContext {
 	 *
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_guards}
-	 * 	<li>Annotation:  {@link RestResource#guards()} / {@link RestMethod#guards()}
-	 * 	<li>Method: {@link RestContextBuilder#guards(Class...)} / {@link RestContextBuilder#guards(RestGuard...)}
+	 * 	<li>Property:  {@link RestContext#REST_guards}
+	 * 	<li>Annotations:  
+	 * 		<ul>
+	 * 			<li>{@link RestResource#guards()}
+	 * 			<li>{@link RestMethod#guards()}
+	 * 		</ul>
+	 * 	<li>Methods: 
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#guards(Class...)}
+	 * 			<li>{@link RestContextBuilder#guards(RestGuard...)}
+	 * 		</ul>
 	 * 	<li>{@link RestGuard} classes must have either a no-arg or {@link PropertyStore} argument constructors.
 	 *	</ul>
 	 */
@@ -378,9 +444,16 @@ public final class RestContext extends BeanContext {
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_responseHandlers}
-	 * 	<li>Annotation:  {@link RestResource#responseHandlers()} 
-	 * 	<li>Method: {@link RestContextBuilder#responseHandlers(Class...)} / {@link RestContextBuilder#responseHandlers(ResponseHandler...)}
+	 * 	<li>Property:  {@link RestContext#REST_responseHandlers}
+	 * 	<li>Annotations: 
+	 * 		<ul>
+	 * 			<li>{@link RestResource#responseHandlers()} 
+	 * 		</ul>
+	 * 	<li>Methods: 
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#responseHandlers(Class...)}
+					<li>{@link RestContextBuilder#responseHandlers(ResponseHandler...)}
+				</ul>
 	 * 	<li>{@link ResponseHandler} classes must have either a no-arg or {@link PropertyStore} argument constructors.
 	 *	</ul>
 	 */
@@ -390,7 +463,7 @@ public final class RestContext extends BeanContext {
 	 * <b>Configuration property:</b>  Default request headers.
 	 *
 	 * <ul>
-	 * 	<li><b>Name:</b> <js>"RestContext.defaultRequestHeaders.sms"</js>
+	 * 	<li><b>Name:</b> <js>"RestContext.defaultRequestHeaders.smo"</js>
 	 * 	<li><b>Data type:</b> <code>Map&lt;String,String&gt;</code>
 	 * 	<li><b>Default:</b> empty map
 	 * 	<li><b>Session-overridable:</b> <jk>false</jk>
@@ -402,21 +475,29 @@ public final class RestContext extends BeanContext {
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_defaultRequestHeaders}
-	 * 	<li>Annotation:  {@link RestResource#defaultRequestHeaders()} / {@link RestMethod#defaultRequestHeaders()} 
-	 * 	<li>Method: {@link RestContextBuilder#defaultRequestHeader(String,Object)} / {@link RestContextBuilder#defaultRequestHeaders(String...)}
+	 * 	<li>Property:  {@link RestContext#REST_defaultRequestHeaders}
+	 * 	<li>Annotations:  
+	 * 		<ul>
+	 * 			<li>{@link RestResource#defaultRequestHeaders()}
+	 * 			<li>{@link RestMethod#defaultRequestHeaders()} 
+	 * 		</ul>
+	 * 	<li>Methods:  
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#defaultRequestHeader(String,Object)}
+	 * 			<li>{@link RestContextBuilder#defaultRequestHeaders(String...)}
+	 * 		</ul>
 	 * 	<li>Affects values returned by {@link RestRequest#getHeader(String)} when the header is not present on the request.
 	 * 	<li>The most useful reason for this annotation is to provide a default <code>Accept</code> header when one is not
 	 * 		specified so that a particular default {@link Serializer} is picked.
 	 *	</ul>
 	 */
-	public static final String REST_defaultRequestHeaders = PREFIX + "defaultRequestHeaders.sms";
+	public static final String REST_defaultRequestHeaders = PREFIX + "defaultRequestHeaders.smo";
 
 	/**
 	 * <b>Configuration property:</b>  Default response headers.
 	 *
 	 * <ul>
-	 * 	<li><b>Name:</b> <js>"RestContext.defaultResponseHeaders.oms"</js>
+	 * 	<li><b>Name:</b> <js>"RestContext.defaultResponseHeaders.omo"</js>
 	 * 	<li><b>Data type:</b> <code>Map&lt;String,String&gt;</code>
 	 * 	<li><b>Default:</b> empty map
 	 * 	<li><b>Session-overridable:</b> <jk>false</jk>
@@ -428,62 +509,93 @@ public final class RestContext extends BeanContext {
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_defaultResponseHeaders}
-	 * 	<li>Annotation:  {@link RestResource#defaultResponseHeaders()} 
-	 * 	<li>Method: {@link RestContextBuilder#defaultResponseHeader(String,Object)} / {@link RestContextBuilder#defaultResponseHeaders(String...)}
+	 * 	<li>Property:  {@link RestContext#REST_defaultResponseHeaders}
+	 * 	<li>Annotations: 
+	 * 		<ul>
+	 * 			<li>{@link RestResource#defaultResponseHeaders()} 
+	 * 		</ul>
+	 * 	<li>Methods:  
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#defaultResponseHeader(String,Object)}
+	 * 			<li>{@link RestContextBuilder#defaultResponseHeaders(String...)}
+	 * 		</ul>
 	 * 	<li>This is equivalent to calling {@link RestResponse#setHeader(String, String)} programmatically in each of 
 	 * 		the Java methods.
 	 * 	<li>The header value will not be set if the header value has already been specified (hence the 'default' in the name).
 	 *	</ul>
 	 */
-	public static final String REST_defaultResponseHeaders = PREFIX + "defaultResponseHeaders.oms";
+	public static final String REST_defaultResponseHeaders = PREFIX + "defaultResponseHeaders.omo";
 
 	/**
-	 * <b>Configuration property:</b>  Supported content media types.
+	 * <b>Configuration property:</b>  Supported accept media types.
 	 *
 	 * <ul>
-	 * 	<li><b>Name:</b> <js>"RestContext.supportedContentTypes.ls"</js>
+	 * 	<li><b>Name:</b> <js>"RestContext.supportedAcceptTypes.ls"</js>
 	 * 	<li><b>Data type:</b> <code>List&lt;String&gt;</code>
 	 * 	<li><b>Default:</b> empty list
 	 * 	<li><b>Session-overridable:</b> <jk>false</jk>
 	 * </ul>
 	 * 
 	 * <p>
-	 * Overrides the media types inferred from the parsers that identify what media types can be consumed by the resource.
+	 * Overrides the media types inferred from the serializers that identify what media types can be produced by the resource.
+	 * 
+	 * <p>
+	 * This affects the values returned by {@link RestRequest#getSupportedAcceptTypes()} and the supported accept
+	 * types shown in {@link RestInfoProvider#getSwagger(RestRequest)}.
 	 *
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_supportedContentTypes}
-	 * 	<li>Annotation:  N/A 
-	 * 	<li>Method: {@link RestContextBuilder#supportedContentTypes(boolean,String...)} / {@link RestContextBuilder#supportedContentTypes(boolean,MediaType...)}
+	 * 	<li>Property:  {@link RestContext#REST_supportedAcceptTypes}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#supportedAcceptTypes()}
+	 * 			<li>{@link RestMethod#supportedAcceptTypes()}
+	 * 		</ul> 
+	 * 	<li>Methods:  
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#supportedAcceptTypes(boolean,String...)}
+	 * 			<li>{@link RestContextBuilder#supportedAcceptTypes(boolean,MediaType...)}
+	 * 		</ul>
 	 *	</ul>
 	 */
-	public static final String REST_supportedContentTypes = PREFIX + "supportedContentTypes.ls";
-	
+	public static final String REST_supportedAcceptTypes = PREFIX + "supportedAcceptTypes.ls";
+
 	/**
-	 * <b>Configuration property:</b>  Supported accept media types.
+	 * <b>Configuration property:</b>  Supported content media types.
 	 *
 	 * <ul>
-	 * 	<li><b>Name:</b> <js>"RestContext.supportedAcceptTypes.ls"</js>
+	 * 	<li><b>Name:</b> <js>"RestContext.supportedContentTypes.ls"</js>
 	 * 	<li><b>Data type:</b> <code>List&lt;String&gt;</code>
 	 * 	<li><b>Default:</b> empty list
 	 * 	<li><b>Session-overridable:</b> <jk>false</jk>
 	 * </ul>
 	 * 
 	 * <p>
-	 * Overrides the media types inferred from the serializers that identify what media types can be produced by the resource.
+	 * Overrides the media types inferred from the parsers that identify what media types can be consumed by the resource.
 	 *
 	 * <p>
+	 * This affects the values returned by {@link RestRequest#getSupportedContentTypes()} and the supported content
+	 * types shown in {@link RestInfoProvider#getSwagger(RestRequest)}.
+	 * 
+	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_supportedAcceptTypes}
-	 * 	<li>Annotation:  N/A 
-	 * 	<li>Method: {@link RestContextBuilder#supportedAcceptTypes(boolean,String...)} / {@link RestContextBuilder#supportedAcceptTypes(boolean,MediaType...)}
+	 * 	<li>Property:  {@link RestContext#REST_supportedContentTypes}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#supportedContentTypes()}
+	 * 			<li>{@link RestMethod#supportedContentTypes()}
+	 * 		</ul> 
+	 * 	<li>Methods:  
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#supportedContentTypes(boolean,String...)}
+	 * 			<li>{@link RestContextBuilder#supportedContentTypes(boolean,MediaType...)}
+	 * 		</ul>
 	 *	</ul>
 	 */
-	public static final String REST_supportedAcceptTypes = PREFIX + "supportedAcceptTypes.ls";
-
+	public static final String REST_supportedContentTypes = PREFIX + "supportedContentTypes.ls";
+	
 	/**
 	 * <b>Configuration property:</b>  Client version header.
 	 *
@@ -504,9 +616,15 @@ public final class RestContext extends BeanContext {
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_clientVersionHeader}
-	 * 	<li>Annotation:  {@link RestResource#clientVersionHeader()} 
-	 * 	<li>Method: {@link RestContextBuilder#clientVersionHeader(String)}
+	 * 	<li>Property:  {@link RestContext#REST_clientVersionHeader}
+	 * 	<li>Annotations: 
+	 * 		<ul>
+	 * 			<li>{@link RestResource#clientVersionHeader()} 
+	 * 		</ul>
+	 * 	<li>Methods: 
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#clientVersionHeader(String)}
+	 * 		</ul>
 	 *	</ul>
 	 */
 	public static final String REST_clientVersionHeader = PREFIX + "clientVersionHeader.s";
@@ -529,9 +647,16 @@ public final class RestContext extends BeanContext {
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_resourceResolver}
-	 * 	<li>Annotation:  {@link RestResource#resourceResolver()} 
-	 * 	<li>Method: {@link RestContextBuilder#resourceResolver(Class)} / {@link RestContextBuilder#resourceResolver(RestResourceResolver)}
+	 * 	<li>Property:  {@link RestContext#REST_resourceResolver}
+	 * 	<li>Annotations: 
+	 * 		<ul>
+	 * 			<li>{@link RestResource#resourceResolver()} 
+	 * 		</ul>
+	 * 	<li>Methods:  
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#resourceResolver(Class)}
+					<li>{@link RestContextBuilder#resourceResolver(RestResourceResolver)}
+				</ul>
 	 * 	<li>Unless overridden, resource resolvers are inherited from parent resources.
 	 *	</ul>
 	 */
@@ -553,9 +678,16 @@ public final class RestContext extends BeanContext {
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_logger}
-	 * 	<li>Annotation:  {@link RestResource#logger()} 
-	 * 	<li>Method: {@link RestContextBuilder#logger(Class)} / {@link RestContextBuilder#logger(RestLogger)} 
+	 * 	<li>Property:  {@link RestContext#REST_logger}
+	 * 	<li>Annotations: 
+	 * 		<ul>
+	 * 			<li>{@link RestResource#logger()} 
+	 * 		</ul>
+	 * 	<li>Methods:  
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#logger(Class)}
+					<li>{@link RestContextBuilder#logger(RestLogger)} 
+				</ul>
 	 * 	<li>The {@link RestLogger.Normal} logger can be used to provide basic error logging to the Java logger.
 	 *	</ul>
 	 */
@@ -578,9 +710,16 @@ public final class RestContext extends BeanContext {
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_callHandler}
-	 * 	<li>Annotation:  {@link RestResource#callHandler()} 
-	 * 	<li>Method: {@link RestContextBuilder#callHandler(Class)} / {@link RestContextBuilder#callHandler(RestCallHandler)} 
+	 * 	<li>Property:  {@link RestContext#REST_callHandler}
+	 * 	<li>Annotations: 
+	 * 		<ul>
+	 * 			<li>{@link RestResource#callHandler()} 
+	 * 		</ul>
+	 * 	<li>Methods: 
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#callHandler(Class)}
+	 * 			<li>{@link RestContextBuilder#callHandler(RestCallHandler)} 
+	 * 		</ul>
 	 *	</ul>
 	 */
 	public static final String REST_callHandler = PREFIX + "callHandler.o";
@@ -604,9 +743,16 @@ public final class RestContext extends BeanContext {
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_infoProvider}
-	 * 	<li>Annotation:  {@link RestResource#infoProvider()} 
-	 * 	<li>Method: {@link RestContextBuilder#infoProvider(Class)} / {@link RestContextBuilder#infoProvider(RestInfoProvider)} 
+	 * 	<li>Property:  {@link RestContext#REST_infoProvider}
+	 * 	<li>Annotations: 
+	 * 		<ul>
+	 * 			<li>{@link RestResource#infoProvider()} 
+	 * 		</ul>
+	 * 	<li>Methods: 
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#infoProvider(Class)}
+	 * 			<li>{@link RestContextBuilder#infoProvider(RestInfoProvider)} 
+	 * 		</ul>
 	 *	</ul>
 	 */
 	public static final String REST_infoProvider = PREFIX + "infoProvider.o";
@@ -627,9 +773,15 @@ public final class RestContext extends BeanContext {
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_path}
-	 * 	<li>Annotation:  {@link RestResource#path()} 
-	 * 	<li>Method: {@link RestContextBuilder#path(String)} 
+	 * 	<li>Property:  {@link RestContext#REST_path}
+	 * 	<li>Annotations: 
+	 * 		<ul>
+	 * 			<li>{@link RestResource#path()} 
+	 * 		</ul>
+	 * 	<li>Methods: 
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#path(String)} 
+	 * 		</ul>
 	 * 	<li>This annotation is ignored on top-level servlets (i.e. servlets defined in <code>web.xml</code> files).
 	 * 		<br>Therefore, implementers can optionally specify a path value for documentation purposes.
 	 * 	<li>Typically, this setting is only applicable to resources defined as children through the 
@@ -661,13 +813,180 @@ public final class RestContext extends BeanContext {
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_contextPath}
-	 * 	<li>Annotation:  {@link RestResource#contextPath()} 
-	 * 	<li>Method: {@link RestContextBuilder#contextPath(String)} 
+	 * 	<li>Property:  {@link RestContext#REST_contextPath}
+	 * 	<li>Annotations: 
+	 * 		<ul>
+	 * 			<li>{@link RestResource#contextPath()} 
+	 * 		</ul>
+	 * 	<li>Methods: 
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#contextPath(String)} 
+	 * 		</ul>
 	 *	</ul>
 	 */
 	public static final String REST_contextPath = PREFIX + "contextPath.s";
 	
+	/**
+	 * <b>Configuration property:</b>  Static file mappings. 
+	 *
+	 * <ul>
+	 * 	<li><b>Name:</b> <js>"RestContext.staticFiles.lo"</js>
+	 * 	<li><b>Data type:</b> <code>List&lt;StaticFileMapping&gt;</code>
+	 * 	<li><b>Default:</b> <jk>null</jk>
+	 * 	<li><b>Session-overridable:</b> <jk>false</jk>
+	 * </ul>
+	 * 
+	 * <p>
+	 * Used to define paths and locations of statically-served files such as images or HTML documents.
+	 * 
+	 * <p>
+	 * Static files are found by calling {@link #getClasspathResource(String,Locale)} which uses the registered 
+	 * {@link ClasspathResourceFinder} for locating files on the classpath (or other location).
+	 * 
+	 * <p>
+	 * An example where this class is used is in the {@link RestResource#staticFiles} annotation:
+	 * <p class='bcode'>
+	 * 	<jk>package</jk> com.foo.mypackage;
+	 * 
+	 * 	<ja>@RestResource</ja>(
+	 * 		path=<js>"/myresource"</js>,
+	 * 		staticFiles={<js>"htdocs:docs"</js>,<js>"styles:styles"</js>}
+	 * 	)
+	 * 	<jk>public class</jk> MyResource <jk>extends</jk> RestServletDefault {...}
+	 * </p>
+	 * 
+	 * <p>
+	 * In the example above, given a GET request to <l>/myresource/htdocs/foobar.html</l>, the servlet will attempt to find 
+	 * the <l>foobar.html</l> file in the following ordered locations:
+	 * <ol>
+	 * 	<li><l>com.foo.mypackage.docs</l> package.
+	 * 	<li><l>org.apache.juneau.rest.docs</l> package (since <l>RestServletDefault</l> is in <l>org.apache.juneau.rest</l>).
+	 * 	<li><l>[working-dir]/docs</l> directory.
+	 * </ol>
+	 * 
+	 * <h6 class='topic'>Notes:</h6>
+	 * <ul class='spaced-list'>
+	 * 	<li>Property:  {@link RestContext#REST_staticFiles}
+	 * 	<li>Annotations: 
+	 * 		<ul>
+	 * 			<li>{@link RestResource#staticFiles()} 
+	 * 		</ul>
+	 * 	<li>Methods: 
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#staticFiles(String)},
+	 * 			<li>{@link RestContextBuilder#staticFiles(Class,String)}
+	 * 			<li>{@link RestContextBuilder#staticFiles(String,String)}
+	 * 			<li>{@link RestContextBuilder#staticFiles(Class,String,String)} 
+	 * 			<li>{@link RestContextBuilder#staticFiles(StaticFileMapping...)} 
+	 * 		</ul>
+	 * 	<li>Mappings are cumulative from parent to child.  
+	 * 	<li>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.RestContext#getMediaTypeForName(String)} method.
+	 * 	<li>The resource finder is configured via the {@link #REST_classpathResourceFinder} setting, and can be
+	 * 		overridden to provide customized handling of resource retrieval.
+	 * 	<li>The {@link #REST_useClasspathResourceCaching} setting can be used to cache static files in memory
+	 * 		to improve performance.
+	 * </ul>
+	 */
+	public static final String REST_staticFiles = PREFIX + "staticFiles.lo";
+	
+	/**
+	 * <b>Configuration property:</b>  Static file response headers. 
+	 *
+	 * <ul>
+	 * 	<li><b>Name:</b> <js>"RestContext.staticFileResponseHeaders.omo"</js>
+	 * 	<li><b>Data type:</b> <code>Map&lt;String,String&gt;</code>
+	 * 	<li><b>Default:</b> <code>{<js>'Cache-Control'</js>: <js>'max-age=86400, public</js>}</code>
+	 * 	<li><b>Session-overridable:</b> <jk>false</jk>
+	 * </ul>
+	 * 
+	 * <p>
+	 * Used to customize the headers on responses returned for statically-served files.
+	 * 
+	 * <h6 class='topic'>Notes:</h6>
+	 * <ul class='spaced-list'>
+	 * 	<li>Property:  {@link RestContext#REST_staticFileResponseHeaders}
+	 * 	<li>Annotations: 
+	 * 		<ul>
+	 * 			<li>{@link RestResource#staticFileResponseHeaders()} 
+	 * 		</ul>
+	 * 	<li>Methods: 
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#staticFileResponseHeaders(boolean,Map)}
+	 * 			<li>{@link RestContextBuilder#staticFileResponseHeaders(String...)}
+	 * 			<li>{@link RestContextBuilder#staticFileResponseHeader(String,String)}
+	 * 		</ul>
+	 * </ul>
+	 */
+	public static final String REST_staticFileResponseHeaders = PREFIX + "staticFileResponseHeaders.omo";
+	
+	/**
+	 * <b>Configuration property:</b>  Classpath resource finder. 
+	 *
+	 * <ul>
+	 * 	<li><b>Name:</b> <js>"RestContext.classpathResourceFinder.o"</js>
+	 * 	<li><b>Data type:</b> {@link ClasspathResourceFinder}
+	 * 	<li><b>Default:</b> {@link ClasspathResourceFinderBasic}
+	 * 	<li><b>Session-overridable:</b> <jk>false</jk>
+	 * </ul>
+	 * 
+	 * <p>
+	 * Used to retrieve localized files from the classpath.
+	 * 
+	 * <p>
+	 * 
+	 * <h6 class='topic'>Notes:</h6>
+	 * <ul class='spaced-list'>
+	 * 	<li>Property:  {@link RestContext#REST_classpathResourceFinder}
+	 * 	<li>Annotations: 
+	 * 		<ul>
+	 * 			<li>{@link RestResource#classpathResourceFinder()} 
+	 * 		</ul>
+	 * 	<li>Methods: 
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#classpathResourceFinder(Class)}
+	 * 			<li>{@link RestContextBuilder#classpathResourceFinder(ClasspathResourceFinder)}
+	 * 		</ul>
+	 * 	<li>
+	 * 		The default value is {@link ClasspathResourceFinderBasic} which provides basic support for finding localized
+	 * 		resources on the classpath and JVM working directory.
+	 * 		<br>The {@link ClasspathResourceFinderRecursive} is another option that also recursively searches for resources
+	 * 		up the parent class hierarchy.
+	 * 		<br>Each of these classes can be extended to provide customized handling of resource retrieval.
+	 * </ul>
+	 */
+	public static final String REST_classpathResourceFinder = PREFIX + "classpathResourceFinder.o";
+	
+	/**
+	 * <b>Configuration property:</b>  Use classpath resource caching. 
+	 *
+	 * <ul>
+	 * 	<li><b>Name:</b> <js>"RestContext.useClasspathResourceCaching.b"</js>
+	 * 	<li><b>Data type:</b> <code>Boolean</code>
+	 * 	<li><b>Default:</b> <jk>true</jk>
+	 * 	<li><b>Session-overridable:</b> <jk>false</jk>
+	 * </ul>
+	 * 
+	 * <p>
+	 * When enabled, resources retrieved via {@link RestContext#getClasspathResource(String, Locale)} (and related 
+	 * methods) will be cached in memory to speed subsequent lookups.
+	 * 
+	 * <h6 class='topic'>Notes:</h6>
+	 * <ul class='spaced-list'>
+	 * 	<li>Property:  {@link RestContext#REST_useClasspathResourceCaching}
+	 * 	<li>Annotations: 
+	 * 		<ul>
+	 * 			<li>{@link RestResource#useClasspathResourceCaching()} 
+	 * 		</ul>
+	 * 	<li>Methods: 
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#useClasspathResourceCaching(boolean)}
+	 * 		</ul>
+	 * </ul>
+	 */
+	public static final String REST_useClasspathResourceCaching = PREFIX + "useClasspathResourceCaching.b";
+
+	
 	//-------------------------------------------------------------------------------------------------------------------
 	// Instance
 	//-------------------------------------------------------------------------------------------------------------------
@@ -702,14 +1021,17 @@ public final class RestContext extends BeanContext {
 	private final List<MediaType>
 		supportedContentTypes,
 		supportedAcceptTypes;
-	private final Map<String,String> defaultRequestHeaders, defaultResponseHeaders;
+	private final Map<String,Object> 
+		defaultRequestHeaders,
+		defaultResponseHeaders,
+		staticFileResponseHeaders;
 	private final BeanContext beanContext;
 	private final RestConverter[] converters;
 	private final RestGuard[] guards;
 	private final ResponseHandler[] responseHandlers;
 	private final MimetypesFileTypeMap mimetypesFileTypeMap;
-	private final Map<String,String> staticFilesMap;
-	private final String[] staticFilesPrefixes;
+	private final StaticFileMapping[] staticFiles;
+	private final String[] staticFilesPaths;
 	private final MessageBundle msgs;
 	private final ConfigFile configFile;
 	private final VarResolver varResolver;
@@ -745,7 +1067,7 @@ public final class RestContext extends BeanContext {
 	// In-memory cache of images and stylesheets in the org.apache.juneau.rest.htdocs package.
 	private final Map<String,StreamResource> staticFilesCache = new ConcurrentHashMap<>();
 
-	private final ResourceFinder resourceFinder;
+	private final ClasspathResourceManager staticResourceManager;
 	private final ConcurrentHashMap<Integer,AtomicInteger> stackTraceHashes = new ConcurrentHashMap<>();
 
 
@@ -789,11 +1111,12 @@ public final class RestContext extends BeanContext {
 				_paramResolvers.put(rp.forClass(), rp);
 			paramResolvers = Collections.unmodifiableMap(_paramResolvers);
 			
-			Map<String,String> _defaultRequestHeaders = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+			Map<String,Object> _defaultRequestHeaders = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
 			_defaultRequestHeaders.putAll(getMapProperty(REST_defaultRequestHeaders, String.class));
 			defaultRequestHeaders = Collections.unmodifiableMap(new LinkedHashMap<>(_defaultRequestHeaders));
 			
-			defaultResponseHeaders = getMapProperty(REST_defaultResponseHeaders, String.class);
+			defaultResponseHeaders = getMapProperty(REST_defaultResponseHeaders, Object.class);
+			staticFileResponseHeaders = getMapProperty(REST_staticFileResponseHeaders, Object.class);	
 			
 			logger = getInstanceProperty(REST_logger, resource, RestLogger.class, RestLogger.NoOp.class, false);
 
@@ -829,14 +1152,20 @@ public final class RestContext extends BeanContext {
 
 			mimetypesFileTypeMap = builder.mimeTypes;
 			
-			resourceFinder = new ResourceFinder(resource.getClass());
+			ClasspathResourceFinder rf = getInstanceProperty(REST_classpathResourceFinder, ClasspathResourceFinder.class, ClasspathResourceFinderBasic.class);
+			boolean useClasspathResourceCaching = getProperty(REST_useClasspathResourceCaching, boolean.class, true);
+			staticResourceManager = new ClasspathResourceManager(resource.getClass(), rf, useClasspathResourceCaching);
 
 			supportedContentTypes = getListProperty(REST_supportedContentTypes, MediaType.class, serializers.getSupportedMediaTypes());
 			supportedAcceptTypes = getListProperty(REST_supportedAcceptTypes, MediaType.class, parsers.getSupportedMediaTypes());
-
+			
+			staticFiles = ArrayUtils.reverse(getArrayProperty(REST_staticFiles, StaticFileMapping.class));
+			Set<String> s = new TreeSet<>();
+			for (StaticFileMapping sfm : staticFiles)
+				s.add(sfm.path);
+			staticFilesPaths = s.toArray(new String[s.size()]);
+			
 			Builder b = new Builder(builder, ps);
-			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.fullPath = b.fullPath;
@@ -1095,13 +1424,10 @@ public final class RestContext extends BeanContext {
 
 	private static final class Builder {
 
-		Map<String,String> staticFilesMap;
-		String[] staticFilesPrefixes;
 		MessageBundle messageBundle;
 		String fullPath;
 		Map<String,Widget> widgets;
 
-		@SuppressWarnings("unchecked")
 		Builder(RestContextBuilder rcb, PropertyStore ps) throws Exception {
 
 			Object resource = rcb.resource;
@@ -1123,23 +1449,6 @@ public final class RestContext extends BeanContext {
 			if (messageBundle == null)
 				messageBundle = new MessageBundle(resource.getClass(), "");
 			
-			VarResolver vr = rcb.getVarResolverBuilder().build();
-
-			staticFilesMap = new LinkedHashMap<>();
-			if (rcb.staticFiles != null) {
-				for (Object o : rcb.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]);
-
-
 			fullPath = (rcb.parentContext == null ? "" : (rcb.parentContext.fullPath + '/')) + rcb.path;
 
 			HtmlDocBuilder hdb = new HtmlDocBuilder(rcb.properties);
@@ -1317,19 +1626,19 @@ public final class RestContext extends BeanContext {
 			String p = urlDecode(trimSlashes(pathInfo));
 			if (p.indexOf("..") != -1)
 				throw new RestException(SC_NOT_FOUND, "Invalid path");
-			for (Map.Entry<String,String> e : staticFilesMap.entrySet()) {
-				String key = trimSlashes(e.getKey());
-				if (p.startsWith(key)) {
-					String remainder = (p.equals(key) ? "" : p.substring(key.length()));
+			for (StaticFileMapping sfm : staticFiles) {
+				String path = sfm.path;
+				if (p.startsWith(path)) {
+					String remainder = (p.equals(path) ? "" : p.substring(path.length()));
 					if (remainder.isEmpty() || remainder.startsWith("/")) {
-						String p2 = trimSlashes(e.getValue()) + remainder;
-						try (InputStream is = getResource(p2, null)) {
+						String p2 = sfm.location + remainder;
+						try (InputStream is = getClasspathResource(sfm.resourceClass, p2, null)) {
 							if (is != null) {
 								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));
+								Map<String,Object> responseHeaders = sfm.responseHeaders != null ? sfm.responseHeaders : staticFileResponseHeaders;
+								staticFilesCache.put(pathInfo, new StreamResource(MediaType.forString(mediaType), responseHeaders, is));
 								return staticFilesCache.get(pathInfo);
 							}
 						}
@@ -1362,24 +1671,56 @@ public final class RestContext extends BeanContext {
 	 * @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 {
-		return resourceFinder.getResourceAsStream(name, locale);
+	protected InputStream getClasspathResource(String name, Locale locale) throws IOException {
+		return staticResourceManager.getStream(name, locale);
+	}
+
+	/**
+	 * Same as {@link #getClasspathResource(String, Locale)}, but allows you to override the class used for looking
+	 * up the classpath resource.
+	 *
+	 * @param baseClass 
+	 * 	Overrides the default class to use for retrieving the classpath resource. 
+	 * 	<br>If <jk>null<jk>, uses the REST resource class.
+	 * @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 getClasspathResource(Class<?> baseClass, String name, Locale locale) throws IOException {
+		return staticResourceManager.getStream(baseClass, name, locale);
 	}
 
 	/**
-	 * Reads the input stream from {@link #getResource(String, Locale)} into a String.
+	 * Reads the input stream from {@link #getClasspathResource(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 {
-		return resourceFinder.getResourceAsString(name, locale);
+	public String getClasspathResourceAsString(String name, Locale locale) throws IOException {
+		return staticResourceManager.getString(name, locale);
 	}
 
 	/**
-	 * Reads the input stream from {@link #getResource(String, Locale)} and parses it into a POJO using the parser
+	 * Same as {@link #getClasspathResourceAsString(String, Locale)}, but allows you to override the class used for looking
+	 * up the classpath resource.
+	 *
+	 * @param baseClass 
+	 * 	Overrides the default class to use for retrieving the classpath resource. 
+	 * 	<br>If <jk>null<jk>, uses the REST resource class.
+	 * @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 getClasspathResourceAsString(Class<?> baseClass, String name, Locale locale) throws IOException {
+		return staticResourceManager.getString(baseClass, name, locale);
+	}
+	
+	/**
+	 * Reads the input stream from {@link #getClasspathResource(String, Locale)} and parses it into a POJO using the parser
 	 * matched by the specified media type.
 	 *
 	 * <p>
@@ -1393,8 +1734,27 @@ public final class RestContext extends BeanContext {
 	 * @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);
+	public <T> T getClasspathResource(Class<T> c, MediaType mediaType, String name, Locale locale) throws IOException, ServletException {
+		return getClasspathResource(null, c, mediaType, name, locale);
+	}
+
+	/**
+	 * Same as {@link #getClasspathResource(Class, MediaType, String, Locale)}, except overrides the class used
+	 * for retrieving the classpath resource.
+	 * 
+	 * @param baseClass 
+	 * 	Overrides the default class to use for retrieving the classpath resource. 
+	 * 	<br>If <jk>null<jk>, uses the REST resource class.
+	 * @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 getClasspathResource(Class<?> baseClass, Class<T> c, MediaType mediaType, String name, Locale locale) throws IOException, ServletException {
+		InputStream is = getClasspathResource(baseClass, name, locale);
 		if (is == null)
 			return null;
 		try {
@@ -2020,7 +2380,7 @@ public final class RestContext extends BeanContext {
 	 *
 	 * @return The default request headers for this resource.  Never <jk>null</jk>.
 	 */
-	protected Map<String,String> getDefaultRequestHeaders() {
+	protected Map<String,Object> getDefaultRequestHeaders() {
 		return defaultRequestHeaders;
 	}
 
@@ -2040,7 +2400,7 @@ public final class RestContext extends BeanContext {
 	 *
 	 * @return The default response headers for this resource.  Never <jk>null</jk>.
 	 */
-	public Map<String,String> getDefaultResponseHeaders() {
+	public Map<String,Object> getDefaultResponseHeaders() {
 		return defaultResponseHeaders;
 	}
 
@@ -2133,7 +2493,7 @@ public final class RestContext extends BeanContext {
 	 * @return <jk>true</jk> if the specified path refers to a static file.
 	 */
 	protected boolean isStaticFile(String p) {
-		return pathStartsWith(p, staticFilesPrefixes);
+		return pathStartsWith(p, staticFilesPaths);
 	}
 
 	/**



[2/4] juneau git commit: Improvements to static files support in REST.

Posted by ja...@apache.org.
http://git-wip-us.apache.org/repos/asf/juneau/blob/b820b273/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java
----------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java
index 8a682fe..cb052e3 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java
@@ -144,6 +144,7 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 		this.parentContext = parentContext;
 		
 		logger(RestLogger.Normal.class);
+		staticFileResponseHeader("Cache-Control", "max-age=86400, public");
 
 		try {
 
@@ -198,8 +199,12 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 				serializers(r.serializers());
 				parsers(r.parsers());
 				encoders(r.encoders());
-				defaultRequestHeaders(r.defaultRequestHeaders());
-				defaultResponseHeaders(r.defaultResponseHeaders());
+				if (r.supportedAcceptTypes().length > 0)
+					supportedAcceptTypes(false, resolveVars(vr, r.supportedAcceptTypes()));
+				if (r.supportedContentTypes().length > 0)
+					supportedContentTypes(false, resolveVars(vr, r.supportedContentTypes()));
+				defaultRequestHeaders(resolveVars(vr, r.defaultRequestHeaders()));
+				defaultResponseHeaders(resolveVars(vr, r.defaultResponseHeaders()));
 				responseHandlers(r.responseHandlers());
 				converters(r.converters());
 				guards(reverse(r.guards()));
@@ -207,16 +212,22 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 				beanFilters(r.beanFilters());
 				pojoSwaps(r.pojoSwaps());
 				paramResolvers(r.paramResolvers());
-				serializerListener(r.serializerListener());
-				parserListener(r.parserListener());
+				if (r.serializerListener() != SerializerListener.Null.class)
+					serializerListener(r.serializerListener());
+				if (r.parserListener() != ParserListener.Null.class)
+					parserListener(r.parserListener());
 				contextPath(r.contextPath());
-				if (! r.staticFiles().isEmpty())
-					staticFiles(c, r.staticFiles());
+				for (String mapping : r.staticFiles())
+					staticFiles(c, vr.resolve(mapping));
+				staticFileResponseHeaders(resolveVars(vr, r.staticFileResponseHeaders()));
+				if (! r.useClasspathResourceCaching().isEmpty())
+					useClasspathResourceCaching(Boolean.valueOf(vr.resolve(r.useClasspathResourceCaching())));
+				if (r.classpathResourceFinder() != ClasspathResourceFinder.Null.class)
+					classpathResourceFinder(r.classpathResourceFinder());
 				if (! r.path().isEmpty())
 					path(r.path());
 				if (! r.clientVersionHeader().isEmpty())
 					clientVersionHeader(r.clientVersionHeader());
-
 				if (r.resourceResolver() != RestResourceResolver.class)
 					resourceResolver(r.resourceResolver());
 				if (r.logger() != RestLogger.Normal.class)
@@ -260,6 +271,13 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 			throw new ServletException(e);
 		}
 	}
+	
+	private static String[] resolveVars(VarResolver vr, String[] in) {
+		String[] out = new String[in.length];
+		for (int i = 0; i < in.length; i++) 
+			out[i] = vr.resolve(in[i]);
+		return out;
+	}
 
 	/*
 	 * Calls all @RestHook(INIT) methods on the specified resource object.
@@ -589,7 +607,7 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 * <p>
 	 * These definitions are used in the following locations for setting the media type on responses:
 	 * <ul>
-	 * 	<li>{@link RestRequest#getReaderResource(String)}
+	 * 	<li>{@link RestRequest#getClasspathReaderResource(String)}
 	 * 	<li>Static files resolved through {@link RestResource#staticFiles()}
 	 * </ul>
 	 *
@@ -662,31 +680,6 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	}
 
 	/**
-	 * 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 RestContextBuilder staticFiles(Class<?> resourceClass, String staticFilesString) {
-		if (staticFiles == null)
-			staticFiles = new ArrayList<>();
-		staticFiles.add(new Pair<Class<?>,Object>(resourceClass, staticFilesString));
-		return this;
-	}
-
-	/**
 	 * Defines widgets that can be used in conjunction with string variables of the form <js>"$W{name}"</js>to quickly
 	 * generate arbitrary replacement text.
 	 *
@@ -805,9 +798,15 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_path}
-	 * 	<li>Annotation:  {@link RestResource#path()} 
-	 * 	<li>Method: {@link RestContextBuilder#path(String)} 
+	 * 	<li>Property:  {@link RestContext#REST_path}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#path()}
+	 * 		</ul> 
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#path(String)} 
+	 * 		</ul>
 	 * 	<li>This annotation is ignored on top-level servlets (i.e. servlets defined in <code>web.xml</code> files).
 	 * 		<br>Therefore, implementers can optionally specify a path value for documentation purposes.
 	 * 	<li>Typically, this setting is only applicable to resources defined as children through the 
@@ -840,9 +839,15 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_contextPath}
-	 * 	<li>Annotation:  {@link RestResource#contextPath()} 
-	 * 	<li>Method: {@link RestContextBuilder#contextPath(String)} 
+	 * 	<li>Property:  {@link RestContext#REST_contextPath}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#contextPath()} 
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>@link RestContextBuilder#contextPath(String)} 
+	 * 		</ul>
 	 *	</ul>
 	 *
 	 * @param contextPath The context path for this resource and any child resources.
@@ -864,13 +869,18 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 * 
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_allowHeaderParams}
-	 * 	<li>Annotation:  {@link RestResource#allowHeaderParams()}
-	 * 	<li>Method: {@link RestContextBuilder#allowHeaderParams(boolean)}
+	 * 	<li>Property:  {@link RestContext#REST_allowHeaderParams}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#allowHeaderParams()}
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#allowHeaderParams(boolean)}
+	 * 		</ul>
 	 * 	<li>Format is a comma-delimited list of HTTP method names that can be passed in as a method parameter.
 	 * 	<li>Parameter name is case-insensitive.
 	 * 	<li>Useful for debugging REST interface using only a browser.
-	 * 	<li>This is equivalent to calling <code>set(<jsf>REST_allowHeaderParams</jsf>, value)</code>.
 	 *	</ul>
 	 *
 	 * @param value The new value for this setting.
@@ -898,12 +908,17 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 * 
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_allowBodyParam}
-	 * 	<li>Annotation:  {@link RestResource#allowBodyParam()}
-	 * 	<li>Method: {@link RestContextBuilder#allowBodyParam(boolean)}
+	 * 	<li>Property:  {@link RestContext#REST_allowBodyParam}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#allowBodyParam()}
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#allowBodyParam(boolean)}
+	 * 		</ul>
 	 * 	<li>Parameter name is case-insensitive.
 	 * 	<li>Useful for debugging PUT and POST methods using only a browser.
-	 * 	<li>This is equivalent to calling <code>set(<jsf>REST_allowBodyParam</jsf>, value)</code>.
 	 *	</ul>
 	 *
 	 * @param value The new value for this setting.
@@ -924,9 +939,15 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 *
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_allowedMethodParams}
-	 * 	<li>Annotation:  {@link RestResource#allowedMethodParams()}
-	 * 	<li>Method: {@link RestContextBuilder#allowedMethodParams(String...)}
+	 * 	<li>Property:  {@link RestContext#REST_allowedMethodParams}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#allowedMethodParams()}
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#allowedMethodParams(String...)}
+	 * 		</ul>
 	 * 	<li>Parameter name is case-insensitive.
 	 * 	<li>Use "*" to represent all methods.
 	 *	</ul>
@@ -951,12 +972,17 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 *
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_renderResponseStackTraces}
-	 * 	<li>Annotation:  {@link RestResource#renderResponseStackTraces()}
-	 * 	<li>Method: {@link RestContextBuilder#renderResponseStackTraces(boolean)}
+	 * 	<li>Property:  {@link RestContext#REST_renderResponseStackTraces}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#renderResponseStackTraces()}
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#renderResponseStackTraces(boolean)}
+	 * 		</ul>
 	 * 	<li>Useful for debugging, although allowing stack traces to be rendered may cause security concerns so use
 	 * 		caution when enabling.
-	 * 	<li>This is equivalent to calling <code>set(<jsf>REST_renderResponseStackTraces</jsf>, value)</code>.
 	 *	</ul>
 	 *
 	 * @param value The new value for this setting.
@@ -975,10 +1001,15 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 *
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_useStackTraceHashes}
-	 * 	<li>Annotation:  {@link RestResource#useStackTraceHashes()} 
-	 * 	<li>Method: {@link RestContextBuilder#useStackTraceHashes(boolean)}
-	 * 	<li>This is equivalent to calling <code>set(<jsf>REST_useStackTraceHashes</jsf>, value)</code>.
+	 * 	<li>Property:  {@link RestContext#REST_useStackTraceHashes}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#useStackTraceHashes()} 
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#useStackTraceHashes(boolean)}
+	 * 		</ul>
 	 *	</ul>
 	 *
 	 * @param value The new value for this setting.
@@ -996,10 +1027,16 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 *
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_defaultCharset}
-	 * 	<li>Annotation:  {@link RestResource#defaultCharset()} / {@link RestMethod#defaultCharset()}
-	 * 	<li>Method: {@link RestContextBuilder#defaultCharset(String)}
-	 * 	<li>This is equivalent to calling <code>set(<jsf>REST_defaultCharset</jsf>, value)</code>.
+	 * 	<li>Property:  {@link RestContext#REST_defaultCharset}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#defaultCharset()}
+					<li>{@link RestMethod#defaultCharset()}
+				</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#defaultCharset(String)}
+	 * 		</ul>
 	 *	</ul>
 	 *
 	 * @param value The new value for this setting.
@@ -1018,9 +1055,16 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 * 
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_maxInput}
-	 * 	<li>Annotation:  {@link RestResource#maxInput()} / {@link RestMethod#maxInput()}
-	 * 	<li>Method: {@link RestContextBuilder#maxInput(String)}
+	 * 	<li>Property:  {@link RestContext#REST_maxInput}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#maxInput()}
+	 * 			<li>{@link RestMethod#maxInput()}
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#maxInput(String)}
+	 * 		</ul>
 	 * 	<li>String value that gets resolved to a <jk>long</jk>.
 	 * 	<li>Can be suffixed with any of the following representing kilobytes, megabytes, and gigabytes:  
 	 * 		<js>'K'</js>, <js>'M'</js>, <js>'G'</js>.
@@ -1064,9 +1108,15 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 *
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_paramResolvers}
-	 * 	<li>Annotation:  {@link RestResource#paramResolvers()}
-	 * 	<li>Method: {@link RestContextBuilder#paramResolvers(Class...)}
+	 * 	<li>Property:  {@link RestContext#REST_paramResolvers}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#paramResolvers()}
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#paramResolvers(Class...)}
+	 * 		</ul>
 	 * 	<li>{@link RestParam} classes must have either a no-arg or {@link PropertyStore} argument constructors.
 	 *	</ul>
 	 *
@@ -1108,9 +1158,15 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 *
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_paramResolvers}
-	 * 	<li>Annotation:  {@link RestResource#paramResolvers()}
-	 * 	<li>Method: {@link RestContextBuilder#paramResolvers(Class...)}
+	 * 	<li>Property:  {@link RestContext#REST_paramResolvers}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#paramResolvers()}
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#paramResolvers(Class...)}
+	 * 		</ul>
 	 * 	<li>{@link RestParam} classes must have either a no-arg or {@link PropertyStore} argument constructors.
 	 *	</ul>
 	 *
@@ -1138,9 +1194,17 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 * 
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_converters}
-	 * 	<li>Annotation:  {@link RestResource#converters()} / {@link RestMethod#converters()}
-	 * 	<li>Method: {@link RestContextBuilder#converters(Class...)} / {@link RestContextBuilder#converters(RestConverter...)}
+	 * 	<li>Property:  {@link RestContext#REST_converters}
+	 * 	<li>Annotation:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#converters()}
+	 * 			<li>{@link RestMethod#converters()}
+	 * 		</ul>
+	 * 	<li>Method:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#converters(Class...)}
+	 * 			<li>{@link RestContextBuilder#converters(RestConverter...)}
+	 * 		</ul>
 	 * 	<li>{@link RestConverter} classes must have either a no-arg or {@link PropertyStore} argument constructors.
 	 *	</ul>
 	 *
@@ -1168,9 +1232,17 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 * 
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_converters}
-	 * 	<li>Annotation:  {@link RestResource#converters()} / {@link RestMethod#converters()}
-	 * 	<li>Method: {@link RestContextBuilder#converters(Class...)} / {@link RestContextBuilder#converters(RestConverter...)}
+	 * 	<li>Property:  {@link RestContext#REST_converters}
+	 * 	<li>Annotation:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#converters()}
+	 * 			<li>{@link RestMethod#converters()}
+	 * 		</ul>
+	 * 	<li>Method:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#converters(Class...)}
+	 * 			<li>{@link RestContextBuilder#converters(RestConverter...)}
+	 * 		</ul>
 	 * 	<li>{@link RestConverter} classes must have either a no-arg or {@link PropertyStore} argument constructors.
 	 *	</ul>
 	 *
@@ -1194,9 +1266,17 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 *
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_guards}
-	 * 	<li>Annotation:  {@link RestResource#guards()} / {@link RestMethod#guards()}
-	 * 	<li>Method: {@link RestContextBuilder#guards(Class...)} / {@link RestContextBuilder#guards(RestGuard...)}
+	 * 	<li>Property:  {@link RestContext#REST_guards}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#guards()}
+	 * 			<li>{@link RestMethod#guards()}
+	 * 		</ul>
+	 * 	<li>Method:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#guards(Class...)}
+	 * 			<li>{@link RestContextBuilder#guards(RestGuard...)}
+	 * 		</ul>
 	 * 	<li>{@link RestGuard} classes must have either a no-arg or {@link PropertyStore} argument constructors.
 	 * 	<li>Values are added AFTER those found in the annotation and therefore take precedence over those defined via the
 	 * 		annotation.
@@ -1222,9 +1302,17 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 *
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_guards}
-	 * 	<li>Annotation:  {@link RestResource#guards()} / {@link RestMethod#guards()}
-	 * 	<li>Method: {@link RestContextBuilder#guards(Class...)} / {@link RestContextBuilder#guards(RestGuard...)}
+	 * 	<li>Property:  {@link RestContext#REST_guards}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#guards()}
+	 * 			<li>{@link RestMethod#guards()}
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#guards(Class...)}
+	 * 			<li>{@link RestContextBuilder#guards(RestGuard...)}
+	 * 		</ul>
 	 * 	<li>{@link RestGuard} classes must have either a no-arg or {@link PropertyStore} argument constructors.
 	 * 	<li>Values are added AFTER those found in the annotation and therefore take precedence over those defined via the
 	 * 		annotation.
@@ -1258,9 +1346,16 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_responseHandlers}
-	 * 	<li>Annotation:  {@link RestResource#responseHandlers()} 
-	 * 	<li>Method: {@link RestContextBuilder#responseHandlers(Class...)} / {@link RestContextBuilder#responseHandlers(ResponseHandler...)}
+	 * 	<li>Property:  {@link RestContext#REST_responseHandlers}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#responseHandlers()} 
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#responseHandlers(Class...)}
+	 * 			<li>{@link RestContextBuilder#responseHandlers(ResponseHandler...)}
+	 * 		</ul>
 	 * 	<li>{@link ResponseHandler} classes must have either a no-arg or {@link PropertyStore} argument constructors.
 	 *	</ul>
 	 *
@@ -1292,9 +1387,16 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_responseHandlers}
-	 * 	<li>Annotation:  {@link RestResource#responseHandlers()} 
-	 * 	<li>Method: {@link RestContextBuilder#responseHandlers(Class...)} / {@link RestContextBuilder#responseHandlers(ResponseHandler...)}
+	 * 	<li>Property:  {@link RestContext#REST_responseHandlers}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#responseHandlers()} 
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#responseHandlers(Class...)}
+	 * 			<li>{@link RestContextBuilder#responseHandlers(ResponseHandler...)}
+	 * 		</ul>
 	 * 	<li>{@link ResponseHandler} classes must have either a no-arg or {@link PropertyStore} argument constructors.
 	 *	</ul>
 	 *
@@ -1314,9 +1416,17 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_defaultRequestHeaders}
-	 * 	<li>Annotation:  {@link RestResource#defaultRequestHeaders()} / {@link RestMethod#defaultRequestHeaders()} 
-	 * 	<li>Method: {@link RestContextBuilder#defaultRequestHeader(String,Object)} / {@link RestContextBuilder#defaultRequestHeaders(String...)}
+	 * 	<li>Property:  {@link RestContext#REST_defaultRequestHeaders}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#defaultRequestHeaders()}
+	 * 			<li>{@link RestMethod#defaultRequestHeaders()} 
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#defaultRequestHeader(String,Object)}
+	 * 			<li>{@link RestContextBuilder#defaultRequestHeaders(String...)}
+	 * 		</ul>
 	 * 	<li>Affects values returned by {@link RestRequest#getHeader(String)} when the header is not present on the request.
 	 * 	<li>The most useful reason for this annotation is to provide a default <code>Accept</code> header when one is not
 	 * 		specified so that a particular default {@link Serializer} is picked.
@@ -1339,9 +1449,17 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_defaultRequestHeaders}
-	 * 	<li>Annotation:  {@link RestResource#defaultRequestHeaders()} / {@link RestMethod#defaultRequestHeaders()} 
-	 * 	<li>Method: {@link RestContextBuilder#defaultRequestHeader(String,Object)} / {@link RestContextBuilder#defaultRequestHeaders(String...)}
+	 * 	<li>Property:  {@link RestContext#REST_defaultRequestHeaders}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#defaultRequestHeaders()}
+	 * 			<li>{@link RestMethod#defaultRequestHeaders()} 
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#defaultRequestHeader(String,Object)}
+	 * 			<li>{@link RestContextBuilder#defaultRequestHeaders(String...)}
+	 * 		</ul>
 	 * 	<li>Strings are of the format <js>"Header-Name: header-value"</js>.
 	 * 	<li>You can use either <js>':'</js> or <js>'='</js> as the key/value delimiter.
 	 * 	<li>Key and value is trimmed of whitespace.
@@ -1374,9 +1492,16 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_defaultResponseHeaders}
-	 * 	<li>Annotation:  {@link RestResource#defaultResponseHeaders()} 
-	 * 	<li>Method: {@link RestContextBuilder#defaultResponseHeader(String,Object)} / {@link RestContextBuilder#defaultResponseHeaders(String...)}
+	 * 	<li>Property:  {@link RestContext#REST_defaultResponseHeaders}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#defaultResponseHeaders()} 
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#defaultResponseHeader(String,Object)}
+	 * 			<li>{@link RestContextBuilder#defaultResponseHeaders(String...)}
+	 * 		</ul>
 	 * 	<li>This is equivalent to calling {@link RestResponse#setHeader(String, String)} programmatically in each of 
 	 * 		the Java methods.
 	 * 	<li>The header value will not be set if the header value has already been specified (hence the 'default' in the name).
@@ -1401,9 +1526,16 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_defaultResponseHeaders}
-	 * 	<li>Annotation:  {@link RestResource#defaultResponseHeaders()} 
-	 * 	<li>Method: {@link RestContextBuilder#defaultResponseHeader(String,Object)} / {@link RestContextBuilder#defaultResponseHeaders(String...)}
+	 * 	<li>Property:  {@link RestContext#REST_defaultResponseHeaders}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#defaultResponseHeaders()} 
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#defaultResponseHeader(String,Object)}
+	 * 			<li>{@link RestContextBuilder#defaultResponseHeaders(String...)}
+	 * 		</ul>
 	 * 	<li>Strings are of the format <js>"Header-Name: header-value"</js>.
 	 * 	<li>You can use either <js>':'</js> or <js>'='</js> as the key/value delimiter.
 	 * 	<li>Key and value is trimmed of whitespace.
@@ -1434,13 +1566,25 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 *
 	 * <p>
 	 * Overrides the media types inferred from the serializers that identify what media types can be produced by the resource.
+	 * 
+	 * <p>
+	 * This affects the values returned by {@link RestRequest#getSupportedAcceptTypes()} and the supported accept
+	 * types shown in {@link RestInfoProvider#getSwagger(RestRequest)}.
 	 *
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_supportedAcceptTypes}
-	 * 	<li>Annotation:  N/A 
-	 * 	<li>Method: {@link RestContextBuilder#supportedAcceptTypes(boolean,String...)} / {@link RestContextBuilder#supportedAcceptTypes(boolean,MediaType...)}
+	 * 	<li>Property:  {@link RestContext#REST_supportedAcceptTypes}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#supportedAcceptTypes()}
+	 * 			<li>{@link RestMethod#supportedAcceptTypes()}
+	 * 		</ul> 
+	 * 	<li>Methods:  
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#supportedAcceptTypes(boolean,String...)}
+	 * 			<li>{@link RestContextBuilder#supportedAcceptTypes(boolean,MediaType...)}
+	 * 		</ul>
 	 *	</ul>
 	 *
 	 * @param append
@@ -1457,13 +1601,25 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 *
 	 * <p>
 	 * Overrides the media types inferred from the serializers that identify what media types can be produced by the resource.
+	 * 
+	 * <p>
+	 * This affects the values returned by {@link RestRequest#getSupportedAcceptTypes()} and the supported accept
+	 * types shown in {@link RestInfoProvider#getSwagger(RestRequest)}.
 	 *
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_supportedAcceptTypes}
-	 * 	<li>Annotation:  N/A 
-	 * 	<li>Method: {@link RestContextBuilder#supportedAcceptTypes(boolean,String...)} / {@link RestContextBuilder#supportedAcceptTypes(boolean,MediaType...)}
+	 * 	<li>Property:  {@link RestContext#REST_supportedAcceptTypes}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#supportedAcceptTypes()}
+	 * 			<li>{@link RestMethod#supportedAcceptTypes()}
+	 * 		</ul> 
+	 * 	<li>Methods:  
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#supportedAcceptTypes(boolean,String...)}
+	 * 			<li>{@link RestContextBuilder#supportedAcceptTypes(boolean,MediaType...)}
+	 * 		</ul>
 	 *	</ul>
 	 *
 	 * @param append
@@ -1482,11 +1638,23 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 * Overrides the media types inferred from the parsers that identify what media types can be consumed by the resource.
 	 *
 	 * <p>
+	 * This affects the values returned by {@link RestRequest#getSupportedContentTypes()} and the supported content
+	 * types shown in {@link RestInfoProvider#getSwagger(RestRequest)}.
+	 * 
+	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_supportedContentTypes}
-	 * 	<li>Annotation:  N/A 
-	 * 	<li>Method: {@link RestContextBuilder#supportedContentTypes(boolean,String...)} / {@link RestContextBuilder#supportedContentTypes(boolean,MediaType...)}
+	 * 	<li>Property:  {@link RestContext#REST_supportedContentTypes}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#supportedContentTypes()}
+	 * 			<li>{@link RestMethod#supportedContentTypes()}
+	 * 		</ul> 
+	 * 	<li>Methods:  
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#supportedContentTypes(boolean,String...)}
+	 * 			<li>{@link RestContextBuilder#supportedContentTypes(boolean,MediaType...)}
+	 * 		</ul>
 	 *	</ul>
 	 *
 	 * @param append
@@ -1505,11 +1673,23 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 * Overrides the media types inferred from the parsers that identify what media types can be consumed by the resource.
 	 *
 	 * <p>
+	 * This affects the values returned by {@link RestRequest#getSupportedContentTypes()} and the supported content
+	 * types shown in {@link RestInfoProvider#getSwagger(RestRequest)}.
+	 * 
+	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_supportedContentTypes}
-	 * 	<li>Annotation:  N/A 
-	 * 	<li>Method: {@link RestContextBuilder#supportedContentTypes(boolean,String...)} / {@link RestContextBuilder#supportedContentTypes(boolean,MediaType...)}
+	 * 	<li>Property:  {@link RestContext#REST_supportedContentTypes}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#supportedContentTypes()}
+	 * 			<li>{@link RestMethod#supportedContentTypes()}
+	 * 		</ul> 
+	 * 	<li>Methods:  
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#supportedContentTypes(boolean,String...)}
+	 * 			<li>{@link RestContextBuilder#supportedContentTypes(boolean,MediaType...)}
+	 * 		</ul>
 	 *	</ul>
 	 *
 	 * @param append
@@ -1534,9 +1714,15 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_clientVersionHeader}
-	 * 	<li>Annotation:  {@link RestResource#clientVersionHeader()} 
-	 * 	<li>Method: {@link RestContextBuilder#clientVersionHeader(String)}
+	 * 	<li>Property:  {@link RestContext#REST_clientVersionHeader}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#clientVersionHeader()} 
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#clientVersionHeader(String)}
+	 * 		</ul>
 	 *	</ul>
 	 *
 	 * @param clientVersionHeader The name of the HTTP header that denotes the client version.
@@ -1558,9 +1744,16 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_resourceResolver}
-	 * 	<li>Annotation:  {@link RestResource#resourceResolver()} 
-	 * 	<li>Method: {@link RestContextBuilder#resourceResolver(Class)} / {@link RestContextBuilder#resourceResolver(RestResourceResolver)}
+	 * 	<li>Property:  {@link RestContext#REST_resourceResolver}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#resourceResolver()} 
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#resourceResolver(Class)}
+	 * 			<li>{@link RestContextBuilder#resourceResolver(RestResourceResolver)}
+	 * 		</ul>
 	 * 	<li>Unless overridden, resource resolvers are inherited from parent resources.
 	 *	</ul>
 	 *
@@ -1583,9 +1776,16 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_resourceResolver}
-	 * 	<li>Annotation:  {@link RestResource#resourceResolver()} 
-	 * 	<li>Method: {@link RestContextBuilder#resourceResolver(Class)} / {@link RestContextBuilder#resourceResolver(RestResourceResolver)}
+	 * 	<li>Property:  {@link RestContext#REST_resourceResolver}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#resourceResolver()} 
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#resourceResolver(Class)}
+					<li>{@link RestContextBuilder#resourceResolver(RestResourceResolver)}
+				</ul>
 	 * 	<li>Unless overridden, resource resolvers are inherited from parent resources.
 	 *	</ul>
 	 *
@@ -1609,9 +1809,16 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_logger}
-	 * 	<li>Annotation:  {@link RestResource#logger()} 
-	 * 	<li>Method: {@link RestContextBuilder#logger(Class)} / {@link RestContextBuilder#logger(RestLogger)} 
+	 * 	<li>Property:  {@link RestContext#REST_logger}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#logger()} 
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#logger(Class)}
+	 * 			<li>{@link RestContextBuilder#logger(RestLogger)} 
+	 * 		</ul>
 	 *	</ul>
 	 *
 	 * @param logger The new logger for this resource.  Can be <jk>null</jk> to disable logging.
@@ -1634,9 +1841,16 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_logger}
-	 * 	<li>Annotation:  {@link RestResource#logger()} 
-	 * 	<li>Method: {@link RestContextBuilder#logger(Class)} / {@link RestContextBuilder#logger(RestLogger)} 
+	 * 	<li>Property:  {@link RestContext#REST_logger}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#logger()} 
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#logger(Class)}
+	 * 			<li>{@link RestContextBuilder#logger(RestLogger)} 
+	 * 		</ul>
 	 *	</ul>
 	 *
 	 * @param logger The new logger for this resource.  Can be <jk>null</jk> to disable logging.
@@ -1656,9 +1870,16 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_callHandler}
-	 * 	<li>Annotation:  {@link RestResource#callHandler()} 
-	 * 	<li>Method: {@link RestContextBuilder#callHandler(Class)} / {@link RestContextBuilder#callHandler(RestCallHandler)} 
+	 * 	<li>Property:  {@link RestContext#REST_callHandler}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#callHandler()} 
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#callHandler(Class)}
+	 * 			<li>{@link RestContextBuilder#callHandler(RestCallHandler)} 
+	 * 		</ul>
 	 *	</ul>
 	 *
 	 * @param restHandler The new call handler for this resource.
@@ -1678,9 +1899,16 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_callHandler}
-	 * 	<li>Annotation:  {@link RestResource#callHandler()} 
-	 * 	<li>Method: {@link RestContextBuilder#callHandler(Class)} / {@link RestContextBuilder#callHandler(RestCallHandler)} 
+	 * 	<li>Property:  {@link RestContext#REST_callHandler}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#callHandler()} 
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#callHandler(Class)}
+	 * 			<li>{@link RestContextBuilder#callHandler(RestCallHandler)} 
+	 * 		</ul>
 	 *	</ul>
 	 *
 	 * @param restHandler The new call handler for this resource.
@@ -1702,9 +1930,16 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_infoProvider}
-	 * 	<li>Annotation:  {@link RestResource#infoProvider()} 
-	 * 	<li>Method: {@link RestContextBuilder#infoProvider(Class)} / {@link RestContextBuilder#infoProvider(RestInfoProvider)} 
+	 * 	<li>Property:  {@link RestContext#REST_infoProvider}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#infoProvider()} 
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#infoProvider(Class)}
+	 * 			<li>{@link RestContextBuilder#infoProvider(RestInfoProvider)} 
+	 * 		</ul>
 	 *	</ul>
 	 *
 	 * @param infoProvider The new info provider for this resource.
@@ -1726,9 +1961,16 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_infoProvider}
-	 * 	<li>Annotation:  {@link RestResource#infoProvider()} 
-	 * 	<li>Method: {@link RestContextBuilder#infoProvider(Class)} / {@link RestContextBuilder#infoProvider(RestInfoProvider)} 
+	 * 	<li>Property:  {@link RestContext#REST_infoProvider}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#infoProvider()} 
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#infoProvider(Class)}
+	 * 			<li>{@link RestContextBuilder#infoProvider(RestInfoProvider)} 
+	 * 		</ul>
 	 *	</ul>
 	 *
 	 * @param infoProvider The new info provider for this resource.
@@ -1739,6 +1981,325 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	}
 
 	/**
+	 * <b>Configuration property:</b>  Static file mappings. 
+	 *
+	 * <p>
+	 * Used to define paths and locations of statically-served files such as images or HTML documents.
+	 * 
+	 * <p>
+	 * Static files are found by calling {@link RestContext#getClasspathResource(String,Locale)} which uses the registered 
+	 * {@link ClasspathResourceFinder} for locating files on the classpath (or other location).
+	 * 
+	 * <p>
+	 * An example where this class is used is in the {@link RestResource#staticFiles} annotation:
+	 * <p class='bcode'>
+	 * 	<jk>package</jk> com.foo.mypackage;
+	 * 
+	 * 	<ja>@RestResource</ja>(
+	 * 		path=<js>"/myresource"</js>,
+	 * 		staticFiles=<js>"htdocs:docs"</js>
+	 * 	)
+	 * 	<jk>public class</jk> MyResource <jk>extends</jk> RestServletDefault {...}
+	 * </p>
+	 * 
+	 * <p>
+	 * In the example above, given a GET request to <l>/myresource/htdocs/foobar.html</l>, the servlet will attempt to find 
+	 * the <l>foobar.html</l> file in the following ordered locations:
+	 * <ol>
+	 * 	<li><l>com.foo.mypackage.docs</l> package.
+	 * 	<li><l>org.apache.juneau.rest.docs</l> package (since <l>RestServletDefault</l> is in <l>org.apache.juneau.rest</l>).
+	 * 	<li><l>[working-dir]/docs</l> directory.
+	 * </ol>
+	 * 
+	 * <h6 class='topic'>Notes:</h6>
+	 * <ul class='spaced-list'>
+	 * 	<li>Property:  {@link RestContext#REST_staticFiles}
+	 * 	<li>Annotations: 
+	 * 		<ul>
+	 * 			<li>{@link RestResource#staticFiles()} 
+	 * 		</ul>
+	 * 	<li>Methods: 
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#staticFiles(String)},
+	 * 			<li>{@link RestContextBuilder#staticFiles(Class,String)}
+	 * 			<li>{@link RestContextBuilder#staticFiles(String,String)}
+	 * 			<li>{@link RestContextBuilder#staticFiles(Class,String,String)} 
+	 * 			<li>{@link RestContextBuilder#staticFiles(StaticFileMapping...)} 
+	 * 		</ul>
+	 * 	<li>Mappings are cumulative from parent to child.  
+	 * 	<li>Child resources can override mappings made on parent resources.
+	 * 	<li>The media type on the response is determined by the {@link RestContext#getMediaTypeForName(String)} method.
+	 * 	<li>The resource finder is configured via the {@link RestContext#REST_classpathResourceFinder} setting, and can be
+	 * 		overridden to provide customized handling of resource retrieval.
+	 * 	<li>The {@link RestContext#REST_useClasspathResourceCaching} setting can be used to cache static files in memory
+	 * 		to improve performance.
+	 * </ul>
+	 *
+	 * @param sfm The static file mappings to add to this resource.
+	 * @return This object (for method chaining).
+	 */
+	public RestContextBuilder staticFiles(StaticFileMapping...sfm) {
+		return addTo(REST_staticFiles, sfm);
+	}
+
+	/**
+	 * Same as {@link #staticFiles(StaticFileMapping...)}, except input is in the form of a mapping string.
+	 * 
+	 * <p>
+	 * Mapping string must be one of these formats:
+	 * <ul>
+	 * 	<li><js>"path:location"</js> (e.g. <js>"foodocs:docs/foo"</js>)
+	 * 	<li><js>"path:location:headers-json"</js> (e.g. <js>"foodocs:docs/foo:{'Cache-Control':'max-age=86400, public'}"</js>)
+	 * </ul>
+	 * 
+	 * @param mappingString The static file mapping string.
+	 * @return This object (for method chaining).
+	 */
+	public RestContextBuilder staticFiles(String mappingString) {
+		return staticFiles(new StaticFileMapping(resourceClass, mappingString));
+	}
+
+	/**
+	 * Same as {@link #staticFiles(String)}, except overrides the base class for retrieving the resource.
+	 * 
+	 * <p>
+	 * Mapping string must be one of these formats:
+	 * <ul>
+	 * 	<li><js>"path:location"</js> (e.g. <js>"foodocs:docs/foo"</js>)
+	 * 	<li><js>"path:location:headers-json"</js> (e.g. <js>"foodocs:docs/foo:{'Cache-Control':'max-age=86400, public'}"</js>)
+	 * </ul>
+	 * 
+	 * @param baseClass 
+	 * 	Overrides the default class to use for retrieving the classpath resource. 
+	 * 	<br>If <jk>null<jk>, uses the REST resource class.
+	 * @param mappingString The static file mapping string.
+	 * @return This object (for method chaining).
+	 */
+	public RestContextBuilder staticFiles(Class<?> baseClass, String mappingString) {
+		return staticFiles(new StaticFileMapping(baseClass, mappingString));
+	}
+	
+	/**
+	 * Same as {@link #staticFiles(String)}, except path and location are already split values.
+	 * 
+	 * @param path 
+	 * 	The mapped URI path.
+	 * 	<br>Leading and trailing slashes are trimmed.
+	 * @param location 
+	 * 	The location relative to the resource class.
+	 * 	<br>Leading and trailing slashes are trimmed.
+	 * @return This object (for method chaining).
+	 */
+	public RestContextBuilder staticFiles(String path, String location) {
+		return staticFiles(new StaticFileMapping(null, path, location, null));
+	}
+
+	/**
+	 * Same as {@link #staticFiles(String,String)},  except overrides the base class for retrieving the resource.
+	 * 
+	 * @param baseClass 
+	 * 	Overrides the default class to use for retrieving the classpath resource. 
+	 * 	<br>If <jk>null<jk>, uses the REST resource class.
+	 * @param path 
+	 * 	The mapped URI path.
+	 * 	<br>Leading and trailing slashes are trimmed.
+	 * @param location 
+	 * 	The location relative to the resource class.
+	 * 	<br>Leading and trailing slashes are trimmed.
+	 * @return This object (for method chaining).
+	 */
+	public RestContextBuilder staticFiles(Class<?> baseClass, String path, String location) {
+		return staticFiles(new StaticFileMapping(baseClass, path, location, null));
+	}
+
+	/**
+	 * <b>Configuration property:</b>  Static file response headers. 
+	 *
+	 * <p>
+	 * Used to customize the headers on responses returned for statically-served files.
+	 * 
+	 * <h6 class='topic'>Notes:</h6>
+	 * <ul class='spaced-list'>
+	 * 	<li>Property:  {@link RestContext#REST_staticFileResponseHeaders}
+	 * 	<li>Annotations: 
+	 * 		<ul>
+	 * 			<li>{@link RestResource#staticFileResponseHeaders()} 
+	 * 		</ul>
+	 * 	<li>Methods: 
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#staticFileResponseHeaders(boolean,Map)}
+	 * 			<li>{@link RestContextBuilder#staticFileResponseHeaders(String...)}
+	 * 			<li>{@link RestContextBuilder#staticFileResponseHeader(String,String)}
+	 * 		</ul>
+	 * </ul>
+	 * 
+	 * @param append
+	 * 	If <jk>true</jk>, append to the existing list, otherwise overwrite the previous value. 
+	 * @param headers The headers to add to this list.
+	 * @return This object (for method chaining).
+	 */
+	public RestContextBuilder staticFileResponseHeaders(boolean append, Map<String,String> headers) {
+		return set(append, REST_staticFileResponseHeaders, headers);
+	}
+
+	/**
+	 * <b>Configuration property:</b>  Static file response headers. 
+	 *
+	 * <p>
+	 * Used to customize the headers on responses returned for statically-served files.
+	 * 
+	 * <h6 class='topic'>Notes:</h6>
+	 * <ul class='spaced-list'>
+	 * 	<li>Property:  {@link RestContext#REST_staticFileResponseHeaders}
+	 * 	<li>Annotations: 
+	 * 		<ul>
+	 * 			<li>{@link RestResource#staticFileResponseHeaders()} 
+	 * 		</ul>
+	 * 	<li>Methods: 
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#staticFileResponseHeaders(boolean,Map)}
+	 * 			<li>{@link RestContextBuilder#staticFileResponseHeaders(String...)}
+	 * 			<li>{@link RestContextBuilder#staticFileResponseHeader(String,String)}
+	 * 		</ul>
+	 * </ul>
+	 * 
+	 * @param headers The headers in the format <js>"Header-Name: header-value"</js>.
+	 * @return This object (for method chaining).
+	 * @throws RestServletException If malformed header is found.
+	 */
+	public RestContextBuilder staticFileResponseHeaders(String...headers) throws RestServletException {
+		for (String header : headers) {
+			String[] h = RestUtils.parseHeader(header);
+			if (h == null)
+				throw new RestServletException("Invalid static file response header specified: ''{0}''.  Must be in the format: ''Header-Name: header-value''", header);
+			staticFileResponseHeader(h[0], h[1]);
+		}
+		return this;
+	}
+
+	/**
+	 * <b>Configuration property:</b>  Static file response headers. 
+	 *
+	 * <p>
+	 * Used to customize the headers on responses returned for statically-served files.
+	 * 
+	 * <h6 class='topic'>Notes:</h6>
+	 * <ul class='spaced-list'>
+	 * 	<li>Property:  {@link RestContext#REST_staticFileResponseHeaders}
+	 * 	<li>Annotations: 
+	 * 		<ul>
+	 * 			<li>{@link RestResource#staticFileResponseHeaders()} 
+	 * 		</ul>
+	 * 	<li>Methods: 
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#staticFileResponseHeaders(boolean,Map)}
+	 * 			<li>{@link RestContextBuilder#staticFileResponseHeaders(String...)}
+	 * 			<li>{@link RestContextBuilder#staticFileResponseHeader(String,String)}
+	 * 		</ul>
+	 * </ul>
+	 * 
+	 * @param name The HTTP header name.
+	 * @param value The HTTP header value.
+	 * @return This object (for method chaining).
+	 */
+	public RestContextBuilder staticFileResponseHeader(String name, String value) {
+		return addTo(REST_staticFileResponseHeaders, name, value);
+	}
+
+	/**
+	 * <b>Configuration property:</b>  Classpath resource finder. 
+	 * 
+	 * <p>
+	 * Used to retrieve localized files from the classpath.
+	 * 
+	 * <h6 class='topic'>Notes:</h6>
+	 * <ul class='spaced-list'>
+	 * 	<li>Property:  {@link RestContext#REST_classpathResourceFinder}
+	 * 	<li>Annotations: 
+	 * 		<ul>
+	 * 			<li>{@link RestResource#classpathResourceFinder()} 
+	 * 		</ul>
+	 * 	<li>Methods: 
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#classpathResourceFinder(Class)}
+	 * 			<li>{@link RestContextBuilder#classpathResourceFinder(ClasspathResourceFinder)}
+	 * 		</ul>
+	 * 	<li>
+	 * 		The default value is {@link ClasspathResourceFinderBasic} which provides basic support for finding localized
+	 * 		resources on the classpath and JVM working directory.
+	 * 		<br>The {@link ClasspathResourceFinderRecursive} is another option that also recursively searches for resources
+	 * 		up the parent class hierarchy.
+	 * 		<br>Each of these classes can be extended to provide customized handling of resource retrieval.
+	 * </ul>
+	 * 
+	 * @param classpathResourceFinder The resource finder class.
+	 * @return This object (for method chaining).
+	 */
+	public RestContextBuilder classpathResourceFinder(Class<? extends ClasspathResourceFinder> classpathResourceFinder) {
+		return set(REST_classpathResourceFinder, classpathResourceFinder);
+	}
+	
+	/**
+	 * <b>Configuration property:</b>  Classpath resource finder. 
+	 * 
+	 * <p>
+	 * Used to retrieve localized files from the classpath.
+	 * 
+	 * <h6 class='topic'>Notes:</h6>
+	 * <ul class='spaced-list'>
+	 * 	<li>Property:  {@link RestContext#REST_classpathResourceFinder}
+	 * 	<li>Annotations: 
+	 * 		<ul>
+	 * 			<li>{@link RestResource#classpathResourceFinder()} 
+	 * 		</ul>
+	 * 	<li>Methods: 
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#classpathResourceFinder(Class)}
+	 * 			<li>{@link RestContextBuilder#classpathResourceFinder(ClasspathResourceFinder)}
+	 * 		</ul>
+	 * 	<li>
+	 * 		The default value is {@link ClasspathResourceFinderBasic} which provides basic support for finding localized
+	 * 		resources on the classpath and JVM working directory.
+	 * 		<br>The {@link ClasspathResourceFinderRecursive} is another option that also recursively searches for resources
+	 * 		up the parent class hierarchy.
+	 * 		<br>Each of these classes can be extended to provide customized handling of resource retrieval.
+	 * </ul>
+	 * 
+	 * @param classpathResourceFinder The resource finder instance.
+	 * @return This object (for method chaining).
+	 */
+	public RestContextBuilder classpathResourceFinder(ClasspathResourceFinder classpathResourceFinder) {
+		return set(REST_classpathResourceFinder, classpathResourceFinder);
+	}
+
+	/**
+	 * <b>Configuration property:</b>  Use classpath resource caching. 
+	 *
+	 * <p>
+	 * When enabled, resources retrieved via {@link RestContext#getClasspathResource(String, Locale)} (and related 
+	 * methods) will be cached in memory to speed subsequent lookups.
+	 * 
+	 * <h6 class='topic'>Notes:</h6>
+	 * <ul class='spaced-list'>
+	 * 	<li>Property:  {@link RestContext#REST_useClasspathResourceCaching}
+	 * 	<li>Annotations: 
+	 * 		<ul>
+	 * 			<li>{@link RestResource#useClasspathResourceCaching()} 
+	 * 		</ul>
+	 * 	<li>Methods: 
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#useClasspathResourceCaching(boolean)}
+	 * 		</ul>
+	 * </ul>
+	 * 
+	 * @param value The new value.
+	 * @return This object (for method chaining).
+	 */
+	public RestContextBuilder useClasspathResourceCaching(boolean value) {
+		return set(REST_useClasspathResourceCaching, value);
+	}
+
+	/**
 	 * <b>Configuration property:</b>  Serializer listener.
 	 * 
 	 * <p>
@@ -1747,17 +2308,21 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link Serializer#SERIALIZER_listener}
-	 * 	<li>Annotation:  {@link RestResource#serializerListener()} 
-	 * 	<li>Method: {@link RestContextBuilder#serializerListener(Class)} 
+	 * 	<li>Property:  {@link Serializer#SERIALIZER_listener}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#serializerListener()} 
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#serializerListener(Class)} 
+	 * 		</ul>
 	 *	</ul>
 	 *
 	 * @param listener The listener to add to this config.
 	 * @return This object (for method chaining).
 	 */
 	public RestContextBuilder serializerListener(Class<? extends SerializerListener> listener) {
-		if (listener == SerializerListener.Null.class)
-			return this;
 		return set(SERIALIZER_listener, listener);
 	}
 
@@ -1770,17 +2335,21 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link Parser#PARSER_listener}
-	 * 	<li>Annotation:  {@link RestResource#parserListener()} 
-	 * 	<li>Method: {@link RestContextBuilder#parserListener(Class)} 
+	 * 	<li>Property:  {@link Parser#PARSER_listener}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#parserListener()} 
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#parserListener(Class)}
+	 * 		</ul> 
 	 *	</ul>
 	 *
 	 * @param listener The listener to add to this config.
 	 * @return This object (for method chaining).
 	 */
 	public RestContextBuilder parserListener(Class<? extends ParserListener> listener) {
-		if (listener == ParserListener.Null.class)
-			return this;
 		return set(PARSER_listener, listener);
 	}
 

http://git-wip-us.apache.org/repos/asf/juneau/blob/b820b273/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestInfoProvider.java
----------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestInfoProvider.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestInfoProvider.java
index 2a3344d..e40e26a 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestInfoProvider.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestInfoProvider.java
@@ -122,7 +122,7 @@ public class RestInfoProvider {
 	 * @return A new Swagger instance.
 	 * @throws RestException
 	 */
-	protected Swagger getSwagger(RestRequest req) throws RestException {
+	public Swagger getSwagger(RestRequest req) throws RestException {
 		try {
 			// If a file is defined, use that.
 			Swagger s = req.getSwaggerFromFile();
@@ -136,8 +136,8 @@ public class RestInfoProvider {
 					.description(getDescription(req))
 					.termsOfService(getTermsOfService(req))
 				)
-				.consumes(context.getSupportedAcceptTypes())
-				.produces(context.getSupportedContentTypes())
+				.consumes(req.getSupportedAcceptTypes())
+				.produces(req.getSupportedContentTypes())
 				.tags(getTags(req))
 				.externalDocs(getExternalDocs(req));
 
@@ -177,7 +177,7 @@ public class RestInfoProvider {
 		Swagger s = swaggers.get(locale);
 		if (s == null) {
 			try {
-				s = context.getResource(Swagger.class, MediaType.JSON, getClass().getSimpleName() + ".json", locale);
+				s = context.getClasspathResource(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);

http://git-wip-us.apache.org/repos/asf/juneau/blob/b820b273/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
----------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
index fd38e24..226b100 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
@@ -87,7 +87,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
 	private VarResolverSession varSession;
 	private final RequestQuery queryParams;
 	private RequestFormData formData;
-	private Map<String,String> defFormData;
+	private Map<String,Object> defFormData;
 	private RequestPathMatch pathParams;
 	private boolean isPost;
 	private UriContext uriContext;
@@ -96,6 +96,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
 	private ConfigFile cf;
 	private Swagger swagger, fileSwagger;
 	private Map<String,Widget> widgets;
+	private List<MediaType> supportedContentTypes, supportedAcceptTypes;
 
 	/**
 	 * Constructor.
@@ -159,10 +160,11 @@ public final class RestRequest extends HttpServletRequestWrapper {
 	/*
 	 * Called from RestServlet after a match has been made but before the guard or method invocation.
 	 */
-	final void init(Method javaMethod, ObjectMap properties, Map<String,String> defHeader,
-			Map<String,String> defQuery, Map<String,String> defFormData, String defaultCharset, long maxInput,
+	final void init(Method javaMethod, ObjectMap properties, Map<String,Object> defHeader,
+			Map<String,Object> defQuery, Map<String,Object> defFormData, String defaultCharset, long maxInput,
 			SerializerGroup mSerializers, ParserGroup mParsers, HttpPartParser mUrlEncodingParser,
-			BeanContext beanContext, EncoderGroup encoders, Map<String,Widget> widgets) {
+			BeanContext beanContext, EncoderGroup encoders, Map<String,Widget> widgets, List<MediaType> supportedAcceptTypes, 
+			List<MediaType> supportedContentTypes) {
 		this.javaMethod = javaMethod;
 		this.properties = properties;
 		this.partParser = mUrlEncodingParser;
@@ -190,6 +192,8 @@ public final class RestRequest extends HttpServletRequestWrapper {
 		this.defaultCharset = defaultCharset;
 		this.defFormData = defFormData;
 		this.widgets = widgets;
+		this.supportedAcceptTypes = supportedAcceptTypes;
+		this.supportedContentTypes = supportedContentTypes;
 
 		String stylesheet = getQuery().getString("stylesheet");
 		if (stylesheet != null)
@@ -301,12 +305,21 @@ public final class RestRequest extends HttpServletRequestWrapper {
 	}
 
 	/**
+	 * Returns the media types that are valid for <code>Accept</code> headers on the request.
+	 *
+	 * @return The set of media types registered in the serializer group of this request.
+	 */
+	public List<MediaType> getSupportedAcceptTypes() {
+		return supportedAcceptTypes;
+	}
+
+	/**
 	 * Returns the media types that are valid for <code>Content-Type</code> headers on the request.
 	 *
 	 * @return The set of media types registered in the parser group of this request.
 	 */
-	public List<MediaType> getSupportedMediaTypes() {
-		return parserGroup.getSupportedMediaTypes();
+	public List<MediaType> getSupportedContentTypes() {
+		return supportedContentTypes;
 	}
 
 	/**
@@ -870,8 +883,8 @@ public final class RestRequest extends HttpServletRequestWrapper {
 	 * @return A new reader resource, or <jk>null</jk> if resource could not be found.
 	 * @throws IOException
 	 */
-	public ReaderResource getReaderResource(String name, boolean resolveVars, MediaType mediaType) throws IOException {
-		String s = context.getResourceAsString(name, getLocale());
+	public ReaderResource getClasspathReaderResource(String name, boolean resolveVars, MediaType mediaType) throws IOException {
+		String s = context.getClasspathResourceAsString(name, getLocale());
 		if (s == null)
 			return null;
 		ReaderResource.Builder b = new ReaderResource.Builder().mediaType(mediaType).contents(s);
@@ -881,7 +894,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
 	}
 
 	/**
-	 * Same as {@link #getReaderResource(String, boolean, MediaType)} except uses the resource mime-type map
+	 * Same as {@link #getClasspathReaderResource(String, boolean, MediaType)} except uses the resource mime-type map
 	 * constructed using {@link RestContextBuilder#mimeTypes(String...)} to determine the media type.
 	 *
 	 * @param name The name of the resource (i.e. the value normally passed to {@link Class#getResourceAsStream(String)}.
@@ -892,19 +905,19 @@ public final class RestRequest extends HttpServletRequestWrapper {
 	 * @return A new reader resource, or <jk>null</jk> if resource could not be found.
 	 * @throws IOException
 	 */
-	public ReaderResource getReaderResource(String name, boolean resolveVars) throws IOException {
-		return getReaderResource(name, resolveVars, MediaType.forString(context.getMediaTypeForName(name)));
+	public ReaderResource getClasspathReaderResource(String name, boolean resolveVars) throws IOException {
+		return getClasspathReaderResource(name, resolveVars, MediaType.forString(context.getMediaTypeForName(name)));
 	}
 
 	/**
-	 * Same as {@link #getReaderResource(String, boolean)} with <code>resolveVars == <jk>false</jk></code>
+	 * Same as {@link #getClasspathReaderResource(String, boolean)} with <code>resolveVars == <jk>false</jk></code>
 	 *
 	 * @param name The name of the resource (i.e. the value normally passed to {@link Class#getResourceAsStream(String)}.
 	 * @return A new reader resource, or <jk>null</jk> if resource could not be found.
 	 * @throws IOException
 	 */
-	public ReaderResource getReaderResource(String name) throws IOException {
-		return getReaderResource(name, false, MediaType.forString(context.getMediaTypeForName(name)));
+	public ReaderResource getClasspathReaderResource(String name) throws IOException {
+		return getClasspathReaderResource(name, false, MediaType.forString(context.getMediaTypeForName(name)));
 	}
 
 	/**
@@ -973,7 +986,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
 			sb.append("\t").append(h).append(": ").append(getHeader(h)).append("\n");
 		}
 		sb.append("---Default Servlet Headers---\n");
-		for (Map.Entry<String,String> e : context.getDefaultRequestHeaders().entrySet()) {
+		for (Map.Entry<String,Object> e : context.getDefaultRequestHeaders().entrySet()) {
 			sb.append("\t").append(e.getKey()).append(": ").append(e.getValue()).append("\n");
 		}
 		if (javaMethod == null) {

http://git-wip-us.apache.org/repos/asf/juneau/blob/b820b273/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java
----------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java
index 63206a6..310d910 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java
@@ -23,6 +23,7 @@ import org.apache.juneau.*;
 import org.apache.juneau.encoders.*;
 import org.apache.juneau.http.*;
 import org.apache.juneau.httppart.*;
+import org.apache.juneau.internal.*;
 import org.apache.juneau.serializer.*;
 
 /**
@@ -69,8 +70,8 @@ public final class RestResponse extends HttpServletResponseWrapper {
 		super(res);
 		this.request = req;
 
-		for (Map.Entry<String,String> e : context.getDefaultResponseHeaders().entrySet())
-			setHeader(e.getKey(), e.getValue());
+		for (Map.Entry<String,Object> e : context.getDefaultResponseHeaders().entrySet())
+			setHeader(e.getKey(), StringUtils.toString(e.getValue()));
 
 		try {
 			String passThroughHeaders = req.getHeader("x-response-headers");

http://git-wip-us.apache.org/repos/asf/juneau/blob/b820b273/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestServletDefault.java
----------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestServletDefault.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestServletDefault.java
index 2c6d8f1..900ac77 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestServletDefault.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestServletDefault.java
@@ -153,7 +153,7 @@ import org.apache.juneau.xml.*;
  * 		to <js>"styles/juneau.css"</js>.
  * 	<li>
  * 		Provides a default classpath entry "htdocs" by setting
- * 		{@link RestResource#staticFiles() @RestResource.staticFiles()} to <js>"{htdocs:'htdocs'}"</js>.
+ * 		{@link RestResource#staticFiles() @RestResource.staticFiles()} to <code>{<js>"htdocs:htdocs"</js>,<js>"styles:styles"</js>}</code>.
  * 		This allows files inside the <code>[servletPackage].htdocs</code> package to be served up under the URL
  * 		<code>/servletPath/htdocs</code>.
  * </ul>
@@ -201,7 +201,8 @@ import org.apache.juneau.xml.*;
 	),
 
 	// These are static files that are served up by the servlet under the specified sub-paths.
-	staticFiles="{htdocs:'htdocs',styles:'styles'}"
+	// For example, "/servletPath/htdocs/javadoc.css" resolves to the file "[servlet-package]/htdocs/javadoc.css"
+	staticFiles={"htdocs:htdocs","styles:styles"}
 )
 public abstract class RestServletDefault extends RestServlet {
 	private static final long serialVersionUID = 1L;

http://git-wip-us.apache.org/repos/asf/juneau/blob/b820b273/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFileMapping.java
----------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFileMapping.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFileMapping.java
new file mode 100644
index 0000000..057da6f
--- /dev/null
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StaticFileMapping.java
@@ -0,0 +1,122 @@
+// ***************************************************************************************************************************
+// * 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.utils.*;
+
+/**
+ * Static file mapping.
+ * 
+ * <p>
+ * Used to define paths and locations of statically-served files such as images or HTML documents.
+ * 
+ * <p>
+ * An example where this class is used is in the {@link RestResource#staticFiles} annotation:
+ * <p class='bcode'>
+ * <jk>package</jk> com.foo.mypackage;
+ * 
+ * <ja>@RestResource</ja>(
+ * 	path=<js>"/myresource"</js>,
+ * 	staticFiles={<js>"htdocs:docs"</js>}
+ * )
+ * <jk>public class</jk> MyResource <jk>extends</jk> RestServletDefault {...}
+ * </p>
+ * 
+ * <p>
+ * Static files are found by using the {@link ClasspathResourceFinder} defined on the resource.
+ * 
+ * <p>
+ * In the example above, given a GET request to <l>/myresource/htdocs/foobar.html</l>, the servlet will attempt to find 
+ * the <l>foobar.html</l> file in the following ordered locations:
+ * <ol>
+ * 	<li><l>com.foo.mypackage.docs</l> package.
+ * 	<li><l>org.apache.juneau.rest.docs</l> package (since <l>RestServletDefault</l> is in <l>org.apache.juneau.rest</l>).
+ * 	<li><l>[working-dir]/docs</l> directory.
+ * </ol>
+ * 
+ * <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.RestContext#getMediaTypeForName(String)} method.
+ * </ul>
+ */
+public class StaticFileMapping {
+	
+	final Class<?> resourceClass;
+	final String path, location;
+	final Map<String,Object> responseHeaders;
+	
+	/**
+	 * Constructor.
+	 * 
+	 * @param resourceClass 
+	 * 	The resource/servlet class which serves as the base location of the location below.
+	 * @param path 
+	 * 	The mapped URI path.
+	 * 	<br>Leading and trailing slashes are trimmed.
+	 * @param location 
+	 * 	The location relative to the resource class.
+	 * 	<br>Leading and trailing slashes are trimmed.
+	 * @param responseHeaders 
+	 * 	The response headers.
+	 * 	Can be <jk>null</jk>. 
+	 */
+	public StaticFileMapping(Class<?> resourceClass, String path, String location, Map<String,Object> responseHeaders) {
+		this.resourceClass = resourceClass;
+		this.path = StringUtils.trimSlashes(path);
+		this.location = StringUtils.trimSlashes(location);
+		this.responseHeaders = responseHeaders == null ? null : Collections.unmodifiableMap(new LinkedHashMap<>(responseHeaders));
+	}
+	
+	/**
+	 * Constructor using a mapping string to represent a path/location pairing.
+	 * 
+	 * <p>
+	 * Mapping string must be one of these formats:
+	 * <ul>
+	 * 	<li><js>"path:location"</js> (e.g. <js>"foodocs:docs/foo"</js>)
+	 * 	<li><js>"path:location:headers-json"</js> (e.g. <js>"foodocs:docs/foo:{'Cache-Control':'max-age=86400, public'}"</js>)
+	 * </ul>
+	 * 
+	 * @param resourceClass 
+	 * 	The resource/servlet class which serves as the base location of the location below.
+	 * @param mappingString 
+	 * 	The mapping string that represents the path/location mapping.
+	 * 	<br>Leading and trailing slashes and whitespace are trimmed from path and location.
+	 */
+	public StaticFileMapping(Class<?> resourceClass, String mappingString) {
+		this.resourceClass = resourceClass;
+		String[] parts = StringUtils.split(mappingString, ':', 3);
+		if (parts == null || parts.length <= 1)
+			throw new FormattedRuntimeException("Invalid mapping string format: ''{0}''", mappingString);
+		this.path = StringUtils.trimSlashes(parts[0]); 
+		this.location = StringUtils.trimSlashes(parts[1]); 
+		if (parts.length == 3) {
+			try {
+				responseHeaders = Collections.unmodifiableMap(new ObjectMap(parts[2]));
+			} catch (ParseException e) {
+				throw new FormattedRuntimeException(e, "Invalid mapping string format: ''{0}''", mappingString);
+			}
+		} else {
+			responseHeaders = null;
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/juneau/blob/b820b273/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestMethod.java
----------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestMethod.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestMethod.java
index b59fa2c..a2eae24 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestMethod.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestMethod.java
@@ -16,9 +16,11 @@ import static java.lang.annotation.ElementType.*;
 import static java.lang.annotation.RetentionPolicy.*;
 
 import java.lang.annotation.*;
+import java.util.*;
 
 import org.apache.juneau.*;
 import org.apache.juneau.encoders.*;
+import org.apache.juneau.http.*;
 import org.apache.juneau.parser.*;
 import org.apache.juneau.remoteable.*;
 import org.apache.juneau.rest.*;
@@ -128,9 +130,17 @@ public @interface RestMethod {
 	 *
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_guards}
-	 * 	<li>Annotation:  {@link RestResource#guards()} / {@link RestMethod#guards()}
-	 * 	<li>Method: {@link RestContextBuilder#guards(Class...)} / {@link RestContextBuilder#guards(RestGuard...)}
+	 * 	<li>Property:  {@link RestContext#REST_guards}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#guards()}
+	 * 			<li>{@link RestMethod#guards()}
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#guards(Class...)}
+	 * 			<li>{@link RestContextBuilder#guards(RestGuard...)}
+	 * 		</ul>
 	 * 	<li>{@link RestGuard} classes must have either a no-arg or {@link PropertyStore} argument constructors.
 	 * 	<li>Values are added AFTER those found in the annotation and therefore take precedence over those defined via the
 	 * 		annotation.
@@ -155,9 +165,17 @@ public @interface RestMethod {
 	 * 
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_converters}
-	 * 	<li>Annotation:  {@link RestResource#converters()} / {@link RestMethod#converters()}
-	 * 	<li>Method: {@link RestContextBuilder#converters(Class...)} / {@link RestContextBuilder#converters(RestConverter...)}
+	 * 	<li>Property:  {@link RestContext#REST_converters}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#converters()}
+	 * 			<li>{@link RestMethod#converters()}
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#converters(Class...)}
+	 * 			<li>{@link RestContextBuilder#converters(RestConverter...)}
+	 * 		</ul>
 	 * 	<li>{@link RestConverter} classes must have either a no-arg or {@link PropertyStore} argument constructors.
 	 *	</ul>
 	 */
@@ -260,6 +278,62 @@ public @interface RestMethod {
 	String inherit() default "";
 	
 	/**
+	 * Supported accept media types.
+	 *
+	 * <p>
+	 * Overrides the media types inferred from the serializers that identify what media types can be produced by the resource.
+	 * 
+	 * <p>
+	 * This affects the values returned by {@link RestRequest#getSupportedAcceptTypes()} and the supported accept
+	 * types shown in {@link RestInfoProvider#getSwagger(RestRequest)}.
+	 *
+	 * <p>
+	 * <h5 class='section'>Notes:</h5>
+	 * <ul class='spaced-list'>
+	 * 	<li>Property:  {@link RestContext#REST_supportedAcceptTypes}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#supportedAcceptTypes()}
+	 * 			<li>{@link RestMethod#supportedAcceptTypes()}
+	 * 		</ul> 
+	 * 	<li>Methods:  
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#supportedAcceptTypes(boolean,String...)}
+	 * 			<li>{@link RestContextBuilder#supportedAcceptTypes(boolean,MediaType...)}
+	 * 		</ul>
+	 *	</ul>
+	 */
+	String[] supportedAcceptTypes() default {};
+	
+	/**
+	 * Supported content media types.
+	 *
+	 * <p>
+	 * Overrides the media types inferred from the parsers that identify what media types can be consumed by the resource.
+	 *
+	 * <p>
+	 * This affects the values returned by {@link RestRequest#getSupportedContentTypes()} and the supported content
+	 * types shown in {@link RestInfoProvider#getSwagger(RestRequest)}.
+	 * 
+	 * <p>
+	 * <h5 class='section'>Notes:</h5>
+	 * <ul class='spaced-list'>
+	 * 	<li>Property:  {@link RestContext#REST_supportedContentTypes}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#supportedContentTypes()}
+	 * 			<li>{@link RestMethod#supportedContentTypes()}
+	 * 		</ul> 
+	 * 	<li>Methods:  
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#supportedContentTypes(boolean,String...)}
+	 * 			<li>{@link RestContextBuilder#supportedContentTypes(boolean,MediaType...)}
+	 * 		</ul>
+	 *	</ul>
+	 */
+	String[] supportedContentTypes() default {};
+
+	/**
 	 * Appends to the list of {@link Encoder encoders} specified on the servlet.
 	 *
 	 * <p>
@@ -313,11 +387,49 @@ public @interface RestMethod {
 
 	/**
 	 * Appends the specified bean filters to all serializers and parsers used by this method.
+	 *
+	 * <h5 class='section'>Notes:</h5>
+	 * <ul class='spaced-list'>
+	 * 	<li>Property:  {@link BeanContext#BEAN_beanFilters}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#beanFilters()}
+	 * 			<li>{@link RestMethod#beanFilters()}
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#beanFilters(Class...)}
+	 * 			<li>{@link RestContextBuilder#beanFilters(Collection)}
+	 * 			<li>{@link RestContextBuilder#beanFilters(boolean, Class...)}
+	 * 			<li>{@link RestContextBuilder#beanFilters(boolean, Collection)}
+	 * 			<li>{@link RestContextBuilder#beanFiltersRemove(Class...)}
+	 * 			<li>{@link RestContextBuilder#beanFiltersRemove(Collection)}
+	 * 		</ul>
+	 *	</ul>
 	 */
 	Class<?>[] beanFilters() default {};
 
 	/**
 	 * Appends the specified POJO swaps to all serializers and parsers used by this method.
+	 *
+	 * <h5 class='section'>Notes:</h5>
+	 * <ul class='spaced-list'>
+	 * 	<li>Property:  {@link BeanContext#BEAN_pojoSwaps}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#pojoSwaps()}
+	 * 			<li>{@link RestMethod#pojoSwaps()}
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#pojoSwaps(Class...)}
+	 * 			<li>{@link RestContextBuilder#pojoSwaps(Collection)}
+	 * 			<li>{@link RestContextBuilder#pojoSwaps(boolean, Class...)}
+	 * 			<li>{@link RestContextBuilder#pojoSwaps(boolean, Collection)}
+	 * 			<li>{@link RestContextBuilder#pojoSwapsRemove(Class...)}
+	 * 			<li>{@link RestContextBuilder#pojoSwapsRemove(Collection)}
+	 * 		</ul>
+	 *	</ul>
 	 */
 	Class<?>[] pojoSwaps() default {};
 
@@ -432,9 +544,17 @@ public @interface RestMethod {
 	 * <p>
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_defaultRequestHeaders}
-	 * 	<li>Annotation:  {@link RestResource#defaultRequestHeaders()} / {@link RestMethod#defaultRequestHeaders()} 
-	 * 	<li>Method: {@link RestContextBuilder#defaultRequestHeader(String,Object)} / {@link RestContextBuilder#defaultRequestHeaders(String...)}
+	 * 	<li>Property:  {@link RestContext#REST_defaultRequestHeaders}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#defaultRequestHeaders()}
+	 * 			<li>{@link RestMethod#defaultRequestHeaders()} 
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#defaultRequestHeader(String,Object)}
+	 * 			<li>{@link RestContextBuilder#defaultRequestHeaders(String...)}
+	 * 		</ul>
 	 * 	<li>Strings are of the format <js>"Header-Name: header-value"</js>.
 	 * 	<li>You can use either <js>':'</js> or <js>'='</js> as the key/value delimiter.
 	 * 	<li>Key and value is trimmed of whitespace.
@@ -635,9 +755,16 @@ public @interface RestMethod {
 	 *
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_defaultCharset}
-	 * 	<li>Annotation:  {@link RestResource#defaultCharset()} / {@link RestMethod#defaultCharset()}
-	 * 	<li>Method: {@link RestContextBuilder#defaultCharset(String)}
+	 * 	<li>Property:  {@link RestContext#REST_defaultCharset}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#defaultCharset()}
+	 * 			<li>{@link RestMethod#defaultCharset()}
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#defaultCharset(String)}
+	 * 		</ul>
 	 * 	<li>String value.
 	 * 	<li>Can contain variables.
 	 *	</ul>
@@ -659,9 +786,16 @@ public @interface RestMethod {
 	 * 
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
-	 * 	<li>Property: {@link RestContext#REST_maxInput}
-	 * 	<li>Annotation:  {@link RestResource#maxInput()} / {@link RestMethod#maxInput()}
-	 * 	<li>Method: {@link RestContextBuilder#maxInput(String)}
+	 * 	<li>Property:  {@link RestContext#REST_maxInput}
+	 * 	<li>Annotations:
+	 * 		<ul>
+	 * 			<li>{@link RestResource#maxInput()}
+	 * 			<li>{@link RestMethod#maxInput()}
+	 * 		</ul>
+	 * 	<li>Methods:
+	 * 		<ul>
+	 * 			<li>{@link RestContextBuilder#maxInput(String)}
+	 * 		</ul>
 	 * 	<li>String value that gets resolved to a <jk>long</jk>.
 	 * 	<li>Can contain variables.
 	 * 	<li>Can be suffixed with any of the following representing kilobytes, megabytes, and gigabytes:  


[4/4] juneau git commit: Improvements to static files support in REST.

Posted by ja...@apache.org.
Improvements to static files support in REST.

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

Branch: refs/heads/master
Commit: b820b27351b06241b74161765086cc26320f9c53
Parents: 2da728a
Author: JamesBognar <ja...@apache.org>
Authored: Thu Jan 4 12:06:21 2018 -0500
Committer: JamesBognar <ja...@apache.org>
Committed: Thu Jan 4 12:06:21 2018 -0500

----------------------------------------------------------------------
 .../apache/juneau/utils/StringUtilsTest.java    |  31 +-
 juneau-core/juneau-marshall/TODO.txt            |   3 +-
 .../org/apache/juneau/internal/ClassUtils.java  |  22 +-
 .../org/apache/juneau/internal/StringUtils.java |  16 +-
 .../juneau/utils/ClasspathResourceFinder.java   |  62 ++
 .../utils/ClasspathResourceFinderBasic.java     |  60 ++
 .../utils/ClasspathResourceFinderRecursive.java |  33 +
 .../utils/ClasspathResourceFinderSimple.java    | 144 +++
 .../juneau/utils/ClasspathResourceManager.java  | 211 +++++
 .../org/apache/juneau/utils/ResourceFinder.java | 306 -------
 juneau-doc/src/main/javadoc/overview.html       | 129 ++-
 .../juneau/examples/rest/PetStoreResource.java  |   3 +-
 .../juneau/rest/test/InheritanceResource.java   |   6 +-
 .../juneau/rest/test/StaticFilesResource.java   |   3 +-
 .../juneau/rest/test/xdocs/xdocs/test.txt       |  13 -
 .../juneau/rest/test/xdocs/xsubdocs/test.txt    |  13 +
 .../org/apache/juneau/rest/test/xdocs2/test.txt |  13 +
 .../juneau/rest/test/StaticFilesTest.java       |  46 +-
 .../org/apache/juneau/rest/client/RestCall.java |  53 +-
 .../java/org/apache/juneau/rest/CallMethod.java |  20 +-
 .../org/apache/juneau/rest/RequestFormData.java |   9 +-
 .../org/apache/juneau/rest/RequestHeaders.java  |   9 +-
 .../org/apache/juneau/rest/RequestQuery.java    |   9 +-
 .../org/apache/juneau/rest/RestContext.java     | 618 ++++++++++---
 .../apache/juneau/rest/RestContextBuilder.java  | 869 +++++++++++++++----
 .../apache/juneau/rest/RestInfoProvider.java    |   8 +-
 .../org/apache/juneau/rest/RestRequest.java     |  43 +-
 .../org/apache/juneau/rest/RestResponse.java    |   5 +-
 .../apache/juneau/rest/RestServletDefault.java  |   5 +-
 .../apache/juneau/rest/StaticFileMapping.java   | 122 +++
 .../juneau/rest/annotation/RestMethod.java      | 164 +++-
 .../juneau/rest/annotation/RestResource.java    | 498 +++++++++--
 .../java/org/apache/juneau/rest/package.html    |   8 +-
 .../org/apache/juneau/rest/vars/FileVar.java    |   8 +-
 .../org/apache/juneau/rest/widget/Widget.java   |   6 +-
 35 files changed, 2787 insertions(+), 781 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/juneau/blob/b820b273/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/StringUtilsTest.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/StringUtilsTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/StringUtilsTest.java
index af1b9ba..1f5d947 100755
--- a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/StringUtilsTest.java
+++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/StringUtilsTest.java
@@ -510,7 +510,36 @@ public class StringUtilsTest {
 		assertEquals("2\\", r[1]);
 		assertEquals("", r[2]);
 	}
-
+	
+	//====================================================================================================
+	// split(String,char,int)
+	//====================================================================================================
+	@Test
+	public void testSplitWithLimit() {
+		String[] r;
+		
+		r = split("boo:and:foo", ':', 10);
+		assertObjectEquals("['boo','and','foo']", r);
+
+		r = split("boo:and:foo", ':', 2);
+		assertObjectEquals("['boo','and:foo']", r);
+
+		r = split("boo:and:foo", ':', 1);
+		assertObjectEquals("['boo:and:foo']", r);
+
+		r = split("boo:and:foo", ':', 0);
+		assertObjectEquals("['boo:and:foo']", r);
+		
+		r = split("boo:and:foo", ':', -1);
+		assertObjectEquals("['boo:and:foo']", r);		
+		
+		r = split("boo : and : foo", ':', 10);
+		assertObjectEquals("['boo','and','foo']", r);
+
+		r = split("boo : and : foo", ':', 2);
+		assertObjectEquals("['boo','and : foo']", r);
+	}
+	
 	//====================================================================================================
 	// nullIfEmpty(String)
 	//====================================================================================================

http://git-wip-us.apache.org/repos/asf/juneau/blob/b820b273/juneau-core/juneau-marshall/TODO.txt
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/TODO.txt b/juneau-core/juneau-marshall/TODO.txt
index 70cfc28..dd66236 100644
--- a/juneau-core/juneau-marshall/TODO.txt
+++ b/juneau-core/juneau-marshall/TODO.txt
@@ -15,4 +15,5 @@ Create tests that ensure serializers don't close output but parsers do close inp
 
 Content tests on examples rest.
 REST example showing how to use NLS.
-REST example showing how to customize look-and-feel.
\ No newline at end of file
+REST example showing how to customize look-and-feel.
+Improve efficiency of RestRequest constructor.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/juneau/blob/b820b273/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java
index 594d9d1..aa78c06 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java
@@ -833,15 +833,27 @@ public final class ClassUtils {
 				Class<?> c3 = (Class<?>)c2;
 				if (c3.isInterface() || isAbstract(c3))
 					return null;
-				Constructor<?> con = findPublicConstructor(c3, fuzzyArgs, args);
+
+				// First look for an exact match.
+				Constructor<?> con = findPublicConstructor(c3, false, args);
 				if (con != null)
-					return (T)con.newInstance(fuzzyArgs ? getMatchingArgs(con, args) : args);
+					return (T)con.newInstance(args);
+
+				// Next look for an exact match including the outer.
 				if (outer != null) {
-					Object[] args2 = new AList<>().append(outer).appendAll(args).toArray();
-					con = findPublicConstructor(c3, fuzzyArgs, args2);
+					args = new AList<>().append(outer).appendAll(args).toArray();
+					con = findPublicConstructor(c3, false, args);
 					if (con != null)
-						return (T)con.newInstance(fuzzyArgs ? getMatchingArgs(con, args) : args);
+						return (T)con.newInstance(args);
 				}
+				
+				// Finally use fuzzy matching.
+				if (fuzzyArgs) {
+					con = findPublicConstructor(c3, true, args);
+					if (con != null)
+						return (T)con.newInstance(getMatchingArgs(con, args));
+				}
+
 				throw new FormattedRuntimeException("Could not instantiate class {0}.  Constructor not found.", c.getName());
 			} catch (Exception e) {
 				throw new FormattedRuntimeException(e, "Could not instantiate class {0}", c.getName());

http://git-wip-us.apache.org/repos/asf/juneau/blob/b820b273/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java
index 953a677..a808b27 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java
@@ -493,6 +493,18 @@ public final class StringUtils {
 	 * @return The tokens, or <jk>null</jk> if the string was null.
 	 */
 	public static String[] split(String s, char c) {
+		return split(s, c, Integer.MAX_VALUE);
+	}
+	
+	/**
+	 * Same as {@link #split(String, char)} but limits the number of tokens returned.
+	 *
+	 * @param s The string to split.  Can be <jk>null</jk>.
+	 * @param c The character to split on.
+	 * @param limit The maximum number of tokens to return.
+	 * @return The tokens, or <jk>null</jk> if the string was null.
+	 */
+	public static String[] split(String s, char c, int limit) {
 
 		char[] unEscapeChars = new char[]{'\\', c};
 
@@ -506,12 +518,14 @@ public final class StringUtils {
 		List<String> l = new LinkedList<>();
 		char[] sArray = s.toCharArray();
 		int x1 = 0, escapeCount = 0;
-		for (int i = 0; i < sArray.length; i++) {
+		limit--;
+		for (int i = 0; i < sArray.length && limit > 0; i++) {
 			if (sArray[i] == '\\') escapeCount++;
 			else if (sArray[i]==c && escapeCount % 2 == 0) {
 				String s2 = new String(sArray, x1, i-x1);
 				String s3 = unEscapeChars(s2, unEscapeChars);
 				l.add(s3.trim());
+				limit--;
 				x1 = i+1;
 			}
 			if (sArray[i] != '\\') escapeCount = 0;

http://git-wip-us.apache.org/repos/asf/juneau/blob/b820b273/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ClasspathResourceFinder.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ClasspathResourceFinder.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ClasspathResourceFinder.java
new file mode 100644
index 0000000..39a9601
--- /dev/null
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ClasspathResourceFinder.java
@@ -0,0 +1,62 @@
+// ***************************************************************************************************************************
+// * 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.utils;
+
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * Interface for finding classpath resources.
+ * 
+ * <p>
+ * Essentially a wrapper around {@link Class#getResourceAsStream(String)}, but with support for looking up resources
+ * with localized names (e.g. <js>"myfile_ja_JP.txt"</js>).
+ * 
+ * <p>
+ * The following predefined implementations are provided:
+ * <ul>
+ * 	<li>{@link ClasspathResourceFinderSimple} - Simple searching of classpath.
+ * 	<li>{@link ClasspathResourceFinderBasic} - Same as above, but looks in local JVM working directory if resource
+ * 		can't be found on classpath.
+ * 	<li>{@link ClasspathResourceFinderRecursive} - Same as above, except if the resource can't be found on the
+ * 		classpath relative to the base class, recursively searches up the parent class hierarchy.
+ * </ul>
+ */
+public interface ClasspathResourceFinder {
+	
+	/**
+	 * Represents "no" classpath resource finder.
+	 */
+	public static final class Null implements ClasspathResourceFinder {
+		@Override
+		public InputStream findResource(Class<?> baseClass, String name, Locale locale) throws IOException {
+			throw new NoSuchMethodError();
+		}
+	}
+
+	/**
+	 * Returns the contents of the resource with the specified name.
+	 * 
+	 * @param baseClass 
+	 * 	The class to use to retrieve the resource.
+	 * @param name The resource name.  
+	 * 	See {@link Class#getResource(String)} for format.
+	 * @param locale 
+	 * 	The locale of the resource to retrieve.
+	 * 	Can be <jk>null</jk>.
+	 * @return The resolved resource contents, or <jk>null</jk> if the resource was not found.
+	 * @throws IOException
+	 */
+	InputStream findResource(Class<?> baseClass, String name, Locale locale) throws IOException;
+}

http://git-wip-us.apache.org/repos/asf/juneau/blob/b820b273/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ClasspathResourceFinderBasic.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ClasspathResourceFinderBasic.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ClasspathResourceFinderBasic.java
new file mode 100644
index 0000000..99114c2
--- /dev/null
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ClasspathResourceFinderBasic.java
@@ -0,0 +1,60 @@
+// ***************************************************************************************************************************
+// * 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.utils;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * Utility class for finding resources for a class.
+ *
+ * <p>
+ * Same as {@link ClasspathResourceFinderSimple}, but if the resource cannot be found in the classpath, then an attempt 
+ * is made to look in the JVM working directory.
+ * <br>Path traversals outside the working directory are not allowed for security reasons.
+ */
+public class ClasspathResourceFinderBasic extends ClasspathResourceFinderSimple {
+
+	@Override /* ClasspathResourceFinder */
+	public InputStream findResource(Class<?> baseClass, String name, Locale locale) throws IOException {
+		InputStream is = findClasspathResource(baseClass, name, locale);
+		if (is != null)
+			return is;
+		return findFileSystemResource(name, locale);
+	}
+	
+	/**
+	 * Workhorse method for retrieving a resource from the file system.
+	 * 
+	 * <p>
+	 * This method can be overridden by subclasses to provide customized handling of resource retrieval from file systems.
+	 * 
+	 * @param name The resource name.
+	 * @param locale 
+	 * 	The resource locale.
+	 * 	<br>Can be <jk>null</jk>.  
+	 * @return The resource stream, or <jk>null</jk> if it couldn't be found.
+	 * @throws IOException
+	 */
+	protected InputStream findFileSystemResource(String name, Locale locale) throws IOException {
+		if (name.indexOf("..") == -1) {
+			for (String n2 : getCandidateFileNames(name, locale)) {
+				File f = new File(n2);
+				if (f.exists() && f.canRead() && ! f.isAbsolute()) {
+					return new FileInputStream(f);
+				}
+			}
+		}
+		return null;
+	}
+}

http://git-wip-us.apache.org/repos/asf/juneau/blob/b820b273/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ClasspathResourceFinderRecursive.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ClasspathResourceFinderRecursive.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ClasspathResourceFinderRecursive.java
new file mode 100644
index 0000000..b023dcb
--- /dev/null
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ClasspathResourceFinderRecursive.java
@@ -0,0 +1,33 @@
+// ***************************************************************************************************************************
+// * 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.utils;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * Same as {@link ClasspathResourceFinderBasic} but searches for resources up the parent class hierarchy chain.
+ */
+public class ClasspathResourceFinderRecursive extends ClasspathResourceFinderBasic {
+
+	@Override /* ResourceFinder2 */
+	public InputStream findResource(Class<?> baseClass, String name, Locale locale) throws IOException {
+		while (baseClass != null) {
+			InputStream is = findClasspathResource(baseClass, name, locale);
+			if (is != null)
+				return is;
+			baseClass = baseClass.getSuperclass();
+		}
+		return findFileSystemResource(name, locale);
+	}
+}

http://git-wip-us.apache.org/repos/asf/juneau/blob/b820b273/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ClasspathResourceFinderSimple.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ClasspathResourceFinderSimple.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ClasspathResourceFinderSimple.java
new file mode 100644
index 0000000..bdec620
--- /dev/null
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ClasspathResourceFinderSimple.java
@@ -0,0 +1,144 @@
+// ***************************************************************************************************************************
+// * 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.utils;
+
+import static org.apache.juneau.internal.FileUtils.*;
+
+import java.io.*;
+import java.util.*;
+import java.util.ResourceBundle.*;
+
+/**
+ * Utility class for finding resources for a class.
+ *
+ * <p>
+ * Same as {@link Class#getResourceAsStream(String)} except looks for resources with localized file names.
+ *
+ * <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>
+ */
+public class ClasspathResourceFinderSimple implements ClasspathResourceFinder {
+
+	private static final ResourceBundle.Control RB_CONTROL = ResourceBundle.Control.getControl(Control.FORMAT_DEFAULT);
+	private static final List<Locale> ROOT_LOCALE = Arrays.asList(Locale.ROOT);
+
+	
+	@Override /* ClasspathResourceFinder */
+	public InputStream findResource(Class<?> baseClass, String name, Locale locale) throws IOException {
+		return findClasspathResource(baseClass, name, locale);
+	}
+	
+	/**
+	 * Workhorse method for retrieving a resource from the classpath.
+	 * 
+	 * <p>
+	 * This method can be overridden by subclasses to provide customized handling of resource retrieval from the classpath.
+	 * 
+	 * @param baseClass The base class providing the classloader.
+	 * @param name The resource name.
+	 * @param locale 
+	 * 	The resource locale.
+	 * 	<br>Can be <jk>null</jk>.  
+	 * @return The resource stream, or <jk>null</jk> if it couldn't be found.
+	 * @throws IOException
+	 */
+	protected InputStream findClasspathResource(Class<?> baseClass, String name, Locale locale) throws IOException {
+		if (locale == null) 
+			return baseClass.getResourceAsStream(name);
+		for (String n : getCandidateFileNames(name, locale)) {
+			InputStream is = baseClass.getResourceAsStream(n);
+			if (is != null)
+				return is;
+		}
+		return null;
+	}
+	
+	/**
+	 * Returns the candidate file names for the specified file name in the specified locale.
+	 *
+	 * <p>
+	 * For example, if looking for the <js>"MyResource.txt"</js> file in the Japanese locale, the iterator will return
+	 * names in the following order:
+	 * <ol>
+	 * 	<li><js>"MyResource_ja_JP.txt"</js>
+	 * 	<li><js>"MyResource_ja.txt"</js>
+	 * 	<li><js>"MyResource.txt"</js>
+	 * </ol>
+	 *
+	 * <p>
+	 * If the locale is <jk>null</jk>, then it will only return <js>"MyResource.txt"</js>.
+	 *
+	 * @param fileName The name of the file to get candidate file names on.
+	 * @param l The locale.
+	 * @return An iterator of file names to look at.
+	 */
+	protected static Iterable<String> getCandidateFileNames(final String fileName, final Locale l) {
+		return new Iterable<String>() {
+			@Override
+			public Iterator<String> iterator() {
+				return new Iterator<String>() {
+					final Iterator<Locale> locales = getCandidateLocales(l).iterator();
+					String baseName, ext;
+
+					@Override
+					public boolean hasNext() {
+						return locales.hasNext();
+					}
+
+					@Override
+					public String next() {
+						Locale l2 = locales.next();
+						if (l2.toString().isEmpty())
+							return fileName;
+						if (baseName == null)
+							baseName = getBaseName(fileName);
+						if (ext == null)
+							ext = getExtension(fileName);
+						return baseName + "_" + l2.toString() + (ext.isEmpty() ? "" : ('.' + ext));
+					}
+					@Override
+					public void remove() {
+						throw new UnsupportedOperationException();
+					}
+				};
+			}
+		};
+	}
+
+	/**
+	 * Returns the candidate locales for the specified locale.
+	 *
+	 * <p>
+	 * For example, if <code>locale</code> is <js>"ja_JP"</js>, then this method will return:
+	 * <ol>
+	 * 	<li><js>"ja_JP"</js>
+	 * 	<li><js>"ja"</js>
+	 * 	<li><js>""</js>
+	 * </ol>
+	 *
+	 * @param locale The locale to get the list of candidate locales for.
+	 * @return The list of candidate locales.
+	 */
+	static final List<Locale> getCandidateLocales(Locale locale) {
+		if (locale == null)
+			return ROOT_LOCALE;
+		return RB_CONTROL.getCandidateLocales("", locale);
+	}
+}

http://git-wip-us.apache.org/repos/asf/juneau/blob/b820b273/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ClasspathResourceManager.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ClasspathResourceManager.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ClasspathResourceManager.java
new file mode 100644
index 0000000..4dcb5c7
--- /dev/null
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ClasspathResourceManager.java
@@ -0,0 +1,211 @@
+// ***************************************************************************************************************************
+// * 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.utils;
+
+import java.io.*;
+import java.util.*;
+import java.util.concurrent.*;
+
+import org.apache.juneau.internal.*;
+
+/**
+ * Class for retrieving and caching resource files from the classpath.
+ */
+public final class ClasspathResourceManager {
+
+	// Maps resource names+locales to found resources.
+	private final ConcurrentHashMap<ResourceKey,byte[]> byteCache;
+	private final ConcurrentHashMap<ResourceKey,String> stringCache;
+
+	private final Class<?> baseClass;
+	private final ClasspathResourceFinder resourceFinder;
+	private final boolean useCache;
+	
+	/**
+	 * Constructor.
+	 * 
+	 * @param baseClass The default class to use for retrieving resources from the classpath.
+	 * @param resourceFinder The resource finder implementation.
+	 * @param useCache If <jk>true</jk>, retrieved resources are stored in an in-memory cache for fast lookup.
+	 */
+	public ClasspathResourceManager(Class<?> baseClass, ClasspathResourceFinder resourceFinder, boolean useCache) {
+		this.baseClass = baseClass;
+		this.resourceFinder = resourceFinder;
+		this.useCache = useCache;
+		this.byteCache = useCache ? new ConcurrentHashMap<ResourceKey,byte[]>() : null;
+		this.stringCache = useCache ? new ConcurrentHashMap<ResourceKey,String>() : null;
+	}
+
+	/**
+	 * Constructor.
+	 * 
+	 * <p>
+	 * Uses default {@link ClasspathResourceFinderBasic} for finding resources.
+	 * 
+	 * @param baseClass The default class to use for retrieving resources from the classpath.
+	 */
+	public ClasspathResourceManager(Class<?> baseClass) {
+		this(baseClass, new ClasspathResourceFinderBasic(), false);
+	}
+
+	/**
+	 * Finds the resource with the given name.
+	 *
+	 * @param name Name of the desired resource.
+	 * @return An input stream to the object, or <jk>null</jk> if the resource could not be found.
+	 * @throws IOException
+	 */
+	public InputStream getStream(String name) throws IOException {
+		return getStream(name, null);
+	}
+
+	/**
+	 * Finds the resource with the given name for the specified locale and returns it as an input stream.
+	 *
+	 * @param name Name of the desired resource.
+	 * @param locale The locale.  Can be <jk>null</jk>.
+	 * @return An input stream to the object, or <jk>null</jk> if the resource could not be found.
+	 * @throws IOException
+	 */
+	public InputStream getStream(String name, Locale locale) throws IOException {
+		return getStream(baseClass, name, locale);
+	}
+
+	/**
+	 * Finds the resource with the given name for the specified locale and returns it as an input stream.
+	 * 
+	 * @param baseClass 
+	 * 	Overrides the default class to use for retrieving the classpath resource. 
+	 * 	<br>If <jk>null<jk>, uses the base class passed in through the constructor of this class.
+	 * @param name Name of the desired resource.
+	 * @param locale The locale.  Can be <jk>null</jk>.
+	 * @return An input stream to the object, or <jk>null</jk> if the resource could not be found.
+	 * @throws IOException
+	 */
+	public InputStream getStream(Class<?> baseClass, String name, Locale locale) throws IOException {
+
+		if (baseClass == null)
+			baseClass = this.baseClass;
+		
+		if (! useCache)
+			return resourceFinder.findResource(baseClass, name, locale);
+
+		ResourceKey key = new ResourceKey(name, locale);;
+		
+		byte[] r = byteCache.get(key);
+		if (r == null) {
+			try (InputStream is = resourceFinder.findResource(baseClass, name, locale)) { 
+				if (is != null)
+					byteCache.putIfAbsent(key, IOUtils.readBytes(is, 1024));
+			}
+		}
+		
+		r = byteCache.get(key);
+		return r == null ? null : new ByteArrayInputStream(r);
+	}
+
+	/**
+	 * Finds the resource with the given name and converts it to a simple string.
+	 *
+	 * @param name Name of the desired resource.
+	 * @return The resource converted to a string, or <jk>null</jk> if the resource could not be found.
+	 * @throws IOException
+	 */
+	public String getString(String name) throws IOException {
+		return getString(baseClass, name, null);
+	}
+
+	/**
+	 * Finds the resource with the given name and converts it to a simple string.
+	 *
+	 * @param baseClass 
+	 * 	Overrides the default class to use for retrieving the classpath resource. 
+	 * 	<br>If <jk>null<jk>, uses the base class passed in through the constructor of this class.
+	 * @param name Name of the desired resource.
+	 * @return The resource converted to a string, or <jk>null</jk> if the resource could not be found.
+	 * @throws IOException
+	 */
+	public String getString(Class<?> baseClass, String name) throws IOException {
+		return getString(baseClass, name, null);
+	}
+
+	/**
+	 * Finds the resource with the given name and converts it to a simple string.
+	 *
+	 * @param name Name of the desired resource.
+	 * @param locale The locale.  Can be <jk>null</jk>.
+	 * @return The resource converted to a string, or <jk>null</jk> if the resource could not be found.
+	 * @throws IOException
+	 */
+	public String getString(String name, Locale locale) throws IOException {
+		return getString(baseClass, name, locale);
+	}
+
+	/**
+	 * Finds the resource with the given name and converts it to a simple string.
+	 *
+	 * @param baseClass 
+	 * 	Overrides the default class to use for retrieving the classpath resource. 
+	 * 	<br>If <jk>null<jk>, uses the base class passed in through the constructor of this class.
+	 * @param name Name of the desired resource.
+	 * @param locale The locale.  Can be <jk>null</jk>.
+	 * @return The resource converted to a string, or <jk>null</jk> if the resource could not be found.
+	 * @throws IOException
+	 */
+	public String getString(Class<?> baseClass, String name, Locale locale) throws IOException {
+
+		if (baseClass == null)
+			baseClass = this.baseClass;
+		
+		if (! useCache) {
+			try (InputStream is = resourceFinder.findResource(baseClass, name, locale)) {
+				return IOUtils.read(is, IOUtils.UTF8);
+			}
+		}
+
+		ResourceKey key = new ResourceKey(name, locale);
+		
+		String r = stringCache.get(key);
+		if (r == null) {
+			try (InputStream is = resourceFinder.findResource(baseClass, name, locale)) { 
+				if (is != null)
+					stringCache.putIfAbsent(key, IOUtils.read(is, IOUtils.UTF8));
+			}
+		}
+		
+		return stringCache.get(key);
+	}
+	
+	private class ResourceKey {
+		final String name;
+		final Locale locale;
+
+		ResourceKey(String name, Locale locale) {
+			this.name = name;
+			this.locale = locale;
+		}
+
+		@Override
+		public int hashCode() {
+			return name.hashCode() + (locale == null ? 0 : locale.hashCode());
+		}
+
+		@Override
+		public boolean equals(Object o) {
+			if (o == null)
+				return false;
+			ResourceKey ok = (ResourceKey)o;
+			return ObjectUtils.equals(name, ok.name) && ObjectUtils.equals(locale, ok.locale);
+		}
+	}	
+}

http://git-wip-us.apache.org/repos/asf/juneau/blob/b820b273/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ResourceFinder.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ResourceFinder.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ResourceFinder.java
deleted file mode 100644
index 8205905..0000000
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ResourceFinder.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.utils;
-
-import static org.apache.juneau.internal.FileUtils.*;
-
-import java.io.*;
-import java.net.*;
-import java.util.*;
-import java.util.ResourceBundle.*;
-import java.util.concurrent.*;
-
-import org.apache.juneau.internal.*;
-
-/**
- * Utility class for finding resources for a class.
- *
- * <p>
- * Same as {@link Class#getResourceAsStream(String)} except if it doesn't find the resource on this class, it 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.
- * <br>Path traversals outside the working directory are not allowed for security reasons.
- *
- * <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>
- *
- * <p>
- * Results are cached for fast lookup.
- */
-public final class ResourceFinder {
-
-	private static final ResourceBundle.Control RB_CONTROL = ResourceBundle.Control.getControl(Control.FORMAT_DEFAULT);
-	private static final List<Locale> ROOT_LOCALE = Arrays.asList(Locale.ROOT);
-
-	// Maps resource names+locales to found resources.
-	private final ConcurrentHashMap<ResourceKey,Resource> cache = new ConcurrentHashMap<>();
-
-	// Maps resolved URLs to resources.
-	private final ConcurrentHashMap<URL,Resource> cacheByUrl = new ConcurrentHashMap<>();
-
-	private final Class<?> c;
-
-	/**
-	 * Constructor.
-	 *
-	 * @param forClass The class that this resource finder searches against.
-	 */
-	public ResourceFinder(Class<?> forClass) {
-		this.c = forClass;
-	}
-
-	/**
-	 * Finds the resource with the given name.
-	 *
-	 * @param name Name of the desired resource.
-	 * @return An input stream to the object, or <jk>null</jk> if the resource could not be found.
-	 * @throws IOException
-	 */
-	public InputStream getResourceAsStream(String name) throws IOException {
-		return getResourceAsStream(name, null);
-	}
-
-	/**
-	 * Finds the resource with the given name for the specified locale.
-	 *
-	 * @param name Name of the desired resource.
-	 * @param locale The locale.  Can be <jk>null</jk>.
-	 * @return An input stream to the object, or <jk>null</jk> if the resource could not be found.
-	 * @throws IOException
-	 */
-	public InputStream getResourceAsStream(String name, Locale locale) throws IOException {
-		return getResource(name, locale).asInputStream();
-	}
-
-	/**
-	 * Finds the resource with the given name and converts it to a simple string.
-	 *
-	 * @param name Name of the desired resource.
-	 * @return The resource converted to a string, or <jk>null</jk> if the resource could not be found.
-	 * @throws IOException
-	 */
-	public String getResourceAsString(String name) throws IOException {
-		return getResourceAsString(name, null);
-	}
-
-	/**
-	 * Finds the resource with the given name and converts it to a simple string.
-	 *
-	 * @param name Name of the desired resource.
-	 * @param locale The locale.  Can be <jk>null</jk>.
-	 * @return The resource converted to a string, or <jk>null</jk> if the resource could not be found.
-	 * @throws IOException
-	 */
-	public String getResourceAsString(String name, Locale locale) throws IOException {
-		return getResource(name, locale).asString();
-	}
-
-
-	//-------------------------------------------------------------------------------------------------------------------
-	// Support classes and methods.
-	//-------------------------------------------------------------------------------------------------------------------
-
-	private static final class ResourceKey {
-		final String name;
-		final Locale locale;
-
-		ResourceKey(String name, Locale locale) {
-			this.name = name;
-			this.locale = locale;
-		}
-
-		@Override
-		public int hashCode() {
-			return name.hashCode() + (locale == null ? 0 : locale.hashCode());
-		}
-
-		@Override
-		public boolean equals(Object o) {
-			if (o == null)
-				return false;
-			ResourceKey ok = (ResourceKey)o;
-			return ObjectUtils.equals(name, ok.name) && ObjectUtils.equals(locale, ok.locale);
-		}
-	}
-
-	private static final class Resource {
-		private byte[] bytes;
-		private String string;
-
-		Resource(byte[] bytes) {
-			this.bytes = bytes;
-		}
-
-		String asString() {
-			if (bytes == null)
-				return null;
-			try {
-				if (string == null)
-					string = new String(bytes, "UTF-8");
-			} catch (UnsupportedEncodingException e) {
-				e.printStackTrace();
-			}
-			return string;
-		}
-
-		InputStream asInputStream() {
-			if (bytes == null)
-				return null;
-			return new ByteArrayInputStream(bytes);
-		}
-	}
-
-	Resource getResource(String name, Locale locale) throws IOException {
-		ResourceKey key = new ResourceKey(name, locale);
-		Resource r = cache.get(key);
-		if (r != null)
-			return r;
-
-		r = findResource(c, key);
-		cache.putIfAbsent(key, r);
-		r = cache.get(key);
-		return r;
-	}
-
-	private Resource findResource(Class<?> c2, ResourceKey key) throws IOException {
-		while (c2 != null) {
-			if (key.locale == null) {
-				URL url = c2.getResource(key.name);
-				if (url != null) {
-					Resource r = cacheByUrl.get(url);
-					if (r == null) {
-						r = new Resource(IOUtils.readBytes(c2.getResourceAsStream(key.name), 1024));
-						cacheByUrl.putIfAbsent(url, r);
-						r = cacheByUrl.get(url);
-					}
-					return r;
-				}
-			} else {
-				for (String n : getCandidateFileNames(key.name, key.locale)) {
-					URL url = c2.getResource(n);
-					if (url != null) {
-						Resource r = cacheByUrl.get(url);
-						if (r == null) {
-							r = new Resource(IOUtils.readBytes(c2.getResourceAsStream(n), 1024));
-							cacheByUrl.putIfAbsent(url, r);
-							r = cacheByUrl.get(url);
-						}
-						return r;
-					}
-				}
-			}
-			c2 = c2.getSuperclass();
-		}
-
-		if (key.name.indexOf("..") == -1) {
-			for (String n2 : getCandidateFileNames(key.name, key.locale)) {
-				File f = new File(n2);
-				if (f.exists() && f.canRead() && ! f.isAbsolute()) {
-					URL url = f.toURI().toURL();
-					Resource r = cacheByUrl.get(url);
-					if (r == null) {
-						try (FileInputStream fis = new FileInputStream(f)) {
-							r = new Resource(IOUtils.readBytes(fis, 1024));
-						}
-						cacheByUrl.putIfAbsent(url, r);
-						r = cacheByUrl.get(url);
-					}
-					return r;
-				}
-			}
-		}
-
-		return new Resource(null);
-	}
-
-	/**
-	 * Returns the candidate file names for the specified file name in the specified locale.
-	 *
-	 * <p>
-	 * For example, if looking for the <js>"MyResource.txt"</js> file in the Japanese locale, the iterator will return
-	 * names in the following order:
-	 * <ol>
-	 * 	<li><js>"MyResource_ja_JP.txt"</js>
-	 * 	<li><js>"MyResource_ja.txt"</js>
-	 * 	<li><js>"MyResource.txt"</js>
-	 * </ol>
-	 *
-	 * <p>
-	 * If the locale is null, then it will only return <js>"MyResource.txt"</js>.
-	 *
-	 * @param fileName The name of the file to get candidate file names on.
-	 * @param l The locale.
-	 * @return An iterator of file names to look at.
-	 */
-	private static Iterable<String> getCandidateFileNames(final String fileName, final Locale l) {
-		return new Iterable<String>() {
-			@Override
-			public Iterator<String> iterator() {
-				return new Iterator<String>() {
-					final Iterator<Locale> locales = getCandidateLocales(l).iterator();
-					String baseName, ext;
-
-					@Override
-					public boolean hasNext() {
-						return locales.hasNext();
-					}
-
-					@Override
-					public String next() {
-						Locale l2 = locales.next();
-						if (l2.toString().isEmpty())
-							return fileName;
-						if (baseName == null)
-							baseName = getBaseName(fileName);
-						if (ext == null)
-							ext = getExtension(fileName);
-						return baseName + "_" + l2.toString() + (ext.isEmpty() ? "" : ('.' + ext));
-					}
-					@Override
-					public void remove() {
-						throw new UnsupportedOperationException();
-					}
-				};
-			}
-		};
-	}
-
-	/**
-	 * Returns the candidate locales for the specified locale.
-	 *
-	 * <p>
-	 * For example, if <code>locale</code> is <js>"ja_JP"</js>, then this method will return:
-	 * <ol>
-	 * 	<li><js>"ja_JP"</js>
-	 * 	<li><js>"ja"</js>
-	 * 	<li><js>""</js>
-	 * </ol>
-	 *
-	 * @param locale The locale to get the list of candidate locales for.
-	 * @return The list of candidate locales.
-	 */
-	static final List<Locale> getCandidateLocales(Locale locale) {
-		if (locale == null)
-			return ROOT_LOCALE;
-		return RB_CONTROL.getCandidateLocales("", locale);
-	}
-}

http://git-wip-us.apache.org/repos/asf/juneau/blob/b820b273/juneau-doc/src/main/javadoc/overview.html
----------------------------------------------------------------------
diff --git a/juneau-doc/src/main/javadoc/overview.html b/juneau-doc/src/main/javadoc/overview.html
index 8a09d75..3f56270 100644
--- a/juneau-doc/src/main/javadoc/overview.html
+++ b/juneau-doc/src/main/javadoc/overview.html
@@ -4924,7 +4924,7 @@
 				<li>
 					How to use form entry beans to process form POSTs.
 				<li>
-					How to use the {@link org.apache.juneau.rest.RestRequest#getReaderResource(String)} method to 
+					How to use the {@link org.apache.juneau.rest.RestRequest#getClasspathReaderResource(String)} method to 
 					serve up static files with embedded string variables.
 			</ul>
 			<p>
@@ -4946,7 +4946,7 @@
 		<jd>/** GET request handler */</jd> 
 		<ja>@RestMethod</ja>(name=<jsf>GET</jsf>, path=<js>"/"</js>) 
 		<jk>public</jk> ReaderResource doGet(RestRequest req) <jk>throws</jk> IOException { 
-			<jk>return</jk> req.getReaderResource(<js>"UrlEncodedForm.html"</js>, <jk>true</jk>); 
+			<jk>return</jk> req.getClasspathReaderResource(<js>"UrlEncodedForm.html"</js>, <jk>true</jk>); 
 		} 
 		
 		<jd>/** POST request handler */</jd> 
@@ -4965,7 +4965,7 @@
 	}		
 			</p>
 			<p>
-				The {@link org.apache.juneau.rest.RestRequest#getReaderResource(String,boolean)} method pulls in the following
+				The {@link org.apache.juneau.rest.RestRequest#getClasspathReaderResource(String,boolean)} method pulls in the following
 					file located in the same package as the class:
 			</p>
 			
@@ -6521,7 +6521,7 @@
 		*/</jd> 
 		<ja>@RestMethod</ja>(name=<jsf>GET</jsf>, path=<js>"/upload"</js>) 
 		<jk>public</jk> ReaderResource getUploadPage(RestRequest req) <jk>throws</jk> IOException { 
-			<jk>return</jk> req.getReaderResource(<js>"TempDirUploadPage.html"</js>, <jk>true</jk>); 
+			<jk>return</jk> req.getClasspathReaderResource(<js>"TempDirUploadPage.html"</js>, <jk>true</jk>); 
 		} 
 		
 		<jd>/** 
@@ -7181,7 +7181,7 @@
 		<jd>/** GET request handler - Display the query entry page. */</jd> 
 		<ja>@RestMethod</ja>(name=<jsf>GET</jsf>, path=<js>"/"</js>) 
 		<jk>public</jk> ReaderResource doGet(RestRequest req) <jk>throws</jk> IOException { 
-			<jk>return</jk> req.getReaderResource(<js>"SqlQueryResource.html"</js>, <jk>true</jk>); 
+			<jk>return</jk> req.getClasspathReaderResource(<js>"SqlQueryResource.html"</js>, <jk>true</jk>); 
 		} 
 		
 		<jd>/** POST request handler - Execute the query. */</jd> 
@@ -7379,7 +7379,7 @@
 			<jc>// Note that you don't want variables in the config file to be resolved,</jc> 
 			<jc>// so you need to escape any $ characters that you see.</jc> 
 			req.setAttribute(<js>"contents"</js>, getConfig().toString().replaceAll(<js>"\\$"</js>, <js>"\\\\\\$"</js>)); 
-			<jk>return</jk> req.getReaderResource(<js>"ConfigEdit.html"</js>, <jk>true</jk>); 
+			<jk>return</jk> req.getClasspathReaderResource(<js>"ConfigEdit.html"</js>, <jk>true</jk>); 
 		} 
 		
 		<jd>/** 
@@ -7910,8 +7910,15 @@
 				</ul>
 				Code for marshalling of parts have been removed from the URL-Encoding serializers and parsers.
 			<li>	
-				<code>ContexBuilder.property(String,Object)</code> renamed to {@link org.apache.juneau.ContextBuilder#set(String,Object)}.
-			
+				<code>ContextBuilder.property(String,Object)</code> renamed to {@link org.apache.juneau.ContextBuilder#set(String,Object)}.
+			<li><code>ResourceFinder</code> class has been replaced with the following:
+				<ul>
+					<li>{@link org.apache.juneau.utils.ClasspathResourceFinder}
+					<li>{@link org.apache.juneau.utils.ClasspathResourceFinderSimple}
+					<li>{@link org.apache.juneau.utils.ClasspathResourceFinderBasic}
+					<li>{@link org.apache.juneau.utils.ClasspathResourceFinderRecursive}
+					<li>{@link org.apache.juneau.utils.ClasspathResourceManager}
+				</ul>	
 		</ul>
 
 		<h6 class='topic'>juneau-rest-server</h6>
@@ -7959,6 +7966,48 @@
 				</ul>
 			<li>
 				Refactored the <code>RestConfig</code> class into {@link org.apache.juneau.rest.RestContextBuilder}.
+				<br>Settings on {@link org.apache.juneau.rest.RestContext} objects can now be set declaratively through the 
+					following new properties:
+				<ul>
+					<li>{@link org.apache.juneau.rest.RestContext#REST_allowHeaderParams REST_allowHeaderParams}
+					<li>{@link org.apache.juneau.rest.RestContext#REST_allowBodyParam REST_allowBodyParam}
+					<li>{@link org.apache.juneau.rest.RestContext#REST_allowedMethodParams REST_allowedMethodParams}
+					<li>{@link org.apache.juneau.rest.RestContext#REST_renderResponseStackTraces REST_renderResponseStackTraces}
+					<li>{@link org.apache.juneau.rest.RestContext#REST_useStackTraceHashes REST_useStackTraceHashes}
+					<li>{@link org.apache.juneau.rest.RestContext#REST_defaultCharset REST_defaultCharset}
+					<li>{@link org.apache.juneau.rest.RestContext#REST_maxInput REST_maxInput}
+					<li>{@link org.apache.juneau.rest.RestContext#REST_paramResolvers REST_paramResolvers}
+					<li>{@link org.apache.juneau.rest.RestContext#REST_converters REST_converters}
+					<li>{@link org.apache.juneau.rest.RestContext#REST_guards REST_guards}
+					<li>{@link org.apache.juneau.rest.RestContext#REST_responseHandlers REST_responseHandlers}
+					<li>{@link org.apache.juneau.rest.RestContext#REST_defaultRequestHeaders REST_defaultRequestHeaders}
+					<li>{@link org.apache.juneau.rest.RestContext#REST_defaultResponseHeaders REST_defaultResponseHeaders}
+					<li>{@link org.apache.juneau.rest.RestContext#REST_supportedAcceptTypes REST_supportedAcceptTypes}
+					<li>{@link org.apache.juneau.rest.RestContext#REST_supportedContentTypes REST_supportedContentTypes}
+					<li>{@link org.apache.juneau.rest.RestContext#REST_clientVersionHeader REST_clientVersionHeader}
+					<li>{@link org.apache.juneau.rest.RestContext#REST_resourceResolver REST_resourceResolver}
+					<li>{@link org.apache.juneau.rest.RestContext#REST_logger REST_logger}
+					<li>{@link org.apache.juneau.rest.RestContext#REST_callHandler REST_callHandler}
+					<li>{@link org.apache.juneau.rest.RestContext#REST_infoProvider REST_infoProvider}
+					<li>{@link org.apache.juneau.rest.RestContext#REST_path REST_path}
+					<li>{@link org.apache.juneau.rest.RestContext#REST_contextPath REST_contextPath}
+					<li>{@link org.apache.juneau.rest.RestContext#REST_staticFiles REST_staticFiles}
+					<li>{@link org.apache.juneau.rest.RestContext#REST_staticFileResponseHeaders REST_staticFileResponseHeaders}
+					<li>{@link org.apache.juneau.rest.RestContext#REST_classpathResourceFinder REST_classpathResourceFinder}
+					<li>{@link org.apache.juneau.rest.RestContext#REST_useClasspathResourceCaching REST_useClasspathResourceCaching}
+				</ul>
+			<li>
+				Support for static files has been simplified and improved.
+				<ul>
+					<li>Syntax on {@link org.apache.juneau.rest.annotation.RestResource#staticFiles()} has been simplified, and
+						now allows you to specify response headers in the strings.
+					<li>Response headers for static files can also be configured through 
+						{@link org.apache.juneau.rest.RestContext#REST_staticFileResponseHeaders REST_staticFileResponseHeaders}.
+					<li>Static file in-memory caching now configurable through 
+						{@link org.apache.juneau.rest.RestContext#REST_useClasspathResourceCaching REST_useClasspathResourceCaching}.
+					<li>Static file retrieval can be customized through 
+						{@link org.apache.juneau.rest.RestContext#REST_classpathResourceFinder REST_classpathResourceFinder}
+				</ul>
 			<li>
 				Eliminated the <code>RestMatcherReflecting</code> class.
 				<br>You can now simply create a {@link org.apache.juneau.rest.RestMatcher} that has a public constructor that 
@@ -7968,6 +8017,58 @@
 			<li>
 				<code>@RestMethod.serializersInherit</code> and <code>@RestMethod.parsersInherit</code> replaced with
 				simplified {@link org.apache.juneau.rest.annotation.RestMethod#inherit()}. 
+			<li>
+				Changes to {@link org.apache.juneau.rest.RequestFormData}:
+				<ul>
+					<li>{@link org.apache.juneau.rest.RequestFormData#addDefault(Map) addDefault(Map)} takes in a 
+						<code>Map&lt;String,Object&gt;</code> instead of <code>Map&lt;String,String&gt;</code>.
+				</ul>
+			<li>
+				Changes to {@link org.apache.juneau.rest.RequestHeaders}:
+				<ul>
+					<li>{@link org.apache.juneau.rest.RequestHeaders#addDefault(Map) addDefault(Map)} takes in a 
+						<code>Map&lt;String,Object&gt;</code> instead of <code>Map&lt;String,String&gt;</code>.
+				</ul>
+			<li>
+				Changes to {@link org.apache.juneau.rest.RequestQuery}:
+				<ul>
+					<li>{@link org.apache.juneau.rest.RequestQuery#addDefault(Map) addDefault(Map)} takes in a 
+						<code>Map&lt;String,Object&gt;</code> instead of <code>Map&lt;String,String&gt;</code>.
+				</ul>
+			<li>
+				Changes to {@link org.apache.juneau.rest.RestContext}:
+				<ul>
+					<li><code>getResource(String,Locale)</code> renamed to {@link org.apache.juneau.rest.RestContext#getClasspathResource(String,Locale) getClasspathResource(String,Locale)}
+					<li><code>getResourceAsString(String,Locale)</code> renamed to {@link org.apache.juneau.rest.RestContext#getClasspathResourceAsString(String,Locale) getClasspathResourceAsString(String,Locale)}
+					<li><code>getResource(Class,MediaType,String,Locale)</code> renamed to {@link org.apache.juneau.rest.RestContext#getClasspathResource(Class,MediaType,String,Locale) getClasspathResourceAsString(Class,MediaType,String,Locale)}
+					<li>New method {@link org.apache.juneau.rest.RestContext#getClasspathResource(Class,String,Locale) getClasspathResource(Class,String,Locale)}.
+					<li>New method {@link org.apache.juneau.rest.RestContext#getClasspathResourceAsString(Class,String,Locale) getClasspathResourceAsString(Class,String,Locale)}.
+					<li>New method {@link org.apache.juneau.rest.RestContext#getClasspathResource(Class,Class,MediaType,String,Locale) getClasspathResource(Class,Class,MediaType,String,Locale)}.
+					<li>{@link org.apache.juneau.rest.RestContext#getDefaultRequestHeaders() getDefaultRequestHeaders} returns a 
+						<code>Map&lt;String,Object&gt;</code> instead of <code>Map&lt;String,String&gt;</code>.
+					<li>{@link org.apache.juneau.rest.RestContext#getDefaultResponseHeaders() getDefaultRequestHeaders} returns a 
+						<code>Map&lt;String,Object&gt;</code> instead of <code>Map&lt;String,String&gt;</code>.
+				</ul>
+			<li>
+				Changes to {@link org.apache.juneau.rest.RestRequest}:
+				<ul>	
+					<li><code>getSupportedMediaTypes()</code> replaced with
+						{@link org.apache.juneau.rest.RestRequest#getSupportedAcceptTypes() getSupportedAcceptTypes()} and
+						{@link org.apache.juneau.rest.RestRequest#getSupportedContentTypes() getSupportedContentTypes()}.
+					<li><code>getReaderResource(String,boolean,MediaType)</code> renamed to 
+						{@link org.apache.juneau.rest.RestRequest#getClasspathReaderResource(String,boolean,MediaType) getClasspathReaderResource(String,boolean,MediaType)}
+					<li><code>getReaderResource(String,boolean)</code> renamed to 
+						{@link org.apache.juneau.rest.RestRequest#getClasspathReaderResource(String,boolean) getClasspathReaderResource(String,boolean)}
+					<li><code>getReaderResource(String)</code> renamed to 
+						{@link org.apache.juneau.rest.RestRequest#getClasspathReaderResource(String) getClasspathReaderResource(String)}
+				</ul>
+			<li>
+				Changes to {@link org.apache.juneau.rest.annotation.RestMethod}:
+				<ul>
+					<li>New {@link org.apache.juneau.rest.annotation.RestMethod#supportedAcceptTypes() supportedAcceptTypes()} and
+						{@link org.apache.juneau.rest.annotation.RestMethod#supportedContentTypes() supportedContentTypes()}
+						for overriding the supported media types inferred from the serializers and parsers.
+				</ul>
 		</ul>
 
 		<h6 class='topic'>juneau-rest-client</h6>
@@ -7980,6 +8081,12 @@
 				<br>The default value is now {@link org.apache.juneau.httppart.SimpleUonPartSerializer} which will
 				serialize strings as plain-text and collections/arrays as comma-delimited lists.
 				<br>We decided to change the default behavior in favor of practicality over purity.
+			<li>
+				New methods on {@link org.apache.juneau.rest.client.RestCall} class:
+				<ul>
+					<li>{@link org.apache.juneau.rest.client.RestCall#getResponseHeader(String) getResponseHeader(String)}
+					<li>{@link org.apache.juneau.rest.client.RestCall#getResponseCode() getResponseCode()}
+				</ul>
 		</ul>
 
 	</div>
@@ -8680,7 +8787,7 @@
 					<li><code><del>HTMLDOC_stylesheet</del></code> - Was <jsf>HTMLDOC_cssUrl</jsf>.  Now an array.
 				</ul>
 			<li>
-				New {@link org.apache.juneau.utils.ResourceFinder} utility class.
+				New <code><del>ResourceFinder</del></code> utility class.
 				Allows you to search for resources up the parent hierarchy chain.
 				Also allows you to search for localized resources.
 			<li>
@@ -10383,8 +10490,8 @@
 			<li>Fixed a bug in the stack trace hash algorithm in {@link org.apache.juneau.rest.RestException}.
 			<li>New methods in {@link org.apache.juneau.rest.RestRequest}:
 				<ul>
-					<li>{@link org.apache.juneau.rest.RestRequest#getReaderResource(String)} - Replaces <code>getVarResource(String)</code>.
-					<li>{@link org.apache.juneau.rest.RestRequest#getReaderResource(String,boolean)} 
+					<li><code><del>RestRequest.getReaderResource(String)</del></code> - Replaces <code>getVarResource(String)</code>.
+					<li><code><del>RestRequest.getReaderResource(String,boolean)</del></code> 
 					<li><code><del>RestRequest.getReaderResource(String,boolean,String)</del></code>
 				</ul>
 			<li>Changes in {@link org.apache.juneau.rest.RestResponse}:

http://git-wip-us.apache.org/repos/asf/juneau/blob/b820b273/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/PetStoreResource.java
----------------------------------------------------------------------
diff --git a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/PetStoreResource.java b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/PetStoreResource.java
index d34be25..7c355e7 100644
--- a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/PetStoreResource.java
+++ b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/PetStoreResource.java
@@ -65,7 +65,8 @@ import org.apache.juneau.transforms.*;
 		head={
 			"<link rel='icon' href='$U{servlet:/htdocs/cat.png}'/>"
 		}
-	)
+	),
+	staticFiles={"htdocs:htdocs"}
 )
 public class PetStoreResource extends ResourceJena {
 	private static final long serialVersionUID = 1L;

http://git-wip-us.apache.org/repos/asf/juneau/blob/b820b273/juneau-microservice/juneau-microservice-test/src/main/java/org/apache/juneau/rest/test/InheritanceResource.java
----------------------------------------------------------------------
diff --git a/juneau-microservice/juneau-microservice-test/src/main/java/org/apache/juneau/rest/test/InheritanceResource.java b/juneau-microservice/juneau-microservice-test/src/main/java/org/apache/juneau/rest/test/InheritanceResource.java
index 492b9a5..a722d1e 100644
--- a/juneau-microservice/juneau-microservice-test/src/main/java/org/apache/juneau/rest/test/InheritanceResource.java
+++ b/juneau-microservice/juneau-microservice-test/src/main/java/org/apache/juneau/rest/test/InheritanceResource.java
@@ -103,7 +103,7 @@ public class InheritanceResource extends RestServlet {
 			path="/test1"
 		)
 		public Reader test1(RestRequest req) {
-			return new StringReader(new ObjectList(req.getSupportedMediaTypes()).toString());
+			return new StringReader(new ObjectList(req.getSupportedContentTypes()).toString());
 		}
 
 		// Should show ['text/p5']
@@ -113,7 +113,7 @@ public class InheritanceResource extends RestServlet {
 			parsers=P5.class
 		)
 		public Reader test2(RestRequest req) {
-			return new StringReader(new ObjectList(req.getSupportedMediaTypes()).toString());
+			return new StringReader(new ObjectList(req.getSupportedContentTypes()).toString());
 		}
 
 		// Should show ['text/p5','text/p3','text/p4','text/p1','text/p2']
@@ -124,7 +124,7 @@ public class InheritanceResource extends RestServlet {
 			inherit="PARSERS"
 		)
 		public Reader test3(RestRequest req) {
-			return new StringReader(new ObjectList(req.getSupportedMediaTypes()).toString());
+			return new StringReader(new ObjectList(req.getSupportedContentTypes()).toString());
 		}
 	}
 

http://git-wip-us.apache.org/repos/asf/juneau/blob/b820b273/juneau-microservice/juneau-microservice-test/src/main/java/org/apache/juneau/rest/test/StaticFilesResource.java
----------------------------------------------------------------------
diff --git a/juneau-microservice/juneau-microservice-test/src/main/java/org/apache/juneau/rest/test/StaticFilesResource.java b/juneau-microservice/juneau-microservice-test/src/main/java/org/apache/juneau/rest/test/StaticFilesResource.java
index c6c2c10..1b0856b 100644
--- a/juneau-microservice/juneau-microservice-test/src/main/java/org/apache/juneau/rest/test/StaticFilesResource.java
+++ b/juneau-microservice/juneau-microservice-test/src/main/java/org/apache/juneau/rest/test/StaticFilesResource.java
@@ -22,7 +22,7 @@ import org.apache.juneau.rest.annotation.*;
  */
 @RestResource(
 	path="/testStaticFiles",
-	staticFiles="{xdocs:'xdocs'}"
+	staticFiles={"xdocs:xdocs","xdocs2:xdocs2:{Foo:'Bar'}"}
 )
 public class StaticFilesResource extends RestServlet {
 	private static final long serialVersionUID = 1L;
@@ -34,5 +34,4 @@ public class StaticFilesResource extends RestServlet {
 	public String testXdocs() {
 		return null;
 	}
-
 }

http://git-wip-us.apache.org/repos/asf/juneau/blob/b820b273/juneau-microservice/juneau-microservice-test/src/main/resources/org/apache/juneau/rest/test/xdocs/xdocs/test.txt
----------------------------------------------------------------------
diff --git a/juneau-microservice/juneau-microservice-test/src/main/resources/org/apache/juneau/rest/test/xdocs/xdocs/test.txt b/juneau-microservice/juneau-microservice-test/src/main/resources/org/apache/juneau/rest/test/xdocs/xdocs/test.txt
deleted file mode 100644
index e3db156..0000000
--- a/juneau-microservice/juneau-microservice-test/src/main/resources/org/apache/juneau/rest/test/xdocs/xdocs/test.txt
+++ /dev/null
@@ -1,13 +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.                                              *
- ***************************************************************************************************************************
- OK-2
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/juneau/blob/b820b273/juneau-microservice/juneau-microservice-test/src/main/resources/org/apache/juneau/rest/test/xdocs/xsubdocs/test.txt
----------------------------------------------------------------------
diff --git a/juneau-microservice/juneau-microservice-test/src/main/resources/org/apache/juneau/rest/test/xdocs/xsubdocs/test.txt b/juneau-microservice/juneau-microservice-test/src/main/resources/org/apache/juneau/rest/test/xdocs/xsubdocs/test.txt
new file mode 100644
index 0000000..e3db156
--- /dev/null
+++ b/juneau-microservice/juneau-microservice-test/src/main/resources/org/apache/juneau/rest/test/xdocs/xsubdocs/test.txt
@@ -0,0 +1,13 @@
+ ***************************************************************************************************************************
+ * 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.                                              *
+ ***************************************************************************************************************************
+ OK-2
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/juneau/blob/b820b273/juneau-microservice/juneau-microservice-test/src/main/resources/org/apache/juneau/rest/test/xdocs2/test.txt
----------------------------------------------------------------------
diff --git a/juneau-microservice/juneau-microservice-test/src/main/resources/org/apache/juneau/rest/test/xdocs2/test.txt b/juneau-microservice/juneau-microservice-test/src/main/resources/org/apache/juneau/rest/test/xdocs2/test.txt
new file mode 100644
index 0000000..b734fee
--- /dev/null
+++ b/juneau-microservice/juneau-microservice-test/src/main/resources/org/apache/juneau/rest/test/xdocs2/test.txt
@@ -0,0 +1,13 @@
+ ***************************************************************************************************************************
+ * 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.                                              *
+ ***************************************************************************************************************************
+ OK-3
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/juneau/blob/b820b273/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/StaticFilesTest.java
----------------------------------------------------------------------
diff --git a/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/StaticFilesTest.java b/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/StaticFilesTest.java
index 42c4c9b..4e2167c 100644
--- a/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/StaticFilesTest.java
+++ b/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/StaticFilesTest.java
@@ -32,22 +32,62 @@ public class StaticFilesTest extends RestTestcase {
 
 		r = client.doGet(url + "/test.txt").getResponseAsString();
 		assertTrue(r.endsWith("OK-1"));
-		r = client.doGet(url + "/xdocs/test.txt").getResponseAsString();
+		r = client.doGet(url + "/xsubdocs/test.txt").getResponseAsString();
 		assertTrue(r.endsWith("OK-2"));
 
 		// For security reasons, paths containing ".." should always return 404.
 		try {
-			client.doGet(url + "/xdocs/../test.txt?noTrace=true").connect();
+			client.doGet(url + "/xsubdocs/../test.txt?noTrace=true").connect();
 			fail("404 exception expected");
 		} catch (RestCallException e) {
 			assertEquals(404, e.getResponseCode());
 		}
 
 		try {
-			client.doGet(url + "/xdocs/%2E%2E/test.txt?noTrace=true").connect();
+			client.doGet(url + "/xsubdocs/%2E%2E/test.txt?noTrace=true").connect();
 			fail("404 exception expected");
 		} catch (RestCallException e) {
 			assertEquals(404, e.getResponseCode());
 		}
 	}
+
+	//====================================================================================================
+	// Tests the @RestResource(staticFiles) annotation.
+	//====================================================================================================
+	@Test
+	public void testXdocsPreventPathTraversal() throws Exception {
+		RestClient client = TestMicroservice.DEFAULT_CLIENT_PLAINTEXT;
+		String url = URL + "/xdocs";
+
+		// For security reasons, paths containing ".." should always return 404.
+		try {
+			client.doGet(url + "/xsubdocs/../test.txt?noTrace=true").connect();
+			fail("404 exception expected");
+		} catch (RestCallException e) {
+			assertEquals(404, e.getResponseCode());
+		}
+
+		try {
+			client.doGet(url + "/xsubdocs/%2E%2E/test.txt?noTrace=true").connect();
+			fail("404 exception expected");
+		} catch (RestCallException e) {
+			assertEquals(404, e.getResponseCode());
+		}
+	}
+
+	//====================================================================================================
+	// Tests the @RestResource(staticFiles) annotation.
+	//====================================================================================================
+	@Test
+	public void testXdocsWithHeader() throws Exception {
+		RestClient client = TestMicroservice.DEFAULT_CLIENT_PLAINTEXT;
+		String r;
+		String url = URL + "/xdocs2";
+
+		r = client.doGet(url + "/test.txt").getResponseAsString();
+		assertTrue(r.endsWith("OK-3"));
+		
+		r = client.doGet(url + "/test.txt").getResponseHeader("Foo");
+		assertEquals("Bar", r);
+	}
 }

http://git-wip-us.apache.org/repos/asf/juneau/blob/b820b273/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCall.java
----------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCall.java b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCall.java
index a24fee5..ba064a2 100644
--- a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCall.java
+++ b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCall.java
@@ -1733,7 +1733,11 @@ public final class RestCall extends BeanSession {
 	/**
 	 * Connects to the remote resource (if {@code connect()} hasn't already been called) and returns the HTTP response
 	 * message body as plain text.
-	 *
+	 * 
+	 * <p>
+	 * The response entity is discarded unless one of the pipe methods have been specified to pipe the output to an
+	 * output stream or writer.
+	 * 
 	 * @return The response as a string.
 	 * @throws RestCallException If an exception or non-200 response code occurred during the connection attempt.
 	 * @throws IOException If an exception occurred while streaming was already occurring.
@@ -1748,6 +1752,53 @@ public final class RestCall extends BeanSession {
 			close();
 		}
 	}
+	
+	/**
+	 * Connects to the remote resource (if {@code connect()} hasn't already been called) and returns the value of
+	 * an HTTP header on the response.
+	 * 
+	 * <p>
+	 * Useful if you're only interested in a particular header value from the response and not the body of the response.
+	 * 
+	 * <p>
+	 * The response entity is discarded unless one of the pipe methods have been specified to pipe the output to an
+	 * output stream or writer.
+	 * 
+	 * @param name The header name. 
+	 * @return The response header as a string, or <jk>null</jk> if the header was not found.
+	 * @throws RestCallException If an exception or non-200 response code occurred during the connection attempt.
+	 * @throws IOException If an exception occurred while streaming was already occurring.
+	 */
+	public String getResponseHeader(String name) throws IOException {
+		try {
+			HttpResponse r = getResponse();
+			Header h = r.getFirstHeader(name);
+			return h == null ? null : h.getValue();
+		} catch (IOException e) {
+			isFailed = true;
+			throw e;
+		} finally {
+			close();
+		}
+	}
+		
+	/**
+	 * Connects to the remote resource (if {@code connect()} hasn't already been called) and returns the HTTP response code.
+	 * 
+	 * <p>
+	 * Useful if you're only interested in the status code and not the body of the response.
+	 * 
+	 * <p>
+	 * The response entity is discarded unless one of the pipe methods have been specified to pipe the output to an
+	 * output stream or writer.
+	 * 
+	 * @return The response code.
+	 * @throws RestCallException If an exception or non-200 response code occurred during the connection attempt.
+	 * @throws IOException If an exception occurred while streaming was already occurring.
+	 */
+	public int getResponseCode() throws IOException {
+		return run();
+	}
 
 	/**
 	 * Same as {@link #getResponse(Class)} but allows you to run the call asynchronously.

http://git-wip-us.apache.org/repos/asf/juneau/blob/b820b273/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/CallMethod.java
----------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/CallMethod.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/CallMethod.java
index ebdbc68..0330c20 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/CallMethod.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/CallMethod.java
@@ -28,6 +28,7 @@ import javax.servlet.http.*;
 import org.apache.juneau.*;
 import org.apache.juneau.dto.swagger.*;
 import org.apache.juneau.encoders.*;
+import org.apache.juneau.http.*;
 import org.apache.juneau.httppart.*;
 import org.apache.juneau.httppart.HttpPartParser;
 import org.apache.juneau.internal.*;
@@ -57,7 +58,7 @@ class CallMethod implements Comparable<CallMethod>  {
 	private final HttpPartParser partParser;
 	private final HttpPartSerializer partSerializer;
 	private final ObjectMap properties;
-	private final Map<String,String> defaultRequestHeaders, defaultQuery, defaultFormData;
+	private final Map<String,Object> defaultRequestHeaders, defaultQuery, defaultFormData;
 	private final String defaultCharset;
 	private final long maxInput;
 	private final boolean deprecated;
@@ -68,6 +69,7 @@ class CallMethod implements Comparable<CallMethod>  {
 	private final RestContext context;
 	private final BeanContext beanContext;
 	private final Map<String,Widget> widgets;
+	private final List<MediaType> supportedAcceptTypes, supportedContentTypes;
 
 	CallMethod(Object servlet, java.lang.reflect.Method method, RestContext context) throws RestServletException {
 		Builder b = new Builder(servlet, method, context);
@@ -100,6 +102,8 @@ class CallMethod implements Comparable<CallMethod>  {
 		this.priority = b.priority;
 		this.parameters = b.parameters;
 		this.responses = b.responses;
+		this.supportedAcceptTypes = b.supportedAcceptTypes;
+		this.supportedContentTypes = b.supportedContentTypes;
 		this.widgets = Collections.unmodifiableMap(b.widgets);
 	}
 
@@ -117,13 +121,14 @@ class CallMethod implements Comparable<CallMethod>  {
 		HttpPartSerializer partSerializer;
 		BeanContext beanContext;
 		ObjectMap properties;
-		Map<String,String> defaultRequestHeaders, defaultQuery, defaultFormData;
+		Map<String,Object> defaultRequestHeaders, defaultQuery, defaultFormData;
 		boolean deprecated;
 		long maxInput;
 		Integer priority;
 		org.apache.juneau.rest.annotation.Parameter[] parameters;
 		Response[] responses;
 		Map<String,Widget> widgets;
+		List<MediaType> supportedAcceptTypes, supportedContentTypes;
 
 		Builder(Object servlet, java.lang.reflect.Method method, RestContext context) throws RestServletException {
 			String sig = method.getDeclaringClass().getName() + '.' + method.getName();
@@ -397,6 +402,15 @@ class CallMethod implements Comparable<CallMethod>  {
 				if (bcb != null)
 					beanContext = bcb.build();
 
+				supportedAcceptTypes = 
+					m.supportedAcceptTypes().length > 0 
+					? Collections.unmodifiableList(new ArrayList<>(Arrays.asList(MediaType.forStrings(m.supportedAcceptTypes())))) 
+					: serializers.getSupportedMediaTypes();
+				supportedContentTypes =
+					m.supportedContentTypes().length > 0 
+					? Collections.unmodifiableList(new ArrayList<>(Arrays.asList(MediaType.forStrings(m.supportedContentTypes())))) 
+					: parsers.getSupportedMediaTypes();
+					
 				params = context.findParams(method, pathPattern, false);
 
 				// Need this to access methods in anonymous inner classes.
@@ -780,7 +794,7 @@ class CallMethod implements Comparable<CallMethod>  {
 		ObjectMap requestProperties = new ResolvingObjectMap(req.getVarResolverSession()).setInner(properties);
 
 		req.init(method, requestProperties, defaultRequestHeaders, defaultQuery, defaultFormData, defaultCharset,
-			maxInput, serializers, parsers, partParser, beanContext, encoders, widgets);
+			maxInput, serializers, parsers, partParser, beanContext, encoders, widgets, supportedAcceptTypes, supportedContentTypes);
 		res.init(requestProperties, defaultCharset, serializers, partSerializer, encoders);
 
 		// Class-level guards

http://git-wip-us.apache.org/repos/asf/juneau/blob/b820b273/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestFormData.java
----------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestFormData.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestFormData.java
index 051eee5..4549870 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestFormData.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestFormData.java
@@ -52,13 +52,14 @@ public class RequestFormData extends LinkedHashMap<String,String[]> {
 	 * @param defaultEntries The default entries.  Can be <jk>null</jk>.
 	 * @return This object (for method chaining).
 	 */
-	public RequestFormData addDefault(Map<String,String> defaultEntries) {
+	public RequestFormData addDefault(Map<String,Object> defaultEntries) {
 		if (defaultEntries != null) {
-			for (Map.Entry<String,String> e : defaultEntries.entrySet()) {
-				String key = e.getKey(), value = e.getValue();
+			for (Map.Entry<String,Object> e : defaultEntries.entrySet()) {
+				String key = e.getKey();
+				Object value = e.getValue();
 				String[] v = get(key);
 				if (v == null || v.length == 0 || StringUtils.isEmpty(v[0]))
-					put(key, new String[]{value});
+					put(key, new String[]{StringUtils.toString(value)});
 			}
 		}
 		return this;

http://git-wip-us.apache.org/repos/asf/juneau/blob/b820b273/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestHeaders.java
----------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestHeaders.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestHeaders.java
index fe6b675..a08bbd7 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestHeaders.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestHeaders.java
@@ -67,13 +67,14 @@ public class RequestHeaders extends TreeMap<String,String[]> {
 	 * @param defaultEntries The default entries.  Can be <jk>null</jk>.
 	 * @return This object (for method chaining).
 	 */
-	public RequestHeaders addDefault(Map<String,String> defaultEntries) {
+	public RequestHeaders addDefault(Map<String,Object> defaultEntries) {
 		if (defaultEntries != null) {
-			for (Map.Entry<String,String> e : defaultEntries.entrySet()) {
-				String key = e.getKey(), value = e.getValue();
+			for (Map.Entry<String,Object> e : defaultEntries.entrySet()) {
+				String key = e.getKey();
+				Object value = e.getValue();
 				String[] v = get(key);
 				if (v == null || v.length == 0 || StringUtils.isEmpty(v[0]))
-					put(key, new String[]{value});
+					put(key, new String[]{StringUtils.toString(value)});
 			}
 		}
 		return this;

http://git-wip-us.apache.org/repos/asf/juneau/blob/b820b273/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestQuery.java
----------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestQuery.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestQuery.java
index 2e51c39..8f1d62f 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestQuery.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestQuery.java
@@ -65,13 +65,14 @@ public final class RequestQuery extends LinkedHashMap<String,String[]> {
 	 * @param defaultEntries The default entries.  Can be <jk>null</jk>.
 	 * @return This object (for method chaining).
 	 */
-	public RequestQuery addDefault(Map<String,String> defaultEntries) {
+	public RequestQuery addDefault(Map<String,Object> defaultEntries) {
 		if (defaultEntries != null) {
-			for (Map.Entry<String,String> e : defaultEntries.entrySet()) {
-				String key = e.getKey(), value = e.getValue();
+			for (Map.Entry<String,Object> e : defaultEntries.entrySet()) {
+				String key = e.getKey();
+				Object value = e.getValue();
 				String[] v = get(key);
 				if (v == null || v.length == 0 || StringUtils.isEmpty(v[0]))
-					put(key, new String[]{value});
+					put(key, new String[]{StringUtils.toString(value)});
 			}
 		}
 		return this;