HanderInterceptor and OSIV async request changes
This change updates Open-Session-in-View filters and interceptors for use in async requests mainly ensuring the open Hibernate session is unbound from the main request processing thread and bound to the to async thread. Issue: SPR-8517
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.web.servlet;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
import org.springframework.web.context.request.async.AbstractDelegatingCallable;
|
||||
|
||||
/**
|
||||
* Extends {@link HanderInterceptor} with lifecycle methods specific to async
|
||||
* request processing.
|
||||
*
|
||||
* <p>This is the sequence of events on the main thread in an async scenario:
|
||||
* <ol>
|
||||
* <li>{@link #preHandle(WebRequest)}
|
||||
* <li>{@link #getAsyncCallable(WebRequest)}
|
||||
* <li>... <em>handler execution</em>
|
||||
* <li>{@link #postHandleAsyncStarted(WebRequest)}
|
||||
* </ol>
|
||||
*
|
||||
* <p>This is the sequence of events on the async thread:
|
||||
* <ol>
|
||||
* <li>Async {@link Callable#call()} (the {@code Callable} returned by {@code getAsyncCallable})
|
||||
* <li>... <em>async handler execution</em>
|
||||
* <li>{@link #postHandle(WebRequest, org.springframework.ui.ModelMap)}
|
||||
* <li>{@link #afterCompletion(WebRequest, Exception)}
|
||||
* </ol>
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.2
|
||||
*/
|
||||
public interface AsyncHandlerInterceptor extends HandlerInterceptor {
|
||||
|
||||
/**
|
||||
* Invoked <em>after</em> {@link #preHandle(WebRequest)} and <em>before</em>
|
||||
* the handler is executed. The returned {@link Callable} is used only if
|
||||
* handler execution leads to teh start of async processing. It is invoked
|
||||
* the async thread before the request is handled fro.
|
||||
* <p>Implementations can use this <code>Callable</code> to initialize
|
||||
* ThreadLocal attributes on the async thread.
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @param handler chosen handler to execute, for type and/or instance examination
|
||||
* @return a {@link Callable} instance or <code>null</code>
|
||||
*/
|
||||
AbstractDelegatingCallable getAsyncCallable(HttpServletRequest request, HttpServletResponse response, Object handler);
|
||||
|
||||
/**
|
||||
* Invoked <em>after</em> the execution of a handler if the handler started
|
||||
* async processing instead of handling the request. Effectively this method
|
||||
* is invoked on the way out of the main processing thread instead of
|
||||
* {@link #postHandle(WebRequest, org.springframework.ui.ModelMap)}. The
|
||||
* <code>postHandle</code> method is invoked after the request is handled
|
||||
* in the async thread.
|
||||
* <p>Implementations of this method can ensure ThreadLocal attributes bound
|
||||
* to the main thread are cleared and also prepare for binding them to the
|
||||
* async thread.
|
||||
* @param request current HTTP request
|
||||
* @param response current HTTP response
|
||||
* @param handler chosen handler to execute, for type and/or instance examination
|
||||
*/
|
||||
void postHandleAsyncStarted(HttpServletRequest request, HttpServletResponse response, Object handler);
|
||||
|
||||
}
|
||||
@@ -941,6 +941,8 @@ public class DispatcherServlet extends FrameworkServlet {
|
||||
return;
|
||||
}
|
||||
|
||||
mappedHandler.addDelegatingCallables(processedRequest, response);
|
||||
|
||||
asyncChain.addDelegatingCallable(
|
||||
getDispatchAsyncCallable(mappedHandler, request, response, processedRequest));
|
||||
|
||||
@@ -948,6 +950,7 @@ public class DispatcherServlet extends FrameworkServlet {
|
||||
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
|
||||
|
||||
if (asyncChain.isAsyncStarted()) {
|
||||
mappedHandler.applyPostHandleAsyncStarted(processedRequest, response);
|
||||
logger.debug("Exiting request thread and leaving the response open");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import javax.servlet.http.HttpServletResponse;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.context.request.async.AsyncExecutionChain;
|
||||
|
||||
/**
|
||||
* Handler execution chain, consisting of handler object and any handler interceptors.
|
||||
@@ -139,7 +140,7 @@ public class HandlerExecutionChain {
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply preHandle methods of registered interceptors.
|
||||
* Apply postHandle methods of registered interceptors.
|
||||
*/
|
||||
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv)
|
||||
throws Exception {
|
||||
@@ -153,6 +154,53 @@ public class HandlerExecutionChain {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add delegating, async Callable instances to the {@link AsyncExecutionChain}
|
||||
* for use in case of asynchronous request processing.
|
||||
*/
|
||||
void addDelegatingCallables(HttpServletRequest request, HttpServletResponse response)
|
||||
throws Exception {
|
||||
|
||||
if (getInterceptors() == null) {
|
||||
return;
|
||||
}
|
||||
for (int i = getInterceptors().length - 1; i >= 0; i--) {
|
||||
HandlerInterceptor interceptor = getInterceptors()[i];
|
||||
if (interceptor instanceof AsyncHandlerInterceptor) {
|
||||
try {
|
||||
AsyncHandlerInterceptor asyncInterceptor = (AsyncHandlerInterceptor) interceptor;
|
||||
AsyncExecutionChain chain = AsyncExecutionChain.getForCurrentRequest(request);
|
||||
chain.addDelegatingCallable(asyncInterceptor.getAsyncCallable(request, response, this.handler));
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
logger.error("HandlerInterceptor.addAsyncCallables threw exception", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger postHandleAsyncStarted callbacks on the mapped HandlerInterceptors.
|
||||
*/
|
||||
void applyPostHandleAsyncStarted(HttpServletRequest request, HttpServletResponse response)
|
||||
throws Exception {
|
||||
|
||||
if (getInterceptors() == null) {
|
||||
return;
|
||||
}
|
||||
for (int i = getInterceptors().length - 1; i >= 0; i--) {
|
||||
HandlerInterceptor interceptor = getInterceptors()[i];
|
||||
if (interceptor instanceof AsyncHandlerInterceptor) {
|
||||
try {
|
||||
((AsyncHandlerInterceptor) interceptor).postHandleAsyncStarted(request, response, this.handler);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
logger.error("HandlerInterceptor.postHandleAsyncStarted threw exception", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger afterCompletion callbacks on the mapped HandlerInterceptors.
|
||||
* Will just invoke afterCompletion for all interceptors whose preHandle invocation
|
||||
|
||||
@@ -21,7 +21,9 @@ import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.context.request.WebRequestInterceptor;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import org.springframework.web.context.request.async.AbstractDelegatingCallable;
|
||||
import org.springframework.web.context.request.async.AsyncWebRequestInterceptor;
|
||||
import org.springframework.web.servlet.AsyncHandlerInterceptor;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
/**
|
||||
@@ -33,7 +35,7 @@ import org.springframework.web.servlet.ModelAndView;
|
||||
* @see org.springframework.web.context.request.WebRequestInterceptor
|
||||
* @see org.springframework.web.servlet.HandlerInterceptor
|
||||
*/
|
||||
public class WebRequestHandlerInterceptorAdapter implements HandlerInterceptor {
|
||||
public class WebRequestHandlerInterceptorAdapter implements AsyncHandlerInterceptor {
|
||||
|
||||
private final WebRequestInterceptor requestInterceptor;
|
||||
|
||||
@@ -55,6 +57,25 @@ public class WebRequestHandlerInterceptorAdapter implements HandlerInterceptor {
|
||||
return true;
|
||||
}
|
||||
|
||||
public AbstractDelegatingCallable getAsyncCallable(HttpServletRequest request,
|
||||
HttpServletResponse response, Object handler) {
|
||||
|
||||
if (this.requestInterceptor instanceof AsyncWebRequestInterceptor) {
|
||||
AsyncWebRequestInterceptor asyncInterceptor = (AsyncWebRequestInterceptor) this.requestInterceptor;
|
||||
DispatcherServletWebRequest webRequest = new DispatcherServletWebRequest(request, response);
|
||||
return asyncInterceptor.getAsyncCallable(webRequest);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void postHandleAsyncStarted(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||
if (this.requestInterceptor instanceof AsyncWebRequestInterceptor) {
|
||||
AsyncWebRequestInterceptor asyncInterceptor = (AsyncWebRequestInterceptor) this.requestInterceptor;
|
||||
DispatcherServletWebRequest webRequest = new DispatcherServletWebRequest(request, response);
|
||||
asyncInterceptor.postHandleAsyncStarted(webRequest);
|
||||
}
|
||||
}
|
||||
|
||||
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
|
||||
throws Exception {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user