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

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

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

Reply via email to