From d952da23382c29a07c422a97ba254dd982b8b3ad Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Sat, 27 Oct 2012 07:20:41 -0400 Subject: [PATCH] Polish (minor) async support in filters Issue: SPR-9895 --- .../support/OpenSessionInViewFilter.java | 13 ++-- .../support/OpenSessionInViewFilter.java | 10 +-- .../OpenEntityManagerInViewFilter.java | 10 +-- .../filter/AbstractRequestLoggingFilter.java | 13 ++-- .../web/filter/OncePerRequestFilter.java | 74 ++++++------------- .../web/filter/RequestContextFilter.java | 6 +- .../web/filter/ShallowEtagHeaderFilter.java | 13 ++-- 7 files changed, 59 insertions(+), 80 deletions(-) diff --git a/spring-orm/src/main/java/org/springframework/orm/hibernate3/support/OpenSessionInViewFilter.java b/spring-orm/src/main/java/org/springframework/orm/hibernate3/support/OpenSessionInViewFilter.java index ddc59cf61a9..0efc636ebf3 100644 --- a/spring-orm/src/main/java/org/springframework/orm/hibernate3/support/OpenSessionInViewFilter.java +++ b/spring-orm/src/main/java/org/springframework/orm/hibernate3/support/OpenSessionInViewFilter.java @@ -169,13 +169,13 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter { } /** - * The default value is "true" so that the filter may re-bind the opened + * The default value is "false" so that the filter may re-bind the opened * {@code Session} to each asynchronously dispatched thread and postpone * closing it until the very last asynchronous dispatch. */ @Override - protected boolean shouldFilterAsyncDispatches() { - return true; + protected boolean shouldNotFilterAsyncDispatch() { + return false; } @Override @@ -187,7 +187,7 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter { boolean participate = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); - boolean isFirstRequest = !isAsyncDispatch(request); + boolean isFirstRequest = !asyncManager.hasConcurrentResult(); String key = getAlreadyFilteredAttributeName(); if (isSingleSession()) { @@ -210,7 +210,8 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter { } else { // deferred close mode - Assert.state(isLastRequestThread(request), "Deferred close mode is not supported on async dispatches"); + Assert.state(!asyncManager.isConcurrentHandlingStarted(), + "Deferred close mode is not supported on async dispatches"); if (SessionFactoryUtils.isDeferredCloseActive(sessionFactory)) { // Do not modify deferred close: just set the participate flag. participate = true; @@ -229,7 +230,7 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter { // single session mode SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory); - if (isLastRequestThread(request)) { + if (!asyncManager.isConcurrentHandlingStarted()) { logger.debug("Closing single Hibernate Session in OpenSessionInViewFilter"); closeSession(sessionHolder.getSession(), sessionFactory); } diff --git a/spring-orm/src/main/java/org/springframework/orm/hibernate4/support/OpenSessionInViewFilter.java b/spring-orm/src/main/java/org/springframework/orm/hibernate4/support/OpenSessionInViewFilter.java index cbc5cd8e46d..5fdef4a18de 100644 --- a/spring-orm/src/main/java/org/springframework/orm/hibernate4/support/OpenSessionInViewFilter.java +++ b/spring-orm/src/main/java/org/springframework/orm/hibernate4/support/OpenSessionInViewFilter.java @@ -102,13 +102,13 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter { } /** - * The default value is "true" so that the filter may re-bind the opened + * The default value is "false" so that the filter may re-bind the opened * {@code Session} to each asynchronously dispatched thread and postpone * closing it until the very last asynchronous dispatch. */ @Override - protected boolean shouldFilterAsyncDispatches() { - return true; + protected boolean shouldNotFilterAsyncDispatch() { + return false; } @Override @@ -120,7 +120,7 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter { boolean participate = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); - boolean isFirstRequest = !isAsyncDispatch(request); + boolean isFirstRequest = !asyncManager.hasConcurrentResult(); String key = getAlreadyFilteredAttributeName(); if (TransactionSynchronizationManager.hasResource(sessionFactory)) { @@ -147,7 +147,7 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter { if (!participate) { SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory); - if (isLastRequestThread(request)) { + if (!asyncManager.isConcurrentHandlingStarted()) { logger.debug("Closing Hibernate Session in OpenSessionInViewFilter"); SessionFactoryUtils.closeSession(sessionHolder.getSession()); } diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/support/OpenEntityManagerInViewFilter.java b/spring-orm/src/main/java/org/springframework/orm/jpa/support/OpenEntityManagerInViewFilter.java index 694b0a42fae..10752757f7d 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/support/OpenEntityManagerInViewFilter.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/support/OpenEntityManagerInViewFilter.java @@ -126,13 +126,13 @@ public class OpenEntityManagerInViewFilter extends OncePerRequestFilter { } /** - * The default value is "true" so that the filter may re-bind the opened + * The default value is "false" so that the filter may re-bind the opened * {@code EntityManager} to each asynchronously dispatched thread and postpone * closing it until the very last asynchronous dispatch. */ @Override - protected boolean shouldFilterAsyncDispatches() { - return true; + protected boolean shouldNotFilterAsyncDispatch() { + return false; } @Override @@ -144,7 +144,7 @@ public class OpenEntityManagerInViewFilter extends OncePerRequestFilter { boolean participate = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); - boolean isFirstRequest = !isAsyncDispatch(request); + boolean isFirstRequest = !asyncManager.hasConcurrentResult(); String key = getAlreadyFilteredAttributeName(); if (TransactionSynchronizationManager.hasResource(emf)) { @@ -175,7 +175,7 @@ public class OpenEntityManagerInViewFilter extends OncePerRequestFilter { if (!participate) { EntityManagerHolder emHolder = (EntityManagerHolder) TransactionSynchronizationManager.unbindResource(emf); - if (isLastRequestThread(request)) { + if (!asyncManager.isConcurrentHandlingStarted()) { logger.debug("Closing JPA EntityManager in OpenEntityManagerInViewFilter"); EntityManagerFactoryUtils.closeEntityManager(emHolder.getEntityManager()); } diff --git a/spring-web/src/main/java/org/springframework/web/filter/AbstractRequestLoggingFilter.java b/spring-web/src/main/java/org/springframework/web/filter/AbstractRequestLoggingFilter.java index 4872cbf8ffe..2cdf4abe83c 100644 --- a/spring-web/src/main/java/org/springframework/web/filter/AbstractRequestLoggingFilter.java +++ b/spring-web/src/main/java/org/springframework/web/filter/AbstractRequestLoggingFilter.java @@ -32,6 +32,8 @@ import javax.servlet.http.HttpSession; import org.springframework.util.Assert; import org.springframework.util.StringUtils; +import org.springframework.web.context.request.async.WebAsyncManager; +import org.springframework.web.context.request.async.WebAsyncUtils; import org.springframework.web.util.WebUtils; /** @@ -178,13 +180,13 @@ public abstract class AbstractRequestLoggingFilter extends OncePerRequestFilter } /** - * The default value is "true" so that the filter may log a "before" message + * The default value is "false" so that the filter may log a "before" message * at the start of request processing and an "after" message at the end from * when the last asynchronously dispatched thread is exiting. */ @Override - protected boolean shouldFilterAsyncDispatches() { - return true; + protected boolean shouldNotFilterAsyncDispatch() { + return false; } /** @@ -198,7 +200,8 @@ public abstract class AbstractRequestLoggingFilter extends OncePerRequestFilter protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - boolean isFirstRequest = !isAsyncDispatch(request); + WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); + boolean isFirstRequest = !asyncManager.hasConcurrentResult(); if (isIncludePayload()) { if (isFirstRequest) { @@ -213,7 +216,7 @@ public abstract class AbstractRequestLoggingFilter extends OncePerRequestFilter filterChain.doFilter(request, response); } finally { - if (isLastRequestThread(request)) { + if (!asyncManager.isConcurrentHandlingStarted()) { afterRequest(request, getAfterMessage(request)); } } diff --git a/spring-web/src/main/java/org/springframework/web/filter/OncePerRequestFilter.java b/spring-web/src/main/java/org/springframework/web/filter/OncePerRequestFilter.java index 78f49debb89..299cb0c99b0 100644 --- a/spring-web/src/main/java/org/springframework/web/filter/OncePerRequestFilter.java +++ b/spring-web/src/main/java/org/springframework/web/filter/OncePerRequestFilter.java @@ -25,6 +25,7 @@ import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.springframework.web.context.request.async.WebAsyncManager; import org.springframework.web.context.request.async.WebAsyncUtils; /** @@ -36,7 +37,7 @@ import org.springframework.web.context.request.async.WebAsyncUtils; * as part of an {@linkplain javax.servlet.DispatcherType.ASYNC ASYNC} dispatch. * Sub-classes may decide whether to be invoked once per request or once per * request thread for as long as the same request is being processed. - * See {@link #shouldFilterAsyncDispatches()}. + * See {@link #shouldNotFilterAsyncDispatch()}. * *

The {@link #getAlreadyFilteredAttributeName} method determines how * to identify that a request is already filtered. The default implementation @@ -73,12 +74,13 @@ public abstract class OncePerRequestFilter extends GenericFilterBean { HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResponse = (HttpServletResponse) response; - boolean processAsyncRequestThread = isAsyncDispatch(httpRequest) && shouldFilterAsyncDispatches(); + WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); + boolean processAsyncDispatch = asyncManager.hasConcurrentResult() && !shouldNotFilterAsyncDispatch(); String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName(); boolean hasAlreadyFilteredAttribute = request.getAttribute(alreadyFilteredAttributeName) != null; - if ((hasAlreadyFilteredAttribute && (!processAsyncRequestThread)) || shouldNotFilter(httpRequest)) { + if ((hasAlreadyFilteredAttribute && (!processAsyncDispatch)) || shouldNotFilter(httpRequest)) { // Proceed without invoking this filter... filterChain.doFilter(request, response); @@ -90,7 +92,7 @@ public abstract class OncePerRequestFilter extends GenericFilterBean { doFilterInternal(httpRequest, httpResponse, filterChain); } finally { - if (isLastRequestThread(httpRequest)) { + if (!asyncManager.isConcurrentHandlingStarted()) { // Remove the "already filtered" request attribute for this request. request.removeAttribute(alreadyFilteredAttributeName); } @@ -128,60 +130,30 @@ public abstract class OncePerRequestFilter extends GenericFilterBean { } /** - * Whether to filter once per request or once per request thread. The dispatcher - * type {@code javax.servlet.DispatcherType.ASYNC} introduced in Servlet 3.0 - * means a filter can be invoked in more than one thread (and exited) over the - * course of a single request. Some filters only need to filter the initial - * thread (e.g. request wrapping) while others may need to be invoked at least - * once in each additional thread for example for setting up thread locals or - * to perform final processing at the very end. + * Whether to filter async dispatches, which occur in a different thread. + * The dispatcher type {@code javax.servlet.DispatcherType.ASYNC} introduced + * in Servlet 3.0 means a filter can be invoked in more than one thread (and + * exited) over the course of a single request. Some filters only need to + * filter the initial thread (e.g. request wrapping) while others may need + * to be invoked at least once in each additional thread for example for + * setting up thread locals or to perform final processing at the very end. *

Note that although a filter can be mapped to handle specific dispatcher * types via {@code web.xml} or in Java through the {@code ServletContext}, - * servlet containers may enforce different defaults with regards to dispatcher - * types. This flag enforces the design intent of the filter. - *

The default setting is "false", which means the filter will be invoked - * once only per request and only on the initial request thread. If "true", the - * filter will also be invoked once only on each additional thread. - * - * @see org.springframework.web.context.request.async.WebAsyncManager + * servlet containers may enforce different defaults with regards to + * dispatcher types. This flag enforces the design intent of the filter. + *

The default setting is "true", which means the filter will not be + * invoked during subsequent async dispatches. If "false", the filter will + * be invoked during async dispatches with the same guarantees of being + * invoked only once during a request within a single thread. */ - protected boolean shouldFilterAsyncDispatches() { - return false; - } - - /** - * Whether the request was dispatched to complete processing of results produced - * in another thread. This aligns with the Servlet 3.0 dispatcher type - * {@code javax.servlet.DispatcherType.ASYNC} and can be used by filters that - * return "true" from {@link #shouldFilterAsyncDispatches()} to detect when - * the filter is being invoked subsequently in additional thread(s). - * - * @see org.springframework.web.context.request.async.WebAsyncManager - */ - protected final boolean isAsyncDispatch(HttpServletRequest request) { - return WebAsyncUtils.getAsyncManager(request).hasConcurrentResult(); - } - - /** - * Whether this is the last thread processing the request. Note the returned - * value may change from {@code true} to {@code false} if the method is - * invoked before and after delegating to the next filter, since the next filter - * or servlet may begin concurrent processing. Therefore this method is most - * useful after delegation for final, end-of-request type processing. - * @param request the current request - * @return {@code true} if the response will be committed when the current - * thread exits; {@code false} if the response will remain open. - * - * @see org.springframework.web.context.request.async.WebAsyncManager - */ - protected final boolean isLastRequestThread(HttpServletRequest request) { - return (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()); + protected boolean shouldNotFilterAsyncDispatch() { + return true; } /** * Same contract as for doFilter, but guaranteed to be - * just invoked once per request or once per request thread. - * See {@link #shouldFilterAsyncDispatches()} for details. + * just invoked once per request within a single request thread. + * See {@link #shouldNotFilterAsyncDispatch()} for details. *

Provides HttpServletRequest and HttpServletResponse arguments instead of the * default ServletRequest and ServletResponse ones. */ diff --git a/spring-web/src/main/java/org/springframework/web/filter/RequestContextFilter.java b/spring-web/src/main/java/org/springframework/web/filter/RequestContextFilter.java index 9fe7cfed8a5..2777604d1be 100644 --- a/spring-web/src/main/java/org/springframework/web/filter/RequestContextFilter.java +++ b/spring-web/src/main/java/org/springframework/web/filter/RequestContextFilter.java @@ -71,12 +71,12 @@ public class RequestContextFilter extends OncePerRequestFilter { /** - * The default value is "true" in which case the filter will set up the request + * The default value is "false" in which case the filter will set up the request * context in each asynchronously dispatched thread. */ @Override - protected boolean shouldFilterAsyncDispatches() { - return true; + protected boolean shouldNotFilterAsyncDispatch() { + return false; } @Override diff --git a/spring-web/src/main/java/org/springframework/web/filter/ShallowEtagHeaderFilter.java b/spring-web/src/main/java/org/springframework/web/filter/ShallowEtagHeaderFilter.java index 6852e126242..86510437085 100644 --- a/spring-web/src/main/java/org/springframework/web/filter/ShallowEtagHeaderFilter.java +++ b/spring-web/src/main/java/org/springframework/web/filter/ShallowEtagHeaderFilter.java @@ -32,6 +32,8 @@ import javax.servlet.http.HttpServletResponseWrapper; import org.springframework.util.Assert; import org.springframework.util.DigestUtils; import org.springframework.util.FileCopyUtils; +import org.springframework.web.context.request.async.WebAsyncManager; +import org.springframework.web.context.request.async.WebAsyncUtils; import org.springframework.web.util.WebUtils; /** @@ -54,19 +56,20 @@ public class ShallowEtagHeaderFilter extends OncePerRequestFilter { /** - * The default value is "true" so that the filter may delay the generation of + * The default value is "false" so that the filter may delay the generation of * an ETag until the last asynchronously dispatched thread. */ @Override - protected boolean shouldFilterAsyncDispatches() { - return true; + protected boolean shouldNotFilterAsyncDispatch() { + return false; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - boolean isFirstRequest = !isAsyncDispatch(request); + WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); + boolean isFirstRequest = !asyncManager.hasConcurrentResult(); if (isFirstRequest) { response = new ShallowEtagResponseWrapper(response); @@ -74,7 +77,7 @@ public class ShallowEtagHeaderFilter extends OncePerRequestFilter { filterChain.doFilter(request, response); - if (isLastRequestThread(request)) { + if (!asyncManager.isConcurrentHandlingStarted()) { updateResponse(request, response); } }