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

[1/2] juneau git commit: RestContext refactoring.

Repository: juneau
Updated Branches:
  refs/heads/master 5a36a0837 -> af355372e


http://git-wip-us.apache.org/repos/asf/juneau/blob/af355372/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestInfoProviderDefault.java
----------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestInfoProviderDefault.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestInfoProviderDefault.java
new file mode 100644
index 0000000..11cad76
--- /dev/null
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestInfoProviderDefault.java
@@ -0,0 +1,632 @@
+// ***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                                                              *
+// *                                                                                                                         *
+// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
+// *                                                                                                                         *
+// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the License.                                              *
+// ***************************************************************************************************************************
+package org.apache.juneau.rest;
+
+import static javax.servlet.http.HttpServletResponse.*;
+import static org.apache.juneau.dto.swagger.SwaggerBuilder.*;
+import static org.apache.juneau.internal.ReflectionUtils.*;
+
+import java.util.*;
+import java.util.concurrent.*;
+
+import org.apache.juneau.dto.swagger.*;
+import org.apache.juneau.http.*;
+import org.apache.juneau.json.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.svl.*;
+
+/**
+ * Default implementation of {@link RestInfoProvider}.
+ * 
+ * <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.
+ * 
+ * 
+ * <h5 class='topic'>Additional Information</h5>
+ * <ul>
+ * 	<li class='jf'>{@link RestContext#REST_infoProvider}
+ * </ul>
+ */
+public class RestInfoProviderDefault implements RestInfoProvider {
+
+	private final RestContext context;
+	private final String
+		siteName,
+		title,
+		description,
+		termsOfService,
+		contact,
+		license,
+		version,
+		tags,
+		externalDocs;
+	private final ConcurrentHashMap<Locale,Swagger> swaggers = new ConcurrentHashMap<>();
+
+	/**
+	 * Constructor.
+	 * 
+	 * @param context The resource context.
+	 */
+	public RestInfoProviderDefault(RestContext context) {
+		this.context = context;
+
+		Builder b = new Builder(context);
+		this.siteName = b.siteName;
+		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 final class Builder {
+		String
+			siteName,
+			title,
+			description,
+			termsOfService,
+			contact,
+			license,
+			version,
+			tags,
+			externalDocs;
+
+		Builder(RestContext context) {
+
+			LinkedHashMap<Class<?>,RestResource> restResourceAnnotationsParentFirst = findAnnotationsMapParentFirst(RestResource.class, context.getResource().getClass());
+
+			for (RestResource r : restResourceAnnotationsParentFirst.values()) {
+				if (! r.siteName().isEmpty())
+					siteName = r.siteName();
+				if (! r.title().isEmpty())
+					title = r.title();
+				if (! r.description().isEmpty())
+					description = r.description();
+				ResourceSwagger sr = r.swagger();
+				if (! sr.termsOfService().isEmpty())
+					termsOfService = sr.termsOfService();
+				if (! sr.contact().isEmpty())
+					contact = sr.contact();
+				if (! sr.license().isEmpty())
+					license = sr.license();
+				if (! sr.version().isEmpty())
+					version = sr.version();
+				if (! sr.tags().isEmpty())
+					tags = sr.tags();
+				if (! sr.externalDocs().isEmpty())
+					externalDocs = sr.externalDocs();
+			}
+		}
+	}
+
+	/**
+	 * 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 req The incoming HTTP request.
+	 * @return The parsed swagger object, or <jk>null</jk> if the swagger file could not be found.
+	 * @throws RestException
+	 */
+	@Override /* RestInfoProvider */
+	public Swagger getSwaggerFromFile(RestRequest req) throws RestException {
+		Locale locale = req.getLocale();
+		Swagger s = swaggers.get(locale);
+		if (s == null) {
+			try {
+				s = context.getClasspathResource(Swagger.class, MediaType.JSON, getClass().getSimpleName() + ".json", locale);
+				swaggers.putIfAbsent(locale, s == null ? Swagger.NULL : s);
+			} catch (Exception e) {
+				throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
+			}
+		}
+		return s == Swagger.NULL ? null : s;
+	}
+
+	/**
+	 * Returns the localized swagger for this REST resource.
+	 * 
+	 * @param req The incoming HTTP request.
+	 * @return A new Swagger instance.
+	 * @throws RestException
+	 */
+	@Override /* RestInfoProvider */
+	public 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(req.getSupportedAcceptTypes())
+				.produces(req.getSupportedContentTypes())
+				.tags(getTags(req))
+				.externalDocs(getExternalDocs(req));
+
+			for (RestJavaMethod 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 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):
+	 * <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.
+	 */
+	@Override /* RestInfoProvider */
+	public String getMethodSummary(String javaMethodName, RestRequest req) {
+		RestJavaMethod m = context.getCallMethods().get(javaMethodName);
+		if (m != null)
+			return m.getSummary(req);
+		return "";
+	}
+
+	/**
+	 * Returns the localized summary of the java method invoked on the specified request.
+	 * 
+	 * <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):
+	 * <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 req The current request.
+	 * @return The localized summary of the method, or a blank string if no summary was found.
+	 */
+	@Override /* RestInfoProvider */
+	public String getMethodSummary(RestRequest req) {
+		return getMethodSummary(req.getJavaMethod().getName(), req);
+	}
+
+	/**
+	 * 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):
+	 * <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.
+	 */
+	@Override /* RestInfoProvider */
+	public String getMethodDescription(String javaMethodName, RestRequest req) {
+		RestJavaMethod m = context.getCallMethods().get(javaMethodName);
+		if (m != null)
+			return m.getDescription(req);
+		return "";
+	}
+
+	/**
+	 * Returns the localized description of the invoked java method on the specified request.
+	 * 
+	 * <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 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 req The current request.
+	 * @return The localized description of the method, or a blank string if no description was found.
+	 */
+	@Override /* RestInfoProvider */
+	public String getMethodDescription(RestRequest req) {
+		return getMethodDescription(req.getJavaMethod().getName(), req);
+	}
+
+	/**
+	 * Returns the localized site name of this REST resource.
+	 * 
+	 * <p>
+	 * Subclasses can override this method to provide their own site name.
+	 * 
+	 * <p>
+	 * The default implementation returns the description from the following locations (whichever matches first):
+	 * <ol>
+	 * 	<li>{@link RestResource#siteName() @RestResource.siteName()} annotation on this class, and then any parent classes.
+	 * 	<li><ck>[ClassName].siteName</ck> property in resource bundle identified by
+	 * 		{@link RestResource#messages() @ResourceBundle.messages()} annotation for this class, then any parent
+	 * 		classes.
+	 * 	<li><ck>siteName</ck> in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+	 * 		annotation for this class, then any parent classes.
+	 * </ol>
+	 * 
+	 * @param req The current request.
+	 * @return The localized description of this REST resource, or <jk>null</jk> if no resource description was found.
+	 */
+	@Override /* RestInfoProvider */
+	public String getSiteName(RestRequest req) {
+		VarResolverSession vr = req.getVarResolverSession();
+		if (this.siteName != null)
+			return vr.resolve(this.siteName);
+		String siteName = context.getMessages().findFirstString(req.getLocale(), "siteName");
+		if (siteName != null)
+			return vr.resolve(siteName);
+		return null;
+	}
+
+	/**
+	 * 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):
+	 * <ol>
+	 * 	<li>{@link RestResource#title() @RestResource.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.
+	 */
+	@Override /* RestInfoProvider */
+	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.
+	 */
+	@Override /* RestInfoProvider */
+	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 ResourceSwagger#contact() @ResourceSwagger.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.
+	 */
+	@Override /* RestInfoProvider */
+	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 ResourceSwagger#license() @ResourceSwagger.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.
+	 */
+	@Override /* RestInfoProvider */
+	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 ResourceSwagger#termsOfService() @ResourceSwagger.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.
+	 */
+	@Override /* RestInfoProvider */
+	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 ResourceSwagger#version() @ResourceSwagger.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.
+	 */
+	@Override /* RestInfoProvider */
+	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 ResourceSwagger#version() @ResourceSwagger.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.
+	 */
+	@Override /* RestInfoProvider */
+	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 ResourceSwagger#version() @ResourceSwagger.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.
+	 */
+	@Override /* RestInfoProvider */
+	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);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/juneau/blob/af355372/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestLogger.java
----------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestLogger.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestLogger.java
index 6e1ec6c..12e0b5d 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestLogger.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestLogger.java
@@ -12,72 +12,51 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.rest;
 
-import static javax.servlet.http.HttpServletResponse.*;
-import static org.apache.juneau.internal.StringUtils.*;
-
 import java.text.*;
 import java.util.logging.*;
 
 import javax.servlet.http.*;
 
-import org.apache.juneau.internal.*;
 import org.apache.juneau.json.*;
-import org.apache.juneau.rest.annotation.*;
 
 /**
  * Logging utility class.
  * 
- * <p>
- * Subclasses can override these methods to tailor logging of HTTP requests.
- * Subclasses MUST implement a no-arg public constructor.
  * 
- * <p>
- * RestLoggers are associated with servlets/resources in one of the following ways:
+ * <h5 class='topic'>Additional Information</h5>
  * <ul>
- * 	<li>The {@link RestResource#logger @RestResource.logger()} annotation.
- * 	<li>The {@link RestContextBuilder#logger(Class)}/{@link RestContextBuilder#logger(RestLogger)} methods.
+ * 	<li class='jf'>{@link RestContext#REST_logger}
  * </ul>
  */
-public abstract class RestLogger {
+public interface RestLogger {
 
 	/**
-	 * Returns the Java logger used for logging.
+	 * Represents no RestLogger.
 	 * 
 	 * <p>
-	 * Subclasses can provide their own logger.
-	 * The default implementation returns the logger created using <code>Logger.getLogger(getClass())</code>.
-	 * 
-	 * @return The logger used for logging.
+	 * Used on annotation to indicate that the value should be inherited from the parent class, and
+	 * ultimately {@link RestLoggerDefault} if not specified at any level.
 	 */
-	protected abstract Logger getLogger();
+	public interface Null extends RestLogger {}
 
 	/**
 	 * Log a message to the logger.
 	 * 
-	 * <p>
-	 * Subclasses can override this method if they wish to log messages using a library other than Java Logging
-	 * (e.g. Apache Commons Logging).
-	 * 
 	 * @param level The log level.
 	 * @param cause The cause.
 	 * @param msg The message to log.
 	 * @param args Optional {@link MessageFormat}-style arguments.
 	 */
-	protected abstract void log(Level level, Throwable cause, String msg, Object...args);
+	public void log(Level level, Throwable cause, String msg, Object...args);
 
 	/**
 	 * Log a message.
 	 * 
-	 * <p>
-	 * Equivalent to calling <code>log(level, <jk>null</jk>, msg, args);</code>
-	 * 
 	 * @param level The log level.
 	 * @param msg The message to log.
 	 * @param args Optional {@link MessageFormat}-style arguments.
 	 */
-	protected void log(Level level, String msg, Object...args) {
-		log(level, null, msg, args);
-	}
+	public void log(Level level, String msg, Object...args);
 
 	/**
 	 * Same as {@link #log(Level, String, Object...)} excepts runs the arguments through {@link JsonSerializer#DEFAULT_LAX_READABLE}.
@@ -95,11 +74,7 @@ public abstract class RestLogger {
 	 * @param msg The message to log.
 	 * @param args Optional {@link MessageFormat}-style arguments.
 	 */
-	protected void logObjects(Level level, String msg, Object...args) {
-		for (int i = 0; i < args.length; i++)
-			args[i] = JsonSerializer.DEFAULT_LAX_READABLE.toStringObject(args[i]);
-		log(level, null, msg, args);
-	}
+	public void logObjects(Level level, String msg, Object...args);
 
 	/**
 	 * Callback method for logging errors during HTTP requests.
@@ -138,120 +113,5 @@ public abstract class RestLogger {
 	 * @param res The servlet response object.
 	 * @param e Exception indicating what error occurred.
 	 */
-	protected void onError(HttpServletRequest req, HttpServletResponse res, RestException e) {
-		if (shouldLog(req, res, e)) {
-			String qs = req.getQueryString();
-			String msg = "HTTP " + req.getMethod() + " " + e.getStatus() + " " + req.getRequestURI() + (qs == null ? "" : "?" + qs);
-			int c = e.getOccurrence();
-			if (shouldLogStackTrace(req, res, e)) {
-				msg = '[' + Integer.toHexString(e.hashCode()) + '.' + e.getStatus() + '.' + c + "] " + msg;
-				log(Level.WARNING, e, msg);
-			} else {
-				msg = '[' + Integer.toHexString(e.hashCode()) + '.' + e.getStatus() + '.' + c + "] " + msg + ", " + e.getLocalizedMessage();
-				log(Level.WARNING, msg);
-			}
-		}
-	}
-
-	/**
-	 * Returns <jk>true</jk> if the specified exception should be logged.
-	 * 
-	 * <p>
-	 * Subclasses can override this method to provide their own logic for determining when exceptions are logged.
-	 * 
-	 * <p>
-	 * The default implementation will return <jk>false</jk> if <js>"noTrace=true"</js> is passed in the query string
-	 * or <code>No-Trace: true</code> is specified in the header.
-	 * 
-	 * @param req The HTTP request.
-	 * @param res The HTTP response.
-	 * @param e The exception.
-	 * @return <jk>true</jk> if exception should be logged.
-	 */
-	protected boolean shouldLog(HttpServletRequest req, HttpServletResponse res, RestException e) {
-		if (isNoTrace(req) && ! isDebug(req))
-			return false;
-		return true;
-	}
-
-	/**
-	 * Returns <jk>true</jk> if a stack trace should be logged for this exception.
-	 * 
-	 * <p>
-	 * Subclasses can override this method to provide their own logic for determining when stack traces are logged.
-	 * 
-	 * <p>
-	 * The default implementation will only log a stack trace if {@link RestException#getOccurrence()} returns
-	 * <code>1</code> and the exception is not one of the following:
-	 * <ul>
-	 * 	<li>{@link HttpServletResponse#SC_UNAUTHORIZED}
-	 * 	<li>{@link HttpServletResponse#SC_FORBIDDEN}
-	 * 	<li>{@link HttpServletResponse#SC_NOT_FOUND}
-	 * </ul>
-	 * 
-	 * @param req The HTTP request.
-	 * @param res The HTTP response.
-	 * @param e The exception.
-	 * @return <jk>true</jk> if stack trace should be logged.
-	 */
-	protected boolean shouldLogStackTrace(HttpServletRequest req, HttpServletResponse res, RestException e) {
-		if (e.getOccurrence() == 1) {
-			switch (e.getStatus()) {
-				case SC_UNAUTHORIZED:
-				case SC_FORBIDDEN:
-				case SC_NOT_FOUND:  return false;
-				default:            return true;
-			}
-		}
-		return false;
-	}
-
-	private static boolean isNoTrace(HttpServletRequest req) {
-		return contains(req.getHeader("No-Trace"), "true") || contains(req.getQueryString(), "noTrace=true");
-	}
-
-	private static boolean isDebug(HttpServletRequest req) {
-		return contains(req.getHeader("Debug"), "true");
-	}
-
-	/**
-	 * NO-OP logger.
-	 * 
-	 * <p>
-	 * Disables all logging.
-	 * 
-	 * @author James Bognar (james.bognar@salesforce.com)
-	 */
-	public static class NoOp extends RestLogger {
-
-		@Override /* RestLogger */
-		protected Logger getLogger() {
-			return null;
-		}
-
-		@Override /* RestLogger */
-		protected void log(Level level, Throwable cause, String msg, Object...args) {}
-	}
-
-	/**
-	 * Default logger.
-	 * 
-	 * <p>
-	 * Logs all messages to the logger returned by <code>Logger.<jsm>getLogger</jsm>(getClass().getName())</code>
-	 */
-	public static class Normal extends RestLogger {
-
-		private final JuneauLogger logger = JuneauLogger.getLogger(getClass());
-
-		@Override /* RestLogger */
-		protected Logger getLogger() {
-			return logger;
-		}
-
-		@Override /* RestLogger */
-		protected void log(Level level, Throwable cause, String msg, Object...args) {
-			msg = format(msg, args);
-			getLogger().log(level, msg, cause);
-		}
-	}
+	public void onError(HttpServletRequest req, HttpServletResponse res, RestException e);
 }

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

http://git-wip-us.apache.org/repos/asf/juneau/blob/af355372/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestLoggerNoOp.java
----------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestLoggerNoOp.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestLoggerNoOp.java
new file mode 100644
index 0000000..49e1821
--- /dev/null
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestLoggerNoOp.java
@@ -0,0 +1,47 @@
+// ***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                                                              *
+// *                                                                                                                         *
+// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
+// *                                                                                                                         *
+// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the License.                                              *
+// ***************************************************************************************************************************
+package org.apache.juneau.rest;
+
+
+import java.text.*;
+import java.util.logging.*;
+
+/**
+ * Logging utility class.
+ * 
+ * <p>
+ * Disables logging entirely.
+ * 
+ * 
+ * <h5 class='topic'>Additional Information</h5>
+ * <ul>
+ * 	<li class='jf'>{@link RestContext#REST_callHandler}
+ * </ul>
+ */
+public class RestLoggerNoOp extends RestLoggerDefault {
+
+	/**
+	 * Log a message to the logger.
+	 * 
+	 * <p>
+	 * Subclasses can override this method if they wish to log messages using a library other than Java Logging
+	 * (e.g. Apache Commons Logging).
+	 * 
+	 * @param level The log level.
+	 * @param cause The cause.
+	 * @param msg The message to log.
+	 * @param args Optional {@link MessageFormat}-style arguments.
+	 */
+	@Override /* RestLogger */
+	public void log(Level level, Throwable cause, String msg, Object...args) {}
+}

http://git-wip-us.apache.org/repos/asf/juneau/blob/af355372/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestParamDefaults.java
----------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestParamDefaults.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestParamDefaults.java
index bfdefd3..633416c 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestParamDefaults.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestParamDefaults.java
@@ -19,7 +19,6 @@ import java.io.*;
 import java.lang.reflect.*;
 import java.lang.reflect.Method;
 import java.util.*;
-import java.util.logging.*;
 
 import javax.servlet.*;
 import javax.servlet.http.*;
@@ -101,8 +100,7 @@ class RestParamDefaults {
 			RequestQueryObject.class,
 			RequestFormDataObject.class,
 			HttpMethodObject.class,
-			LoggerObject.class,
-			JuneauLoggerObject.class,
+			RestLoggerObject.class,
 			RestContextObject.class,
 			ParserObject.class,
 			LocaleObject.class,
@@ -832,27 +830,15 @@ class RestParamDefaults {
 		}
 	}
 
-	static final class LoggerObject extends RestParam {
+	static final class RestLoggerObject extends RestParam {
 
-		protected LoggerObject() {
-			super(OTHER, null, Logger.class);
+		protected RestLoggerObject() {
+			super(OTHER, null, RestLogger.class);
 		}
 
 		@Override /* RestParam */
-		public Object resolve(RestRequest req, RestResponse res) throws Exception {
-			return req.getContext().getLogger().getLogger();
-		}
-	}
-
-	static final class JuneauLoggerObject extends RestParam {
-
-		protected JuneauLoggerObject() {
-			super(OTHER, null, JuneauLogger.class);
-		}
-
-		@Override /* RestParam */
-		public Object resolve(RestRequest req, RestResponse res) throws Exception {
-			return req.getContext().getLogger().getLogger();
+		public RestLogger resolve(RestRequest req, RestResponse res) throws Exception {
+			return req.getContext().getLogger();
 		}
 	}
 

http://git-wip-us.apache.org/repos/asf/juneau/blob/af355372/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
----------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
index c36e432..2354d36 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
@@ -950,7 +950,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
 	 */
 	protected Swagger getSwaggerFromFile() {
 		if (fileSwagger == null)
-			fileSwagger = context.getInfoProvider().getSwaggerFromFile(this.getLocale());
+			fileSwagger = context.getInfoProvider().getSwaggerFromFile(this);
 		if (fileSwagger == null)
 			fileSwagger = Swagger.NULL;
 		return fileSwagger == Swagger.NULL ? null : fileSwagger;

http://git-wip-us.apache.org/repos/asf/juneau/blob/af355372/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResourceResolver.java
----------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResourceResolver.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResourceResolver.java
index 893ef7d..5041552 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResourceResolver.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResourceResolver.java
@@ -38,6 +38,15 @@ import org.apache.juneau.rest.annotation.*;
 public interface RestResourceResolver {
 
 	/**
+	 * Represents no RestResourceResolver.
+	 * 
+	 * <p>
+	 * Used on annotation to indicate that the value should be inherited from the parent class, and
+	 * ultimately {@link RestResourceResolverDefault} if not specified at any level.
+	 */
+	public interface Null extends RestResourceResolver {}
+	
+	/**
 	 * Resolves the specified class to a resource object.
 	 * 
 	 * <p>

http://git-wip-us.apache.org/repos/asf/juneau/blob/af355372/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResourceResolverDefault.java
----------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResourceResolverDefault.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResourceResolverDefault.java
new file mode 100644
index 0000000..0f319f2
--- /dev/null
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResourceResolverDefault.java
@@ -0,0 +1,55 @@
+// ***************************************************************************************************************************
+// * 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 org.apache.juneau.internal.*;
+
+/**
+ * Denotes the default resolver for child resources.
+ * 
+ * The default implementation simply instantiates the class using one of the following constructors:
+ * <ul>
+ * 	<li><code><jk>public</jk> T(RestContext)</code>
+ * 	<li><code><jk>public</jk> T()</code>
+ * </ul>
+ * 
+ * <p>
+ * The former constructor can be used to get access to the {@link RestContextBuilder} object to get access to the
+ * config file and initialization information or make programmatic modifications to the resource before
+ * full initialization.
+ * 
+ * <p>
+ * Child classes can also be defined as inner-classes of the parent resource class.
+ * 
+ * <p>
+ * Non-<code>RestServlet</code> classes can also add the following method to get access to the {@link RestContextBuilder} 
+ * object:
+ * <ul>
+ * 	<li><code><jk>public void</jk> init(RestContextBuilder);</code>
+ * </ul>
+ * 
+ */
+public class RestResourceResolverDefault implements RestResourceResolver {
+
+	@Override /* RestResourceResolver */
+	public Object resolve(Object parent, Class<?> c, RestContextBuilder builder) throws Exception {
+		try {
+			Object r = ClassUtils.newInstanceFromOuter(parent, Object.class, c, true, builder);
+			if (r != null)
+				return r;
+		} catch (Exception e) {
+			throw new RestServletException("Could not instantiate resource class ''{0}''", c.getName()).initCause(e);
+		}
+		throw new RestServletException("Could not find public constructor for class ''{0}''.", c);
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/juneau/blob/af355372/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResourceResolverSimple.java
----------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResourceResolverSimple.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResourceResolverSimple.java
deleted file mode 100644
index 3d63597..0000000
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResourceResolverSimple.java
+++ /dev/null
@@ -1,55 +0,0 @@
-// ***************************************************************************************************************************
-// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
-// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
-// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
-// * with the License.  You may obtain a copy of the License at                                                              *
-// *                                                                                                                         *
-// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
-// *                                                                                                                         *
-// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
-// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
-// * specific language governing permissions and limitations under the License.                                              *
-// ***************************************************************************************************************************
-package org.apache.juneau.rest;
-
-import org.apache.juneau.internal.*;
-
-/**
- * Denotes the default resolver for child resources.
- * 
- * The default implementation simply instantiates the class using one of the following constructors:
- * <ul>
- * 	<li><code><jk>public</jk> T(RestContextBuilder)</code>
- * 	<li><code><jk>public</jk> T()</code>
- * </ul>
- * 
- * <p>
- * The former constructor can be used to get access to the {@link RestContextBuilder} object to get access to the
- * config file and initialization information or make programmatic modifications to the resource before
- * full initialization.
- * 
- * <p>
- * Child classes can also be defined as inner-classes of the parent resource class.
- * 
- * <p>
- * Non-<code>RestServlet</code> classes can also add the following method to get access to the {@link RestContextBuilder} 
- * object:
- * <ul>
- * 	<li><code><jk>public void</jk> init(RestContextBuilder);</code>
- * </ul>
- * 
- */
-public class RestResourceResolverSimple implements RestResourceResolver {
-
-	@Override /* RestResourceResolver */
-	public Object resolve(Object parent, Class<?> c, RestContextBuilder builder) throws Exception {
-		try {
-			Object r = ClassUtils.newInstanceFromOuter(parent, Object.class, c, true, builder);
-			if (r != null)
-				return r;
-		} catch (Exception e) {
-			throw new RestServletException("Could not instantiate resource class ''{0}''", c.getName()).initCause(e);
-		}
-		throw new RestServletException("Could not find public constructor for class ''{0}''.", c);
-	}
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/juneau/blob/af355372/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestResource.java
----------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestResource.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestResource.java
index ca895a3..46d8497 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestResource.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestResource.java
@@ -160,7 +160,7 @@ public @interface RestResource {
 	 * 	<li class='jf'>{@link RestContext#REST_callHandler}
 	 * </ul>
 	 */
-	Class<? extends RestCallHandler> callHandler() default RestCallHandler.class;
+	Class<? extends RestCallHandler> callHandler() default RestCallHandler.Null.class;
 
 	/**
 	 * Children.
@@ -478,7 +478,7 @@ public @interface RestResource {
 	 * 	<li class='jf'>{@link RestContext#REST_infoProvider}
 	 * </ul>
 	 */
-	Class<? extends RestInfoProvider> infoProvider() default RestInfoProvider.class;
+	Class<? extends RestInfoProvider> infoProvider() default RestInfoProvider.Null.class;
 
 	/**
 	 * REST logger.
@@ -491,7 +491,7 @@ public @interface RestResource {
 	 * 	<li class='jf'>{@link RestContext#REST_logger}
 	 * </ul>
 	 */
-	Class<? extends RestLogger> logger() default RestLogger.Normal.class;
+	Class<? extends RestLogger> logger() default RestLogger.Null.class;
 
 	/**
 	 * The maximum allowed input size (in bytes) on HTTP requests.
@@ -731,7 +731,7 @@ public @interface RestResource {
 	 * 	<li class='jf'>{@link RestContext#REST_resourceResolver}
 	 * </ul>
 	 */
-	Class<? extends RestResourceResolver> resourceResolver() default RestResourceResolverSimple.class;
+	Class<? extends RestResourceResolver> resourceResolver() default RestResourceResolver.Null.class;
 
 	/**
 	 * Response handlers.

http://git-wip-us.apache.org/repos/asf/juneau/blob/af355372/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/package.html
----------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/package.html b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/package.html
index 185e8d5..840dbbe 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/package.html
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/package.html
@@ -2699,17 +2699,15 @@
 				{@link org.apache.juneau.rest.RestLogger}
 				<ul>
 					<li class='jm'>
-						{@link org.apache.juneau.rest.RestLogger#getLogger() getLogger()}
-					<li class='jm'>
-						{@link org.apache.juneau.rest.RestLogger#onError(HttpServletRequest,HttpServletResponse,RestException) onError(HttpServletRequest,HttpServletResponse,RestException)}
-						<br>Gets called when an error occurs on a request call.
-						<br>Default implementation logs the error.
-					<li class='jm'>
 						{@link org.apache.juneau.rest.RestLogger#log(Level,String,Object[]) log(Level,String,Object[])}
 					<li class='jm'>
 						{@link org.apache.juneau.rest.RestLogger#log(Level,Throwable,String,Object[]) log(Level,Throwable,String,Object[])}
 					<li class='jm'>
 						{@link org.apache.juneau.rest.RestLogger#logObjects(Level,String,Object[]) logObject(Level,String,Object[])}
+					<li class='jm'>
+						{@link org.apache.juneau.rest.RestLogger#onError(HttpServletRequest,HttpServletResponse,RestException) onError(HttpServletRequest,HttpServletResponse,RestException)}
+						<br>Gets called when an error occurs on a request call.
+						<br>Default implementation logs the error.
 				</ul>
 			</li>
 		</ul>


[2/2] juneau git commit: RestContext refactoring.

Posted by ja...@apache.org.
RestContext refactoring.

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

Branch: refs/heads/master
Commit: af355372e7fbfab62eb7daac87885a9a1e760e43
Parents: 5a36a08
Author: JamesBognar <ja...@apache.org>
Authored: Thu Jan 18 16:43:41 2018 -0500
Committer: JamesBognar <ja...@apache.org>
Committed: Thu Jan 18 16:43:41 2018 -0500

----------------------------------------------------------------------
 juneau-doc/src/main/javadoc/overview.html       |  12 +
 .../apache/juneau/rest/test/ParamsResource.java |  13 +-
 .../org/apache/juneau/rest/test/ParamsTest.java |  10 +-
 .../org/apache/juneau/rest/RestCallHandler.java | 249 +-------
 .../juneau/rest/RestCallHandlerDefault.java     | 343 ++++++++++
 .../org/apache/juneau/rest/RestContext.java     |  87 ++-
 .../apache/juneau/rest/RestContextBuilder.java  |  26 +-
 .../apache/juneau/rest/RestInfoProvider.java    | 513 ++-------------
 .../juneau/rest/RestInfoProviderDefault.java    | 632 +++++++++++++++++++
 .../java/org/apache/juneau/rest/RestLogger.java | 162 +----
 .../apache/juneau/rest/RestLoggerDefault.java   | 225 +++++++
 .../org/apache/juneau/rest/RestLoggerNoOp.java  |  47 ++
 .../apache/juneau/rest/RestParamDefaults.java   |  26 +-
 .../org/apache/juneau/rest/RestRequest.java     |   2 +-
 .../juneau/rest/RestResourceResolver.java       |   9 +
 .../rest/RestResourceResolverDefault.java       |  55 ++
 .../juneau/rest/RestResourceResolverSimple.java |  55 --
 .../juneau/rest/annotation/RestResource.java    |   8 +-
 .../java/org/apache/juneau/rest/package.html    |  10 +-
 19 files changed, 1486 insertions(+), 998 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/juneau/blob/af355372/juneau-doc/src/main/javadoc/overview.html
----------------------------------------------------------------------
diff --git a/juneau-doc/src/main/javadoc/overview.html b/juneau-doc/src/main/javadoc/overview.html
index 613c71b..fee50e3 100644
--- a/juneau-doc/src/main/javadoc/overview.html
+++ b/juneau-doc/src/main/javadoc/overview.html
@@ -9351,6 +9351,17 @@
 						{@link org.apache.juneau.rest.annotation.RestMethod#supportedContentTypes() supportedContentTypes()}
 						for overriding the supported media types inferred from the serializers and parsers.
 				</ul>
+			<li>
+				<code>RestCallHandler</code> split up into {@link org.apache.juneau.rest.RestCallHandler} and 
+				{@link org.apache.juneau.rest.RestCallHandlerDefault}
+			<li>
+				<code>RestInfoProvider</code> split up into {@link org.apache.juneau.rest.RestInfoProvider} and 
+				{@link org.apache.juneau.rest.RestInfoProviderDefault}
+			<li>
+				<code>RestLogger</code> split up into {@link org.apache.juneau.rest.RestLogger}, 
+				{@link org.apache.juneau.rest.RestLoggerDefault} and {@link org.apache.juneau.rest.RestLoggerNoOp}
+			<li>
+				<code>RestResourceResolverSimple</code> renamed to {@link org.apache.juneau.rest.RestResourceResolverDefault}
 		</ul>
 
 		<h6 class='topic'>juneau-rest-client</h6>
@@ -9401,6 +9412,7 @@
 					<li>{@link org.apache.juneau.BeanContextBuilder}
 					<li>{@link org.apache.juneau.rest.RestContext}
 					<li>{@link org.apache.juneau.rest.RestContextBuilder}
+					<li>Swagger DTOs.
 				</ul>		
 		</ul>
 	</div>

http://git-wip-us.apache.org/repos/asf/juneau/blob/af355372/juneau-microservice/juneau-microservice-test/src/main/java/org/apache/juneau/rest/test/ParamsResource.java
----------------------------------------------------------------------
diff --git a/juneau-microservice/juneau-microservice-test/src/main/java/org/apache/juneau/rest/test/ParamsResource.java b/juneau-microservice/juneau-microservice-test/src/main/java/org/apache/juneau/rest/test/ParamsResource.java
index 3e50752..bc546c6 100644
--- a/juneau-microservice/juneau-microservice-test/src/main/java/org/apache/juneau/rest/test/ParamsResource.java
+++ b/juneau-microservice/juneau-microservice-test/src/main/java/org/apache/juneau/rest/test/ParamsResource.java
@@ -17,7 +17,6 @@ import static org.apache.juneau.internal.IOUtils.*;
 
 import java.io.*;
 import java.util.*;
-import java.util.logging.*;
 
 import javax.servlet.*;
 import javax.servlet.http.*;
@@ -28,7 +27,6 @@ import org.apache.juneau.examples.addressbook.*;
 import org.apache.juneau.http.*;
 import org.apache.juneau.httppart.*;
 import org.apache.juneau.ini.*;
-import org.apache.juneau.internal.*;
 import org.apache.juneau.json.*;
 import org.apache.juneau.parser.*;
 import org.apache.juneau.plaintext.*;
@@ -372,16 +370,11 @@ public class ParamsResource extends RestServletDefault {
 		return t.toString();
 	}
 
-	@RestMethod(name=GET, path="/otherObjects/Logger")
-	public boolean testOtherLogger(Logger t) {
+	@RestMethod(name=GET, path="/otherObjects/RestLogger")
+	public boolean testOtherLogger(RestLogger t) {
 		return t != null;
 	}
-
-	@RestMethod(name=GET, path="/otherObjects/JuneauLogger")
-	public boolean testOtherJuneauLogger(JuneauLogger t) {
-		return t != null;
-	}
-
+	
 	@RestMethod(name=GET, path="/otherObjects/RestContext")
 	public boolean testOtherRestContext(RestContext t) {
 		return t != null;

http://git-wip-us.apache.org/repos/asf/juneau/blob/af355372/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/ParamsTest.java
----------------------------------------------------------------------
diff --git a/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/ParamsTest.java b/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/ParamsTest.java
index d655e91..b1da497 100644
--- a/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/ParamsTest.java
+++ b/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/ParamsTest.java
@@ -798,14 +798,8 @@ public class ParamsTest extends RestTestcase {
 	}
 
 	@Test
-	public void testOtherLogger() throws Exception {
-		String r = CLIENT.doGet(URL + "/otherObjects/Logger").getResponseAsString();
-		assertEquals("true", r);
-	}
-
-	@Test
-	public void testOtherJuneauLogger() throws Exception {
-		String r = CLIENT.doGet(URL + "/otherObjects/JuneauLogger").getResponseAsString();
+	public void testOtherRestLogger() throws Exception {
+		String r = CLIENT.doGet(URL + "/otherObjects/RestLogger").getResponseAsString();
 		assertEquals("true", r);
 	}
 

http://git-wip-us.apache.org/repos/asf/juneau/blob/af355372/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestCallHandler.java
----------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestCallHandler.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestCallHandler.java
index 69a5c4f..c7dabc9 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestCallHandler.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestCallHandler.java
@@ -12,10 +12,6 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.rest;
 
-import static java.util.logging.Level.*;
-import static javax.servlet.http.HttpServletResponse.*;
-import static org.apache.juneau.internal.IOUtils.*;
-import static org.apache.juneau.internal.StringUtils.*;
 
 import java.io.*;
 import java.util.*;
@@ -23,319 +19,102 @@ import java.util.*;
 import javax.servlet.*;
 import javax.servlet.http.*;
 
-import org.apache.juneau.rest.annotation.*;
-import org.apache.juneau.rest.vars.*;
-
 /**
  * Class that handles the basic lifecycle of an HTTP REST call.
  * 
- * <p>
- * Subclasses can override these methods to tailor how HTTP REST calls are handled.
- * <br>Subclasses MUST implement a public constructor that takes in a {@link RestContext} object.
- * 
- * <p>
- * RestCallHandlers are associated with servlets/resources in one of the following ways:
- * <ul>
- * 	<li>The {@link RestResource#callHandler @RestResource.callHandler()} annotation.
- * 	<li>The {@link RestContextBuilder#callHandler(Class)} method.
- * </ul>
- * 
  * <h5 class='topic'>Additional Information</h5>
  * <ul>
  * 	<li class='jf'>{@link RestContext#REST_callHandler}
  * </ul>
  */
-public class RestCallHandler {
-
-	private final RestContext context;
-	private final RestLogger logger;
-	private final Map<String,RestCallRouter> restCallRouters;
+public interface RestCallHandler {
 
 	/**
-	 * Constructor.
+	 * Represents no RestCallHandler.
 	 * 
-	 * @param context The resource context.
+	 * <p>
+	 * Used on annotation to indicate that the value should be inherited from the parent class, and
+	 * ultimately {@link RestCallHandlerDefault} if not specified at any level.
 	 */
-	public RestCallHandler(RestContext context) {
-		this.context = context;
-		this.logger = context.getLogger();
-		this.restCallRouters = context.getCallRouters();
-	}
+	public interface Null extends RestCallHandler {}
 
 	/**
 	 * Creates a {@link RestRequest} object based on the specified incoming {@link HttpServletRequest} object.
 	 * 
-	 * <p>
-	 * Subclasses may choose to override this method to provide a specialized request object.
-	 * 
 	 * @param req The request object from the {@link #service(HttpServletRequest, HttpServletResponse)} method.
 	 * @return The wrapped request object.
 	 * @throws ServletException If any errors occur trying to interpret the request.
 	 */
-	protected RestRequest createRequest(HttpServletRequest req) throws ServletException {
-		return new RestRequest(context, req);
-	}
+	public RestRequest createRequest(HttpServletRequest req) throws ServletException;
 
 	/**
 	 * Creates a {@link RestResponse} object based on the specified incoming {@link HttpServletResponse} object
 	 * and the request returned by {@link #createRequest(HttpServletRequest)}.
 	 * 
-	 * <p>
-	 * Subclasses may choose to override this method to provide a specialized response object.
-	 * 
 	 * @param req The request object returned by {@link #createRequest(HttpServletRequest)}.
 	 * @param res The response object from the {@link #service(HttpServletRequest, HttpServletResponse)} method.
 	 * @return The wrapped response object.
 	 * @throws ServletException If any errors occur trying to interpret the request or response.
 	 */
-	protected RestResponse createResponse(RestRequest req, HttpServletResponse res) throws ServletException {
-		return new RestResponse(context, req, res);
-	}
+	public RestResponse createResponse(RestRequest req, HttpServletResponse res) throws ServletException;
 
 	/**
 	 * The main service method.
 	 * 
-	 * <p>
-	 * Subclasses can optionally override this method if they want to tailor the behavior of requests.
-	 * 
 	 * @param r1 The incoming HTTP servlet request object.
 	 * @param r2 The incoming HTTP servlet response object.
 	 * @throws ServletException
 	 * @throws IOException
 	 */
-	protected void service(HttpServletRequest r1, HttpServletResponse r2) throws ServletException, IOException {
-
-		logger.log(FINE, "HTTP: {0} {1}", r1.getMethod(), r1.getRequestURI());
-		long startTime = System.currentTimeMillis();
-
-		try {
-			context.checkForInitException();
-
-			String pathInfo = RestUtils.getPathInfoUndecoded(r1);  // Can't use r1.getPathInfo() because we don't want '%2F' resolved.
-
-			// If this resource has child resources, try to recursively call them.
-			if (pathInfo != null && context.hasChildResources() && (! pathInfo.equals("/"))) {
-				int i = pathInfo.indexOf('/', 1);
-				String pathInfoPart = i == -1 ? pathInfo.substring(1) : pathInfo.substring(1, i);
-				RestContext childResource = context.getChildResource(pathInfoPart);
-				if (childResource != null) {
-					final String pathInfoRemainder = (i == -1 ? null : pathInfo.substring(i));
-					final String servletPath = r1.getServletPath() + "/" + pathInfoPart;
-					final HttpServletRequest childRequest = new HttpServletRequestWrapper(r1) {
-						@Override /* ServletRequest */
-						public String getPathInfo() {
-							return urlDecode(pathInfoRemainder);
-						}
-						@Override /* ServletRequest */
-						public String getServletPath() {
-							return servletPath;
-						}
-					};
-					childResource.getCallHandler().service(childRequest, r2);
-					return;
-				}
-			}
-
-			context.startCall(r1, r2);
-
-			RestRequest req = createRequest(r1);
-			RestResponse res = createResponse(req, r2);
-			String method = req.getMethod();
-			String methodUC = method.toUpperCase(Locale.ENGLISH);
-
-			StreamResource r = null;
-			if (pathInfo != null) {
-				String p = pathInfo.substring(1);
-				if (context.isStaticFile(p))
-					r = context.resolveStaticFile(p);
-				else if (p.equals("favicon.ico"))
-					res.setOutput(null);
-			}
-
-			if (r != null) {
-				res.setStatus(SC_OK);
-				res.setOutput(r);
-			} else {
-				// If the specified method has been defined in a subclass, invoke it.
-				int rc = SC_METHOD_NOT_ALLOWED;
-				if (restCallRouters.containsKey(methodUC)) {
-					rc = restCallRouters.get(methodUC).invoke(pathInfo, req, res);
-				} else if (restCallRouters.containsKey("*")) {
-					rc = restCallRouters.get("*").invoke(pathInfo, req, res);
-				}
-
-				// If not invoked above, see if it's an OPTIONs request
-				if (rc != SC_OK)
-					handleNotFound(rc, req, res);
-			}
-
-			if (res.hasOutput()) {
-				Object output = res.getOutput();
-
-				// Do any class-level transforming.
-				for (RestConverter converter : context.getConverters())
-					output = converter.convert(req, output);
-
-				res.setOutput(output);
-
-				// Now serialize the output if there was any.
-				// Some subclasses may write to the OutputStream or Writer directly.
-				handleResponse(req, res, output);
-			}
-
-			// Make sure our writer in RestResponse gets written.
-			res.flushBuffer();
-
-			r1.setAttribute("ExecTime", System.currentTimeMillis() - startTime);
-
-		} catch (RestException e) {
-			r1.setAttribute("Exception", e);
-			r1.setAttribute("ExecTime", System.currentTimeMillis() - startTime);
-			handleError(r1, r2, e);
-		} catch (Throwable e) {
-			RestException e2 = new RestException(SC_INTERNAL_SERVER_ERROR, e);
-			r1.setAttribute("Exception", e);
-			r1.setAttribute("ExecTime", System.currentTimeMillis() - startTime);
-			handleError(r1, r2, e2);
-		}
-
-		context.finishCall(r1, r2);
-
-		logger.log(FINE, "HTTP: [{0} {1}] finished in {2}ms", r1.getMethod(), r1.getRequestURI(), System.currentTimeMillis()-startTime);
-	}
+	public void service(HttpServletRequest r1, HttpServletResponse r2) throws ServletException, IOException;
 
 	/**
 	 * The main method for serializing POJOs passed in through the {@link RestResponse#setOutput(Object)} method or
 	 * returned by the Java method.
 	 * 
-	 * <p>
-	 * Subclasses may override this method if they wish to modify the way the output is rendered or support other output
-	 * formats.
-	 * 
-	 * <p>
-	 * The default implementation simply iterates through the response handlers on this resource
-	 * looking for the first one whose {@link ResponseHandler#handle(RestRequest, RestResponse, Object)} method returns
-	 * <jk>true</jk>.
-	 * 
 	 * @param req The HTTP request.
 	 * @param res The HTTP response.
 	 * @param output The output to serialize in the response.
 	 * @throws IOException
 	 * @throws RestException
 	 */
-	protected void handleResponse(RestRequest req, RestResponse res, Object output) throws IOException, RestException {
-		// Loop until we find the correct handler for the POJO.
-		for (ResponseHandler h : context.getResponseHandlers())
-			if (h.handle(req, res, output))
-				return;
-		throw new RestException(SC_NOT_IMPLEMENTED, "No response handlers found to process output of type '"+(output == null ? null : output.getClass().getName())+"'");
-	}
+	public void handleResponse(RestRequest req, RestResponse res, Object output) throws IOException, RestException ;
 
 	/**
 	 * Handle the case where a matching method was not found.
 	 * 
-	 * <p>
-	 * Subclasses can override this method to provide a 2nd-chance for specifying a response.
-	 * The default implementation will simply throw an exception with an appropriate message.
-	 * 
 	 * @param rc The HTTP response code.
 	 * @param req The HTTP request.
 	 * @param res The HTTP response.
 	 * @throws Exception
 	 */
-	protected void handleNotFound(int rc, RestRequest req, RestResponse res) throws Exception {
-		String pathInfo = req.getPathInfo();
-		String methodUC = req.getMethod();
-		String onPath = pathInfo == null ? " on no pathInfo"  : String.format(" on path '%s'", pathInfo);
-		if (rc == SC_NOT_FOUND)
-			throw new RestException(rc, "Method ''{0}'' not found on resource with matching pattern{1}.", methodUC, onPath);
-		else if (rc == SC_PRECONDITION_FAILED)
-			throw new RestException(rc, "Method ''{0}'' not found on resource{1} with matching matcher.", methodUC, onPath);
-		else if (rc == SC_METHOD_NOT_ALLOWED)
-			throw new RestException(rc, "Method ''{0}'' not found on resource.", methodUC);
-		else
-			throw new ServletException("Invalid method response: " + rc);
-	}
+	public void handleNotFound(int rc, RestRequest req, RestResponse res) throws Exception;
 
 	/**
 	 * Method for handling response errors.
 	 * 
-	 * <p>
-	 * The default implementation logs the error and calls
-	 * {@link #renderError(HttpServletRequest,HttpServletResponse,RestException)}.
-	 * 
-	 * <p>
-	 * Subclasses can override this method to provide their own custom error response handling.
-	 * 
 	 * @param req The servlet request.
 	 * @param res The servlet response.
 	 * @param e The exception that occurred.
 	 * @throws IOException Can be thrown if a problem occurred trying to write to the output stream.
 	 */
-	protected synchronized void handleError(HttpServletRequest req, HttpServletResponse res, RestException e) throws IOException {
-		e.setOccurrence(context == null ? 0 : context.getStackTraceOccurrence(e));
-		logger.onError(req, res, e);
-		renderError(req, res, e);
-	}
+	public void handleError(HttpServletRequest req, HttpServletResponse res, RestException e) throws IOException;
 
 	/**
 	 * Method for rendering response errors.
 	 * 
-	 * <p>
-	 * The default implementation renders a plain text English message, optionally with a stack trace if
-	 * {@link RestResource#renderResponseStackTraces() @RestResource.renderResponseStackTraces()} is enabled.
-	 * 
-	 * <p>
-	 * Subclasses can override this method to provide their own custom error response handling.
-	 * 
 	 * @param req The servlet request.
 	 * @param res The servlet response.
 	 * @param e The exception that occurred.
 	 * @throws IOException Can be thrown if a problem occurred trying to write to the output stream.
 	 */
-	protected void renderError(HttpServletRequest req, HttpServletResponse res, RestException e) throws IOException {
-
-		int status = e.getStatus();
-		res.setStatus(status);
-		res.setContentType("text/plain");
-		res.setHeader("Content-Encoding", "identity");
-
-		Throwable t = e.getRootCause();
-		if (t != null) {
-			res.setHeader("Exception-Name", t.getClass().getName());
-			res.setHeader("Exception-Message", t.getMessage());
-		}
-
-		PrintWriter w = null;
-		try {
-			w = res.getWriter();
-		} catch (IllegalStateException e2) {
-			w = new PrintWriter(new OutputStreamWriter(res.getOutputStream(), UTF8));
-		}
-		
-		try (PrintWriter w2 = w) {
-			String httpMessage = RestUtils.getHttpResponseText(status);
-			if (httpMessage != null)
-				w2.append("HTTP ").append(String.valueOf(status)).append(": ").append(httpMessage).append("\n\n");
-			if (context != null && context.isRenderResponseStackTraces())
-				e.printStackTrace(w2);
-			else
-				w2.append(e.getFullStackMessage(true));
-		}
-	}
+	public void renderError(HttpServletRequest req, HttpServletResponse res, RestException e) throws IOException;
 
 	/**
 	 * Returns the session objects for the specified request.
 	 * 
-	 * <p>
-	 * The default implementation simply returns a single map containing <code>{'req':req}</code>.
-	 * 
 	 * @param req The REST request.
 	 * @return The session objects for that request.
 	 */
-	protected Map<String,Object> getSessionObjects(RestRequest req) {
-		Map<String,Object> m = new HashMap<>();
-		m.put(RequestVar.SESSION_req, req);
-		return m;
-	}
+	public Map<String,Object> getSessionObjects(RestRequest req);
 }

http://git-wip-us.apache.org/repos/asf/juneau/blob/af355372/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestCallHandlerDefault.java
----------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestCallHandlerDefault.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestCallHandlerDefault.java
new file mode 100644
index 0000000..fa5408e
--- /dev/null
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestCallHandlerDefault.java
@@ -0,0 +1,343 @@
+// ***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                                                              *
+// *                                                                                                                         *
+// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
+// *                                                                                                                         *
+// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the License.                                              *
+// ***************************************************************************************************************************
+package org.apache.juneau.rest;
+
+import static java.util.logging.Level.*;
+import static javax.servlet.http.HttpServletResponse.*;
+import static org.apache.juneau.internal.IOUtils.*;
+import static org.apache.juneau.internal.StringUtils.*;
+
+import java.io.*;
+import java.util.*;
+
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.rest.vars.*;
+
+/**
+ * Default implementation of {@link RestCallHandler}.
+ * 
+ * <p>
+ * Subclasses can override these methods to tailor how HTTP REST calls are handled.
+ * <br>Subclasses MUST implement a public constructor that takes in a {@link RestContext} object.
+ * 
+ * 
+ * <h5 class='topic'>Additional Information</h5>
+ * <ul>
+ * 	<li class='jf'>{@link RestContext#REST_callHandler}
+ * </ul>
+ */
+public class RestCallHandlerDefault implements RestCallHandler {
+
+	private final RestContext context;
+	private final RestLogger logger;
+	private final Map<String,RestCallRouter> restCallRouters;
+
+	/**
+	 * Constructor.
+	 * 
+	 * @param context The resource context.
+	 */
+	public RestCallHandlerDefault(RestContext context) {
+		this.context = context;
+		this.logger = context.getLogger();
+		this.restCallRouters = context.getCallRouters();
+	}
+
+	/**
+	 * Creates a {@link RestRequest} object based on the specified incoming {@link HttpServletRequest} object.
+	 * 
+	 * <p>
+	 * Subclasses may choose to override this method to provide a specialized request object.
+	 * 
+	 * @param req The request object from the {@link #service(HttpServletRequest, HttpServletResponse)} method.
+	 * @return The wrapped request object.
+	 * @throws ServletException If any errors occur trying to interpret the request.
+	 */
+	@Override /* RestCallHandler */
+	public RestRequest createRequest(HttpServletRequest req) throws ServletException {
+		return new RestRequest(context, req);
+	}
+
+	/**
+	 * Creates a {@link RestResponse} object based on the specified incoming {@link HttpServletResponse} object
+	 * and the request returned by {@link #createRequest(HttpServletRequest)}.
+	 * 
+	 * <p>
+	 * Subclasses may choose to override this method to provide a specialized response object.
+	 * 
+	 * @param req The request object returned by {@link #createRequest(HttpServletRequest)}.
+	 * @param res The response object from the {@link #service(HttpServletRequest, HttpServletResponse)} method.
+	 * @return The wrapped response object.
+	 * @throws ServletException If any errors occur trying to interpret the request or response.
+	 */
+	@Override /* RestCallHandler */
+	public RestResponse createResponse(RestRequest req, HttpServletResponse res) throws ServletException {
+		return new RestResponse(context, req, res);
+	}
+
+	/**
+	 * The main service method.
+	 * 
+	 * <p>
+	 * Subclasses can optionally override this method if they want to tailor the behavior of requests.
+	 * 
+	 * @param r1 The incoming HTTP servlet request object.
+	 * @param r2 The incoming HTTP servlet response object.
+	 * @throws ServletException
+	 * @throws IOException
+	 */
+	@Override /* RestCallHandler */
+	public void service(HttpServletRequest r1, HttpServletResponse r2) throws ServletException, IOException {
+
+		logger.log(FINE, "HTTP: {0} {1}", r1.getMethod(), r1.getRequestURI());
+		long startTime = System.currentTimeMillis();
+
+		try {
+			context.checkForInitException();
+
+			String pathInfo = RestUtils.getPathInfoUndecoded(r1);  // Can't use r1.getPathInfo() because we don't want '%2F' resolved.
+
+			// If this resource has child resources, try to recursively call them.
+			if (pathInfo != null && context.hasChildResources() && (! pathInfo.equals("/"))) {
+				int i = pathInfo.indexOf('/', 1);
+				String pathInfoPart = i == -1 ? pathInfo.substring(1) : pathInfo.substring(1, i);
+				RestContext childResource = context.getChildResource(pathInfoPart);
+				if (childResource != null) {
+					final String pathInfoRemainder = (i == -1 ? null : pathInfo.substring(i));
+					final String servletPath = r1.getServletPath() + "/" + pathInfoPart;
+					final HttpServletRequest childRequest = new HttpServletRequestWrapper(r1) {
+						@Override /* ServletRequest */
+						public String getPathInfo() {
+							return urlDecode(pathInfoRemainder);
+						}
+						@Override /* ServletRequest */
+						public String getServletPath() {
+							return servletPath;
+						}
+					};
+					childResource.getCallHandler().service(childRequest, r2);
+					return;
+				}
+			}
+
+			context.startCall(r1, r2);
+
+			RestRequest req = createRequest(r1);
+			RestResponse res = createResponse(req, r2);
+			String method = req.getMethod();
+			String methodUC = method.toUpperCase(Locale.ENGLISH);
+
+			StreamResource r = null;
+			if (pathInfo != null) {
+				String p = pathInfo.substring(1);
+				if (context.isStaticFile(p))
+					r = context.resolveStaticFile(p);
+				else if (p.equals("favicon.ico"))
+					res.setOutput(null);
+			}
+
+			if (r != null) {
+				res.setStatus(SC_OK);
+				res.setOutput(r);
+			} else {
+				// If the specified method has been defined in a subclass, invoke it.
+				int rc = SC_METHOD_NOT_ALLOWED;
+				if (restCallRouters.containsKey(methodUC)) {
+					rc = restCallRouters.get(methodUC).invoke(pathInfo, req, res);
+				} else if (restCallRouters.containsKey("*")) {
+					rc = restCallRouters.get("*").invoke(pathInfo, req, res);
+				}
+
+				// If not invoked above, see if it's an OPTIONs request
+				if (rc != SC_OK)
+					handleNotFound(rc, req, res);
+			}
+
+			if (res.hasOutput()) {
+				Object output = res.getOutput();
+
+				// Do any class-level transforming.
+				for (RestConverter converter : context.getConverters())
+					output = converter.convert(req, output);
+
+				res.setOutput(output);
+
+				// Now serialize the output if there was any.
+				// Some subclasses may write to the OutputStream or Writer directly.
+				handleResponse(req, res, output);
+			}
+
+			// Make sure our writer in RestResponse gets written.
+			res.flushBuffer();
+
+			r1.setAttribute("ExecTime", System.currentTimeMillis() - startTime);
+
+		} catch (RestException e) {
+			r1.setAttribute("Exception", e);
+			r1.setAttribute("ExecTime", System.currentTimeMillis() - startTime);
+			handleError(r1, r2, e);
+		} catch (Throwable e) {
+			RestException e2 = new RestException(SC_INTERNAL_SERVER_ERROR, e);
+			r1.setAttribute("Exception", e);
+			r1.setAttribute("ExecTime", System.currentTimeMillis() - startTime);
+			handleError(r1, r2, e2);
+		}
+
+		context.finishCall(r1, r2);
+
+		logger.log(FINE, "HTTP: [{0} {1}] finished in {2}ms", r1.getMethod(), r1.getRequestURI(), System.currentTimeMillis()-startTime);
+	}
+
+	/**
+	 * The main method for serializing POJOs passed in through the {@link RestResponse#setOutput(Object)} method or
+	 * returned by the Java method.
+	 * 
+	 * <p>
+	 * Subclasses may override this method if they wish to modify the way the output is rendered or support other output
+	 * formats.
+	 * 
+	 * <p>
+	 * The default implementation simply iterates through the response handlers on this resource
+	 * looking for the first one whose {@link ResponseHandler#handle(RestRequest, RestResponse, Object)} method returns
+	 * <jk>true</jk>.
+	 * 
+	 * @param req The HTTP request.
+	 * @param res The HTTP response.
+	 * @param output The output to serialize in the response.
+	 * @throws IOException
+	 * @throws RestException
+	 */
+	@Override /* RestCallHandler */
+	public void handleResponse(RestRequest req, RestResponse res, Object output) throws IOException, RestException {
+		// Loop until we find the correct handler for the POJO.
+		for (ResponseHandler h : context.getResponseHandlers())
+			if (h.handle(req, res, output))
+				return;
+		throw new RestException(SC_NOT_IMPLEMENTED, "No response handlers found to process output of type '"+(output == null ? null : output.getClass().getName())+"'");
+	}
+
+	/**
+	 * Handle the case where a matching method was not found.
+	 * 
+	 * <p>
+	 * Subclasses can override this method to provide a 2nd-chance for specifying a response.
+	 * The default implementation will simply throw an exception with an appropriate message.
+	 * 
+	 * @param rc The HTTP response code.
+	 * @param req The HTTP request.
+	 * @param res The HTTP response.
+	 * @throws Exception
+	 */
+	@Override /* RestCallHandler */
+	public void handleNotFound(int rc, RestRequest req, RestResponse res) throws Exception {
+		String pathInfo = req.getPathInfo();
+		String methodUC = req.getMethod();
+		String onPath = pathInfo == null ? " on no pathInfo"  : String.format(" on path '%s'", pathInfo);
+		if (rc == SC_NOT_FOUND)
+			throw new RestException(rc, "Method ''{0}'' not found on resource with matching pattern{1}.", methodUC, onPath);
+		else if (rc == SC_PRECONDITION_FAILED)
+			throw new RestException(rc, "Method ''{0}'' not found on resource{1} with matching matcher.", methodUC, onPath);
+		else if (rc == SC_METHOD_NOT_ALLOWED)
+			throw new RestException(rc, "Method ''{0}'' not found on resource.", methodUC);
+		else
+			throw new ServletException("Invalid method response: " + rc);
+	}
+
+	/**
+	 * Method for handling response errors.
+	 * 
+	 * <p>
+	 * The default implementation logs the error and calls
+	 * {@link #renderError(HttpServletRequest,HttpServletResponse,RestException)}.
+	 * 
+	 * <p>
+	 * Subclasses can override this method to provide their own custom error response handling.
+	 * 
+	 * @param req The servlet request.
+	 * @param res The servlet response.
+	 * @param e The exception that occurred.
+	 * @throws IOException Can be thrown if a problem occurred trying to write to the output stream.
+	 */
+	@Override /* RestCallHandler */
+	public synchronized void handleError(HttpServletRequest req, HttpServletResponse res, RestException e) throws IOException {
+		e.setOccurrence(context == null ? 0 : context.getStackTraceOccurrence(e));
+		logger.onError(req, res, e);
+		renderError(req, res, e);
+	}
+
+	/**
+	 * Method for rendering response errors.
+	 * 
+	 * <p>
+	 * The default implementation renders a plain text English message, optionally with a stack trace if
+	 * {@link RestResource#renderResponseStackTraces() @RestResource.renderResponseStackTraces()} is enabled.
+	 * 
+	 * <p>
+	 * Subclasses can override this method to provide their own custom error response handling.
+	 * 
+	 * @param req The servlet request.
+	 * @param res The servlet response.
+	 * @param e The exception that occurred.
+	 * @throws IOException Can be thrown if a problem occurred trying to write to the output stream.
+	 */
+	@Override /* RestCallHandler */
+	public void renderError(HttpServletRequest req, HttpServletResponse res, RestException e) throws IOException {
+
+		int status = e.getStatus();
+		res.setStatus(status);
+		res.setContentType("text/plain");
+		res.setHeader("Content-Encoding", "identity");
+
+		Throwable t = e.getRootCause();
+		if (t != null) {
+			res.setHeader("Exception-Name", t.getClass().getName());
+			res.setHeader("Exception-Message", t.getMessage());
+		}
+
+		PrintWriter w = null;
+		try {
+			w = res.getWriter();
+		} catch (IllegalStateException e2) {
+			w = new PrintWriter(new OutputStreamWriter(res.getOutputStream(), UTF8));
+		}
+		
+		try (PrintWriter w2 = w) {
+			String httpMessage = RestUtils.getHttpResponseText(status);
+			if (httpMessage != null)
+				w2.append("HTTP ").append(String.valueOf(status)).append(": ").append(httpMessage).append("\n\n");
+			if (context != null && context.isRenderResponseStackTraces())
+				e.printStackTrace(w2);
+			else
+				w2.append(e.getFullStackMessage(true));
+		}
+	}
+
+	/**
+	 * Returns the session objects for the specified request.
+	 * 
+	 * <p>
+	 * The default implementation simply returns a single map containing <code>{'req':req}</code>.
+	 * 
+	 * @param req The REST request.
+	 * @return The session objects for that request.
+	 */
+	@Override /* RestCallHandler */
+	public Map<String,Object> getSessionObjects(RestRequest req) {
+		Map<String,Object> m = new HashMap<>();
+		m.put(RequestVar.SESSION_req, req);
+		return m;
+	}
+}

http://git-wip-us.apache.org/repos/asf/juneau/blob/af355372/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
----------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
index 8ca7b99..af7ac81 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
@@ -269,7 +269,7 @@ public final class RestContext extends BeanContext {
 	 * <ul>
 	 * 	<li><b>Name:</b>  <js>"RestContext.callHandler.o"</js>
 	 * 	<li><b>Data type:</b>  <code>Class&lt;? <jk>extends</jk> {@link RestCallHandler}&gt; | {@link RestCallHandler}</code>
-	 * 	<li><b>Default:</b>  {@link RestCallHandler}
+	 * 	<li><b>Default:</b>  {@link RestCallHandlerDefault}
 	 * 	<li><b>Session-overridable:</b>  <jk>false</jk>
 	 * 	<li><b>Annotations:</b> 
 	 * 		<ul>
@@ -290,7 +290,7 @@ public final class RestContext extends BeanContext {
 	 * <h5 class='section'>Example:</h5>
 	 * <p class='bcode'>
 	 * 	<jc>// Our customized call handler.</jc>
-	 * 	<jk>public class</jk> MyRestCallHandler <jk>extends</jk> RestCallHandler {
+	 * 	<jk>public class</jk> MyRestCallHandler <jk>extends</jk> RestCallHandlerDefault {
 	 * 		
 	 * 		<jc>// Must provide this constructor!</jc>
 	 * 		<jk>public</jk> MyRestCallHandler(RestContext context) {
@@ -298,19 +298,19 @@ public final class RestContext extends BeanContext {
 	 * 		}
 	 * 
 	 * 		<ja>@Override</ja>
-	 * 		<jk>protected</jk> RestRequest createRequest(HttpServletRequest req) <jk>throws</jk> ServletException {
+	 * 		<jk>public</jk> RestRequest createRequest(HttpServletRequest req) <jk>throws</jk> ServletException {
 	 * 			<jc>// Low-level handling of requests.</jc>
 	 * 			...
 	 * 		}
 	 * 		
 	 * 		<ja>@Override</ja>
-	 * 		<jk>protected void</jk> handleResponse(RestRequest req, RestResponse res, Object output) <jk>throws</jk> IOException, RestException {
+	 * 		<jk>public void</jk> handleResponse(RestRequest req, RestResponse res, Object output) <jk>throws</jk> IOException, RestException {
 	 * 			<jc>// Low-level handling of responses.</jc>
 	 * 			...
 	 * 		}
 	 * 
 	 * 		<ja>@Override</ja>
-	 * 		<jk>protected void</jk> handleNotFound(int rc, RestRequest req, RestResponse res) <jk>throws</jk> Exception {
+	 * 		<jk>public void</jk> handleNotFound(int rc, RestRequest req, RestResponse res) <jk>throws</jk> Exception {
 	 * 			<jc>// Low-level handling of various error conditions.</jc>
 	 * 			...
 	 * 		}
@@ -410,7 +410,7 @@ public final class RestContext extends BeanContext {
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
 	 * 	<li>When defined as classes, instances are resolved using the registered {@link #REST_resourceResolver} which
-	 * 		by default is {@link RestResourceResolverSimple} which requires the class have one of the following
+	 * 		by default is {@link RestResourceResolverDefault} which requires the class have one of the following
 	 * 		constructors:
 	 * 		<ul>
 	 * 			<li><code><jk>public</jk> T(RestContextBuilder)</code>
@@ -1145,7 +1145,7 @@ public final class RestContext extends BeanContext {
 	 * <ul>
 	 * 	<li><b>Name:</b>  <js>"RestContext.infoProvider.o"</js>
 	 * 	<li><b>Data type:</b>  <code>Class&lt;? <jk>extends</jk> {@link RestInfoProvider}&gt; | {@link RestInfoProvider}</code>
-	 * 	<li><b>Default:</b>  {@link RestInfoProvider}
+	 * 	<li><b>Default:</b>  {@link RestInfoProviderDefault}
 	 * 	<li><b>Session-overridable:</b>  <jk>false</jk>
 	 * 	<li><b>Annotations:</b> 
 	 * 		<ul>
@@ -1162,8 +1162,58 @@ public final class RestContext extends BeanContext {
 	 * <p>
 	 * Class used to retrieve title/description/swagger information about a resource.
 	 * 
-	 * <p>
-	 * Subclasses can be used to customize the documentation on a resource.
+	 * <h5 class='section'>Example:</h5>
+	 * <p class='bcode'>
+	 * 	<jc>// Our customized info provider.</jc>
+	 * 	<jk>public class</jk> MyRestInfoProvider <jk>extends</jk> RestInfoProviderDefault {
+	 * 		
+	 * 		<jc>// Must provide this constructor!</jc>
+	 * 		<jk>public</jk> MyRestInfoProvider(RestContext context) {
+	 * 			<jk>super</jk>(context);
+	 * 		}
+	 * 
+	 * 		<ja>@Override</ja>
+	 * 		<jk>public</jk> Swagger getSwaggerFromFile(RestRequest req) <jk>throws</jk> RestException {
+	 * 			<jc>// Provide our own method of retrieving swagger from file system.</jc>
+	 * 		}
+	 * 
+	 * 		<ja>@Override</ja>
+	 * 		<jk>public</jk> Swagger getSwagger(RestRequest req) <jk>throws</jk> RestException {
+	 * 			Swagger s = super.getSwagger(req);
+	 * 			<jc>// Made inline modifications to generated swagger.</jc>
+	 * 			<jk>return</jk> s;
+	 * 		}
+	 * 
+	 * 		<ja>@Override</ja>
+	 * 		<jk>public</jk> String getSiteName(RestRequest req) {
+	 * 			<jc>// Override the site name.</jc>
+	 * 		}
+	 * 	}
+	 * 
+	 * 	<jc>// Registered via annotation resolving to a config file setting with default value.</jc>
+	 * 	<ja>@RestResource</ja>(infoProvider=MyRestInfoProvider.<jk>class</jk>)
+	 * 	<jk>public class</jk> MyResource {...}
+	 * 
+	 * 	<jc>// Registered via builder passed in through resource constructor.</jc>
+	 * 	<jk>public class</jk> MyResource {
+	 * 		<jk>public</jk> MyResource(RestContextBuilder builder) <jk>throws</jk> Exception {
+	 * 			
+	 * 			<jc>// Using method on builder.</jc>
+	 * 			builder.infoProvider(MyRestInfoProvider.<jk>class</jk>);
+	 * 
+	 * 			<jc>// Same, but using property.</jc>
+	 * 			builder.set(<jsf>REST_infoProvider</jsf>, MyRestInfoProvider.<jk>class</jk>);
+	 * 		}
+	 * 	}
+	 * 
+	 * 	<jc>// Registered via builder passed in through init method.</jc>
+	 * 	<jk>public class</jk> MyResource {
+	 * 		<ja>@RestHook</ja>(<jsf>INIT</jsf>)
+	 * 		<jk>public void</jk> init(RestContextBuilder builder) <jk>throws</jk> Exception {
+	 * 			builder.infoProvider(MyRestInfoProvider.<jk>class</jk>);
+	 * 		}
+	 * 	}
+	 * </p>
 	 */
 	public static final String REST_infoProvider = PREFIX + "infoProvider.o";
 	
@@ -1174,7 +1224,7 @@ public final class RestContext extends BeanContext {
 	 * <ul>
 	 * 	<li><b>Name:</b>  <js>"RestContext.logger.o"</js>
 	 * 	<li><b>Data type:</b>  <code>Class&lt;? <jk>extends</jk> RestLogger&gt; | RestLogger</code>
-	 * 	<li><b>Default:</b>  {@link RestLogger.Normal}
+	 * 	<li><b>Default:</b>  {@link RestLoggerDefault}
 	 * 	<li><b>Session-overridable:</b>  <jk>false</jk>
 	 * 	<li><b>Annotations:</b> 
 	 * 		<ul>
@@ -1195,7 +1245,7 @@ public final class RestContext extends BeanContext {
 	 * <h5 class='section'>Notes:</h5>
 	 * <ul class='spaced-list'>
 	 * 	<li>Property:  {@link RestContext#REST_logger}
-	 * 	<li>The {@link RestLogger.Normal} logger can be used to provide basic error logging to the Java logger.
+	 * 	<li>The {@link RestLoggerDefault} logger can be used to provide basic error logging to the Java logger.
 	 * </ul>
 	 */
 	public static final String REST_logger = PREFIX + "logger.o";
@@ -1554,7 +1604,7 @@ public final class RestContext extends BeanContext {
 	 * <ul>
 	 * 	<li><b>Name:</b>  <js>"RestContext.resourceResolver.o"</js>
 	 * 	<li><b>Data type:</b>  <code>Class&lt;? <jk>extends</jk> RestResourceResolver&gt; | RestResourceResolver</code>
-	 * 	<li><b>Default:</b>  {@link RestResourceResolverSimple}
+	 * 	<li><b>Default:</b>  {@link RestResourceResolverDefault}
 	 * 	<li><b>Session-overridable:</b>  <jk>false</jk>
 	 * 	<li><b>Annotations:</b> 
 	 * 		<ul>
@@ -2067,7 +2117,7 @@ public final class RestContext extends BeanContext {
 			defaultResponseHeaders = getMapProperty(REST_defaultResponseHeaders, Object.class);
 			staticFileResponseHeaders = getMapProperty(REST_staticFileResponseHeaders, Object.class);	
 			
-			logger = getInstanceProperty(REST_logger, resource, RestLogger.class, RestLogger.NoOp.class, true, ps);
+			logger = getInstanceProperty(REST_logger, resource, RestLogger.class, RestLoggerNoOp.class, true, ps);
 
 			varResolver = builder.varResolverBuilder
 				.vars(
@@ -2325,7 +2375,7 @@ public final class RestContext extends BeanContext {
 			this.callRouters = Collections.unmodifiableMap(_callRouters);
 
 			// Initialize our child resources.
-			resourceResolver = getInstanceProperty(REST_resourceResolver, resource, RestResourceResolver.class, parentContext == null ? RestResourceResolverSimple.class : parentContext.resourceResolver, true, this, ps);
+			resourceResolver = getInstanceProperty(REST_resourceResolver, resource, RestResourceResolver.class, parentContext == null ? RestResourceResolverDefault.class : parentContext.resourceResolver, true, this, ps);
 			for (Object o : getArrayProperty(REST_children, Object.class)) {
 				String path = null;
 				Object r = null;
@@ -2365,8 +2415,8 @@ public final class RestContext extends BeanContext {
 				childResources.put(path, rc2);
 			}
 
-			callHandler = getInstanceProperty(REST_callHandler, resource, RestCallHandler.class, RestCallHandler.class, true, this, ps);
-			infoProvider = getInstanceProperty(REST_infoProvider, resource, RestInfoProvider.class, RestInfoProvider.class, true, this, ps);
+			callHandler = getInstanceProperty(REST_callHandler, resource, RestCallHandler.class, RestCallHandlerDefault.class, true, this, ps);
+			infoProvider = getInstanceProperty(REST_infoProvider, resource, RestInfoProvider.class, RestInfoProviderDefault.class, true, this, ps);
 
 		} catch (RestException e) {
 			_initException = e;
@@ -2805,8 +2855,9 @@ public final class RestContext extends BeanContext {
 	 * <p>
 	 * The logger for a resource is defined via one of the following:
 	 * <ul>
-	 * 	<li>{@link RestResource#logger() @RestResource.logger()} annotation.
-	 * 	<li>{@link RestContextBuilder#logger(Class)}/{@link RestContextBuilder#logger(RestLogger)} methods.
+	 * 	<li class='ja'>{@link RestResource#logger() @RestResource.logger()}\
+	 * 	<li class='jm'>{@link RestContextBuilder#logger(Class)}
+	 * 	<li class='jm'>{@link RestContextBuilder#logger(RestLogger)}
 	 * </ul>
 	 * 
 	 * @return The logger to use for this resource.  Never <jk>null</jk>.

http://git-wip-us.apache.org/repos/asf/juneau/blob/af355372/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java
----------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java
index 091dcc1..96f421a 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java
@@ -130,7 +130,7 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 		this.resourceClass = resourceClass;
 		this.parentContext = parentContext;
 		
-		logger(RestLogger.Normal.class);
+		logger(RestLoggerDefault.class);
 		staticFileResponseHeader("Cache-Control", "max-age=86400, public");
 		encoders(IdentityEncoder.INSTANCE);
 
@@ -220,13 +220,13 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 					path(vr.resolve(r.path()));
 				if (! r.clientVersionHeader().isEmpty())
 					clientVersionHeader(vr.resolve(r.clientVersionHeader()));
-				if (r.resourceResolver() != RestResourceResolver.class)
+				if (r.resourceResolver() != RestResourceResolver.Null.class)
 					resourceResolver(r.resourceResolver());
-				if (r.logger() != RestLogger.Normal.class)
+				if (r.logger() != RestLogger.Null.class)
 					logger(r.logger());
-				if (r.callHandler() != RestCallHandler.class)
+				if (r.callHandler() != RestCallHandler.Null.class)
 					callHandler(r.callHandler());
-				if (r.infoProvider() != RestInfoProvider.class)
+				if (r.infoProvider() != RestInfoProvider.Null.class)
 					infoProvider(r.infoProvider());
 				if (! r.allowHeaderParams().isEmpty())
 					allowHeaderParams(Boolean.valueOf(vr.resolve(r.allowHeaderParams())));
@@ -598,7 +598,7 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 * 
 	 * @param value 
 	 * 	The new value for this setting.
-	 * 	<br>The default is {@link RestCallHandler}.
+	 * 	<br>The default is {@link RestCallHandlerDefault}.
 	 * @return This object (for method chaining).
 	 */
 	public RestContextBuilder callHandler(Class<? extends RestCallHandler> value) {
@@ -618,7 +618,7 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 * 
 	 * @param value 
 	 * 	The new value for this setting.
-	 * 	<br>The default is {@link RestCallHandler}.
+	 * 	<br>The default is {@link RestCallHandlerDefault}.
 	 * @return This object (for method chaining).
 	 */
 	public RestContextBuilder callHandler(RestCallHandler value) {
@@ -1024,7 +1024,7 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 * 
 	 * @param value 
 	 * 	The new value for this setting.
-	 * 	<br>The default is {@link RestInfoProvider}.
+	 * 	<br>The default is {@link RestInfoProviderDefault}.
 	 * @return This object (for method chaining).
 	 */
 	public RestContextBuilder infoProvider(Class<? extends RestInfoProvider> value) {
@@ -1044,7 +1044,7 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 * 
 	 * @param value 
 	 * 	The new value for this setting.
-	 * 	<br>The default is {@link RestInfoProvider}.
+	 * 	<br>The default is {@link RestInfoProviderDefault}.
 	 * @return This object (for method chaining).
 	 */
 	public RestContextBuilder infoProvider(RestInfoProvider value) {
@@ -1064,7 +1064,7 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 * 
 	 * @param value 
 	 * 	The new value for this setting.  
-	 * 	<br>The default is {@link RestLogger.Normal}.
+	 * 	<br>The default is {@link RestLoggerDefault}.
 	 * 	<br>Can be <jk>null</jk> to disable logging.
 	 * @return This object (for method chaining).
 	 */
@@ -1085,7 +1085,7 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 * 
 	 * @param value 
 	 * 	The new value for this setting.  
-	 * 	<br>The default is {@link RestLogger.Normal}.
+	 * 	<br>The default is {@link RestLoggerDefault}.
 	 * 	<br>Can be <jk>null</jk> to disable logging.
 	 * @return This object (for method chaining).
 	 */
@@ -1461,7 +1461,7 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 * 
 	 * @param value 
 	 * 	The new value for this setting.
-	 * 	<br>The default is {@link RestResourceResolverSimple}.
+	 * 	<br>The default is {@link RestResourceResolverDefault}.
 	 * @return This object (for method chaining).
 	 */
 	public RestContextBuilder resourceResolver(Class<? extends RestResourceResolver> value) {
@@ -1481,7 +1481,7 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	 * 
 	 * @param value 
 	 * 	The new value for this setting.
-	 * 	<br>The default is {@link RestResourceResolverSimple}.
+	 * 	<br>The default is {@link RestResourceResolverDefault}.
 	 * @return This object (for method chaining).
 	 */
 	public RestContextBuilder resourceResolver(RestResourceResolver value) {

http://git-wip-us.apache.org/repos/asf/juneau/blob/af355372/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestInfoProvider.java
----------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestInfoProvider.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestInfoProvider.java
index d1f14a1..b817fb4 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestInfoProvider.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestInfoProvider.java
@@ -12,606 +12,165 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.rest;
 
-import static javax.servlet.http.HttpServletResponse.*;
-import static org.apache.juneau.dto.swagger.SwaggerBuilder.*;
-import static org.apache.juneau.internal.ReflectionUtils.*;
-
 import java.util.*;
-import java.util.concurrent.*;
 
 import org.apache.juneau.dto.swagger.*;
-import org.apache.juneau.http.*;
-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:
+ * <h5 class='topic'>Additional Information</h5>
  * <ul>
- * 	<li>The {@link RestResource#infoProvider @RestResource.infoProvider()} annotation.
- * 	<li>The {@link RestContextBuilder#infoProvider(Class)}/{@link RestContextBuilder#infoProvider(RestInfoProvider)} methods.
+ * 	<li class='jf'>{@link RestContext#REST_infoProvider}
  * </ul>
  */
-public class RestInfoProvider {
-
-	private final RestContext context;
-	private final String
-		siteName,
-		title,
-		description,
-		termsOfService,
-		contact,
-		license,
-		version,
-		tags,
-		externalDocs;
-	private final ConcurrentHashMap<Locale,Swagger> swaggers = new ConcurrentHashMap<>();
-
+public interface RestInfoProvider {
+	
 	/**
-	 * Constructor.
+	 * Represents no RestInfoProvider.
 	 * 
-	 * @param context The resource context.
+	 * <p>
+	 * Used on annotation to indicate that the value should be inherited from the parent class, and
+	 * ultimately {@link RestInfoProviderDefault} if not specified at any level.
 	 */
-	public RestInfoProvider(RestContext context) {
-		this.context = context;
-
-		Builder b = new Builder(context);
-		this.siteName = b.siteName;
-		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 final class Builder {
-		String
-			siteName,
-			title,
-			description,
-			termsOfService,
-			contact,
-			license,
-			version,
-			tags,
-			externalDocs;
-
-		Builder(RestContext context) {
-
-			LinkedHashMap<Class<?>,RestResource> restResourceAnnotationsParentFirst = findAnnotationsMapParentFirst(RestResource.class, context.getResource().getClass());
-
-			for (RestResource r : restResourceAnnotationsParentFirst.values()) {
-				if (! r.siteName().isEmpty())
-					siteName = r.siteName();
-				if (! r.title().isEmpty())
-					title = r.title();
-				if (! r.description().isEmpty())
-					description = r.description();
-				ResourceSwagger sr = r.swagger();
-				if (! sr.termsOfService().isEmpty())
-					termsOfService = sr.termsOfService();
-				if (! sr.contact().isEmpty())
-					contact = sr.contact();
-				if (! sr.license().isEmpty())
-					license = sr.license();
-				if (! sr.version().isEmpty())
-					version = sr.version();
-				if (! sr.tags().isEmpty())
-					tags = sr.tags();
-				if (! sr.externalDocs().isEmpty())
-					externalDocs = sr.externalDocs();
-			}
-		}
-	}
+	public interface Null extends RestInfoProvider {}
 
 	/**
-	 * Returns the localized swagger for this REST resource.
+	 * Returns the contents of the localized swagger file for the specified request.
 	 * 
 	 * @param req The incoming HTTP request.
-	 * @return A new Swagger instance.
+	 * @return 
+	 * 	The contents of the parsed swagger file.
+	 * 	Returns <jk>null</jk> if a swagger file could not be found.
 	 * @throws RestException
 	 */
-	public 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(req.getSupportedAcceptTypes())
-				.produces(req.getSupportedContentTypes())
-				.tags(getTags(req))
-				.externalDocs(getExternalDocs(req));
-
-			for (RestJavaMethod 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);
-		}
-	}
+	public Swagger getSwaggerFromFile(RestRequest req) throws RestException;
 
 	/**
-	 * 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.
+	 * Returns the localized swagger for this REST resource.
 	 * 
 	 * <p>
-	 * Returned objects are cached for later quick-lookup.
+	 * If {@link #getSwaggerFromFile(RestRequest)} returns a non-<jk>null</jk> value, then 
+	 * that swagger is returned by this method.
+	 * <br>Otherwise, a new swagger object is return with information gathered via various means.
 	 * 
-	 * @param locale The locale of the swagger.
-	 * @return The parsed swagger object, or <jk>null</jk> if the swagger file could not be found.
+	 * @param req The incoming HTTP request.
+	 * @return A new Swagger instance.
 	 * @throws RestException
 	 */
-	protected Swagger getSwaggerFromFile(Locale locale) throws RestException {
-		Swagger s = swaggers.get(locale);
-		if (s == null) {
-			try {
-				s = context.getClasspathResource(Swagger.class, MediaType.JSON, getClass().getSimpleName() + ".json", locale);
-				swaggers.putIfAbsent(locale, s == null ? Swagger.NULL : s);
-			} catch (Exception e) {
-				throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
-			}
-		}
-		return s == Swagger.NULL ? null : s;
-	}
+	public Swagger getSwagger(RestRequest req) throws RestException;
+
 
 	/**
 	 * 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):
-	 * <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) {
-		RestJavaMethod m = context.getCallMethods().get(javaMethodName);
-		if (m != null)
-			return m.getSummary(req);
-		return "";
-	}
+	public String getMethodSummary(String javaMethodName, RestRequest req);
 
 	/**
 	 * Returns the localized summary of the java method invoked on the specified request.
 	 * 
-	 * <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):
-	 * <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 req The current request.
 	 * @return The localized summary of the method, or a blank string if no summary was found.
 	 */
-	public String getMethodSummary(RestRequest req) {
-		return getMethodSummary(req.getJavaMethod().getName(), req);
-	}
+	public String getMethodSummary(RestRequest req);
 
 	/**
 	 * 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):
-	 * <ol>
-	 * 	<li>{@link RestMethod#description() @RestMethod.description()} annotation on the method.
-	 * 	<li><ck>[ClassName].[javaMethodName].description</ck> property in resource bundle identified by
-	 * 		{@link RestResource#messages() @RestResource.messages()} annotation for this class, then any parent classes.
-	 * 	<li><ck>[javaMethodName].description</ck> property in resource bundle identified by
-	 * 		{@link RestResource#messages() @RestResource.messages()} annotation for this class, then any parent classes.
-	 * </ol>
-	 * 
 	 * @param javaMethodName The name of the Java method whose description we're retrieving.
 	 * @param req The current request.
 	 * @return The localized description of the method, or a blank string if no description was found.
 	 */
-	public String getMethodDescription(String javaMethodName, RestRequest req) {
-		RestJavaMethod m = context.getCallMethods().get(javaMethodName);
-		if (m != null)
-			return m.getDescription(req);
-		return "";
-	}
+	public String getMethodDescription(String javaMethodName, RestRequest req);
 
 	/**
 	 * Returns the localized description of the invoked java method on the specified request.
 	 * 
-	 * <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 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 req The current request.
 	 * @return The localized description of the method, or a blank string if no description was found.
 	 */
-	public String getMethodDescription(RestRequest req) {
-		return getMethodDescription(req.getJavaMethod().getName(), req);
-	}
+	public String getMethodDescription(RestRequest req);
 
 	/**
 	 * Returns the localized site name of this REST resource.
 	 * 
-	 * <p>
-	 * Subclasses can override this method to provide their own site name.
-	 * 
-	 * <p>
-	 * The default implementation returns the description from the following locations (whichever matches first):
-	 * <ol>
-	 * 	<li>{@link RestResource#siteName() @RestResource.siteName()} annotation on this class, and then any parent classes.
-	 * 	<li><ck>[ClassName].siteName</ck> property in resource bundle identified by
-	 * 		{@link RestResource#messages() @ResourceBundle.messages()} annotation for this class, then any parent
-	 * 		classes.
-	 * 	<li><ck>siteName</ck> in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
-	 * 		annotation for this class, then any parent classes.
-	 * </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 getSiteName(RestRequest req) {
-		VarResolverSession vr = req.getVarResolverSession();
-		if (this.siteName != null)
-			return vr.resolve(this.siteName);
-		String siteName = context.getMessages().findFirstString(req.getLocale(), "siteName");
-		if (siteName != null)
-			return vr.resolve(siteName);
-		return null;
-	}
+	public String getSiteName(RestRequest req);
 
 	/**
 	 * 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):
-	 * <ol>
-	 * 	<li>{@link RestResource#title() @RestResource.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;
-	}
+	public String getTitle(RestRequest req);
 
 	/**
 	 * 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;
-	}
+	public String getDescription(RestRequest req);
 
 	/**
 	 * 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 ResourceSwagger#contact() @ResourceSwagger.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);
-		}
-	}
+	public Contact getContact(RestRequest req) ;
 
 	/**
 	 * 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 ResourceSwagger#license() @ResourceSwagger.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);
-		}
-	}
+	public License getLicense(RestRequest req);
 
 	/**
 	 * 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 ResourceSwagger#termsOfService() @ResourceSwagger.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;
-	}
+	public String getTermsOfService(RestRequest req);
 
 	/**
 	 * 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 ResourceSwagger#version() @ResourceSwagger.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;
-	}
+	public String getVersion(RestRequest req);
 
 	/**
 	 * 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 ResourceSwagger#version() @ResourceSwagger.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);
-		}
-	}
+	public List<Tag> getTags(RestRequest req);
 
 	/**
-	 * 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 ResourceSwagger#version() @ResourceSwagger.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>
+	 * Returns the external documentation of this REST resource.
 	 * 
 	 * @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);
-		}
-	}
+	public ExternalDocumentation getExternalDocs(RestRequest req);
 }