Browse Source

Add timeout async request handling to OSIV components

This change adds async web request timeout handling to OSIV filters
and interceptors to ensure the session or entity manager is released.

Issue: SPR-10874
pull/390/merge
Rossen Stoyanchev 11 years ago
parent
commit
5d8fac86d7
  1. 109
      spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/support/AsyncRequestInterceptor.java
  2. 52
      spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/support/OpenSessionInViewFilter.java
  3. 39
      spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/support/OpenSessionInViewInterceptor.java
  4. 110
      spring-orm/src/main/java/org/springframework/orm/hibernate3/support/AsyncRequestInterceptor.java
  5. 43
      spring-orm/src/main/java/org/springframework/orm/hibernate3/support/OpenSessionInViewFilter.java
  6. 38
      spring-orm/src/main/java/org/springframework/orm/hibernate3/support/OpenSessionInViewInterceptor.java
  7. 111
      spring-orm/src/main/java/org/springframework/orm/jpa/support/AsyncRequestInterceptor.java
  8. 40
      spring-orm/src/main/java/org/springframework/orm/jpa/support/OpenEntityManagerInViewFilter.java
  9. 35
      spring-orm/src/main/java/org/springframework/orm/jpa/support/OpenEntityManagerInViewInterceptor.java
  10. 152
      spring-orm/src/test/java/org/springframework/orm/hibernate3/support/OpenSessionInViewTests.java
  11. 108
      spring-orm/src/test/java/org/springframework/orm/jpa/support/OpenEntityManagerInViewTests.java
  12. 6
      spring-web/src/main/java/org/springframework/web/context/request/async/DeferredResultProcessingInterceptorAdapter.java

109
spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/support/AsyncRequestInterceptor.java

@ -0,0 +1,109 @@ @@ -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 <T> void preProcess(NativeWebRequest request, Callable<T> task) {
bindSession();
}
public void bindSession() {
this.timeoutInProgress = false;
TransactionSynchronizationManager.bindResource(this.sessionFactory, this.sessionHolder);
}
@Override
public <T> void postProcess(NativeWebRequest request, Callable<T> task, Object concurrentResult) {
TransactionSynchronizationManager.unbindResource(this.sessionFactory);
}
@Override
public <T> Object handleTimeout(NativeWebRequest request, Callable<T> task) {
this.timeoutInProgress = true;
return RESULT_NONE; // give other interceptors a chance to handle the timeout
}
@Override
public <T> void afterCompletion(NativeWebRequest request, Callable<T> 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 <T> void beforeConcurrentHandling(NativeWebRequest request, DeferredResult<T> deferredResult) { }
public <T> void preProcess(NativeWebRequest request, DeferredResult<T> deferredResult) { }
public <T> void postProcess(NativeWebRequest request, DeferredResult<T> deferredResult, Object result) { }
@Override
public <T> boolean handleTimeout(NativeWebRequest request, DeferredResult<T> deferredResult) {
this.timeoutInProgress = true;
return true; // give other interceptors a chance to handle the timeout
}
@Override
public <T> void afterCompletion(NativeWebRequest request, DeferredResult<T> deferredResult) {
closeAfterTimeout();
}
}

52
spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/support/OpenSessionInViewFilter.java

@ -16,14 +16,6 @@ @@ -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; @@ -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 { @@ -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 { @@ -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 <T> void preProcess(NativeWebRequest request, Callable<T> task) {
initializeThread();
}
@Override
public <T> void postProcess(NativeWebRequest request, Callable<T> task, Object concurrentResult) {
TransactionSynchronizationManager.unbindResource(this.sessionFactory);
}
private void initializeThread() {
TransactionSynchronizationManager.bindResource(this.sessionFactory, this.sessionHolder);
}
}
}

39
spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/support/OpenSessionInViewInterceptor.java

@ -33,9 +33,7 @@ import org.springframework.ui.ModelMap; @@ -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
@ -119,8 +117,10 @@ public class OpenSessionInViewInterceptor implements AsyncWebRequestInterceptor @@ -119,8 +117,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);
}
}
@ -200,35 +200,8 @@ public class OpenSessionInViewInterceptor implements AsyncWebRequestInterceptor @@ -200,35 +200,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 <T> void preProcess(NativeWebRequest request, Callable<T> task) {
initializeThread();
}
@Override
public <T> void postProcess(NativeWebRequest request, Callable<T> task, Object concurrentResult) {
TransactionSynchronizationManager.unbindResource(getSessionFactory());
}
private void initializeThread() {
TransactionSynchronizationManager.bindResource(getSessionFactory(), this.sessionHolder);
}
}
}

110
spring-orm/src/main/java/org/springframework/orm/hibernate3/support/AsyncRequestInterceptor.java

@ -0,0 +1,110 @@ @@ -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 <T> void preProcess(NativeWebRequest request, Callable<T> task) {
bindSession();
}
public void bindSession() {
this.timeoutInProgress = false;
TransactionSynchronizationManager.bindResource(this.sessionFactory, this.sessionHolder);
}
@Override
public <T> void postProcess(NativeWebRequest request, Callable<T> task, Object concurrentResult) {
TransactionSynchronizationManager.unbindResource(this.sessionFactory);
}
@Override
public <T> Object handleTimeout(NativeWebRequest request, Callable<T> task) {
this.timeoutInProgress = true;
return RESULT_NONE; // give other interceptors a chance to handle the timeout
}
@Override
public <T> void afterCompletion(NativeWebRequest request, Callable<T> 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 <T> void beforeConcurrentHandling(NativeWebRequest request, DeferredResult<T> deferredResult) {}
public <T> void preProcess(NativeWebRequest request, DeferredResult<T> deferredResult) {}
public <T> void postProcess(NativeWebRequest request, DeferredResult<T> deferredResult, Object result) {}
@Override
public <T> boolean handleTimeout(NativeWebRequest request, DeferredResult<T> deferredResult) {
this.timeoutInProgress = true;
return true; // give other interceptors a chance to handle the timeout
}
@Override
public <T> void afterCompletion(NativeWebRequest request, DeferredResult<T> deferredResult) {
closeAfterTimeout();
}
}

43
spring-orm/src/main/java/org/springframework/orm/hibernate3/support/OpenSessionInViewFilter.java

@ -17,7 +17,6 @@ @@ -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; @@ -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 { @@ -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 { @@ -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 <T> void preProcess(NativeWebRequest request, Callable<T> task) {
initializeThread();
}
@Override
public <T> void postProcess(NativeWebRequest request, Callable<T> task, Object concurrentResult) {
TransactionSynchronizationManager.unbindResource(this.sessionFactory);
}
private void initializeThread() {
TransactionSynchronizationManager.bindResource(this.sessionFactory, this.sessionHolder);
}
}
}

38
spring-orm/src/main/java/org/springframework/orm/hibernate3/support/OpenSessionInViewInterceptor.java

@ -29,9 +29,7 @@ import org.springframework.ui.ModelMap; @@ -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
@ -173,8 +171,10 @@ public class OpenSessionInViewInterceptor extends HibernateAccessor implements A @@ -173,8 +171,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
@ -272,34 +272,8 @@ public class OpenSessionInViewInterceptor extends HibernateAccessor implements A @@ -272,34 +272,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 <T> void preProcess(NativeWebRequest request, Callable<T> task) {
initializeThread();
}
@Override
public <T> void postProcess(NativeWebRequest request, Callable<T> task, Object concurrentResult) {
TransactionSynchronizationManager.unbindResource(getSessionFactory());
}
private void initializeThread() {
TransactionSynchronizationManager.bindResource(getSessionFactory(), this.sessionHolder);
}
}
}

111
spring-orm/src/main/java/org/springframework/orm/jpa/support/AsyncRequestInterceptor.java

@ -0,0 +1,111 @@ @@ -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 <T> void preProcess(NativeWebRequest request, Callable<T> task) {
bindSession();
}
public void bindSession() {
this.timeoutInProgress = false;
TransactionSynchronizationManager.bindResource(this.emFactory, this.emHolder);
}
@Override
public <T> void postProcess(NativeWebRequest request, Callable<T> task, Object concurrentResult) {
TransactionSynchronizationManager.unbindResource(this.emFactory);
}
@Override
public <T> Object handleTimeout(NativeWebRequest request, Callable<T> task) {
this.timeoutInProgress = true;
return RESULT_NONE; // give other interceptors a chance to handle the timeout
}
@Override
public <T> void afterCompletion(NativeWebRequest request, Callable<T> 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 <T> void beforeConcurrentHandling(NativeWebRequest request, DeferredResult<T> deferredResult) { }
public <T> void preProcess(NativeWebRequest request, DeferredResult<T> deferredResult) { }
public <T> void postProcess(NativeWebRequest request, DeferredResult<T> deferredResult, Object result) { }
@Override
public <T> boolean handleTimeout(NativeWebRequest request, DeferredResult<T> deferredResult) {
this.timeoutInProgress = true;
return true; // give other interceptors a chance to handle the timeout
}
@Override
public <T> void afterCompletion(NativeWebRequest request, DeferredResult<T> deferredResult) {
closeAfterTimeout();
}
}

40
spring-orm/src/main/java/org/springframework/orm/jpa/support/OpenEntityManagerInViewFilter.java

@ -34,9 +34,7 @@ import org.springframework.transaction.support.TransactionSynchronizationManager @@ -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;
@ -166,7 +164,9 @@ public class OpenEntityManagerInViewFilter extends OncePerRequestFilter { @@ -166,7 +164,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);
@ -242,38 +242,8 @@ public class OpenEntityManagerInViewFilter extends OncePerRequestFilter { @@ -242,38 +242,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 <T> void preProcess(NativeWebRequest request, Callable<T> task) {
initializeThread();
}
@Override
public <T> void postProcess(NativeWebRequest request, Callable<T> task, Object concurrentResult) {
TransactionSynchronizationManager.unbindResource(this.emFactory);
}
private void initializeThread() {
TransactionSynchronizationManager.bindResource(this.emFactory, this.emHolder);
}
}
}

35
spring-orm/src/main/java/org/springframework/orm/jpa/support/OpenEntityManagerInViewInterceptor.java

@ -93,8 +93,9 @@ public class OpenEntityManagerInViewInterceptor extends EntityManagerFactoryAcce @@ -93,8 +93,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);
@ -154,36 +155,8 @@ public class OpenEntityManagerInViewInterceptor extends EntityManagerFactoryAcce @@ -154,36 +155,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 <T> void preProcess(NativeWebRequest request, Callable<T> task) {
initializeThread();
}
@Override
public <T> void postProcess(NativeWebRequest request, Callable<T> task, Object concurrentResult) {
TransactionSynchronizationManager.unbindResource(getEntityManagerFactory());
}
private void initializeThread() {
TransactionSynchronizationManager.bindResource(getEntityManagerFactory(), this.emHolder);
}
}
}

152
spring-orm/src/test/java/org/springframework/orm/hibernate3/support/OpenSessionInViewTests.java

@ -18,13 +18,11 @@ package org.springframework.orm.hibernate3.support; @@ -18,13 +18,11 @@ package org.springframework.orm.hibernate3.support;
import java.io.IOException;
import java.sql.Connection;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.*;
import javax.transaction.TransactionManager;
import org.hibernate.FlushMode;
@ -35,11 +33,7 @@ import org.hibernate.engine.SessionFactoryImplementor; @@ -35,11 +33,7 @@ import org.hibernate.engine.SessionFactoryImplementor;
import org.junit.Before;
import org.junit.Test;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.mock.web.test.MockFilterConfig;
import org.springframework.mock.web.test.MockHttpServletRequest;
import org.springframework.mock.web.test.MockHttpServletResponse;
import org.springframework.mock.web.test.MockServletContext;
import org.springframework.mock.web.test.PassThroughFilterChain;
import org.springframework.mock.web.test.*;
import org.springframework.orm.hibernate3.HibernateAccessor;
import org.springframework.orm.hibernate3.HibernateTransactionManager;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
@ -50,9 +44,11 @@ import org.springframework.transaction.support.TransactionSynchronizationManager @@ -50,9 +44,11 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.async.AsyncWebRequest;
import org.springframework.web.context.request.async.StandardServletAsyncWebRequest;
import org.springframework.web.context.request.async.WebAsyncManager;
import org.springframework.web.context.request.async.WebAsyncUtils;
import org.springframework.web.context.support.StaticWebApplicationContext;
import org.springframework.web.util.NestedServletException;
import static org.junit.Assert.*;
import static org.mockito.BDDMockito.*;
@ -79,6 +75,7 @@ public class OpenSessionInViewTests { @@ -79,6 +75,7 @@ public class OpenSessionInViewTests {
public void setup() {
this.sc = new MockServletContext();
this.request = new MockHttpServletRequest(sc);
this.request.setAsyncSupported(true);
this.response = new MockHttpServletResponse();
this.webRequest = new ServletWebRequest(this.request);
}
@ -142,12 +139,10 @@ public class OpenSessionInViewTests { @@ -142,12 +139,10 @@ public class OpenSessionInViewTests {
interceptor.preHandle(this.webRequest);
assertTrue(TransactionSynchronizationManager.hasResource(sf));
AsyncWebRequest asyncWebRequest = mock(AsyncWebRequest.class);
AsyncWebRequest asyncWebRequest = new StandardServletAsyncWebRequest(this.request, this.response);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(this.request);
asyncManager.setTaskExecutor(new SyncTaskExecutor());
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.startCallableProcessing(new Callable<String>() {
@Override
public String call() throws Exception {
@ -166,13 +161,58 @@ public class OpenSessionInViewTests { @@ -166,13 +161,58 @@ public class OpenSessionInViewTests {
interceptor.postHandle(this.webRequest, null);
assertTrue(TransactionSynchronizationManager.hasResource(sf));
verify(session, never()).close();
interceptor.afterCompletion(this.webRequest, null);
assertFalse(TransactionSynchronizationManager.hasResource(sf));
verify(session).setFlushMode(FlushMode.MANUAL);
verify(asyncWebRequest, times(2)).addCompletionHandler(any(Runnable.class));
verify(asyncWebRequest).addTimeoutHandler(any(Runnable.class));
verify(asyncWebRequest).startAsync();
verify(session).close();
}
@Test
public void testOpenSessionInViewInterceptorAsyncTimeoutScenario() throws Exception {
// Initial request thread
final SessionFactory sf = mock(SessionFactory.class);
Session session = mock(Session.class);
OpenSessionInViewInterceptor interceptor = new OpenSessionInViewInterceptor();
interceptor.setSessionFactory(sf);
given(sf.openSession()).willReturn(session);
given(session.getSessionFactory()).willReturn(sf);
interceptor.preHandle(this.webRequest);
assertTrue(TransactionSynchronizationManager.hasResource(sf));
AsyncWebRequest asyncWebRequest = new StandardServletAsyncWebRequest(this.request, this.response);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(this.request);
asyncManager.setTaskExecutor(new SyncTaskExecutor());
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.startCallableProcessing(new Callable<String>() {
@Override
public String call() throws Exception {
return "anything";
}
});
interceptor.afterConcurrentHandlingStarted(this.webRequest);
assertFalse(TransactionSynchronizationManager.hasResource(sf));
verify(session, never()).close();
// Async request timeout
MockAsyncContext asyncContext = (MockAsyncContext) this.request.getAsyncContext();
for (AsyncListener listener : asyncContext.getListeners()) {
listener.onTimeout(new AsyncEvent(asyncContext));
}
for (AsyncListener listener : asyncContext.getListeners()) {
listener.onComplete(new AsyncEvent(asyncContext));
}
verify(session).close();
}
@Test
@ -376,9 +416,7 @@ public class OpenSessionInViewTests { @@ -376,9 +416,7 @@ public class OpenSessionInViewTests {
}
};
AsyncWebRequest asyncWebRequest = mock(AsyncWebRequest.class);
given(asyncWebRequest.isAsyncStarted()).willReturn(true);
AsyncWebRequest asyncWebRequest = new StandardServletAsyncWebRequest(this.request, this.response);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(this.request);
asyncManager.setTaskExecutor(new SyncTaskExecutor());
asyncManager.setAsyncWebRequest(asyncWebRequest);
@ -393,19 +431,89 @@ public class OpenSessionInViewTests { @@ -393,19 +431,89 @@ public class OpenSessionInViewTests {
filter.doFilter(this.request, this.response, filterChain);
assertFalse(TransactionSynchronizationManager.hasResource(sf));
assertEquals(1, count.get());
verify(session, never()).close();
// Async dispatch after concurrent handling produces result ...
this.request.setAsyncStarted(false);
assertFalse(TransactionSynchronizationManager.hasResource(sf));
filter.doFilter(this.request, this.response, filterChain);
assertFalse(TransactionSynchronizationManager.hasResource(sf));
assertEquals(2, count.get());
verify(session).setFlushMode(FlushMode.MANUAL);
verify(asyncWebRequest, times(2)).addCompletionHandler(any(Runnable.class));
verify(asyncWebRequest).addTimeoutHandler(any(Runnable.class));
verify(asyncWebRequest).startAsync();
verify(session).close();
wac.close();
}
@Test
public void testOpenSessionInViewFilterAsyncTimeoutScenario() throws Exception {
final SessionFactory sf = mock(SessionFactory.class);
Session session = mock(Session.class);
// Initial request during which concurrent handling starts..
given(sf.openSession()).willReturn(session);
given(session.getSessionFactory()).willReturn(sf);
StaticWebApplicationContext wac = new StaticWebApplicationContext();
wac.setServletContext(sc);
wac.getDefaultListableBeanFactory().registerSingleton("sessionFactory", sf);
wac.refresh();
sc.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac);
MockFilterConfig filterConfig = new MockFilterConfig(wac.getServletContext(), "filter");
final OpenSessionInViewFilter filter = new OpenSessionInViewFilter();
filter.init(filterConfig);
final AtomicInteger count = new AtomicInteger(0);
final AsyncWebRequest asyncWebRequest = new StandardServletAsyncWebRequest(this.request, this.response);
final MockHttpServletRequest request = this.request;
final FilterChain filterChain = new FilterChain() {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse)
throws NestedServletException {
assertTrue(TransactionSynchronizationManager.hasResource(sf));
count.incrementAndGet();
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(new SyncTaskExecutor());
asyncManager.setAsyncWebRequest(asyncWebRequest);
try {
asyncManager.startCallableProcessing(new Callable<String>() {
@Override
public String call() throws Exception {
return "anything";
}
});
}
catch (Exception e) {
throw new NestedServletException("", e);
}
}
};
assertFalse(TransactionSynchronizationManager.hasResource(sf));
filter.doFilter(this.request, this.response, filterChain);
assertFalse(TransactionSynchronizationManager.hasResource(sf));
assertEquals(1, count.get());
verify(session, never()).close();
// Async request timeout ...
MockAsyncContext asyncContext = (MockAsyncContext) this.request.getAsyncContext();
for (AsyncListener listener : asyncContext.getListeners()) {
listener.onTimeout(new AsyncEvent(asyncContext));
}
for (AsyncListener listener : asyncContext.getListeners()) {
listener.onComplete(new AsyncEvent(asyncContext));
}
verify(session).close();
wac.close();
}

108
spring-orm/src/test/java/org/springframework/orm/jpa/support/OpenEntityManagerInViewTests.java

@ -21,25 +21,19 @@ import java.util.concurrent.Callable; @@ -21,25 +21,19 @@ import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicInteger;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.mock.web.test.MockFilterConfig;
import org.springframework.mock.web.test.MockHttpServletRequest;
import org.springframework.mock.web.test.MockHttpServletResponse;
import org.springframework.mock.web.test.MockServletContext;
import org.springframework.mock.web.test.PassThroughFilterChain;
import org.springframework.mock.web.test.*;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.async.AsyncWebRequest;
import org.springframework.web.context.request.async.StandardServletAsyncWebRequest;
import org.springframework.web.context.request.async.WebAsyncManager;
import org.springframework.web.context.request.async.WebAsyncUtils;
import org.springframework.web.context.support.StaticWebApplicationContext;
@ -59,6 +53,12 @@ public class OpenEntityManagerInViewTests { @@ -59,6 +53,12 @@ public class OpenEntityManagerInViewTests {
private EntityManagerFactory factory;
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private ServletWebRequest webRequest;
@Before
public void setUp() throws Exception {
@ -66,6 +66,11 @@ public class OpenEntityManagerInViewTests { @@ -66,6 +66,11 @@ public class OpenEntityManagerInViewTests {
manager = mock(EntityManager.class);
given(factory.createEntityManager()).willReturn(manager);
this.request = new MockHttpServletRequest();
this.request.setAsyncSupported(true);
this.response = new MockHttpServletResponse();
this.webRequest = new ServletWebRequest(this.request);
}
@After
@ -79,13 +84,13 @@ public class OpenEntityManagerInViewTests { @@ -79,13 +84,13 @@ public class OpenEntityManagerInViewTests {
@Test
public void testOpenEntityManagerInViewInterceptor() throws Exception {
OpenEntityManagerInViewInterceptor interceptor = new OpenEntityManagerInViewInterceptor();
interceptor.setEntityManagerFactory(factory);
interceptor.setEntityManagerFactory(this.factory);
MockServletContext sc = new MockServletContext();
MockHttpServletRequest request = new MockHttpServletRequest(sc);
interceptor.preHandle(new ServletWebRequest(request));
assertTrue(TransactionSynchronizationManager.hasResource(factory));
assertTrue(TransactionSynchronizationManager.hasResource(this.factory));
// check that further invocations simply participate
interceptor.preHandle(new ServletWebRequest(request));
@ -120,16 +125,13 @@ public class OpenEntityManagerInViewTests { @@ -120,16 +125,13 @@ public class OpenEntityManagerInViewTests {
OpenEntityManagerInViewInterceptor interceptor = new OpenEntityManagerInViewInterceptor();
interceptor.setEntityManagerFactory(factory);
MockServletContext sc = new MockServletContext();
MockHttpServletRequest request = new MockHttpServletRequest(sc);
ServletWebRequest webRequest = new ServletWebRequest(request);
given(factory.createEntityManager()).willReturn(this.manager);
interceptor.preHandle(webRequest);
interceptor.preHandle(this.webRequest);
assertTrue(TransactionSynchronizationManager.hasResource(factory));
AsyncWebRequest asyncWebRequest = mock(AsyncWebRequest.class);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(webRequest);
AsyncWebRequest asyncWebRequest = new StandardServletAsyncWebRequest(this.request, this.response);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(this.webRequest);
asyncManager.setTaskExecutor(new SyncTaskExecutor());
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.startCallableProcessing(new Callable<String>() {
@ -139,17 +141,12 @@ public class OpenEntityManagerInViewTests { @@ -139,17 +141,12 @@ public class OpenEntityManagerInViewTests {
}
});
verify(asyncWebRequest, times(2)).addCompletionHandler(any(Runnable.class));
verify(asyncWebRequest).addTimeoutHandler(any(Runnable.class));
verify(asyncWebRequest, times(2)).addCompletionHandler(any(Runnable.class));
verify(asyncWebRequest).startAsync();
interceptor.afterConcurrentHandlingStarted(webRequest);
interceptor.afterConcurrentHandlingStarted(this.webRequest);
assertFalse(TransactionSynchronizationManager.hasResource(factory));
// Async dispatch thread
interceptor.preHandle(webRequest);
interceptor.preHandle(this.webRequest);
assertTrue(TransactionSynchronizationManager.hasResource(factory));
asyncManager.clearConcurrentResult();
@ -168,15 +165,57 @@ public class OpenEntityManagerInViewTests { @@ -168,15 +165,57 @@ public class OpenEntityManagerInViewTests {
interceptor.postHandle(new ServletWebRequest(request), null);
interceptor.afterCompletion(new ServletWebRequest(request), null);
interceptor.postHandle(webRequest, null);
interceptor.postHandle(this.webRequest, null);
assertTrue(TransactionSynchronizationManager.hasResource(factory));
given(manager.isOpen()).willReturn(true);
given(this.manager.isOpen()).willReturn(true);
interceptor.afterCompletion(webRequest, null);
interceptor.afterCompletion(this.webRequest, null);
assertFalse(TransactionSynchronizationManager.hasResource(factory));
verify(manager).close();
verify(this.manager).close();
}
@Test
public void testOpenEntityManagerInViewInterceptorAsyncTimeoutScenario() throws Exception {
// Initial request thread
OpenEntityManagerInViewInterceptor interceptor = new OpenEntityManagerInViewInterceptor();
interceptor.setEntityManagerFactory(factory);
given(this.factory.createEntityManager()).willReturn(this.manager);
interceptor.preHandle(this.webRequest);
assertTrue(TransactionSynchronizationManager.hasResource(this.factory));
AsyncWebRequest asyncWebRequest = new StandardServletAsyncWebRequest(this.request, this.response);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(this.request);
asyncManager.setTaskExecutor(new SyncTaskExecutor());
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.startCallableProcessing(new Callable<String>() {
@Override
public String call() throws Exception {
return "anything";
}
});
interceptor.afterConcurrentHandlingStarted(this.webRequest);
assertFalse(TransactionSynchronizationManager.hasResource(this.factory));
// Async request timeout
given(this.manager.isOpen()).willReturn(true);
MockAsyncContext asyncContext = (MockAsyncContext) this.request.getAsyncContext();
for (AsyncListener listener : asyncContext.getListeners()) {
listener.onTimeout(new AsyncEvent(asyncContext));
}
for (AsyncListener listener : asyncContext.getListeners()) {
listener.onComplete(new AsyncEvent(asyncContext));
}
verify(this.manager).close();
}
@Test
@ -257,8 +296,6 @@ public class OpenEntityManagerInViewTests { @@ -257,8 +296,6 @@ public class OpenEntityManagerInViewTests {
wac.getDefaultListableBeanFactory().registerSingleton("myEntityManagerFactory", factory2);
wac.refresh();
sc.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac);
MockHttpServletRequest request = new MockHttpServletRequest(sc);
MockHttpServletResponse response = new MockHttpServletResponse();
MockFilterConfig filterConfig = new MockFilterConfig(wac.getServletContext(), "filter");
MockFilterConfig filterConfig2 = new MockFilterConfig(wac.getServletContext(), "filter2");
@ -297,7 +334,7 @@ public class OpenEntityManagerInViewTests { @@ -297,7 +334,7 @@ public class OpenEntityManagerInViewTests {
AsyncWebRequest asyncWebRequest = mock(AsyncWebRequest.class);
given(asyncWebRequest.isAsyncStarted()).willReturn(true);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(this.request);
asyncManager.setTaskExecutor(new SyncTaskExecutor());
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.startCallableProcessing(new Callable<String>() {
@ -309,13 +346,12 @@ public class OpenEntityManagerInViewTests { @@ -309,13 +346,12 @@ public class OpenEntityManagerInViewTests {
assertFalse(TransactionSynchronizationManager.hasResource(factory));
assertFalse(TransactionSynchronizationManager.hasResource(factory2));
filter2.doFilter(request, response, filterChain3);
filter2.doFilter(this.request, this.response, filterChain3);
assertFalse(TransactionSynchronizationManager.hasResource(factory));
assertFalse(TransactionSynchronizationManager.hasResource(factory2));
assertEquals(1, count.get());
assertEquals(1, count2.get());
assertNotNull(request.getAttribute("invoked"));
verify(asyncWebRequest, times(2)).addCompletionHandler(any(Runnable.class));
verify(asyncWebRequest).addTimeoutHandler(any(Runnable.class));
verify(asyncWebRequest, times(2)).addCompletionHandler(any(Runnable.class));
@ -328,13 +364,13 @@ public class OpenEntityManagerInViewTests { @@ -328,13 +364,13 @@ public class OpenEntityManagerInViewTests {
assertFalse(TransactionSynchronizationManager.hasResource(factory));
assertFalse(TransactionSynchronizationManager.hasResource(factory2));
filter.doFilter(request, response, filterChain3);
filter.doFilter(this.request, this.response, filterChain3);
assertFalse(TransactionSynchronizationManager.hasResource(factory));
assertFalse(TransactionSynchronizationManager.hasResource(factory2));
assertEquals(2, count.get());
assertEquals(2, count2.get());
verify(manager).close();
verify(this.manager).close();
verify(manager2).close();
wac.close();

6
spring-web/src/main/java/org/springframework/web/context/request/async/DeferredResultProcessingInterceptorAdapter.java

@ -31,7 +31,8 @@ public abstract class DeferredResultProcessingInterceptorAdapter implements Defe @@ -31,7 +31,8 @@ public abstract class DeferredResultProcessingInterceptorAdapter implements Defe
* This implementation is empty.
*/
@Override
public <T> void beforeConcurrentHandling(NativeWebRequest request, DeferredResult<T> deferredResult) throws Exception {
public <T> void beforeConcurrentHandling(NativeWebRequest request, DeferredResult<T> deferredResult)
throws Exception {
}
/**
@ -50,7 +51,8 @@ public abstract class DeferredResultProcessingInterceptorAdapter implements Defe @@ -50,7 +51,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.
*/
@Override
public <T> boolean handleTimeout(NativeWebRequest request, DeferredResult<T> deferredResult) throws Exception {

Loading…
Cancel
Save