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/03/15 22:55:18 UTC

[juneau] branch master updated: JUNEAU-186

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 015fb11  JUNEAU-186
015fb11 is described below

commit 015fb1195a7010152f18ffc10bd7ae73ffc15cbb
Author: JamesBognar <ja...@apache.org>
AuthorDate: Sun Mar 15 18:55:03 2020 -0400

    JUNEAU-186
    
    REST classes should allow themselves to implement helper classes.
---
 juneau-doc/docs/ReleaseNotes/8.1.4.html            |  61 +++
 .../juneau/rest/mock2/MockServletRequest.java      |   2 +-
 .../apache/juneau/rest/BasicRestCallHandler.java   |   4 +-
 .../org/apache/juneau/rest/RestCallHandler.java    |   2 +-
 .../java/org/apache/juneau/rest/RestContext.java   |  10 +-
 .../java/org/apache/juneau/rest/RestServlet.java   | 407 ++++++++++++++++++++-
 .../apache/juneau/rest/annotation/HookEvent.java   |   2 +-
 .../juneau/rest/mock/MockServletRequest.java       |   2 +-
 8 files changed, 475 insertions(+), 15 deletions(-)

diff --git a/juneau-doc/docs/ReleaseNotes/8.1.4.html b/juneau-doc/docs/ReleaseNotes/8.1.4.html
index 4fc0812..df120ac 100644
--- a/juneau-doc/docs/ReleaseNotes/8.1.4.html
+++ b/juneau-doc/docs/ReleaseNotes/8.1.4.html
@@ -153,6 +153,67 @@
 
 <h5 class='topic w800'>juneau-rest-server</h5>
 <ul class='spaced-list'>
+	<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>
+			<li class='jic'>{@link oajr.RestCallHandler} - Normally defined through {@link oajr.annotation.Rest#callHandler() @Rest.callHandler()}.
+			<li class='jic'>{@link oajr.RestInfoProvider} - Normally defined through {@link oajr.annotation.Rest#infoProvider() @Rest.infoProvider()}.
+			<li class='jic'>{@link oajr.RestCallLogger} - Normally defined through {@link oajr.annotation.Rest#callLogger() @Rest.callLogger()}.
+		</ul>
+	<li>
+		{@link oajr.RestServlet} now implements the {@link oajr.RestCallHandler}, {@link oajr.RestInfoProvider}, and {@link oajr.RestCallLogger}
+		interfaces directly and provides the same functionality as {@link oajr.BasicRestCallHandler}, {@link oajr.BasicRestInfoProvider}, and
+		{@link oajr.BasicRestCallLogger}.  This makes it easier to tweak these methods without having to implement your own classes.
+		<br>The methods added for {@link oajr.RestCallHandler} are:
+		<ul>
+			<li class='jac'>{@link oaj.RestServlet}
+			<ul>
+				<li class='jm'>{@link oaj.RestServlet#execute(HttpServletRequest, HttpServletResponse) execute(HttpServletRequest, HttpServletResponse)}
+				<li class='jm'>{@link oaj.RestServlet#createCall(HttpServletRequest, HttpServletResponse) createCall(HttpServletRequest, HttpServletResponse)}
+				<li class='jm'>{@link oaj.RestServlet#createRequest(RestCall) createRequest(RestCall)}
+				<li class='jm'>{@link oaj.RestServlet#createResponse(RestCall) createResponse(RestCall)}
+				<li class='jm'>{@link oaj.RestServlet#handleResponse(RestCall) handleResponse(RestCall)}
+				<li class='jm'>{@link oaj.RestServlet#handleNotFound(RestCall) handleNotFound(RestCall)}
+				<li class='jm'>{@link oaj.RestServlet#handleError(RestCall,Throwable) handleError(RestCall,Throwable)}
+				<li class='jm'>{@link oaj.RestServlet#convertThrowable(Throwable) throwable(Throwable)}
+				<li class='jm'>{@link oaj.RestServlet#getSessionObjects(RestRequest req, RestResponse res) getSessionObjects(RestRequest,RestResponse)}
+			</ul>
+		</ul>
+		<br>The methods added for {@link oajr.RestInfoProvider} are:
+		<ul>
+			<li class='jac'>{@link oaj.RestServlet}
+			<ul>
+				<li class='jm'>{@link oaj.RestServlet#getSwagger(RestRequest) getSwagger(RestRequest)}
+				<li class='jm'>{@link oaj.RestServlet#getSiteName(RestRequest) getSiteName(RestRequest)}
+				<li class='jm'>{@link oaj.RestServlet#getTitle(RestRequest) getTitle(RestRequest)}
+				<li class='jm'>{@link oaj.RestServlet#getDescription(RestRequest) getDescription(RestRequest)}
+				<li class='jm'>{@link oaj.RestServlet#getMethodSummary(Method,RestRequest) getMethodSummary(Method,RestRequest)}
+				<li class='jm'>{@link oaj.RestServlet#getMethodDescription(Method,RestRequest) getMethodDescription(Method,RestRequest)}
+			</ul>
+		</ul>
+		<br>The methods added for {@link oajr.RestCallLogger} are:
+		<ul>
+			<li class='jac'>{@link oaj.RestServlet}
+			<ul>
+				<li class='jm'>{@link oaj.RestServlet#log(RestCallLoggerConfig,HttpServletRequest,HttpServletResponse) log(RestCallLoggerConfig,HttpServletRequest,HttpServletResponse)}
+			</ul>
+		</ul>
+	<li>
+		Added the following convenience hook methods on the {@link RestServlet} class:
+		<ul>
+			<li class='jac'>{@link oaj.RestServlet}
+			<ul>
+				<li class='jm'>{@link oaj.RestServlet#onInit(RestContextBuilder) onInit(RestContextBuilder)}
+				<li class='jm'>{@link oaj.RestServlet#onPostInit(RestContext) onPostInit(RestContext)}
+				<li class='jm'>{@link oaj.RestServlet#onPostInitChildFirst(RestContext) onPostInitChildFirst(RestContext)}
+				<li class='jm'>{@link oaj.RestServlet#onDestroy(RestContext) onDestroy(RestContext)}
+				<li class='jm'>{@link oaj.RestServlet#onStartCall(HttpServletRequest,HttpServletResponse) onStartCall(HttpServletRequest,HttpServletResponse)}
+				<li class='jm'>{@link oaj.RestServlet#onPreCall(RestRequest,RestResponse) onPreCall(RestRequest,RestResponse)}
+				<li class='jm'>{@link oaj.RestServlet#onPostCall(RestRequest,RestResponse) onPostCall(RestRequest,RestResponse)}
+				<li class='jm'>{@link oaj.RestServlet#onEndCall(HttpServletRequest,HttpServletResponse) onEndCall(HttpServletRequest,HttpServletResponse)}
+			</ul>
+		</ul>
 </ul>
 
 <h5 class='topic w800'>juneau-rest-server-springboot</h5>
diff --git a/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock2/MockServletRequest.java b/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock2/MockServletRequest.java
index 132c78c..e8f332c 100644
--- a/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock2/MockServletRequest.java
+++ b/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock2/MockServletRequest.java
@@ -281,7 +281,7 @@ public class MockServletRequest implements HttpServletRequest, MockHttpRequest {
 	@Override /* MockHttpRequest */
 	public MockServletResponse execute() throws ServletException, IOException {
 		MockServletResponse res = MockServletResponse.create();
-		restContext.getCallHandler().service(this, res);
+		restContext.getCallHandler().execute(this, res);
 
 		// If the status isn't set, something's broken.
 		if (res.getStatus() == 0)
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 9c6e4a1..9a4be75 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
@@ -86,7 +86,7 @@ public class BasicRestCallHandler implements RestCallHandler {
 	 * @throws IOException Thrown by underlying stream.
 	 */
 	@Override /* RestCallHandler */
-	public void service(HttpServletRequest r1, HttpServletResponse r2) throws ServletException, IOException {
+	public void execute(HttpServletRequest r1, HttpServletResponse r2) throws ServletException, IOException {
 
 		RestCall call = createCall(r1, r2);
 
@@ -127,7 +127,7 @@ public class BasicRestCallHandler implements RestCallHandler {
 							HttpServletRequest childRequest = new OverrideableHttpServletRequest(call.getRequest())
 								.pathInfo(nullIfEmpty(urlDecode(uppm.getSuffix())))
 								.servletPath(call.getServletPath() + uppm.getPrefix());
-							rc.getCallHandler().service(childRequest, call.getResponse());
+							rc.getCallHandler().execute(childRequest, call.getResponse());
 						} else {
 							call.debug(isDebug(call)).status(SC_NOT_FOUND).finish();
 						}
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 6946f12..9d24fe7 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
@@ -45,7 +45,7 @@ public interface RestCallHandler {
 	 * @throws ServletException Error occurred.
 	 * @throws IOException Thrown by underlying stream.
 	 */
-	public void service(HttpServletRequest r1, HttpServletResponse r2) throws ServletException, IOException;
+	public void execute(HttpServletRequest r1, HttpServletResponse r2) throws ServletException, IOException;
 
 	/**
 	 * Wraps an incoming servlet request/response pair into a single {@link RestCall} object.
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 0472e78..c01ea17 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
@@ -3668,7 +3668,8 @@ public final class RestContext extends BeanContext {
 
 			this.stackTraceDb = new StackTraceDatabase(callLoggerConfig.getStackTraceHashingTimeout(), RestMethodContext.class);
 
-			callLogger = getInstanceProperty(REST_callLogger, resource, RestCallLogger.class, BasicRestCallLogger.class, resourceResolver, this);
+			Object defaultRestCallLogger = resource instanceof RestCallLogger ? resource : BasicRestCallLogger.class;
+			callLogger = getInstanceProperty(REST_callLogger, resource, RestCallLogger.class, defaultRestCallLogger, resourceResolver, this);
 
 			properties = builder.properties;
 			serializers =
@@ -3983,8 +3984,11 @@ public final class RestContext extends BeanContext {
 				childResources.put(path, rc2);
 			}
 
-			callHandler = getInstanceProperty(REST_callHandler, resource, RestCallHandler.class, BasicRestCallHandler.class, resourceResolver, this);
-			infoProvider = getInstanceProperty(REST_infoProvider, resource, RestInfoProvider.class, BasicRestInfoProvider.class, resourceResolver, this);
+			Object defaultRestCallHandler = resource instanceof RestCallHandler ? resource : BasicRestCallHandler.class;
+			callHandler = getInstanceProperty(REST_callHandler, resource, RestCallHandler.class, defaultRestCallHandler, resourceResolver, this);
+
+			Object defaultRestInfoProvider = resource instanceof RestInfoProvider ? resource : BasicRestInfoProvider.class;
+			infoProvider = getInstanceProperty(REST_infoProvider, resource, RestInfoProvider.class, defaultRestInfoProvider, resourceResolver, this);
 
 		} catch (HttpException e) {
 			_initException = e;
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestServlet.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestServlet.java
index 3d75307..5fe912a 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestServlet.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestServlet.java
@@ -16,9 +16,12 @@ import static java.util.logging.Level.*;
 import static javax.servlet.http.HttpServletResponse.*;
 import static org.apache.juneau.internal.StringUtils.*;
 import static org.apache.juneau.rest.HttpRuntimeException.*;
+import static org.apache.juneau.rest.annotation.HookEvent.*;
 
 import java.io.*;
+import java.lang.reflect.Method;
 import java.text.*;
+import java.util.*;
 import java.util.logging.*;
 
 import javax.servlet.*;
@@ -27,6 +30,7 @@ import javax.servlet.http.*;
 import org.apache.juneau.internal.*;
 import org.apache.juneau.reflect.*;
 import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.dto.swagger.*;
 import org.apache.juneau.http.exception.*;
 
 /**
@@ -36,7 +40,7 @@ import org.apache.juneau.http.exception.*;
  * 	<li class='link'>{@doc juneau-rest-server.Instantiation.RestServlet}
  * </ul>
  */
-public abstract class RestServlet extends HttpServlet {
+public abstract class RestServlet extends HttpServlet implements RestCallHandler, RestInfoProvider, RestCallLogger {
 
 	private static final long serialVersionUID = 1L;
 
@@ -46,7 +50,9 @@ public abstract class RestServlet extends HttpServlet {
 	private boolean isInitialized = false;  // Should not be volatile.
 	private volatile RestResourceResolver resourceResolver;
 	private JuneauLogger logger = JuneauLogger.getLogger(getClass());
-
+	private RestCallHandler callHandler;
+	private RestInfoProvider infoProvider;
+	private RestCallLogger callLogger;
 
 	@Override /* Servlet */
 	public final synchronized void init(ServletConfig servletConfig) throws ServletException {
@@ -89,6 +95,9 @@ public abstract class RestServlet extends HttpServlet {
 		this.builder = context.builder;
 		this.context = context;
 		isInitialized = true;
+		callHandler = new BasicRestCallHandler(context);
+		infoProvider = new BasicRestInfoProvider(context);
+		callLogger = new BasicRestCallLogger(context);
 		context.postInit();
 	}
 
@@ -176,12 +185,33 @@ public abstract class RestServlet extends HttpServlet {
 
 	@Override /* GenericServlet */
 	public void log(String msg) {
-		logger.info(msg);
+		super.log(msg);
 	}
 
 	@Override /* GenericServlet */
 	public void log(String msg, Throwable cause) {
-		logger.info(cause, msg);
+		super.log(msg, cause);
+	}
+
+	/**
+	 * Convenience method for calling {@link #log(String)} with message arguments.
+	 *
+	 * @param msg The message containing {@link MessageFormat}-style arguments.
+	 * @param args The arguments.
+	 */
+	public void log(String msg, Object...args) {
+		super.log(args.length == 0 ? msg : MessageFormat.format(msg, args));
+	}
+
+	/**
+	 * Convenience method for calling {@link #log(String)} with message arguments.
+	 *
+	 * @param msg The message containing {@link MessageFormat}-style arguments.
+	 * @param cause The cause.
+	 * @param args The arguments.
+	 */
+	public void log(String msg, Throwable cause, Object...args) {
+		super.log(args.length == 0 ? msg : MessageFormat.format(msg, args), cause);
 	}
 
 	/**
@@ -240,7 +270,7 @@ public abstract class RestServlet extends HttpServlet {
 				isInitialized = true;
 			}
 
-			context.getCallHandler().service(r1, r2);
+			context.getCallHandler().execute(r1, r2);
 
 		} catch (Throwable e) {
 			r2.sendError(SC_INTERNAL_SERVER_ERROR, e.getLocalizedMessage());
@@ -255,7 +285,280 @@ public abstract class RestServlet extends HttpServlet {
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
-	// Request-time methods.
+	// Hook events
+	//-----------------------------------------------------------------------------------------------------------------
+
+	/**
+	 * Method that gets called during servlet initialization.
+	 *
+	 * <p>
+	 * This method is called from within the {@link Servlet#init(ServletConfig)} method after the {@link RestContextBuilder}
+	 * object has been created and initialized with the annotations defined on the class, but before the
+	 * {@link RestContext} object has been created.
+	 *
+	 * <p>
+	 * An example of this is the <c>PetStoreResource</c> class that uses an init method to perform initialization
+	 * of an internal data structure.
+	 *
+	 * <h5 class='figure'>Example:</h5>
+	 * <p class='bcode w800'>
+	 * 	<ja>@Rest</ja>(...)
+	 * 	<jk>public class</jk> PetStoreResource <jk>extends</jk> ResourceJena {
+	 *
+	 * 		<jc>// Our database.</jc>
+	 * 		<jk>private</jk> Map&lt;Integer,Pet&gt; <jf>petDB</jf>;
+	 *
+	 * 		<ja>@Override</ja>
+	 * 		<jk>public void</jk> onInit(RestContextBuilder builder) <jk>throws</jk> Exception {
+	 * 			<jc>// Load our database from a local JSON file.</jc>
+	 * 			<jf>petDB</jf> = JsonParser.<jsf>DEFAULT</jsf>.parse(getClass().getResourceAsStream(<js>"PetStore.json"</js>), LinkedHashMap.<jk>class</jk>, Integer.<jk>class</jk>, Pet.<jk>class</jk>);
+	 * 		}
+	 * 	}
+	 * </p>
+	 *
+	 * <ul class='notes'>
+	 * 	<li>
+	 * 		The default implementation of this method is a no-op.
+	 * 	<li>
+	 * 		Multiple INIT methods can be defined on a class.
+	 * 		<br>INIT methods on parent classes are invoked before INIT methods on child classes.
+	 * 		<br>The order of INIT method invocations within a class is alphabetical, then by parameter count, then by parameter types.
+	 * 	<li>
+	 * 		The method can throw any exception causing initialization of the servlet to fail.
+	 * </ul>
+	 *
+	 * @param builder Context builder which can be used to configure the servlet.
+	 * @throws Exception Any exception thrown will cause servlet to fail startup.
+	 */
+	@RestHook(INIT)
+	public void onInit(RestContextBuilder builder) throws Exception {}
+
+	/**
+	 * Method that gets called immediately after servlet initialization.
+	 *
+	 * <p>
+	 * This method is called from within the {@link Servlet#init(ServletConfig)} method after the {@link RestContext}
+	 * object has been created.
+	 *
+	 * <ul class='notes'>
+	 * 	<li>
+	 * 		The default implementation of this method is a no-op.
+	 * 	<li>
+	 * 		Multiple POST_INIT methods can be defined on a class.
+	 * 		<br>POST_INIT methods on parent classes are invoked before POST_INIT methods on child classes.
+	 * 		<br>The order of POST_INIT method invocations within a class is alphabetical, then by parameter count, then by parameter types.
+	 * 	<li>
+	 * 		The method can throw any exception causing initialization of the servlet to fail.
+	 * </ul>
+	 *
+	 * @param context The initialized context object.
+	 * @throws Exception Any exception thrown will cause servlet to fail startup.
+	 */
+	@RestHook(POST_INIT)
+	public void onPostInit(RestContext context) throws Exception {}
+
+	/**
+	 * Identical to {@link #onPostInit(RestContext)} except the order of execution is child-resources first.
+	 *
+	 * <p>
+	 * Use this method if you need to perform any kind of initialization on child resources before the parent resource.
+	 *
+	 * <p>
+	 * This method is called from within the {@link Servlet#init(ServletConfig)} method after the {@link RestContext}
+	 * object has been created and after the {@link #POST_INIT} methods have been called.
+	 *
+	 * <p>
+	 * The only valid parameter type for this method is {@link RestContext} which can be used to retrieve information
+	 * about the servlet.
+	 *
+	 * <ul class='notes'>
+	 * 	<li>
+	 * 		The default implementation of this method is a no-op.
+	 * 	<li>
+	 * 		Multiple POST_INIT_CHILD_FIRST methods can be defined on a class.
+	 * 		<br>POST_INIT_CHILD_FIRST methods on parent classes are invoked before POST_INIT_CHILD_FIRST methods on child classes.
+	 * 		<br>The order of POST_INIT_CHILD_FIRST method invocations within a class is alphabetical, then by parameter count, then by parameter types.
+	 * 	<li>
+	 * 		The method can throw any exception causing initialization of the servlet to fail.
+	 * </ul>
+	 *
+	 * @param context The initialized context object.
+	 * @throws Exception Any exception thrown will cause servlet to fail startup.
+	 */
+	@RestHook(POST_INIT_CHILD_FIRST)
+	public void onPostInitChildFirst(RestContext context) throws Exception {}
+
+	/**
+	 * Method that gets called during servlet destroy.
+	 *
+	 * <p>
+	 * This method is called from within the {@link Servlet#destroy()}.
+	 *
+	 * <h5 class='figure'>Example:</h5>
+	 * <p class='bcode w800'>
+	 * 	<ja>@Rest</ja>(...)
+	 * 	<jk>public class</jk> PetStoreResource <jk>extends</jk> ResourceJena {
+	 *
+	 * 		<jc>// Our database.</jc>
+	 * 		<jk>private</jk> Map&lt;Integer,Pet&gt; <jf>petDB</jf>;
+	 *
+	 * 		<ja>@Override</ja>
+	 * 		<jk>public void</jk> onDestroy(RestContext context) {
+	 * 			<jf>petDB</jf> = <jk>null</jk>;
+	 * 		}
+	 * 	}
+	 * </p>
+	 *
+	 * <ul class='notes'>
+	 * 	<li>
+	 * 		The default implementation of this method is a no-op.
+	 * 	<li>
+	 * 		Multiple DESTROY methods can be defined on a class.
+	 * 		<br>DESTROY methods on child classes are invoked before DESTROY methods on parent classes.
+	 * 		<br>The order of DESTROY method invocations within a class is alphabetical, then by parameter count, then by parameter types.
+	 * 	<li>
+	 * 		In general, destroy methods should not throw any exceptions, although if any are thrown, the stack trace will be
+	 * 		printed to <c>System.err</c>.
+	 * </ul>
+	 *
+	 * @param context The initialized context object.
+	 * @throws Exception Any exception thrown will cause stack trace to be printed to <c>System.err</c>.
+	 */
+	@RestHook(DESTROY)
+	public void onDestroy(RestContext context) throws Exception {}
+
+	/**
+	 * A method that is called immediately after the <c>HttpServlet.service(HttpServletRequest, HttpServletResponse)</c>
+	 * method is called.
+	 *
+	 * <p>
+	 * Note that you only have access to the raw request and response objects at this point.
+	 *
+	 * <h5 class='figure'>Example:</h5>
+	 * <p class='bcode w800'>
+	 * 	<ja>@Rest</ja>(...)
+	 * 	<jk>public class</jk> MyResource <jk>extends</jk> BasicRestServlet {
+	 *
+	 * 		<jc>// Add a request attribute to all incoming requests.</jc>
+	 * 		<ja>@Override</ja>
+	 * 		<jk>public void</jk> onStartCall(HttpServletRequest req, HttpServletResponse res) {
+	 * 			req.setAttribute(<js>"foobar"</js>, <jk>new</jk> FooBar());
+	 * 		}
+	 * 	}
+	 * </p>
+	 *
+	 * <ul class='notes'>
+	 * 	<li>
+	 * 		The default implementation of this method is a no-op.
+	 * 	<li>
+	 * 		Multiple START_CALL methods can be defined on a class.
+	 * 		<br>START_CALL methods on parent classes are invoked before START_CALL methods on child classes.
+	 * 		<br>The order of START_CALL method invocations within a class is alphabetical, then by parameter count, then by parameter types.
+	 * 	<li>
+	 * 		The method can throw any exception.
+	 * 		<br>{@link HttpException HttpExceptions} can be thrown to cause a particular HTTP error status code.
+	 * 		<br>All other exceptions cause an HTTP 500 error status code.
+	 * </ul>
+	 *
+	 * @param req The HTTP servlet request object.
+	 * @param res The HTTP servlet response object.
+	 * @throws Exception Any exception.
+	 */
+	@RestHook(START_CALL)
+	public void onStartCall(HttpServletRequest req, HttpServletResponse res) throws Exception {}
+
+	/**
+	 * Method that gets called immediately before the <ja>@RestMethod</ja> annotated method gets called.
+	 *
+	 * <p>
+	 * At this point, the {@link RestRequest} object has been fully initialized, and all {@link RestGuard} and
+	 * {@link RestMatcher} objects have been called.
+	 *
+	 * <ul class='notes'>
+	 * 	<li>
+	 * 		The default implementation of this method is a no-op.
+	 * 	<li>
+	 * 		Multiple PRE_CALL methods can be defined on a class.
+	 * 		<br>PRE_CALL methods on parent classes are invoked before PRE_CALL methods on child classes.
+	 * 		<br>The order of PRE_CALL method invocations within a class is alphabetical, then by parameter count, then by parameter types.
+	 * 	<li>
+	 * 		The method can throw any exception.
+	 * 		<br>{@link HttpException HttpExceptions} can be thrown to cause a particular HTTP error status code.
+	 * 		<br>All other exceptions cause an HTTP 500 error status code.
+	 * 	<li>
+	 * 		It's advisable not to mess around with the HTTP body itself since you may end up consuming the body
+	 * 		before the actual REST method has a chance to use it.
+	 * </ul>
+	 *
+	 * @param req The request object.
+	 * @param res The response object.
+	 * @throws Exception Any exception.
+	 */
+	@RestHook(PRE_CALL)
+	public void onPreCall(RestRequest req, RestResponse res) throws Exception {}
+
+	/**
+	 * Method that gets called immediately after the <ja>@RestMethod</ja> annotated method gets called.
+	 *
+	 * <p>
+	 * At this point, the output object returned by the method call has been set on the response, but
+	 * {@link RestConverter RestConverters} have not yet been executed and the response has not yet been written.
+	 *
+	 * <ul class='notes'>
+	 * 	<li>
+	 * 		The default implementation of this method is a no-op.
+	 * 	<li>
+	 * 		Multiple POST_CALL methods can be defined on a class.
+	 * 		<br>POST_CALL methods on parent classes are invoked before POST_CALL methods on child classes.
+	 * 		<br>The order of POST_CALL method invocations within a class is alphabetical, then by parameter count, then by parameter types.
+	 * 	<li>
+	 * 		The method can throw any exception, although at this point it is too late to set an HTTP error status code.
+	 * </ul>
+	 *
+	 * @param req The request object.
+	 * @param res The response object.
+	 * @throws Exception Any exception.
+	 */
+	@RestHook(POST_CALL)
+	public void onPostCall(RestRequest req, RestResponse res) throws Exception {}
+
+	/**
+	 * Method that gets called right before we exit the servlet service method.
+	 *
+	 * <p>
+	 * At this point, the output has been written and flushed.
+	 *
+	 * <p>
+	 * The following attributes are set on the {@link HttpServletRequest} object that can be useful for logging purposes:
+	 * <ul>
+	 * 	<li><js>"Exception"</js> - Any exceptions thrown during the request.
+	 * 	<li><js>"ExecTime"</js> - Execution time of the request.
+	 * </ul>
+	 *
+	 * <ul class='notes'>
+	 * 	<li>
+	 * 		The default implementation of this method is a no-op.
+	 * 	<li>
+	 * 		Multiple END_CALL methods can be defined on a class.
+	 * 		<br>END_CALL methods on parent classes are invoked before END_CALL methods on child classes.
+	 * 		<br>The order of END_CALL method invocations within a class is alphabetical, then by parameter count, then by parameter types.
+	 * 	<li>
+	 * 		The method can throw any exception, although at this point it is too late to set an HTTP error status code.
+	 * 	<li>
+	 * 		Note that if you override a parent method, you probably need to call <code><jk>super</jk>.parentMethod(...)</code>.
+	 * 		<br>The method is still considered part of the parent class for ordering purposes even though it's
+	 * 		overridden by the child class.
+	 * </ul>
+	 *
+	 * @param req The HTTP servlet request object.
+	 * @param res The HTTP servlet response object.
+	 * @throws Exception Any exception.
+	 */
+	@RestHook(END_CALL)
+	public void onEndCall(HttpServletRequest req, HttpServletResponse res) throws Exception {}
+
+	//-----------------------------------------------------------------------------------------------------------------
+	// Other methods.
 	//-----------------------------------------------------------------------------------------------------------------
 
 	/**
@@ -275,4 +578,96 @@ public abstract class RestServlet extends HttpServlet {
 	public synchronized RestResponse getResponse() {
 		return getContext().getResponse();
 	}
+
+	//-----------------------------------------------------------------------------------------------------------------
+	// RestCallHandler
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@Override /* RestCallHandler */
+	public void execute(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
+		callHandler.execute(req, res);
+	}
+
+	@Override /* RestCallHandler */
+	public RestCall createCall(HttpServletRequest req, HttpServletResponse res) {
+		return callHandler.createCall(req, res);
+	}
+
+	@Override /* RestCallHandler */
+	public RestRequest createRequest(RestCall call) throws ServletException {
+		return callHandler.createRequest(call);
+	}
+
+	@Override /* RestCallHandler */
+	public RestResponse createResponse(RestCall call) throws ServletException {
+		return callHandler.createResponse(call);
+	}
+
+	@Override /* RestCallHandler */
+	public void handleResponse(RestCall call) throws Exception {
+		callHandler.handleResponse(call);
+	}
+
+	@Override /* RestCallHandler */
+	public void handleNotFound(RestCall call) throws Exception {
+		callHandler.handleNotFound(call);
+	}
+
+	@Override /* RestCallHandler */
+	public void handleError(RestCall call, Throwable e) throws Exception {
+		callHandler.handleError(call, e);
+	}
+
+	@Override /* RestCallHandler */
+	public Throwable convertThrowable(Throwable t) {
+		return callHandler.convertThrowable(t);
+	}
+
+	@Override /* RestCallHandler */
+	public Map<String,Object> getSessionObjects(RestRequest req, RestResponse res) {
+		return callHandler.getSessionObjects(req, res);
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
+	// RestInfoProvider
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@Override /* RestInfoProvider */
+	public Swagger getSwagger(RestRequest req) throws Exception {
+		return infoProvider.getSwagger(req);
+	}
+
+	@Override /* RestInfoProvider */
+	public String getSiteName(RestRequest req) throws Exception {
+		return infoProvider.getSiteName(req);
+	}
+
+	@Override /* RestInfoProvider */
+	public String getTitle(RestRequest req) throws Exception {
+		return infoProvider.getTitle(req);
+	}
+
+	@Override /* RestInfoProvider */
+	public String getDescription(RestRequest req) throws Exception {
+		return infoProvider.getDescription(req);
+	}
+
+	@Override /* RestInfoProvider */
+	public String getMethodSummary(Method method, RestRequest req) throws Exception {
+		return infoProvider.getMethodSummary(method, req);
+	}
+
+	@Override /* RestInfoProvider */
+	public String getMethodDescription(Method method, RestRequest req) throws Exception {
+		return infoProvider.getMethodDescription(method, req);
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
+	// RestCallLogger
+	//-----------------------------------------------------------------------------------------------------------------
+
+	@Override /* RestCallLogger */
+	public void log(RestCallLoggerConfig config, HttpServletRequest req, HttpServletResponse res) {
+		callLogger.log(config, req, res);
+	}
 }
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/HookEvent.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/HookEvent.java
index cf12769..79889f7 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/HookEvent.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/HookEvent.java
@@ -39,7 +39,7 @@ import org.apache.juneau.utils.*;
 public enum HookEvent {
 
 	/**
-	 * Identifies a method that should be called immediately after the <c>HttpServlet.service(HttpServletRequest, HttpServletResponse)</c>
+	 * Identifies a method that is called immediately after the <c>HttpServlet.service(HttpServletRequest, HttpServletResponse)</c>
 	 * method is called.
 	 *
 	 * <p>
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/mock/MockServletRequest.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/mock/MockServletRequest.java
index 575b301..173fb5d 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/mock/MockServletRequest.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/mock/MockServletRequest.java
@@ -217,7 +217,7 @@ public class MockServletRequest implements HttpServletRequest, MockHttpRequest {
 	@Override /* MockHttpRequest */
 	public MockServletResponse execute() throws Exception {
 		MockServletResponse res = MockServletResponse.create();
-		restContext.getCallHandler().service(this, res);
+		restContext.getCallHandler().execute(this, res);
 
 		// If the status isn't set, something's broken.
 		if (res.getStatus() == 0)