Session to the
@@ -147,7 +150,7 @@ public class OpenSessionInViewInterceptor extends HibernateAccessor implements A
String participateAttributeName = getParticipateAttributeName();
if (asyncManager.hasConcurrentResult()) {
- if (asyncManager.initializeAsyncThread(participateAttributeName)) {
+ if (applySessionBindingInterceptor(asyncManager, participateAttributeName)) {
return;
}
}
@@ -169,8 +172,8 @@ public class OpenSessionInViewInterceptor extends HibernateAccessor implements A
SessionHolder sessionHolder = new SessionHolder(session);
TransactionSynchronizationManager.bindResource(getSessionFactory(), sessionHolder);
- WebAsyncThreadInitializer asyncThreadInitializer = createThreadInitializer(sessionHolder);
- asyncManager.registerAsyncThreadInitializer(participateAttributeName, asyncThreadInitializer);
+ asyncManager.registerCallableInterceptor(participateAttributeName,
+ new SessionBindingCallableInterceptor(sessionHolder));
}
else {
// deferred close mode
@@ -261,15 +264,36 @@ public class OpenSessionInViewInterceptor extends HibernateAccessor implements A
return getSessionFactory().toString() + PARTICIPATE_SUFFIX;
}
- private WebAsyncThreadInitializer createThreadInitializer(final SessionHolder sessionHolder) {
- return new WebAsyncThreadInitializer() {
- public void initialize() {
- TransactionSynchronizationManager.bindResource(getSessionFactory(), sessionHolder);
- }
- public void reset() {
- TransactionSynchronizationManager.unbindResource(getSessionFactory());
- }
- };
+ private boolean applySessionBindingInterceptor(WebAsyncManager asyncManager, String key) {
+ if (asyncManager.getCallableInterceptor(key) == null) {
+ return false;
+ }
+ ((SessionBindingCallableInterceptor) asyncManager.getCallableInterceptor(key)).initializeThread();
+ return true;
}
+
+ /**
+ * Bind and unbind the Hibernate {@code Session} to the current thread.
+ */
+ private class SessionBindingCallableInterceptor implements CallableProcessingInterceptor {
+
+ private final SessionHolder sessionHolder;
+
+ public SessionBindingCallableInterceptor(SessionHolder sessionHolder) {
+ this.sessionHolder = sessionHolder;
+ }
+
+ public void preProcess(NativeWebRequest request, Callable> task) {
+ initializeThread();
+ }
+
+ private void initializeThread() {
+ TransactionSynchronizationManager.bindResource(getSessionFactory(), this.sessionHolder);
+ }
+
+ public void postProcess(NativeWebRequest request, Callable> task, Object concurrentResult) {
+ TransactionSynchronizationManager.unbindResource(getSessionFactory());
+ }
+ }
}
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 a75aba8733..4393d31f67 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
@@ -17,6 +17,7 @@
package org.springframework.orm.hibernate4.support;
import java.io.IOException;
+import java.util.concurrent.Callable;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
@@ -32,9 +33,10 @@ import org.springframework.orm.hibernate4.SessionFactoryUtils;
import org.springframework.orm.hibernate4.SessionHolder;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.web.context.WebApplicationContext;
-import org.springframework.web.context.request.async.WebAsyncUtils;
+import org.springframework.web.context.request.NativeWebRequest;
+import org.springframework.web.context.request.async.CallableProcessingInterceptor;
import org.springframework.web.context.request.async.WebAsyncManager;
-import org.springframework.web.context.request.async.WebAsyncManager.WebAsyncThreadInitializer;
+import org.springframework.web.context.request.async.WebAsyncUtils;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.filter.OncePerRequestFilter;
@@ -126,14 +128,14 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter {
participate = true;
}
else {
- if (isFirstRequest || !asyncManager.initializeAsyncThread(key)) {
+ if (isFirstRequest || !applySessionBindingInterceptor(asyncManager, key)) {
logger.debug("Opening Hibernate Session in OpenSessionInViewFilter");
Session session = openSession(sessionFactory);
SessionHolder sessionHolder = new SessionHolder(session);
TransactionSynchronizationManager.bindResource(sessionFactory, sessionHolder);
- WebAsyncThreadInitializer initializer = createAsyncThreadInitializer(sessionFactory, sessionHolder);
- asyncManager.registerAsyncThreadInitializer(key, initializer);
+ asyncManager.registerCallableInterceptor(key,
+ new SessionBindingCallableInterceptor(sessionFactory, sessionHolder));
}
}
@@ -201,17 +203,39 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter {
}
}
- private WebAsyncThreadInitializer createAsyncThreadInitializer(final SessionFactory sessionFactory,
- final SessionHolder sessionHolder) {
-
- return new WebAsyncThreadInitializer() {
- public void initialize() {
- TransactionSynchronizationManager.bindResource(sessionFactory, sessionHolder);
- }
- public void reset() {
- TransactionSynchronizationManager.unbindResource(sessionFactory);
- }
- };
+ private boolean applySessionBindingInterceptor(WebAsyncManager asyncManager, String key) {
+ if (asyncManager.getCallableInterceptor(key) == null) {
+ return false;
+ }
+ ((SessionBindingCallableInterceptor) asyncManager.getCallableInterceptor(key)).initializeThread();
+ return true;
}
+
+ /**
+ * Bind and unbind the Hibernate {@code Session} to the current thread.
+ */
+ private static class SessionBindingCallableInterceptor implements CallableProcessingInterceptor {
+
+ private final SessionFactory sessionFactory;
+
+ private final SessionHolder sessionHolder;
+
+ public SessionBindingCallableInterceptor(SessionFactory sessionFactory, SessionHolder sessionHolder) {
+ this.sessionFactory = sessionFactory;
+ this.sessionHolder = sessionHolder;
+ }
+
+ public void preProcess(NativeWebRequest request, Callable> task) {
+ initializeThread();
+ }
+
+ private void initializeThread() {
+ TransactionSynchronizationManager.bindResource(this.sessionFactory, this.sessionHolder);
+ }
+
+ public void postProcess(NativeWebRequest request, Callable> task, Object concurrentResult) {
+ TransactionSynchronizationManager.unbindResource(this.sessionFactory);
+ }
+ }
}
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 05ae7f5fa2..58faf09215 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
@@ -16,6 +16,8 @@
package org.springframework.orm.hibernate4.support;
+import java.util.concurrent.Callable;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.FlushMode;
@@ -29,10 +31,11 @@ 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.NativeWebRequest;
import org.springframework.web.context.request.WebRequest;
-import org.springframework.web.context.request.async.WebAsyncUtils;
+import org.springframework.web.context.request.async.CallableProcessingInterceptor;
import org.springframework.web.context.request.async.WebAsyncManager;
-import org.springframework.web.context.request.async.WebAsyncManager.WebAsyncThreadInitializer;
+import org.springframework.web.context.request.async.WebAsyncUtils;
/**
* Spring web request interceptor that binds a Hibernate Session to the
@@ -109,7 +112,7 @@ public class OpenSessionInViewInterceptor implements AsyncWebRequestInterceptor
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
if (asyncManager.hasConcurrentResult()) {
- if (asyncManager.initializeAsyncThread(participateAttributeName)) {
+ if (applySessionBindingInterceptor(asyncManager, participateAttributeName)) {
return;
}
}
@@ -126,8 +129,8 @@ public class OpenSessionInViewInterceptor implements AsyncWebRequestInterceptor
SessionHolder sessionHolder = new SessionHolder(session);
TransactionSynchronizationManager.bindResource(getSessionFactory(), sessionHolder);
- WebAsyncThreadInitializer asyncThreadInitializer = createThreadInitializer(sessionHolder);
- asyncManager.registerAsyncThreadInitializer(participateAttributeName, asyncThreadInitializer);
+ asyncManager.registerCallableInterceptor(participateAttributeName,
+ new SessionBindingCallableInterceptor(sessionHolder));
}
}
@@ -200,15 +203,37 @@ public class OpenSessionInViewInterceptor implements AsyncWebRequestInterceptor
return getSessionFactory().toString() + PARTICIPATE_SUFFIX;
}
- private WebAsyncThreadInitializer createThreadInitializer(final SessionHolder sessionHolder) {
- return new WebAsyncThreadInitializer() {
- public void initialize() {
- TransactionSynchronizationManager.bindResource(getSessionFactory(), sessionHolder);
- }
- public void reset() {
- TransactionSynchronizationManager.unbindResource(getSessionFactory());
- }
- };
+ private boolean applySessionBindingInterceptor(WebAsyncManager asyncManager, String key) {
+ if (asyncManager.getCallableInterceptor(key) == null) {
+ return false;
+ }
+ ((SessionBindingCallableInterceptor) asyncManager.getCallableInterceptor(key)).initializeThread();
+ return true;
+ }
+
+
+ /**
+ * Bind and unbind the Hibernate {@code Session} to the current thread.
+ */
+ private class SessionBindingCallableInterceptor implements CallableProcessingInterceptor {
+
+ private final SessionHolder sessionHolder;
+
+ public SessionBindingCallableInterceptor(SessionHolder sessionHolder) {
+ this.sessionHolder = sessionHolder;
+ }
+
+ public void preProcess(NativeWebRequest request, Callable> task) {
+ initializeThread();
+ }
+
+ private void initializeThread() {
+ TransactionSynchronizationManager.bindResource(getSessionFactory(), this.sessionHolder);
+ }
+
+ public void postProcess(NativeWebRequest request, Callable> task, Object concurrentResult) {
+ TransactionSynchronizationManager.unbindResource(getSessionFactory());
+ }
}
}
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 7c9061cbea..e4fe37f8b3 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
@@ -17,6 +17,7 @@
package org.springframework.orm.jpa.support;
import java.io.IOException;
+import java.util.concurrent.Callable;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
@@ -32,9 +33,10 @@ import org.springframework.orm.jpa.EntityManagerHolder;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.StringUtils;
import org.springframework.web.context.WebApplicationContext;
-import org.springframework.web.context.request.async.WebAsyncUtils;
+import org.springframework.web.context.request.NativeWebRequest;
+import org.springframework.web.context.request.async.CallableProcessingInterceptor;
import org.springframework.web.context.request.async.WebAsyncManager;
-import org.springframework.web.context.request.async.WebAsyncManager.WebAsyncThreadInitializer;
+import org.springframework.web.context.request.async.WebAsyncUtils;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.filter.OncePerRequestFilter;
@@ -150,15 +152,14 @@ public class OpenEntityManagerInViewFilter extends OncePerRequestFilter {
participate = true;
}
else {
- if (isFirstRequest || !asyncManager.initializeAsyncThread(key)) {
+ if (isFirstRequest || !applyEntityManagerBindingInterceptor(asyncManager, key)) {
logger.debug("Opening JPA EntityManager in OpenEntityManagerInViewFilter");
try {
EntityManager em = createEntityManager(emf);
EntityManagerHolder emHolder = new EntityManagerHolder(em);
TransactionSynchronizationManager.bindResource(emf, emHolder);
- WebAsyncThreadInitializer initializer = createAsyncThreadInitializer(emf, emHolder);
- asyncManager.registerAsyncThreadInitializer(key, initializer);
+ asyncManager.registerCallableInterceptor(key, new EntityManagerBindingCallableInterceptor(emf, emHolder));
}
catch (PersistenceException ex) {
throw new DataAccessResourceFailureException("Could not create JPA EntityManager", ex);
@@ -230,17 +231,40 @@ public class OpenEntityManagerInViewFilter extends OncePerRequestFilter {
return emf.createEntityManager();
}
- private WebAsyncThreadInitializer createAsyncThreadInitializer(final EntityManagerFactory emFactory,
- final EntityManagerHolder emHolder) {
+ private boolean applyEntityManagerBindingInterceptor(WebAsyncManager asyncManager, String key) {
+ if (asyncManager.getCallableInterceptor(key) == null) {
+ return false;
+ }
+ ((EntityManagerBindingCallableInterceptor) asyncManager.getCallableInterceptor(key)).initializeThread();
+ return true;
+ }
- return new WebAsyncThreadInitializer() {
- public void initialize() {
- TransactionSynchronizationManager.bindResource(emFactory, emHolder);
- }
- public void reset() {
- TransactionSynchronizationManager.unbindResource(emFactory);
- }
- };
+ /**
+ * Bind and unbind the {@code EntityManager} to the current thread.
+ */
+ private static class EntityManagerBindingCallableInterceptor implements CallableProcessingInterceptor {
+
+ private final EntityManagerFactory emFactory;
+
+ private final EntityManagerHolder emHolder;
+
+
+ public EntityManagerBindingCallableInterceptor(EntityManagerFactory emFactory, EntityManagerHolder emHolder) {
+ this.emFactory = emFactory;
+ this.emHolder = emHolder;
+ }
+
+ public void preProcess(NativeWebRequest request, Callable> task) {
+ initializeThread();
+ }
+
+ private void initializeThread() {
+ TransactionSynchronizationManager.bindResource(this.emFactory, this.emHolder);
+ }
+
+ public void postProcess(NativeWebRequest request, Callable> task, Object concurrentResult) {
+ TransactionSynchronizationManager.unbindResource(this.emFactory);
+ }
}
}
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 fc9e39c54d..3930a1ae8c 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
@@ -16,6 +16,8 @@
package org.springframework.orm.jpa.support;
+import java.util.concurrent.Callable;
+
import javax.persistence.EntityManager;
import javax.persistence.PersistenceException;
@@ -27,10 +29,11 @@ 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.NativeWebRequest;
import org.springframework.web.context.request.WebRequest;
-import org.springframework.web.context.request.async.WebAsyncUtils;
+import org.springframework.web.context.request.async.CallableProcessingInterceptor;
import org.springframework.web.context.request.async.WebAsyncManager;
-import org.springframework.web.context.request.async.WebAsyncManager.WebAsyncThreadInitializer;
+import org.springframework.web.context.request.async.WebAsyncUtils;
/**
* Spring web request interceptor that binds a JPA EntityManager to the
@@ -76,7 +79,7 @@ public class OpenEntityManagerInViewInterceptor extends EntityManagerFactoryAcce
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
if (asyncManager.hasConcurrentResult()) {
- if (asyncManager.initializeAsyncThread(participateAttributeName)) {
+ if (applyCallableInterceptor(asyncManager, participateAttributeName)) {
return;
}
}
@@ -94,8 +97,8 @@ public class OpenEntityManagerInViewInterceptor extends EntityManagerFactoryAcce
EntityManagerHolder emHolder = new EntityManagerHolder(em);
TransactionSynchronizationManager.bindResource(getEntityManagerFactory(), emHolder);
- WebAsyncThreadInitializer asyncThreadInitializer = createThreadInitializer(emHolder);
- asyncManager.registerAsyncThreadInitializer(participateAttributeName, asyncThreadInitializer);
+ asyncManager.registerCallableInterceptor(participateAttributeName,
+ new EntityManagerBindingCallableInterceptor(emHolder));
}
catch (PersistenceException ex) {
throw new DataAccessResourceFailureException("Could not create JPA EntityManager", ex);
@@ -147,15 +150,39 @@ public class OpenEntityManagerInViewInterceptor extends EntityManagerFactoryAcce
return getEntityManagerFactory().toString() + PARTICIPATE_SUFFIX;
}
- private WebAsyncThreadInitializer createThreadInitializer(final EntityManagerHolder emHolder) {
- return new WebAsyncThreadInitializer() {
- public void initialize() {
- TransactionSynchronizationManager.bindResource(getEntityManagerFactory(), emHolder);
- }
- public void reset() {
- TransactionSynchronizationManager.unbindResource(getEntityManagerFactory());
- }
- };
+
+ private boolean applyCallableInterceptor(WebAsyncManager asyncManager, String key) {
+ if (asyncManager.getCallableInterceptor(key) == null) {
+ return false;
+ }
+ ((EntityManagerBindingCallableInterceptor) asyncManager.getCallableInterceptor(key)).initializeThread();
+ return true;
+ }
+
+
+ /**
+ * Bind and unbind the Hibernate {@code Session} to the current thread.
+ */
+ private class EntityManagerBindingCallableInterceptor implements CallableProcessingInterceptor {
+
+ private final EntityManagerHolder emHolder;
+
+
+ public EntityManagerBindingCallableInterceptor(EntityManagerHolder emHolder) {
+ this.emHolder = emHolder;
+ }
+
+ public void preProcess(NativeWebRequest request, Callable> task) {
+ initializeThread();
+ }
+
+ private void initializeThread() {
+ TransactionSynchronizationManager.bindResource(getEntityManagerFactory(), this.emHolder);
+ }
+
+ public void postProcess(NativeWebRequest request, Callable> task, Object concurrentResult) {
+ TransactionSynchronizationManager.unbindResource(getEntityManagerFactory());
+ }
}
}
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 24566685ca..6eb9b8ae34 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
@@ -181,6 +181,7 @@ public class OpenSessionInViewTests {
replay(asyncWebRequest);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(this.request);
+ asyncManager.setTaskExecutor(new SyncTaskExecutor());
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.startCallableProcessing(new Callable+ * A {@code CallableProcessingInterceptor} is invoked before and after the + * invocation of the {@code Callable} task in the asynchronous thread. + * + *
A {@code CallableProcessingInterceptor} may be registered as follows: + *
+ * CallableProcessingInterceptor interceptor = ... ;
+ * WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
+ * asyncManager.registerCallableInterceptor("key", interceptor);
+ *
+ *
+ * To register an interceptor for every request, the above can be done through
+ * a {@link WebRequestInterceptor} during pre-handling.
+ *
+ * @author Rossen Stoyanchev
+ * @since 3.2
+ */
+public interface CallableProcessingInterceptor {
+
+ /**
+ * Invoked from the asynchronous thread in which the {@code Callable} is
+ * executed, before the {@code Callable} is invoked.
+ *
+ * @param request the current request
+ * @param task the task that will produce a result
+ */
+ void preProcess(NativeWebRequest request, Callable> task) throws Exception;
+
+ /**
+ * Invoked from the asynchronous thread in which the {@code Callable} is
+ * executed, after the {@code Callable} returned a result.
+ *
+ * @param request the current request
+ * @param task the task that produced the result
+ * @param concurrentResult the result of concurrent processing, which could
+ * be a {@link Throwable} if the {@code Callable} raised an exception
+ */
+ void postProcess(NativeWebRequest request, Callable> task, Object concurrentResult) throws Exception;
+
+}
diff --git a/spring-web/src/main/java/org/springframework/web/context/request/async/DeferredResult.java b/spring-web/src/main/java/org/springframework/web/context/request/async/DeferredResult.java
index b0675c8f49..b833db4159 100644
--- a/spring-web/src/main/java/org/springframework/web/context/request/async/DeferredResult.java
+++ b/spring-web/src/main/java/org/springframework/web/context/request/async/DeferredResult.java
@@ -25,9 +25,9 @@ import org.apache.commons.logging.LogFactory;
/**
* {@code DeferredResult} provides an alternative to using a {@link Callable}
- * for asynchronous request processing. While a Callable is executed concurrently
- * on behalf of the application, with a DeferredResult the application can produce
- * the result from a thread of its choice.
+ * for asynchronous request processing. While a {@code Callable} is executed
+ * concurrently on behalf of the application, with a {@code DeferredResult} the
+ * application can produce the result from a thread of its choice.
*
* @author Rossen Stoyanchev
* @since 3.2
@@ -45,8 +45,6 @@ public final class DeferredResult If you need to be called back when a {@code DeferredResult} is set or
+ * expires, register a {@link DeferredResultProcessingInterceptor} instead.
*/
public void setResultHandler(DeferredResultHandler resultHandler) {
this.resultHandler = resultHandler;
@@ -122,14 +123,14 @@ public final class DeferredResult
+ * The result may have been set with a call to {@link #setResult(Object)},
+ * or {@link #setErrorResult(Object)}, or following a timeout, assuming a
+ * timeout result was provided to the constructor. The request may before
+ * the result set due to a timeout or network error.
*/
public boolean isSetOrExpired() {
- return (this.expired.get() || (this.result != RESULT_NONE));
+ return this.expired.get();
}
- void setExpired() {
- this.expired.set(true);
+ /**
+ * Atomically set the expired flag and return its previous value.
+ */
+ boolean getAndSetExpired() {
+ return this.expired.getAndSet(true);
}
boolean hasTimeoutResult() {
diff --git a/spring-web/src/main/java/org/springframework/web/context/request/async/DeferredResultInterceptorChain.java b/spring-web/src/main/java/org/springframework/web/context/request/async/DeferredResultInterceptorChain.java
new file mode 100644
index 0000000000..600fe0bbde
--- /dev/null
+++ b/spring-web/src/main/java/org/springframework/web/context/request/async/DeferredResultInterceptorChain.java
@@ -0,0 +1,71 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.web.context.request.NativeWebRequest;
+
+/**
+ * Assists with the invocation of {@link DeferredResultProcessingInterceptor}'s.
+ *
+ * @author Rossen Stoyanchev
+ * @since 3.2
+ */
+class DeferredResultInterceptorChain {
+
+ private static Log logger = LogFactory.getLog(DeferredResultInterceptorChain.class);
+
+ private final List A {@code DeferredResultProcessingInterceptor} is invoked before the start of
+ * asynchronous processing and either when the {@code DeferredResult} is set or
+ * when when the underlying request ends, whichever comes fist.
+ *
+ * A {@code DeferredResultProcessingInterceptor} may be registered as follows:
+ * To register an interceptor for every request, the above can be done through
+ * a {@link WebRequestInterceptor} during pre-handling.
+ *
+ * @author Rossen Stoyanchev
+ * @since 3.2
+ */
+public interface DeferredResultProcessingInterceptor {
+
+ /**
+ * Invoked before the start of concurrent handling using a
+ * {@link DeferredResult}. The invocation occurs in the thread that
+ * initiated concurrent handling.
+ *
+ * @param request the current request
+ * @param deferredResult the DeferredResult instance
+ */
+ void preProcess(NativeWebRequest request, DeferredResult> deferredResult) throws Exception;
+
+ /**
+ * Invoked when a {@link DeferredResult} is set either with a normal value
+ * or with a {@link DeferredResult#DeferredResult(Long, Object) timeout
+ * result}. The invocation occurs in the thread that set the result.
+ *
+ * If the request ends before the {@code DeferredResult} is set, then
+ * {@link #afterExpiration(NativeWebRequest, DeferredResult)} is called.
+ *
+ * @param request the current request
+ * @param deferredResult the DeferredResult that has been set
+ * @param concurrentResult the result to which the {@code DeferredResult}
+ * was set
+ */
+ void postProcess(NativeWebRequest request, DeferredResult> deferredResult,
+ Object concurrentResult) throws Exception;
+
+
+ /**
+ * Invoked when a {@link DeferredResult} expires before a result has been
+ * set possibly due to a timeout or a network error. This invocation occurs
+ * in the thread where the timeout or network error notification is
+ * processed.
+ *
+ * @param request the current request
+ * @param deferredResult the DeferredResult that has been set
+ */
+ void afterExpiration(NativeWebRequest request, DeferredResult> deferredResult) throws Exception;
+
+}
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 7f626985c2..956b6b406d 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
@@ -49,10 +49,6 @@ public class NoSupportAsyncWebRequest extends ServletWebRequest implements Async
return false;
}
- public boolean isDispatched() {
- return false;
- }
-
// Not supported
public void startAsync() {
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 ba249e0831..4049c11a66 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
@@ -24,7 +24,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
-import javax.servlet.DispatcherType;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -94,10 +93,6 @@ public class StandardServletAsyncWebRequest extends ServletWebRequest implements
return ((this.asyncContext != null) && getRequest().isAsyncStarted());
}
- public boolean isDispatched() {
- return (DispatcherType.ASYNC.equals(getRequest().getDispatcherType()));
- }
-
/**
* Whether async request processing has completed.
* It is important to avoid use of request and response objects after async
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 5349e0bc88..384199bdad 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
@@ -15,10 +15,7 @@
*/
package org.springframework.web.context.request.async;
-import java.util.ArrayList;
-import java.util.Collections;
import java.util.LinkedHashMap;
-import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
@@ -73,7 +70,11 @@ public final class WebAsyncManager {
private Object[] concurrentResultContext;
- private final Map
+ * DeferredResultProcessingInterceptor interceptor = ... ;
+ * WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
+ * asyncManager.registerDeferredResultInterceptor("key", interceptor);
+ *
+ *
+ *