diff --git a/spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/support/AsyncRequestInterceptor.java b/spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/support/AsyncRequestInterceptor.java new file mode 100644 index 00000000000..38879158544 --- /dev/null +++ b/spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/support/AsyncRequestInterceptor.java @@ -0,0 +1,109 @@ +/* + * Copyright 2002-2013 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.orm.hibernate4.support; + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.hibernate.SessionFactory; +import org.springframework.orm.hibernate4.SessionFactoryUtils; +import org.springframework.orm.hibernate4.SessionHolder; +import org.springframework.transaction.support.TransactionSynchronizationManager; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.context.request.async.CallableProcessingInterceptorAdapter; +import org.springframework.web.context.request.async.DeferredResult; +import org.springframework.web.context.request.async.DeferredResultProcessingInterceptor; + +import java.util.concurrent.Callable; + +/** + * An interceptor with asynchronous web requests used in OpenSessionInViewFilter and + * OpenSessionInViewInterceptor. + * + * Ensures the following: + * 1) The session is bound/unbound when "callable processing" is started + * 2) The session is closed if an async request times out + * + * @author Rossen Stoyanchev + * @since 3.2.5 + */ +public class AsyncRequestInterceptor extends CallableProcessingInterceptorAdapter + implements DeferredResultProcessingInterceptor { + + private static Log logger = LogFactory.getLog(AsyncRequestInterceptor.class); + + private final SessionFactory sessionFactory; + + private final SessionHolder sessionHolder; + + private volatile boolean timeoutInProgress; + + public AsyncRequestInterceptor(SessionFactory sessionFactory, SessionHolder sessionHolder) { + this.sessionFactory = sessionFactory; + this.sessionHolder = sessionHolder; + } + + @Override + public void preProcess(NativeWebRequest request, Callable task) { + bindSession(); + } + + public void bindSession() { + this.timeoutInProgress = false; + TransactionSynchronizationManager.bindResource(this.sessionFactory, this.sessionHolder); + } + + @Override + public void postProcess(NativeWebRequest request, Callable task, Object concurrentResult) { + TransactionSynchronizationManager.unbindResource(this.sessionFactory); + } + + @Override + public Object handleTimeout(NativeWebRequest request, Callable task) { + this.timeoutInProgress = true; + return RESULT_NONE; // give other interceptors a chance to handle the timeout + } + + @Override + public void afterCompletion(NativeWebRequest request, Callable task) throws Exception { + closeAfterTimeout(); + } + + private void closeAfterTimeout() { + if (this.timeoutInProgress) { + logger.debug("Closing Hibernate Session after async request timeout"); + SessionFactoryUtils.closeSession(sessionHolder.getSession()); + } + } + + // Implementation of DeferredResultProcessingInterceptor methods + + public void beforeConcurrentHandling(NativeWebRequest request, DeferredResult deferredResult) { } + public void preProcess(NativeWebRequest request, DeferredResult deferredResult) { } + public void postProcess(NativeWebRequest request, DeferredResult deferredResult, Object result) { } + + @Override + public boolean handleTimeout(NativeWebRequest request, DeferredResult deferredResult) { + this.timeoutInProgress = true; + return true; // give other interceptors a chance to handle the timeout + } + + @Override + public void afterCompletion(NativeWebRequest request, DeferredResult deferredResult) { + closeAfterTimeout(); + } +} diff --git a/spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/support/OpenSessionInViewFilter.java b/spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/support/OpenSessionInViewFilter.java index 0045a5d3f87..6ab5668293c 100644 --- a/spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/support/OpenSessionInViewFilter.java +++ b/spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/support/OpenSessionInViewFilter.java @@ -16,14 +16,6 @@ package org.springframework.orm.hibernate4.support; -import java.io.IOException; -import java.util.concurrent.Callable; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - import org.hibernate.FlushMode; import org.hibernate.HibernateException; import org.hibernate.Session; @@ -33,13 +25,17 @@ 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.NativeWebRequest; -import org.springframework.web.context.request.async.CallableProcessingInterceptorAdapter; import org.springframework.web.context.request.async.WebAsyncManager; import org.springframework.web.context.request.async.WebAsyncUtils; import org.springframework.web.context.support.WebApplicationContextUtils; import org.springframework.web.filter.OncePerRequestFilter; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + /** * Servlet 2.3 Filter that binds a Hibernate Session to the thread for the entire * processing of the request. Intended for the "Open Session in View" pattern, @@ -143,8 +139,9 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter { SessionHolder sessionHolder = new SessionHolder(session); TransactionSynchronizationManager.bindResource(sessionFactory, sessionHolder); - asyncManager.registerCallableInterceptor(key, - new SessionBindingCallableInterceptor(sessionFactory, sessionHolder)); + AsyncRequestInterceptor interceptor = new AsyncRequestInterceptor(sessionFactory, sessionHolder); + asyncManager.registerCallableInterceptor(key, interceptor); + asyncManager.registerDeferredResultInterceptor(key, interceptor); } } @@ -216,37 +213,8 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter { if (asyncManager.getCallableInterceptor(key) == null) { return false; } - ((SessionBindingCallableInterceptor) asyncManager.getCallableInterceptor(key)).initializeThread(); + ((AsyncRequestInterceptor) asyncManager.getCallableInterceptor(key)).bindSession(); return true; } - - /** - * Bind and unbind the Hibernate {@code Session} to the current thread. - */ - private static class SessionBindingCallableInterceptor extends CallableProcessingInterceptorAdapter { - - private final SessionFactory sessionFactory; - - private final SessionHolder sessionHolder; - - public SessionBindingCallableInterceptor(SessionFactory sessionFactory, SessionHolder sessionHolder) { - this.sessionFactory = sessionFactory; - this.sessionHolder = sessionHolder; - } - - @Override - public void preProcess(NativeWebRequest request, Callable task) { - initializeThread(); - } - - @Override - public void postProcess(NativeWebRequest request, Callable task, Object concurrentResult) { - TransactionSynchronizationManager.unbindResource(this.sessionFactory); - } - - private void initializeThread() { - TransactionSynchronizationManager.bindResource(this.sessionFactory, this.sessionHolder); - } - } } diff --git a/spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/support/OpenSessionInViewInterceptor.java b/spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/support/OpenSessionInViewInterceptor.java index 8994255e755..f3e4ade5939 100644 --- a/spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/support/OpenSessionInViewInterceptor.java +++ b/spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/support/OpenSessionInViewInterceptor.java @@ -33,9 +33,7 @@ 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.CallableProcessingInterceptorAdapter; -import org.springframework.web.context.request.async.WebAsyncManager; -import org.springframework.web.context.request.async.WebAsyncUtils; +import org.springframework.web.context.request.async.*; /** * Spring web request interceptor that binds a Hibernate {@code Session} to the @@ -118,8 +116,10 @@ public class OpenSessionInViewInterceptor implements AsyncWebRequestInterceptor SessionHolder sessionHolder = new SessionHolder(session); TransactionSynchronizationManager.bindResource(getSessionFactory(), sessionHolder); - asyncManager.registerCallableInterceptor(participateAttributeName, - new SessionBindingCallableInterceptor(sessionHolder)); + AsyncRequestInterceptor asyncRequestInterceptor = + new AsyncRequestInterceptor(getSessionFactory(), sessionHolder); + asyncManager.registerCallableInterceptor(participateAttributeName, asyncRequestInterceptor); + asyncManager.registerDeferredResultInterceptor(participateAttributeName, asyncRequestInterceptor); } } @@ -196,35 +196,8 @@ public class OpenSessionInViewInterceptor implements AsyncWebRequestInterceptor if (asyncManager.getCallableInterceptor(key) == null) { return false; } - ((SessionBindingCallableInterceptor) asyncManager.getCallableInterceptor(key)).initializeThread(); + ((AsyncRequestInterceptor) asyncManager.getCallableInterceptor(key)).bindSession(); return true; } - - /** - * Bind and unbind the Hibernate {@code Session} to the current thread. - */ - private class SessionBindingCallableInterceptor extends CallableProcessingInterceptorAdapter { - - private final SessionHolder sessionHolder; - - public SessionBindingCallableInterceptor(SessionHolder sessionHolder) { - this.sessionHolder = sessionHolder; - } - - @Override - public void preProcess(NativeWebRequest request, Callable task) { - initializeThread(); - } - - @Override - public void postProcess(NativeWebRequest request, Callable task, Object concurrentResult) { - TransactionSynchronizationManager.unbindResource(getSessionFactory()); - } - - private void initializeThread() { - TransactionSynchronizationManager.bindResource(getSessionFactory(), this.sessionHolder); - } - } - } diff --git a/spring-orm/src/main/java/org/springframework/orm/hibernate3/support/AsyncRequestInterceptor.java b/spring-orm/src/main/java/org/springframework/orm/hibernate3/support/AsyncRequestInterceptor.java new file mode 100644 index 00000000000..4e161d87df7 --- /dev/null +++ b/spring-orm/src/main/java/org/springframework/orm/hibernate3/support/AsyncRequestInterceptor.java @@ -0,0 +1,110 @@ +/* + * Copyright 2002-2013 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.orm.hibernate3.support; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.hibernate.SessionFactory; +import org.springframework.orm.hibernate3.SessionFactoryUtils; +import org.springframework.orm.hibernate3.SessionHolder; +import org.springframework.transaction.support.TransactionSynchronizationManager; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.context.request.async.CallableProcessingInterceptorAdapter; +import org.springframework.web.context.request.async.DeferredResult; +import org.springframework.web.context.request.async.DeferredResultProcessingInterceptor; + +import java.util.concurrent.Callable; + +/** + * An interceptor with asynchronous web requests used in OpenSessionInViewFilter and + * OpenSessionInViewInterceptor. + * + * Ensures the following: + * 1) The session is bound/unbound when "callable processing" is started + * 2) The session is closed if an async request times out + * + * @author Rossen Stoyanchev + * @since 3.2.5 + */ +class AsyncRequestInterceptor extends CallableProcessingInterceptorAdapter + implements DeferredResultProcessingInterceptor { + + private static final Log logger = LogFactory.getLog(AsyncRequestInterceptor.class); + + private final SessionFactory sessionFactory; + + private final SessionHolder sessionHolder; + + private volatile boolean timeoutInProgress; + + + public AsyncRequestInterceptor(SessionFactory sessionFactory, SessionHolder sessionHolder) { + this.sessionFactory = sessionFactory; + this.sessionHolder = sessionHolder; + } + + @Override + public void preProcess(NativeWebRequest request, Callable task) { + bindSession(); + } + + public void bindSession() { + this.timeoutInProgress = false; + TransactionSynchronizationManager.bindResource(this.sessionFactory, this.sessionHolder); + } + + @Override + public void postProcess(NativeWebRequest request, Callable task, Object concurrentResult) { + TransactionSynchronizationManager.unbindResource(this.sessionFactory); + } + + @Override + public Object handleTimeout(NativeWebRequest request, Callable task) { + this.timeoutInProgress = true; + return RESULT_NONE; // give other interceptors a chance to handle the timeout + } + + @Override + public void afterCompletion(NativeWebRequest request, Callable task) throws Exception { + closeAfterTimeout(); + } + + private void closeAfterTimeout() { + if (this.timeoutInProgress) { + logger.debug("Closing Hibernate Session after async request timeout"); + SessionFactoryUtils.closeSession(sessionHolder.getSession()); + } + } + + // Implementation of DeferredResultProcessingInterceptor methods + + public void beforeConcurrentHandling(NativeWebRequest request, DeferredResult deferredResult) {} + public void preProcess(NativeWebRequest request, DeferredResult deferredResult) {} + public void postProcess(NativeWebRequest request, DeferredResult deferredResult, Object result) {} + + @Override + public boolean handleTimeout(NativeWebRequest request, DeferredResult deferredResult) { + this.timeoutInProgress = true; + return true; // give other interceptors a chance to handle the timeout + } + + @Override + public void afterCompletion(NativeWebRequest request, DeferredResult deferredResult) { + closeAfterTimeout(); + } + +} 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 7aab7c89100..ced44d6f5e2 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 @@ -17,7 +17,6 @@ package org.springframework.orm.hibernate3.support; import java.io.IOException; -import java.util.concurrent.Callable; import javax.servlet.FilterChain; import javax.servlet.ServletException; @@ -33,10 +32,7 @@ import org.springframework.orm.hibernate3.SessionHolder; import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.util.Assert; import org.springframework.web.context.WebApplicationContext; -import org.springframework.web.context.request.NativeWebRequest; -import org.springframework.web.context.request.async.CallableProcessingInterceptorAdapter; -import org.springframework.web.context.request.async.WebAsyncManager; -import org.springframework.web.context.request.async.WebAsyncUtils; +import org.springframework.web.context.request.async.*; import org.springframework.web.context.support.WebApplicationContextUtils; import org.springframework.web.filter.OncePerRequestFilter; @@ -212,8 +208,9 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter { SessionHolder sessionHolder = new SessionHolder(session); TransactionSynchronizationManager.bindResource(sessionFactory, sessionHolder); - asyncManager.registerCallableInterceptor(key, - new SessionBindingCallableInterceptor(sessionFactory, sessionHolder)); + AsyncRequestInterceptor interceptor = new AsyncRequestInterceptor(sessionFactory, sessionHolder); + asyncManager.registerCallableInterceptor(key, interceptor); + asyncManager.registerDeferredResultInterceptor(key, interceptor); } } } @@ -319,38 +316,8 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter { if (asyncManager.getCallableInterceptor(key) == null) { return false; } - ((SessionBindingCallableInterceptor) asyncManager.getCallableInterceptor(key)).initializeThread(); + ((AsyncRequestInterceptor) asyncManager.getCallableInterceptor(key)).bindSession(); return true; } - - /** - * Bind and unbind the Hibernate {@code Session} to the current thread. - */ - private static class SessionBindingCallableInterceptor extends CallableProcessingInterceptorAdapter { - - private final SessionFactory sessionFactory; - - private final SessionHolder sessionHolder; - - public SessionBindingCallableInterceptor(SessionFactory sessionFactory, SessionHolder sessionHolder) { - this.sessionFactory = sessionFactory; - this.sessionHolder = sessionHolder; - } - - @Override - public void preProcess(NativeWebRequest request, Callable task) { - initializeThread(); - } - - @Override - public void postProcess(NativeWebRequest request, Callable task, Object concurrentResult) { - TransactionSynchronizationManager.unbindResource(this.sessionFactory); - } - - private void initializeThread() { - TransactionSynchronizationManager.bindResource(this.sessionFactory, this.sessionHolder); - } - } - } 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 692db0ba20d..a713ea9740c 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 @@ -29,9 +29,7 @@ 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.CallableProcessingInterceptorAdapter; -import org.springframework.web.context.request.async.WebAsyncManager; -import org.springframework.web.context.request.async.WebAsyncUtils; +import org.springframework.web.context.request.async.*; /** * Spring web request interceptor that binds a Hibernate {@code Session} to the @@ -172,8 +170,10 @@ public class OpenSessionInViewInterceptor extends HibernateAccessor implements A SessionHolder sessionHolder = new SessionHolder(session); TransactionSynchronizationManager.bindResource(getSessionFactory(), sessionHolder); - asyncManager.registerCallableInterceptor(participateAttributeName, - new SessionBindingCallableInterceptor(sessionHolder)); + AsyncRequestInterceptor asyncRequestInterceptor = + new AsyncRequestInterceptor(getSessionFactory(), sessionHolder); + asyncManager.registerCallableInterceptor(participateAttributeName, asyncRequestInterceptor); + asyncManager.registerDeferredResultInterceptor(participateAttributeName, asyncRequestInterceptor); } else { // deferred close mode @@ -268,34 +268,8 @@ public class OpenSessionInViewInterceptor extends HibernateAccessor implements A if (asyncManager.getCallableInterceptor(key) == null) { return false; } - ((SessionBindingCallableInterceptor) asyncManager.getCallableInterceptor(key)).initializeThread(); + ((AsyncRequestInterceptor) asyncManager.getCallableInterceptor(key)).bindSession(); return true; } - - /** - * Bind and unbind the Hibernate {@code Session} to the current thread. - */ - private class SessionBindingCallableInterceptor extends CallableProcessingInterceptorAdapter { - - private final SessionHolder sessionHolder; - - public SessionBindingCallableInterceptor(SessionHolder sessionHolder) { - this.sessionHolder = sessionHolder; - } - - @Override - public void preProcess(NativeWebRequest request, Callable task) { - initializeThread(); - } - - @Override - public void postProcess(NativeWebRequest request, Callable task, Object concurrentResult) { - TransactionSynchronizationManager.unbindResource(getSessionFactory()); - } - - private void initializeThread() { - TransactionSynchronizationManager.bindResource(getSessionFactory(), this.sessionHolder); - } - } } diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/support/AsyncRequestInterceptor.java b/spring-orm/src/main/java/org/springframework/orm/jpa/support/AsyncRequestInterceptor.java new file mode 100644 index 00000000000..046bd8f6501 --- /dev/null +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/support/AsyncRequestInterceptor.java @@ -0,0 +1,111 @@ +/* + * Copyright 2002-2013 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.orm.jpa.support; + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.orm.jpa.EntityManagerFactoryUtils; +import org.springframework.orm.jpa.EntityManagerHolder; +import org.springframework.transaction.support.TransactionSynchronizationManager; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.context.request.async.CallableProcessingInterceptorAdapter; +import org.springframework.web.context.request.async.DeferredResult; +import org.springframework.web.context.request.async.DeferredResultProcessingInterceptor; + +import javax.persistence.EntityManagerFactory; +import java.util.concurrent.Callable; + +/** + * An interceptor with asynchronous web requests used in OpenSessionInViewFilter and + * OpenSessionInViewInterceptor. + * + * Ensures the following: + * 1) The session is bound/unbound when "callable processing" is started + * 2) The session is closed if an async request times out + * + * @author Rossen Stoyanchev + * @since 3.2.5 + */ +public class AsyncRequestInterceptor extends CallableProcessingInterceptorAdapter + implements DeferredResultProcessingInterceptor { + + private static Log logger = LogFactory.getLog(AsyncRequestInterceptor.class); + + private final EntityManagerFactory emFactory; + + private final EntityManagerHolder emHolder; + + private volatile boolean timeoutInProgress; + + + public AsyncRequestInterceptor(EntityManagerFactory emFactory, EntityManagerHolder emHolder) { + this.emFactory = emFactory; + this.emHolder = emHolder; + } + + @Override + public void preProcess(NativeWebRequest request, Callable task) { + bindSession(); + } + + public void bindSession() { + this.timeoutInProgress = false; + TransactionSynchronizationManager.bindResource(this.emFactory, this.emHolder); + } + + @Override + public void postProcess(NativeWebRequest request, Callable task, Object concurrentResult) { + TransactionSynchronizationManager.unbindResource(this.emFactory); + } + + @Override + public Object handleTimeout(NativeWebRequest request, Callable task) { + this.timeoutInProgress = true; + return RESULT_NONE; // give other interceptors a chance to handle the timeout + } + + @Override + public void afterCompletion(NativeWebRequest request, Callable task) throws Exception { + closeAfterTimeout(); + } + + private void closeAfterTimeout() { + if (this.timeoutInProgress) { + logger.debug("Closing JPA EntityManager after async request timeout"); + EntityManagerFactoryUtils.closeEntityManager(emHolder.getEntityManager()); + } + } + + // Implementation of DeferredResultProcessingInterceptor methods + + public void beforeConcurrentHandling(NativeWebRequest request, DeferredResult deferredResult) { } + public void preProcess(NativeWebRequest request, DeferredResult deferredResult) { } + public void postProcess(NativeWebRequest request, DeferredResult deferredResult, Object result) { } + + @Override + public boolean handleTimeout(NativeWebRequest request, DeferredResult deferredResult) { + this.timeoutInProgress = true; + return true; // give other interceptors a chance to handle the timeout + } + + @Override + public void afterCompletion(NativeWebRequest request, DeferredResult deferredResult) { + closeAfterTimeout(); + } +} + 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 7de9bf34297..673a685a183 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 @@ -34,9 +34,7 @@ import org.springframework.transaction.support.TransactionSynchronizationManager import org.springframework.util.StringUtils; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.request.NativeWebRequest; -import org.springframework.web.context.request.async.CallableProcessingInterceptorAdapter; -import org.springframework.web.context.request.async.WebAsyncManager; -import org.springframework.web.context.request.async.WebAsyncUtils; +import org.springframework.web.context.request.async.*; import org.springframework.web.context.support.WebApplicationContextUtils; import org.springframework.web.filter.OncePerRequestFilter; @@ -168,7 +166,9 @@ public class OpenEntityManagerInViewFilter extends OncePerRequestFilter { EntityManagerHolder emHolder = new EntityManagerHolder(em); TransactionSynchronizationManager.bindResource(emf, emHolder); - asyncManager.registerCallableInterceptor(key, new EntityManagerBindingCallableInterceptor(emf, emHolder)); + AsyncRequestInterceptor interceptor = new AsyncRequestInterceptor(emf, emHolder); + asyncManager.registerCallableInterceptor(key, interceptor); + asyncManager.registerDeferredResultInterceptor(key, interceptor); } catch (PersistenceException ex) { throw new DataAccessResourceFailureException("Could not create JPA EntityManager", ex); @@ -244,38 +244,8 @@ public class OpenEntityManagerInViewFilter extends OncePerRequestFilter { if (asyncManager.getCallableInterceptor(key) == null) { return false; } - ((EntityManagerBindingCallableInterceptor) asyncManager.getCallableInterceptor(key)).initializeThread(); + ((AsyncRequestInterceptor) asyncManager.getCallableInterceptor(key)).bindSession(); return true; } - /** - * Bind and unbind the {@code EntityManager} to the current thread. - */ - private static class EntityManagerBindingCallableInterceptor extends CallableProcessingInterceptorAdapter { - - private final EntityManagerFactory emFactory; - - private final EntityManagerHolder emHolder; - - - public EntityManagerBindingCallableInterceptor(EntityManagerFactory emFactory, EntityManagerHolder emHolder) { - this.emFactory = emFactory; - this.emHolder = emHolder; - } - - @Override - public void preProcess(NativeWebRequest request, Callable task) { - initializeThread(); - } - - @Override - public void postProcess(NativeWebRequest request, Callable task, Object concurrentResult) { - TransactionSynchronizationManager.unbindResource(this.emFactory); - } - - private void initializeThread() { - TransactionSynchronizationManager.bindResource(this.emFactory, this.emHolder); - } - } - } 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 8c9cf595ca8..2ca81c61d39 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 @@ -97,8 +97,9 @@ public class OpenEntityManagerInViewInterceptor extends EntityManagerFactoryAcce EntityManagerHolder emHolder = new EntityManagerHolder(em); TransactionSynchronizationManager.bindResource(getEntityManagerFactory(), emHolder); - asyncManager.registerCallableInterceptor(participateAttributeName, - new EntityManagerBindingCallableInterceptor(emHolder)); + AsyncRequestInterceptor interceptor = new AsyncRequestInterceptor(getEntityManagerFactory(), emHolder); + asyncManager.registerCallableInterceptor(participateAttributeName, interceptor); + asyncManager.registerDeferredResultInterceptor(participateAttributeName, interceptor); } catch (PersistenceException ex) { throw new DataAccessResourceFailureException("Could not create JPA EntityManager", ex); @@ -155,36 +156,8 @@ public class OpenEntityManagerInViewInterceptor extends EntityManagerFactoryAcce if (asyncManager.getCallableInterceptor(key) == null) { return false; } - ((EntityManagerBindingCallableInterceptor) asyncManager.getCallableInterceptor(key)).initializeThread(); + ((AsyncRequestInterceptor) asyncManager.getCallableInterceptor(key)).bindSession(); return true; } - - /** - * Bind and unbind the Hibernate {@code Session} to the current thread. - */ - private class EntityManagerBindingCallableInterceptor extends CallableProcessingInterceptorAdapter { - - private final EntityManagerHolder emHolder; - - - public EntityManagerBindingCallableInterceptor(EntityManagerHolder emHolder) { - this.emHolder = emHolder; - } - - @Override - public void preProcess(NativeWebRequest request, Callable task) { - initializeThread(); - } - - @Override - public void postProcess(NativeWebRequest request, Callable task, Object concurrentResult) { - TransactionSynchronizationManager.unbindResource(getEntityManagerFactory()); - } - - private void initializeThread() { - TransactionSynchronizationManager.bindResource(getEntityManagerFactory(), this.emHolder); - } - } - } diff --git a/spring-web/src/main/java/org/springframework/web/context/request/async/DeferredResultProcessingInterceptorAdapter.java b/spring-web/src/main/java/org/springframework/web/context/request/async/DeferredResultProcessingInterceptorAdapter.java index 774761bdb65..9a7387486a1 100644 --- a/spring-web/src/main/java/org/springframework/web/context/request/async/DeferredResultProcessingInterceptorAdapter.java +++ b/spring-web/src/main/java/org/springframework/web/context/request/async/DeferredResultProcessingInterceptorAdapter.java @@ -30,7 +30,9 @@ public abstract class DeferredResultProcessingInterceptorAdapter implements Defe /** * This implementation is empty. */ - public void beforeConcurrentHandling(NativeWebRequest request, DeferredResult deferredResult) throws Exception { + @Override + public void beforeConcurrentHandling(NativeWebRequest request, DeferredResult deferredResult) + throws Exception { } /** @@ -47,7 +49,8 @@ public abstract class DeferredResultProcessingInterceptorAdapter implements Defe } /** - * This implementation returns {@code true} by default. + * This implementation returns {@code true} by default allowing other interceptors + * to be given a chance to handle the timeout. */ public boolean handleTimeout(NativeWebRequest request, DeferredResult deferredResult) throws Exception { return true;