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 2020/07/29 14:58:24 UTC

[juneau] branch master updated: New @Rest(context) setting.

This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new 0cf73e8  New @Rest(context) setting.
0cf73e8 is described below

commit 0cf73e8b23212f1e90ba70bbc8a65fe50331e262
Author: JamesBognar <ja...@salesforce.com>
AuthorDate: Wed Jul 29 10:58:15 2020 -0400

    New @Rest(context) setting.
---
 juneau-doc/docs/ReleaseNotes/8.1.4.html            |   3 +
 juneau-doc/src/main/javadoc/overview.html          | 113 +++++++++++++++------
 .../src/main/javadoc/resources/juneau-doc.css      |  12 ++-
 .../juneau/rest/RestContext_context_Test.java      | 104 +++++++++++++++++++
 .../java/org/apache/juneau/rest/BasicRest.java     |   4 +
 .../apache/juneau/rest/BasicRestCallHandler.java   |   2 +-
 .../java/org/apache/juneau/rest/RestContext.java   |  91 +++++++++++++----
 .../org/apache/juneau/rest/RestContextBuilder.java |  47 ++++++++-
 .../org/apache/juneau/rest/annotation/Rest.java    |  35 +++++++
 .../juneau/rest/annotation/RestConfigApply.java    |   4 +
 10 files changed, 364 insertions(+), 51 deletions(-)

diff --git a/juneau-doc/docs/ReleaseNotes/8.1.4.html b/juneau-doc/docs/ReleaseNotes/8.1.4.html
index d0ac1a9..7330b58 100644
--- a/juneau-doc/docs/ReleaseNotes/8.1.4.html
+++ b/juneau-doc/docs/ReleaseNotes/8.1.4.html
@@ -253,6 +253,9 @@
 <h5 class='topic w800'>juneau-rest-server</h5>
 <ul class='spaced-list'>
 	<li>
+		New {@link oajr.RestContent#REST_context REST_context}/{@link oajr.annotation.Rest#context() @Rest(context)} setting to allow you to extend the {@link oajr.RestContext}
+		class.
+	<li>
 		{@link oajr.annotation.Rest}-annotated classes can now implement the following interfaces directly instead of having
 		to define secondary classes and hook them up through annotations:
 		<ul>
diff --git a/juneau-doc/src/main/javadoc/overview.html b/juneau-doc/src/main/javadoc/overview.html
index a45d501..d589129 100644
--- a/juneau-doc/src/main/javadoc/overview.html
+++ b/juneau-doc/src/main/javadoc/overview.html
@@ -406,7 +406,7 @@
 		<li><p><a class='doclink' href='#juneau-rest-server.Guards'>Guards</a></p>
 		<li><p><a class='doclink' href='#juneau-rest-server.RoleGuards'>Role guards</a><span class='update'>8.1.0-new</span></p>
 		<li><p><a class='doclink' href='#juneau-rest-server.Converters'>Converters</a></p>
-		<li><p><a class='doclink' href='#juneau-rest-server.Messages'>Messages</a></p>
+		<li><p><a class='doclink' href='#juneau-rest-server.Messages'>Messages</a><span class='update'><b>8.1.4-updated</b></span></p>
 		<li><p><a class='doclink' href='#juneau-rest-server.Encoders'>Encoders</a></p>
 		<li><p><a class='doclink' href='#juneau-rest-server.SvlVariables'>SVL Variables</a></p>
 		<li><p><a class='doclink' href='#juneau-rest-server.ConfigurationFiles'>Configuration Files</a></p>
@@ -18731,50 +18731,97 @@
 
 <!-- ==================================================================================================== -->
 
-<h3 class='topic' onclick='toggle(this)'><a href='#juneau-rest-server.Messages' id='juneau-rest-server.Messages'>6.22 - Messages</a></h3>
+<h3 class='topic' onclick='toggle(this)'><a href='#juneau-rest-server.Messages' id='juneau-rest-server.Messages'>6.22 - Messages</a><span class='update'><b>8.1.4-updated</b></span></h3>
 <div class='topic'><!-- START: 6.22 - juneau-rest-server.Messages -->
 <p>
-	The {@link org.apache.juneau.rest.annotation.Rest#messages @Rest(messages)} annotation is used to associate a resource bundle with a servlet class.
+	The {@link org.apache.juneau.rest.annotation.Rest#messages @Rest(messages)} annotation identifies the location of the resource bundle 
+	for a <ja>@Rest</ja>-annotated class if it's different from the class name.
 </p>
-<p class='bpcode w800'>
-	<jc>// Servlet with associated resource bundle</jc>
-	<ja>@Rest</ja>(messages=<js>"nls/MyMessages"</js>)
-	<jk>public</jk> MyRestServlet <jk>extends</jk> BasicRestServlet {
-
-		<jc>// Returns the localized greeting from the "greeting" key in MyMessages.properties</jc>
-		<ja>@RestMethod</ja>(name=<jsf>GET</jsf>, path=<js>"/"</js>)
-		<jk>public</jk> String printLocalizedGreeting(RestRequest req) {
-			<jk>return</jk> req.getMessage(<js>"greeting"</js>);
-		}
+<p>
+	By default, the resource bundle name is assumed to match the class name.  For example, given the class
+	<c>MyClass.java</c>, the resource bundle is assumed to be <c>MyClass.properties</c>.  This property
+	allows you to override this setting to specify a different location such as <c>MyMessages.properties</c> by
+	specifying a value of <js>"MyMessages"</js>.
 </p>
-<p>	
-	The resource bundle can also be passed into the method by simply specifying a parameter
-	of type {@link java.util.ResourceBundle} or {@link org.apache.juneau.utils.MessageBundle}:
+<p>
+Resource bundles are searched using the following base name patterns:
 </p>
-<p class='bpcode w800'>
-	<ja>@RestMethod</ja>(name=<jsf>GET</jsf>)
-	<jk>public</jk> String printLocalizedGreeting(MessageBundle messages) {
-		<jk>return</jk> messages.getString(<js>"greeting"</js>);
-	}
+<ul>
+	<li><js>"{package}.{name}"</js>
+	<li><js>"{package}.i18n.{name}"</js>
+	<li><js>"{package}.nls.{name}"</js>
+	<li><js>"{package}.messages.{name}"</js>
+</ul>
+
+<p>
+	This annotation is used to provide request-localized (based on <c>Accept-Language</c>) messages for the following methods:
+</p>
+<ul class='javatree'>
+	<li class='jm'>{@link RestRequest#getMessage(String, Object...)}
+	<li class='jm'>{@link RestContext#getMessages() RestContext.getMessages()}
+</ul>
+
+<p>
+	Request-localized messages are also available by passing either of the following parameter types into your Java method:
 </p>
+<ul class='javatree'>
+	<li class='jc'>{@link ResourceBundle} - Basic Java resource bundle.
+	<li class='jc'>{@link Messages} - Extended resource bundle with several convenience methods.
+</ul>
 <p>
-	If a resource bundle is shared by multiple servlets, the label and description can be prefixed by the class 
-	name:
+	The value can be a relative path like <js>"nls/Messages"</js>, indicating to look for the resource bundle
+	<js>"com.foo.sample.nls.Messages"</js> if the resource class is in <js>"com.foo.sample"</js>, or it can be an
+	absolute path like <js>"com.foo.sample.nls.Messages"</js>
 </p>
+
+<h5 class='figure'>Examples:</h5>
 <p class='bpcode w800'>
 	<cc>#--------------------------------------------------------------------------------
-	# Contents of MyMessages.properties
+	# Contents of org/apache/foo/nls/MyMessages.properties
 	#--------------------------------------------------------------------------------</cc>
-	<ck>greeting</ck> = Hello!
-</p>	 	
+	<ck>HelloMessage</ck> = <cv>Hello {0}!</cv>
+</p>
+<p class='bpcode w800'>
+	<jc>// Contents of org/apache/foo/MyResource.java</jc>
+
+	<ja>@Rest</ja>(messages=<js>"nls/MyMessages"</js>)
+	<jk>public class</jk> MyResource {...}
+
+		<ja>@RestMethod</ja>(name=<js>"GET"</js>, path=<js>"/hello/{you}"</js>)
+		<jk>public</jk> Object helloYou(RestRequest <jv>req</jv>, Messages <jv>messages</jv>, <ja>@Path</ja>(<js>"name"</js>) String <jv>you</jv>) {
+			String <jv>s</jv>;
+
+			<jc>// Get it from the RestRequest object.</jc>
+			<jv>s</jv> = <jv>req</jv>.getMessage(<js>"HelloMessage"</js>, <jv>you</jv>);
+
+			<jc>// Or get it from the method parameter.</jc>
+			<jv>s</jv> = <jv>messages</jv>.getString(<js>"HelloMessage"</js>, <jv>you</jv>);
+
+			<jc>// Or get the message in a locale different from the request.</jc>
+			<jv>s</jv> = <jv>messages</jv>.forLocale(Locale.<jsf>UK</jsf>).getString(<js>"HelloMessage"</js>, <jv>you</jv>);
+
+			<jk>return</jk> <jv>s</jv>;
+		}
+	}
+</p>
+<p>
+	When using shared resource bundles, keys can be prefixed by class names like so and still retrieve by simple
+	key names:
+</p>
 <p class='bpcode w800'>
 	<cc>#--------------------------------------------------------------------------------
-	# Contents of shared MyMessages.properties
+	# Contents of shared org/apache/foo/nls/MyMessages.properties
 	#--------------------------------------------------------------------------------</cc>
-	<ck>MyRestServlet.greeting</ck> = Hello!
+	<ck>MyResource.HelloMessage</ck> = <cv>Hello {0}!</cv>
+</p>
+
+<p>
+	Messages are automatically inherited from super classes.  If a string cannot be found in the bundle of the current
+	class, it will be searched for up the class hierarchy.
 </p>
 
 <ul class='seealso'>
+	<li class='jc'>{@link org.apache.juneau.cp.Messages}
 	<li class='jf'>{@link org.apache.juneau.rest.RestContext#REST_messages}
 </ul>
 </div><!-- END: 6.22 - juneau-rest-server.Messages -->
@@ -30936,6 +30983,12 @@
 	</tr>
 	<tr>
 		<td></td>
+		<td>{@link org.apache.juneau.rest.RestContext#REST_context REST_context}</td>
+		<td>REST context class.</td>
+		<td style='max-width:250px;overflow:hidden'><c>Class&lt;? extends {@link org.apache.juneau.rest.RestContext}&gt;</c></td>
+	</tr>
+	<tr>
+		<td></td>
 		<td>{@link org.apache.juneau.rest.RestContext#REST_converters REST_converters}</td>
 		<td>Class-level response converters.</td>
 		<td style='max-width:250px;overflow:hidden'><c>List&lt;{@link org.apache.juneau.rest.RestConverter}|Class&lt;{@link org.apache.juneau.rest.RestConverter}&gt;&gt;</c></td>
@@ -30986,7 +31039,7 @@
 		<td></td>
 		<td>{@link org.apache.juneau.rest.RestContext#REST_messages REST_messages}</td>
 		<td>Messages.</td>
-		<td style='max-width:250px;overflow:hidden'><c>List&lt;{@link org.apache.juneau.rest.MessageBundleLocation}&gt;</c></td>
+		<td style='max-width:250px;overflow:hidden'><c>List&lt;{@link org.apache.juneau.utils.Tuple2}&lt;Class,String&gt;&gt;</c></td>
 	</tr>
 	<tr>
 		<td></td>
@@ -38836,6 +38889,8 @@
 		String getFoo();  <jc>// @RestMethod(name=GET,path="/foo") is implied.</jc>
 	}
 		</p>
+	<li>
+		Improved {@link org.apache.juneau.rest.annotation.RestMethod#REST_messages REST_messages} support (mostly bug fixes).
 </ul>
 
 <h5 class='topic w800'>juneau-rest-server-springboot</h5>
diff --git a/juneau-doc/src/main/javadoc/resources/juneau-doc.css b/juneau-doc/src/main/javadoc/resources/juneau-doc.css
index e61a042..ec0f464 100755
--- a/juneau-doc/src/main/javadoc/resources/juneau-doc.css
+++ b/juneau-doc/src/main/javadoc/resources/juneau-doc.css
@@ -41,6 +41,7 @@
  *
  * Available tags:
  * 	<l> - A literal.
+ *	<review> - Identifies code that needs review.
  ***************************************************************************************************************************/
 
 .fixedWidth {
@@ -493,7 +494,7 @@ hr {
 }
 
 div.info, div.warn, div.severe {
-   	background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnIAoJaWQ9InN2ZzI4MTAiIAoJeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiAKCXhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiAKCWhlaWdodD0iMjQiIAoJd2lkdGg9IjQyIiAKCXZlcnNpb249IjEuMCIgCgl2aWV3Qm94PSIwIDAgNDgwIDQ4MCI+Cgk8ZGVmcyBpZD0iZGVmczI4MzYiPgoJCTxsaW5lYXJHcmFkaWVudCBpZD0ibGl [...]
+	background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnIAoJaWQ9InN2ZzI4MTAiIAoJeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiAKCXhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiAKCWhlaWdodD0iMjQiIAoJd2lkdGg9IjQyIiAKCXZlcnNpb249IjEuMCIgCgl2aWV3Qm94PSIwIDAgNDgwIDQ4MCI+Cgk8ZGVmcyBpZD0iZGVmczI4MzYiPgoJCTxsaW5lYXJHcmFkaWVudCBpZD0ibGluZW [...]
 	background-repeat: no-repeat;
 	background-position: left center;
 	padding: 15px 50px;
@@ -527,3 +528,12 @@ div.severe {
 .todo {
 	background-color:#FD8;
 }
+
+review {
+	background-color: #ffdf00;
+	padding: 10px;
+	border-radius: 5px;
+	width: 800px;
+	text-align: center;
+	box-shadow: 1px 1px 1px 0px rgba(0, 0, 0, 0.5);
+}
diff --git a/juneau-rest/juneau-rest-server-utest/src/test/java/org/apache/juneau/rest/RestContext_context_Test.java b/juneau-rest/juneau-rest-server-utest/src/test/java/org/apache/juneau/rest/RestContext_context_Test.java
new file mode 100644
index 0000000..2a4c299
--- /dev/null
+++ b/juneau-rest/juneau-rest-server-utest/src/test/java/org/apache/juneau/rest/RestContext_context_Test.java
@@ -0,0 +1,104 @@
+// ***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                                                              *
+// *                                                                                                                         *
+// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
+// *                                                                                                                         *
+// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the License.                                              *
+// ***************************************************************************************************************************
+package org.apache.juneau.rest;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.runners.MethodSorters.*;
+
+import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.rest.mock2.*;
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class RestContext_context_Test {
+
+	public static class MyRestContext extends RestContext {
+		public MyRestContext(RestContextBuilder builder) throws Exception {
+			super(builder);
+		}
+	}
+
+	@Rest
+	public static class A1 {
+		@RestMethod
+		public String get(RestContext context) {
+			return context.getClass().getSimpleName();
+		}
+	}
+
+	@Test
+	public void a01_default() throws Exception {
+		MockRestClient x = client(A1.class);
+		x.get().run().assertBody().is("RestContext");
+	}
+
+	@Rest(context=MyRestContext.class)
+	public static class A2 extends A1 {}
+
+	@Test
+	public void a02_custom() throws Exception {
+		MockRestClient x = client(A2.class);
+		x.get().run().assertBody().is("MyRestContext");
+	}
+
+	@Rest
+	public static class A3 extends A2 {}
+
+	@Test
+	public void a03_notOverriddenByChild() throws Exception {
+		MockRestClient x = client(A3.class);
+		x.get().run().assertBody().is("MyRestContext");
+	}
+
+	@Rest
+	public static class A4 extends A1 {
+		 @RestHook(HookEvent.INIT)
+		 public void init(RestContextBuilder builder) throws Exception {
+			 builder.context(MyRestContext.class);
+		 }
+	}
+
+	@Test
+	public void a04_definedInBuilder() throws Exception {
+		MockRestClient x = client(A4.class);
+		x.get().run().assertBody().is("MyRestContext");
+	}
+
+
+	public static class MyBadRestContext extends RestContext {
+		public MyBadRestContext() throws Exception {
+			super(null);
+		}
+	}
+
+	@Rest(context=MyBadRestContext.class)
+	public static class A5 {
+		@RestMethod
+		public String get(RestContext context) {
+			return context.getClass().getSimpleName();
+		}
+	}
+
+	@Test
+	public void a05_invalidConstructor() throws Exception {
+		assertThrown(()->client(A5.class)).contains("Invalid class specified for REST_context");
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Helper methods
+	//------------------------------------------------------------------------------------------------------------------
+
+	private static MockRestClient client(Class<?> c) {
+		return MockRestClient.create(c).build();
+	}
+}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRest.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRest.java
index a4ff1f7..ff842cf 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRest.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRest.java
@@ -36,6 +36,10 @@ import org.apache.juneau.http.exception.*;
 /**
  * Identical to {@link BasicRestServlet} but doesn't extend from {@link HttpServlet}.
  *
+ * <p>
+ * This is particularly useful in Spring Boot environments that auto-detect servlets to deploy in servlet containers,
+ * but you want this resource to be deployed as a child instead.
+ *
  * <ul class='seealso'>
  * 	<li class='link'>{@doc juneau-rest-server.Instantiation.BasicRest}
  * </ul>
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java
index 6888649..af776d5 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java
@@ -149,7 +149,7 @@ public class BasicRestCallHandler implements RestCallHandler {
 			if (call.getPathInfoUndecoded() != null) {
 				String p = call.getPathInfoUndecoded().substring(1);
 				if (context.isStaticFile(p)) {
-					r = context.resolveStaticFile(p);
+					r = context.getStaticFile(p);
 					if (! r.exists()) {
 						call.output(null);
 						r = null;
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 03312dd..7677d47 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
@@ -87,7 +87,15 @@ import org.apache.juneau.xml.*;
  * </ul>
  */
 @ConfigurableContext(nocache=true)
-public final class RestContext extends BeanContext {
+public class RestContext extends BeanContext {
+
+	/** Represents a null value for the {@link Rest#context()} annotation.*/
+	@SuppressWarnings("javadoc")
+	public static final class Null extends RestContext {
+		public Null(RestContextBuilder builder) throws Exception {
+			super(builder);
+		}
+	}
 
 	//-------------------------------------------------------------------------------------------------------------------
 	// Configurable properties
@@ -804,7 +812,7 @@ public final class RestContext extends BeanContext {
 	 * 		<li class='jm'>{@link #getClasspathResource(String,Locale) getClasspathResource(String,Locale)}
 	 * 		<li class='jm'>{@link #getClasspathResource(Class,MediaType,String,Locale) getClasspathResource(Class,MediaType,String,Locale)}
 	 * 		<li class='jm'>{@link #getClasspathResourceAsString(String,Locale) getClasspathResourceAsString(String,Locale)}
-	 * 		<li class='jm'>{@link #resolveStaticFile(String) resolveStaticFile(String)}
+	 * 		<li class='jm'>{@link #getStaticFile(String) resolveStaticFile(String)}
 	 * 	</ul>
 	 * 	<li class='jc'>{@link RestRequest}
 	 * 	<ul>
@@ -1676,7 +1684,7 @@ public final class RestContext extends BeanContext {
 	 * <p>
 	 * Used for specifying the content type on file resources retrieved through the following methods:
 	 * <ul class='javatree'>
-	 * 	<li class='jm'>{@link RestContext#resolveStaticFile(String) RestContext.resolveStaticFile(String)}
+	 * 	<li class='jm'>{@link RestContext#getStaticFile(String) RestContext.resolveStaticFile(String)}
 	 * 	<li class='jm'>{@link RestRequest#getClasspathHttpResource(String,boolean,MediaType,boolean)}
 	 * 	<li class='jm'>{@link RestRequest#getClasspathHttpResource(String,boolean)}
 	 * 	<li class='jm'>{@link RestRequest#getClasspathHttpResource(String)}
@@ -3193,6 +3201,65 @@ public final class RestContext extends BeanContext {
 	public static final String REST_consumes = PREFIX + ".consumes.ls";
 
 	/**
+	 * Configuration property:  REST context class.
+	 *
+	 * <review>NEEDS REVIEW</review>
+	 *
+	 * <h5 class='section'>Property:</h5>
+	 * <ul class='spaced-list'>
+	 * 	<li><b>ID:</b>  {@link org.apache.juneau.rest.RestContext#REST_context REST_context}
+	 * 	<li><b>Name:</b>  <js>"RestContext.context.c"</js>
+	 * 	<li><b>Data type:</b>  <c>Class&lt;? extends {@link org.apache.juneau.rest.RestContext}&gt;</c>
+	 * 	<li><b>Default:</b>  {@link org.apache.juneau.rest.RestContext}
+	 * 	<li><b>Session property:</b>  <jk>false</jk>
+	 * 	<li><b>Annotations:</b>
+	 * 		<ul>
+	 * 			<li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#context()}
+	 * 		</ul>
+	 * 	<li><b>Methods:</b>
+	 * 		<ul>
+	 * 			<li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#context(Class)}
+	 * 		</ul>
+	 * </ul>
+	 *
+	 * <h5 class='section'>Description:</h5>
+	 * <p>
+	 * Allows you to extend the {@link RestContext} class to modify how any of the methods are implemented.
+	 *
+	 * <p>
+	 * The subclass must provide the following:
+	 * <ul>
+	 * 	<li>A public constructor that takes in one parameter that should be passed to the super constructor:  {@link RestContextBuilder}.
+	 * </ul>
+	 *
+	 * <h5 class='section'>Example:</h5>
+	 * <p class='bcode w800'>
+	 * 	<jc>// Our extended context class</jc>
+	 * 	<jk>public</jk> MyRestContext <jk>extends</jk> RestContext {
+	 * 		<jk>public</jk> MyRestContext(RestContextBuilder <jv>builder</jv>) {
+	 * 			<jk>super</jk>(<jv>builder</jv>);
+	 * 		}
+	 *
+	 * 		<jc>// Override any methods.</jc>
+	 * 	}
+	 * </p>
+	 * <p class='bcode w800'>
+	 * 	<jc>// Option #1 - Defined via annotation.</jc>
+	 * 	<ja>@Rest</ja>(context=MyRestContext.<jk>class</jk>)
+	 * 	<jk>public class</jk> MyResource {
+	 * 		...
+	 *
+	 * 		<jc>// Option #2 - Defined via builder passed in through init method.</jc>
+	 * 		<ja>@RestHook</ja>(<jsf>INIT</jsf>)
+	 * 		<jk>public void</jk> init(RestContextBuilder <jv>builder</jv>) <jk>throws</jk> Exception {
+	 * 			<jv>builder</jv>.context(MyRestContext.<jk>class</jk>);
+	 * 		}
+	 * 	}
+	 * </p>
+	 */
+	public static final String REST_context = PREFIX + ".context.c";
+
+	/**
 	 * Configuration property:  Use classpath resource caching.
 	 *
 	 * <h5 class='section'>Property:</h5>
@@ -3704,7 +3771,7 @@ public final class RestContext extends BeanContext {
 	 * @throws Exception If any initialization problems were encountered.
 	 */
 	@SuppressWarnings("deprecation")
-	RestContext(RestContextBuilder builder) throws Exception {
+	public RestContext(RestContextBuilder builder) throws Exception {
 		super(builder.getPropertyStore());
 
 		startTime = Instant.now();
@@ -4277,7 +4344,7 @@ public final class RestContext extends BeanContext {
 	 * @throws NotFound Invalid path.
 	 * @throws IOException Thrown by underlying stream.
 	 */
-	protected StaticFile resolveStaticFile(String pathInfo) throws NotFound, IOException {
+	protected StaticFile getStaticFile(String pathInfo) throws NotFound, IOException {
 		if (! staticFilesCache.containsKey(pathInfo)) {
 			String p = urlDecode(trimSlashes(pathInfo));
 			if (p.indexOf("..") != -1)
@@ -4921,20 +4988,6 @@ public final class RestContext extends BeanContext {
 	}
 
 	/**
-	 * Returns the context of the child resource associated with the specified path.
-	 *
-	 * <ul class='seealso'>
-	 * 	<li class='jf'>{@link RestContext#REST_children}
-	 * </ul>
-	 *
-	 * @param path The path of the child resource to resolve.
-	 * @return The resolved context, or <jk>null</jk> if it could not be resolved.
-	 */
-	public RestContext getChildResource(String path) {
-		return childResources.get(path);
-	}
-
-	/**
 	 * Returns the authority path of the resource.
 	 *
 	 * <ul class='seealso'>
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 d3ea603..d9a0d14 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
@@ -204,7 +204,12 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	@Override /* BeanContextBuilder */
 	public RestContext build() {
 		try {
-			return new RestContext(this);
+			PropertyStore ps = getPropertyStore();
+			Class<? extends RestContext> c = ps.getClassProperty(REST_context, RestContext.class, RestContext.class);
+			ConstructorInfo ci = ClassInfo.of(c).getConstructor(Visibility.PUBLIC, RestContextBuilder.class);
+			if (ci == null)
+				throw new InternalServerError("Invalid class specified for REST_context.  Must extend from RestContext and provide a public constructor of the form T(RestContextBuilder).");
+			return ci.invoke(this);
 		} catch (Exception e) {
 			throw toHttpException(e, InternalServerError.class);
 		}
@@ -799,6 +804,46 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	}
 
 	/**
+	 * <i><l>RestContext</l> configuration property:&emsp;</i>  REST context class.
+	 *
+	 * <review>NEEDS REVIEW</review>
+	 * <p>
+	 * Allows you to extend the {@link RestContext} class to modify how any of the methods are implemented.
+	 *
+	 * <p>
+	 * The subclass must provide the following:
+	 * <ul>
+	 * 	<li>A public constructor that takes in one parameter that should be passed to the super constructor:  {@link RestContextBuilder}.
+	 * </ul>
+	 *
+	 * <h5 class='section'>Example:</h5>
+	 * <p class='bcode w800'>
+	 * 	<jc>// Our REST class</jc>
+	 * 	<ja>@Rest</ja>(context=MyRestContext.<jk>class</jk>)
+	 * 	<jk>public class</jk> MyResource {
+	 * 		...
+	 * 	}
+	 * </p>
+	 * <p class='bcode w800'>
+	 * 	<ja>@Rest</ja>
+	 * 	<jk>public class</jk> MyResource {
+	 * 		...
+	 * 		<ja>@RestHook</ja>(<jsf>INIT</jsf>)
+	 * 		<jk>public void</jk> init(RestContextBuilder <jv>builder</jv>) <jk>throws</jk> Exception {
+	 * 			<jv>builder</jv>.context(MyRestContext.<jk>class</jk>);
+	 * 		}
+	 * 	}
+	 * </p>
+	 *
+	 * @param value The new value for this setting.
+	 * @return This object (for method chaining).
+	 */
+	@FluentSetter
+	public RestContextBuilder context(Class<? extends RestContext> value) {
+		return set(REST_context, value);
+	}
+
+	/**
 	 * <i><l>RestContext</l> configuration property:&emsp;</i>  Class-level response converters.
 	 *
 	 * <p>
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Rest.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Rest.java
index 96a72be..904e97c 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Rest.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Rest.java
@@ -279,6 +279,41 @@ public @interface Rest {
 	String config() default "";
 
 	/**
+	 * Allows you to extend the {@link RestContext} class to modify how any of the methods are implemented.
+	 *
+	 * <review>NEEDS REVIEW</review>
+	 * <p>
+	 * The subclass must provide the following:
+	 * <ul>
+	 * 	<li>A public constructor that takes in one parameter that should be passed to the super constructor:  {@link RestContextBuilder}.
+	 * </ul>
+	 *
+	 * <h5 class='section'>Example:</h5>
+	 * <p class='bcode w800'>
+	 * 	<jc>// Our extended context class</jc>
+	 * 	<jk>public</jk> MyRestContext <jk>extends</jk> RestContext {
+	 * 		<jk>public</jk> MyRestContext(RestContextBuilder <jv>builder</jv>) {
+	 * 			<jk>super</jk>(<jv>builder</jv>);
+	 * 		}
+	 *
+	 * 		// Override any methods.
+	 * 	}
+	 * </p>
+	 * <p class='bcode w800'>
+	 * 	<jc>// Our REST class</jc>
+	 * 	<ja>@Rest</ja>(context=MyRestContext.<jk>class</jk>)
+	 * 	<jk>public class</jk> MyResource {
+	 * 		...
+	 * 	}
+	 * </p>
+	 *
+	 * <ul class='seealso'>
+	 * 	<li class='jm'>{@link RestContextBuilder#context(Class)}
+	 * </ul>
+	 */
+	Class<? extends RestContext> context() default RestContext.Null.class;
+
+	/**
 	 * Class-level response converters.
 	 *
 	 * <p>
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestConfigApply.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestConfigApply.java
index 74c31a8..76daa0a 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestConfigApply.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestConfigApply.java
@@ -157,6 +157,10 @@ public class RestConfigApply extends ConfigApply<Rest> {
 
 		psb.prependTo(REST_paramResolvers, a.paramResolvers());
 
+		Class<?> cc = a.context();
+		if (! cc.equals(RestContext.Null.class))
+			psb.set(REST_context, cc);
+
 		s = string(a.uriContext());
 		if (isNotEmpty(s))
 			psb.set(REST_uriContext, s);