Update remaining filter with async support

Issue: SPR-9433
This commit is contained in:
Rossen Stoyanchev
2012-08-17 13:04:38 -04:00
parent cdab04a032
commit 4f55518290
10 changed files with 389 additions and 127 deletions

View File

@@ -166,7 +166,6 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter {
return this.flushMode;
}
/**
* The default value is "true" so that the filter may re-bind the opened
* {@code Session} to each asynchronously dispatched thread and postpone
@@ -186,6 +185,7 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter {
boolean participate = false;
WebAsyncManager asyncManager = AsyncWebUtils.getAsyncManager(request);
boolean isFirstRequest = !isAsyncDispatch(request);
String key = getAlreadyFilteredAttributeName();
if (isSingleSession()) {
@@ -195,7 +195,7 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter {
participate = true;
}
else {
if (!isAsyncDispatch(request) || !asyncManager.initializeAsyncThread(key)) {
if (isFirstRequest || !asyncManager.initializeAsyncThread(key)) {
logger.debug("Opening single Hibernate Session in OpenSessionInViewFilter");
Session session = getSession(sessionFactory);
SessionHolder sessionHolder = new SessionHolder(session);
@@ -240,19 +240,6 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter {
}
}
private WebAsyncThreadInitializer createAsyncThreadInitializer(final SessionFactory sessionFactory,
final SessionHolder sessionHolder) {
return new WebAsyncThreadInitializer() {
public void initialize() {
TransactionSynchronizationManager.bindResource(sessionFactory, sessionHolder);
}
public void reset() {
TransactionSynchronizationManager.unbindResource(sessionFactory);
}
};
}
/**
* Look up the SessionFactory that this filter should use,
* taking the current HTTP request as argument.
@@ -317,4 +304,17 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter {
SessionFactoryUtils.closeSession(session);
}
private WebAsyncThreadInitializer createAsyncThreadInitializer(final SessionFactory sessionFactory,
final SessionHolder sessionHolder) {
return new WebAsyncThreadInitializer() {
public void initialize() {
TransactionSynchronizationManager.bindResource(sessionFactory, sessionHolder);
}
public void reset() {
TransactionSynchronizationManager.unbindResource(sessionFactory);
}
};
}
}

View File

@@ -99,7 +99,6 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter {
return this.sessionFactoryBeanName;
}
/**
* The default value is "true" so that the filter may re-bind the opened
* {@code Session} to each asynchronously dispatched thread and postpone
@@ -119,6 +118,7 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter {
boolean participate = false;
WebAsyncManager asyncManager = AsyncWebUtils.getAsyncManager(request);
boolean isFirstRequest = !isAsyncDispatch(request);
String key = getAlreadyFilteredAttributeName();
if (TransactionSynchronizationManager.hasResource(sessionFactory)) {
@@ -126,7 +126,7 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter {
participate = true;
}
else {
if (!isAsyncDispatch(request) || !asyncManager.initializeAsyncThread(key)) {
if (isFirstRequest || !asyncManager.initializeAsyncThread(key)) {
logger.debug("Opening Hibernate Session in OpenSessionInViewFilter");
Session session = openSession(sessionFactory);
SessionHolder sessionHolder = new SessionHolder(session);
@@ -153,19 +153,6 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter {
}
}
private WebAsyncThreadInitializer createAsyncThreadInitializer(final SessionFactory sessionFactory,
final SessionHolder sessionHolder) {
return new WebAsyncThreadInitializer() {
public void initialize() {
TransactionSynchronizationManager.bindResource(sessionFactory, sessionHolder);
}
public void reset() {
TransactionSynchronizationManager.unbindResource(sessionFactory);
}
};
}
/**
* Look up the SessionFactory that this filter should use,
* taking the current HTTP request as argument.
@@ -214,4 +201,17 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter {
}
}
private WebAsyncThreadInitializer createAsyncThreadInitializer(final SessionFactory sessionFactory,
final SessionHolder sessionHolder) {
return new WebAsyncThreadInitializer() {
public void initialize() {
TransactionSynchronizationManager.bindResource(sessionFactory, sessionHolder);
}
public void reset() {
TransactionSynchronizationManager.unbindResource(sessionFactory);
}
};
}
}

View File

@@ -105,9 +105,9 @@ public class OpenSessionInViewInterceptor implements AsyncWebRequestInterceptor
*/
public void preHandle(WebRequest request) throws DataAccessException {
WebAsyncManager asyncManager = AsyncWebUtils.getAsyncManager(request);
String participateAttributeName = getParticipateAttributeName();
WebAsyncManager asyncManager = AsyncWebUtils.getAsyncManager(request);
if (asyncManager.hasConcurrentResult()) {
if (asyncManager.initializeAsyncThread(participateAttributeName)) {
return;
@@ -148,12 +148,6 @@ public class OpenSessionInViewInterceptor implements AsyncWebRequestInterceptor
}
}
public void afterConcurrentHandlingStarted(WebRequest request) {
if (!decrementParticipateCount(request)) {
TransactionSynchronizationManager.unbindResource(getSessionFactory());
}
}
private boolean decrementParticipateCount(WebRequest request) {
String participateAttributeName = getParticipateAttributeName();
Integer count = (Integer) request.getAttribute(participateAttributeName, WebRequest.SCOPE_REQUEST);
@@ -170,6 +164,12 @@ public class OpenSessionInViewInterceptor implements AsyncWebRequestInterceptor
return true;
}
public void afterConcurrentHandlingStarted(WebRequest request) {
if (!decrementParticipateCount(request)) {
TransactionSynchronizationManager.unbindResource(getSessionFactory());
}
}
/**
* Open a Session for the SessionFactory that this interceptor uses.
* <p>The default implementation delegates to the

View File

@@ -17,6 +17,7 @@
package org.springframework.orm.jpa.support;
import java.io.IOException;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceException;
@@ -31,6 +32,9 @@ import org.springframework.orm.jpa.EntityManagerHolder;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.StringUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.async.AsyncWebUtils;
import org.springframework.web.context.request.async.WebAsyncManager;
import org.springframework.web.context.request.async.WebAsyncManager.WebAsyncThreadInitializer;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.filter.OncePerRequestFilter;
@@ -119,6 +123,15 @@ public class OpenEntityManagerInViewFilter extends OncePerRequestFilter {
return this.persistenceUnitName;
}
/**
* The default value is "true" so that the filter may re-bind the opened
* {@code EntityManager} to each asynchronously dispatched thread and postpone
* closing it until the very last asynchronous dispatch.
*/
@Override
protected boolean shouldFilterAsyncDispatches() {
return true;
}
@Override
protected void doFilterInternal(
@@ -128,18 +141,28 @@ public class OpenEntityManagerInViewFilter extends OncePerRequestFilter {
EntityManagerFactory emf = lookupEntityManagerFactory(request);
boolean participate = false;
WebAsyncManager asyncManager = AsyncWebUtils.getAsyncManager(request);
boolean isFirstRequest = !isAsyncDispatch(request);
String key = getAlreadyFilteredAttributeName();
if (TransactionSynchronizationManager.hasResource(emf)) {
// Do not modify the EntityManager: just set the participate flag.
participate = true;
}
else {
logger.debug("Opening JPA EntityManager in OpenEntityManagerInViewFilter");
try {
EntityManager em = createEntityManager(emf);
TransactionSynchronizationManager.bindResource(emf, new EntityManagerHolder(em));
}
catch (PersistenceException ex) {
throw new DataAccessResourceFailureException("Could not create JPA EntityManager", ex);
if (isFirstRequest || !asyncManager.initializeAsyncThread(key)) {
logger.debug("Opening JPA EntityManager in OpenEntityManagerInViewFilter");
try {
EntityManager em = createEntityManager(emf);
EntityManagerHolder emHolder = new EntityManagerHolder(em);
TransactionSynchronizationManager.bindResource(emf, emHolder);
WebAsyncThreadInitializer initializer = createAsyncThreadInitializer(emf, emHolder);
asyncManager.registerAsyncThreadInitializer(key, initializer);
}
catch (PersistenceException ex) {
throw new DataAccessResourceFailureException("Could not create JPA EntityManager", ex);
}
}
}
@@ -151,8 +174,10 @@ public class OpenEntityManagerInViewFilter extends OncePerRequestFilter {
if (!participate) {
EntityManagerHolder emHolder = (EntityManagerHolder)
TransactionSynchronizationManager.unbindResource(emf);
logger.debug("Closing JPA EntityManager in OpenEntityManagerInViewFilter");
EntityManagerFactoryUtils.closeEntityManager(emHolder.getEntityManager());
if (isLastRequestThread(request)) {
logger.debug("Closing JPA EntityManager in OpenEntityManagerInViewFilter");
EntityManagerFactoryUtils.closeEntityManager(emHolder.getEntityManager());
}
}
}
}
@@ -205,4 +230,17 @@ public class OpenEntityManagerInViewFilter extends OncePerRequestFilter {
return emf.createEntityManager();
}
private WebAsyncThreadInitializer createAsyncThreadInitializer(final EntityManagerFactory emFactory,
final EntityManagerHolder emHolder) {
return new WebAsyncThreadInitializer() {
public void initialize() {
TransactionSynchronizationManager.bindResource(emFactory, emHolder);
}
public void reset() {
TransactionSynchronizationManager.unbindResource(emFactory);
}
};
}
}

View File

@@ -22,12 +22,15 @@ import javax.persistence.PersistenceException;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.orm.jpa.EntityManagerFactoryAccessor;
import org.springframework.orm.jpa.EntityManagerHolder;
import org.springframework.orm.jpa.EntityManagerFactoryUtils;
import org.springframework.orm.jpa.EntityManagerHolder;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.ui.ModelMap;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.context.request.WebRequestInterceptor;
import org.springframework.web.context.request.async.AsyncWebRequestInterceptor;
import org.springframework.web.context.request.async.AsyncWebUtils;
import org.springframework.web.context.request.async.WebAsyncManager;
import org.springframework.web.context.request.async.WebAsyncManager.WebAsyncThreadInitializer;
/**
* Spring web request interceptor that binds a JPA EntityManager to the
@@ -56,7 +59,7 @@ import org.springframework.web.context.request.WebRequestInterceptor;
* @see org.springframework.orm.jpa.SharedEntityManagerCreator
* @see org.springframework.transaction.support.TransactionSynchronizationManager
*/
public class OpenEntityManagerInViewInterceptor extends EntityManagerFactoryAccessor implements WebRequestInterceptor {
public class OpenEntityManagerInViewInterceptor extends EntityManagerFactoryAccessor implements AsyncWebRequestInterceptor {
/**
* Suffix that gets appended to the EntityManagerFactory toString
@@ -68,9 +71,18 @@ public class OpenEntityManagerInViewInterceptor extends EntityManagerFactoryAcce
public void preHandle(WebRequest request) throws DataAccessException {
String participateAttributeName = getParticipateAttributeName();
WebAsyncManager asyncManager = AsyncWebUtils.getAsyncManager(request);
if (asyncManager.hasConcurrentResult()) {
if (asyncManager.initializeAsyncThread(participateAttributeName)) {
return;
}
}
if (TransactionSynchronizationManager.hasResource(getEntityManagerFactory())) {
// do not modify the EntityManager: just mark the request accordingly
String participateAttributeName = getParticipateAttributeName();
Integer count = (Integer) request.getAttribute(participateAttributeName, WebRequest.SCOPE_REQUEST);
int newCount = (count != null ? count + 1 : 1);
request.setAttribute(getParticipateAttributeName(), newCount, WebRequest.SCOPE_REQUEST);
@@ -79,7 +91,11 @@ public class OpenEntityManagerInViewInterceptor extends EntityManagerFactoryAcce
logger.debug("Opening JPA EntityManager in OpenEntityManagerInViewInterceptor");
try {
EntityManager em = createEntityManager();
TransactionSynchronizationManager.bindResource(getEntityManagerFactory(), new EntityManagerHolder(em));
EntityManagerHolder emHolder = new EntityManagerHolder(em);
TransactionSynchronizationManager.bindResource(getEntityManagerFactory(), emHolder);
WebAsyncThreadInitializer asyncThreadInitializer = createThreadInitializer(emHolder);
asyncManager.registerAsyncThreadInitializer(participateAttributeName, asyncThreadInitializer);
}
catch (PersistenceException ex) {
throw new DataAccessResourceFailureException("Could not create JPA EntityManager", ex);
@@ -91,18 +107,7 @@ public class OpenEntityManagerInViewInterceptor extends EntityManagerFactoryAcce
}
public void afterCompletion(WebRequest request, Exception ex) throws DataAccessException {
String participateAttributeName = getParticipateAttributeName();
Integer count = (Integer) request.getAttribute(participateAttributeName, WebRequest.SCOPE_REQUEST);
if (count != null) {
// Do not modify the EntityManager: just clear the marker.
if (count > 1) {
request.setAttribute(participateAttributeName, count - 1, WebRequest.SCOPE_REQUEST);
}
else {
request.removeAttribute(participateAttributeName, WebRequest.SCOPE_REQUEST);
}
}
else {
if (!decrementParticipateCount(request)) {
EntityManagerHolder emHolder = (EntityManagerHolder)
TransactionSynchronizationManager.unbindResource(getEntityManagerFactory());
logger.debug("Closing JPA EntityManager in OpenEntityManagerInViewInterceptor");
@@ -110,6 +115,28 @@ public class OpenEntityManagerInViewInterceptor extends EntityManagerFactoryAcce
}
}
private boolean decrementParticipateCount(WebRequest request) {
String participateAttributeName = getParticipateAttributeName();
Integer count = (Integer) request.getAttribute(participateAttributeName, WebRequest.SCOPE_REQUEST);
if (count == null) {
return false;
}
// Do not modify the Session: just clear the marker.
if (count > 1) {
request.setAttribute(participateAttributeName, count - 1, WebRequest.SCOPE_REQUEST);
}
else {
request.removeAttribute(participateAttributeName, WebRequest.SCOPE_REQUEST);
}
return true;
}
public void afterConcurrentHandlingStarted(WebRequest request) {
if (!decrementParticipateCount(request)) {
TransactionSynchronizationManager.unbindResource(getEntityManagerFactory());
}
}
/**
* Return the name of the request attribute that identifies that a request is
* already filtered. Default implementation takes the toString representation
@@ -120,4 +147,15 @@ public class OpenEntityManagerInViewInterceptor extends EntityManagerFactoryAcce
return getEntityManagerFactory().toString() + PARTICIPATE_SUFFIX;
}
private WebAsyncThreadInitializer createThreadInitializer(final EntityManagerHolder emHolder) {
return new WebAsyncThreadInitializer() {
public void initialize() {
TransactionSynchronizationManager.bindResource(getEntityManagerFactory(), emHolder);
}
public void reset() {
TransactionSynchronizationManager.unbindResource(getEntityManagerFactory());
}
};
}
}