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
This commit is contained in:
parent
1ac8e48ebf
commit
f9081bedb4
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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 <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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 <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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 <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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 <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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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 <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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 <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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,9 @@ public abstract class DeferredResultProcessingInterceptorAdapter implements Defe
|
|||
/**
|
||||
* This implementation is empty.
|
||||
*/
|
||||
public <T> void beforeConcurrentHandling(NativeWebRequest request, DeferredResult<T> deferredResult) throws Exception {
|
||||
@Override
|
||||
public <T> void beforeConcurrentHandling(NativeWebRequest request, DeferredResult<T> 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 <T> boolean handleTimeout(NativeWebRequest request, DeferredResult<T> deferredResult) throws Exception {
|
||||
return true;
|
||||
|
|
|
|||
Loading…
Reference in New Issue