diff --git a/spring-orm/src/main/java/org/springframework/orm/hibernate3/support/OpenSessionInViewInterceptor.java b/spring-orm/src/main/java/org/springframework/orm/hibernate3/support/OpenSessionInViewInterceptor.java index 22f49c2c0ab..abf3f865f7f 100644 --- a/spring-orm/src/main/java/org/springframework/orm/hibernate3/support/OpenSessionInViewInterceptor.java +++ b/spring-orm/src/main/java/org/springframework/orm/hibernate3/support/OpenSessionInViewInterceptor.java @@ -24,8 +24,8 @@ import org.springframework.orm.hibernate3.SessionFactoryUtils; import org.springframework.orm.hibernate3.SessionHolder; import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.ui.ModelMap; +import org.springframework.web.context.request.AsyncWebRequestInterceptor; import org.springframework.web.context.request.WebRequest; -import org.springframework.web.context.request.async.AsyncWebRequestInterceptor; import org.springframework.web.context.request.async.AsyncWebUtils; import org.springframework.web.context.request.async.WebAsyncManager; import org.springframework.web.context.request.async.WebAsyncManager.WebAsyncThreadInitializer; diff --git a/spring-orm/src/main/java/org/springframework/orm/hibernate4/support/OpenSessionInViewInterceptor.java b/spring-orm/src/main/java/org/springframework/orm/hibernate4/support/OpenSessionInViewInterceptor.java index 84cd22f0017..57f4d27787a 100644 --- a/spring-orm/src/main/java/org/springframework/orm/hibernate4/support/OpenSessionInViewInterceptor.java +++ b/spring-orm/src/main/java/org/springframework/orm/hibernate4/support/OpenSessionInViewInterceptor.java @@ -28,8 +28,8 @@ import org.springframework.orm.hibernate4.SessionFactoryUtils; import org.springframework.orm.hibernate4.SessionHolder; import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.ui.ModelMap; +import org.springframework.web.context.request.AsyncWebRequestInterceptor; import org.springframework.web.context.request.WebRequest; -import org.springframework.web.context.request.async.AsyncWebRequestInterceptor; import org.springframework.web.context.request.async.AsyncWebUtils; import org.springframework.web.context.request.async.WebAsyncManager; import org.springframework.web.context.request.async.WebAsyncManager.WebAsyncThreadInitializer; diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/support/OpenEntityManagerInViewInterceptor.java b/spring-orm/src/main/java/org/springframework/orm/jpa/support/OpenEntityManagerInViewInterceptor.java index 7b30b818302..365d7bab2f6 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/support/OpenEntityManagerInViewInterceptor.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/support/OpenEntityManagerInViewInterceptor.java @@ -26,8 +26,8 @@ import org.springframework.orm.jpa.EntityManagerFactoryUtils; import org.springframework.orm.jpa.EntityManagerHolder; import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.ui.ModelMap; +import org.springframework.web.context.request.AsyncWebRequestInterceptor; import org.springframework.web.context.request.WebRequest; -import org.springframework.web.context.request.async.AsyncWebRequestInterceptor; import org.springframework.web.context.request.async.AsyncWebUtils; import org.springframework.web.context.request.async.WebAsyncManager; import org.springframework.web.context.request.async.WebAsyncManager.WebAsyncThreadInitializer; diff --git a/spring-orm/src/test/java/org/springframework/orm/hibernate3/support/OpenSessionInViewTests.java b/spring-orm/src/test/java/org/springframework/orm/hibernate3/support/OpenSessionInViewTests.java index 087020ee3fe..2ba61a044e5 100644 --- a/spring-orm/src/test/java/org/springframework/orm/hibernate3/support/OpenSessionInViewTests.java +++ b/spring-orm/src/test/java/org/springframework/orm/hibernate3/support/OpenSessionInViewTests.java @@ -17,9 +17,9 @@ package org.springframework.orm.hibernate3.support; import static org.easymock.EasyMock.anyObject; +import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.createStrictMock; import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.expectLastCall; import static org.easymock.EasyMock.replay; import static org.easymock.EasyMock.reset; import static org.easymock.EasyMock.verify; @@ -196,6 +196,10 @@ public class OpenSessionInViewTests { // Async dispatch thread + reset(asyncWebRequest); + expect(asyncWebRequest.isDispatched()).andReturn(true); + replay(asyncWebRequest); + interceptor.preHandle(this.webRequest); assertTrue("Session not bound to async thread", TransactionSynchronizationManager.hasResource(sf)); @@ -488,11 +492,11 @@ public class OpenSessionInViewTests { } }; - AsyncWebRequest asyncWebRequest = createStrictMock(AsyncWebRequest.class); + AsyncWebRequest asyncWebRequest = createMock(AsyncWebRequest.class); asyncWebRequest.addCompletionHandler((Runnable) anyObject()); asyncWebRequest.startAsync(); - expect(asyncWebRequest.isAsyncStarted()).andReturn(true); - expectLastCall().anyTimes(); + expect(asyncWebRequest.isAsyncStarted()).andReturn(true).anyTimes(); + expect(asyncWebRequest.isDispatched()).andReturn(false).anyTimes(); replay(asyncWebRequest); WebAsyncManager asyncManager = AsyncWebUtils.getAsyncManager(this.request); @@ -520,6 +524,7 @@ public class OpenSessionInViewTests { expect(session.close()).andReturn(null); expect(asyncWebRequest.isAsyncStarted()).andReturn(false).anyTimes(); + expect(asyncWebRequest.isDispatched()).andReturn(true).anyTimes(); replay(sf); replay(session); diff --git a/spring-orm/src/test/java/org/springframework/orm/jpa/support/OpenEntityManagerInViewTests.java b/spring-orm/src/test/java/org/springframework/orm/jpa/support/OpenEntityManagerInViewTests.java index dbccafd7f2c..cd731c3b324 100644 --- a/spring-orm/src/test/java/org/springframework/orm/jpa/support/OpenEntityManagerInViewTests.java +++ b/spring-orm/src/test/java/org/springframework/orm/jpa/support/OpenEntityManagerInViewTests.java @@ -24,8 +24,6 @@ import static org.easymock.EasyMock.expectLastCall; import static org.easymock.EasyMock.replay; import static org.easymock.EasyMock.reset; import static org.easymock.EasyMock.verify; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; import java.io.IOException; import java.util.concurrent.Callable; @@ -161,7 +159,6 @@ public class OpenEntityManagerInViewTests extends TestCase { WebAsyncManager asyncManager = AsyncWebUtils.getAsyncManager(webRequest); asyncManager.setAsyncWebRequest(asyncWebRequest); - asyncManager.startCallableProcessing(new Callable() { public String call() throws Exception { return "anything"; @@ -169,14 +166,16 @@ public class OpenEntityManagerInViewTests extends TestCase { }); verify(asyncWebRequest); - reset(asyncWebRequest); - replay(asyncWebRequest); interceptor.afterConcurrentHandlingStarted(webRequest); assertFalse(TransactionSynchronizationManager.hasResource(factory)); // Async dispatch thread + reset(asyncWebRequest); + expect(asyncWebRequest.isDispatched()).andReturn(true); + replay(asyncWebRequest); + reset(manager, factory); replay(manager, factory); @@ -345,11 +344,11 @@ public class OpenEntityManagerInViewTests extends TestCase { FilterChain filterChain3 = new PassThroughFilterChain(filter2, filterChain2); - AsyncWebRequest asyncWebRequest = createStrictMock(AsyncWebRequest.class); + AsyncWebRequest asyncWebRequest = createMock(AsyncWebRequest.class); asyncWebRequest.addCompletionHandler((Runnable) anyObject()); asyncWebRequest.startAsync(); - expect(asyncWebRequest.isAsyncStarted()).andReturn(true); - expectLastCall().anyTimes(); + expect(asyncWebRequest.isAsyncStarted()).andReturn(true).anyTimes(); + expect(asyncWebRequest.isDispatched()).andReturn(false).anyTimes(); replay(asyncWebRequest); WebAsyncManager asyncManager = AsyncWebUtils.getAsyncManager(request); @@ -373,6 +372,7 @@ public class OpenEntityManagerInViewTests extends TestCase { reset(asyncWebRequest); expect(asyncWebRequest.isAsyncStarted()).andReturn(false).anyTimes(); + expect(asyncWebRequest.isDispatched()).andReturn(true).anyTimes(); replay(asyncWebRequest); assertFalse(TransactionSynchronizationManager.hasResource(factory)); diff --git a/spring-web/src/main/java/org/springframework/web/context/request/AsyncWebRequestInterceptor.java b/spring-web/src/main/java/org/springframework/web/context/request/AsyncWebRequestInterceptor.java new file mode 100644 index 00000000000..1b26cd8094f --- /dev/null +++ b/spring-web/src/main/java/org/springframework/web/context/request/AsyncWebRequestInterceptor.java @@ -0,0 +1,49 @@ +/* + * 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.context.request; + +/** + * Extends {@code WebRequestInterceptor} with a callback method invoked during + * asynchronous request handling. + * + *

When a handler starts asynchronous request handling, the DispatcherServlet + * exits without invoking {@code postHandle} and {@code afterCompletion}, as it + * normally does, since the results of request handling (e.g. ModelAndView) are + * not available in the current thread and handling is not yet complete. + * In such scenarios, the {@link #afterConcurrentHandlingStarted(WebRequest)} + * method is invoked instead allowing implementations to perform tasks such as + * cleaning up thread bound attributes. + * + *

When asynchronous handling completes, the request is dispatched to the + * container for further processing. At this stage the DispatcherServlet invokes + * {@code preHandle}, {@code postHandle} and {@code afterCompletion} as usual. + * + * @author Rossen Stoyanchev + * @since 3.2 + * + * @see org.springframework.web.context.request.async.WebAsyncManager + */ +public interface AsyncWebRequestInterceptor extends WebRequestInterceptor{ + + /** + * Called instead of {@code postHandle} and {@code afterCompletion}, when the + * the handler started handling the request concurrently. + * + * @param request the current request + */ + void afterConcurrentHandlingStarted(WebRequest request); + +} diff --git a/spring-web/src/main/java/org/springframework/web/context/request/Log4jNestedDiagnosticContextInterceptor.java b/spring-web/src/main/java/org/springframework/web/context/request/Log4jNestedDiagnosticContextInterceptor.java index e8283f8846f..207d57d26d3 100644 --- a/spring-web/src/main/java/org/springframework/web/context/request/Log4jNestedDiagnosticContextInterceptor.java +++ b/spring-web/src/main/java/org/springframework/web/context/request/Log4jNestedDiagnosticContextInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * 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. @@ -19,7 +19,6 @@ package org.springframework.web.context.request; import org.apache.log4j.Logger; import org.apache.log4j.NDC; import org.springframework.ui.ModelMap; -import org.springframework.web.context.request.async.AsyncWebUtils; /** * Request logging interceptor that adds a request context message to the @@ -31,7 +30,7 @@ import org.springframework.web.context.request.async.AsyncWebUtils; * @see org.apache.log4j.NDC#push(String) * @see org.apache.log4j.NDC#pop() */ -public class Log4jNestedDiagnosticContextInterceptor implements WebRequestInterceptor { +public class Log4jNestedDiagnosticContextInterceptor implements AsyncWebRequestInterceptor { /** Logger available to subclasses */ protected final Logger log4jLogger = Logger.getLogger(getClass()); @@ -60,11 +59,6 @@ public class Log4jNestedDiagnosticContextInterceptor implements WebRequestInterc * Adds a message the Log4J NDC before the request is processed. */ public void preHandle(WebRequest request) throws Exception { - - if (AsyncWebUtils.getAsyncManager(request).hasConcurrentResult()) { - return; - } - NDC.push(getNestedDiagnosticContextMessage(request)); } @@ -93,4 +87,15 @@ public class Log4jNestedDiagnosticContextInterceptor implements WebRequestInterc } } + /** + * Removes the log message from the Log4J NDC when the processing thread is + * exited after the start of asynchronous request handling. + */ + public void afterConcurrentHandlingStarted(WebRequest request) { + NDC.pop(); + if (NDC.getDepth() == 0) { + NDC.remove(); + } + } + } diff --git a/spring-web/src/main/java/org/springframework/web/context/request/async/AsyncWebRequest.java b/spring-web/src/main/java/org/springframework/web/context/request/async/AsyncWebRequest.java index bfadad785ae..07a4acb5743 100644 --- a/spring-web/src/main/java/org/springframework/web/context/request/async/AsyncWebRequest.java +++ b/spring-web/src/main/java/org/springframework/web/context/request/async/AsyncWebRequest.java @@ -37,7 +37,7 @@ public interface AsyncWebRequest extends NativeWebRequest { void setTimeout(Long timeout); /** - * Set a handler to be invoked if concurrent processing times out. + * Set the handler to use when concurrent handling has timed out. */ void setTimeoutHandler(Runnable runnable); diff --git a/spring-web/src/main/java/org/springframework/web/context/request/async/AsyncWebRequestInterceptor.java b/spring-web/src/main/java/org/springframework/web/context/request/async/AsyncWebRequestInterceptor.java deleted file mode 100644 index 93f85f6082a..00000000000 --- a/spring-web/src/main/java/org/springframework/web/context/request/async/AsyncWebRequestInterceptor.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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.context.request.async; - -import org.springframework.web.context.request.WebRequest; -import org.springframework.web.context.request.WebRequestInterceptor; - -/** - * Extends the WebRequestInterceptor contract for scenarios where a handler may be - * executed asynchronously. Since the handler will complete execution in another - * thread, the results are not available in the current thread, and therefore the - * DispatcherServlet exits quickly and on its way out invokes - * {@link #afterConcurrentHandlingStarted(WebRequest)} instead of - * {@code postHandle} and {@code afterCompletion}. - * When the async handler execution completes, and the request is dispatched back - * for further processing, the DispatcherServlet will invoke {@code preHandle} - * again, as well as {@code postHandle} and {@code afterCompletion}. - * - *

Existing implementations should consider the fact that {@code preHandle} may - * be invoked twice before {@code postHandle} and {@code afterCompletion} are - * called if they don't implement this contract. Once before the start of concurrent - * handling and a second time as part of an asynchronous dispatch after concurrent - * handling is done. This may be not important in most cases but when some work - * needs to be done after concurrent handling starts (e.g. clearing thread locals) - * then this contract can be implemented. - * - * @author Rossen Stoyanchev - * @since 3.2 - * - * @see WebAsyncManager - */ -public interface AsyncWebRequestInterceptor extends WebRequestInterceptor{ - - /** - * Called instead of {@code postHandle} and {@code afterCompletion}, when the - * the handler started handling the request concurrently. - * - * @param request the current request - */ - void afterConcurrentHandlingStarted(WebRequest request); - -} diff --git a/spring-web/src/main/java/org/springframework/web/context/request/async/NoSupportAsyncWebRequest.java b/spring-web/src/main/java/org/springframework/web/context/request/async/NoSupportAsyncWebRequest.java index a4f0eb07024..7f626985c2a 100644 --- a/spring-web/src/main/java/org/springframework/web/context/request/async/NoSupportAsyncWebRequest.java +++ b/spring-web/src/main/java/org/springframework/web/context/request/async/NoSupportAsyncWebRequest.java @@ -22,8 +22,7 @@ import javax.servlet.http.HttpServletResponse; import org.springframework.web.context.request.ServletWebRequest; /** - * An implementation of {@link AsyncWebRequest} to use when no underlying support is available. - * The methods {@link #startAsync()} and {@link #dispatch()} raise {@link UnsupportedOperationException}. + * An {@code AsyncWebRequest} to use when there is no underlying async support. * * @author Rossen Stoyanchev * @since 3.2 @@ -54,11 +53,13 @@ public class NoSupportAsyncWebRequest extends ServletWebRequest implements Async return false; } - public boolean isAsyncComplete() { + // Not supported + + public void startAsync() { throw new UnsupportedOperationException("No async support in a pre-Servlet 3.0 runtime"); } - public void startAsync() { + public boolean isAsyncComplete() { throw new UnsupportedOperationException("No async support in a pre-Servlet 3.0 runtime"); } diff --git a/spring-web/src/main/java/org/springframework/web/context/request/async/StandardServletAsyncWebRequest.java b/spring-web/src/main/java/org/springframework/web/context/request/async/StandardServletAsyncWebRequest.java index 593376ed7bc..ba249e08318 100644 --- a/spring-web/src/main/java/org/springframework/web/context/request/async/StandardServletAsyncWebRequest.java +++ b/spring-web/src/main/java/org/springframework/web/context/request/async/StandardServletAsyncWebRequest.java @@ -67,13 +67,19 @@ public class StandardServletAsyncWebRequest extends ServletWebRequest implements /** * {@inheritDoc} - *

The timeout period begins when the main processing thread has exited. + *

In Servlet 3 async processing, the timeout period begins after the + * container processing thread has exited. */ public void setTimeout(Long timeout) { Assert.state(!isAsyncStarted(), "Cannot change the timeout with concurrent handling in progress"); this.timeout = timeout; } + /** + * {@inheritDoc} + *

If not set, by default a timeout is handled by returning + * SERVICE_UNAVAILABLE (503). + */ public void setTimeoutHandler(Runnable timeoutHandler) { if (timeoutHandler != null) { this.timeoutHandler = timeoutHandler; diff --git a/spring-web/src/main/java/org/springframework/web/context/request/async/WebAsyncManager.java b/spring-web/src/main/java/org/springframework/web/context/request/async/WebAsyncManager.java index 08b3e5a0300..afb30989e2b 100644 --- a/spring-web/src/main/java/org/springframework/web/context/request/async/WebAsyncManager.java +++ b/spring-web/src/main/java/org/springframework/web/context/request/async/WebAsyncManager.java @@ -52,7 +52,7 @@ import org.springframework.web.util.UrlPathHelper; * @author Rossen Stoyanchev * @since 3.2 * - * @see org.springframework.web.context.request.async.AsyncWebRequestInterceptor + * @see org.springframework.web.context.request.AsyncWebRequestInterceptor * @see org.springframework.web.servlet.AsyncHandlerInterceptor * @see org.springframework.web.filter.OncePerRequestFilter#shouldFilterAsyncDispatches * @see org.springframework.web.filter.OncePerRequestFilter#isAsyncDispatch @@ -115,26 +115,23 @@ public final class WebAsyncManager { } /** - * Whether the target handler chose to handle the request asynchronously. - * A return value of "true" indicates concurrent handling is under way and the - * response will remain open. A return value of "false" will be returned again after concurrent - * handling produces a result and the request is dispatched to resume processing. + * Whether the selected handler for the current request chose to handle the + * request asynchronously. A return value of "true" indicates concurrent + * handling is under way and the response will remain open. A return value + * of "false" means concurrent handling was either not started or possibly + * that it has completed and the request was dispatched for further + * processing of the concurrent result. */ public boolean isConcurrentHandlingStarted() { - return ((this.asyncWebRequest != null) && (this.asyncWebRequest.isAsyncStarted())); + return ((this.asyncWebRequest != null) && this.asyncWebRequest.isAsyncStarted()); } /** - * Whether the request was dispatched to resume processing the result of + * Whether the request has been dispatched to process the result of * concurrent handling. */ public boolean hasConcurrentResult() { - - // TODO: - // Add check for asyncWebRequest.isDispatched() once Apache id=53632 is fixed. - // That ensure "true" is returned in the dispatched thread only. - - return this.concurrentResult != RESULT_NONE; + return ((this.concurrentResult != RESULT_NONE) && this.asyncWebRequest.isDispatched()); } /** diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/AsyncHandlerInterceptor.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/AsyncHandlerInterceptor.java index 3ce34718351..38d2c521df0 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/AsyncHandlerInterceptor.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/AsyncHandlerInterceptor.java @@ -20,23 +20,21 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** - * Extends the HandlerInterceptor contract for scenarios where a handler may be - * executed asynchronously. Since the handler will complete execution in another - * thread, the results are not available in the current thread, and therefore the - * DispatcherServlet exits quickly and on its way out invokes - * {@link #afterConcurrentHandlingStarted(HttpServletRequest, HttpServletResponse)} - * instead of {@code postHandle} and {@code afterCompletion}. - * When the async handler execution completes, and the request is dispatched back - * for further processing, the DispatcherServlet will invoke {@code preHandle} - * again, as well as {@code postHandle} and {@code afterCompletion}. + * Extends {@code HandlerInterceptor} with a callback method invoked during + * asynchronous request handling. * - *

Existing implementations should consider the fact that {@code preHandle} may - * be invoked twice before {@code postHandle} and {@code afterCompletion} are - * called if they don't implement this contract. Once before the start of concurrent - * handling and a second time as part of an asynchronous dispatch after concurrent - * handling is done. This may be not important in most cases but when some work - * needs to be done after concurrent handling starts (e.g. clearing thread locals) - * then this contract can be implemented. + *

When a handler starts asynchronous request handling, the DispatcherServlet + * exits without invoking {@code postHandle} and {@code afterCompletion}, as it + * normally does, since the results of request handling (e.g. ModelAndView) are + * not available in the current thread and handling is not yet complete. + * In such scenarios, the + * {@link #afterConcurrentHandlingStarted(HttpServletRequest, HttpServletResponse)} + * method is invoked instead allowing implementations to perform tasks such as + * cleaning up thread bound attributes. + * + *

When asynchronous handling completes, the request is dispatched to the + * container for further processing. At this stage the DispatcherServlet invokes + * {@code preHandle}, {@code postHandle} and {@code afterCompletion} as usual. * * @author Rossen Stoyanchev * @since 3.2 diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/WebRequestHandlerInterceptorAdapter.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/WebRequestHandlerInterceptorAdapter.java index d50885ab0f2..29ef20bde68 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/WebRequestHandlerInterceptorAdapter.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/WebRequestHandlerInterceptorAdapter.java @@ -20,8 +20,8 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.util.Assert; +import org.springframework.web.context.request.AsyncWebRequestInterceptor; import org.springframework.web.context.request.WebRequestInterceptor; -import org.springframework.web.context.request.async.AsyncWebRequestInterceptor; import org.springframework.web.servlet.AsyncHandlerInterceptor; import org.springframework.web.servlet.ModelAndView;