diff --git a/spring-orm/src/main/java/org/springframework/orm/hibernate3/support/OpenSessionInViewFilter.java b/spring-orm/src/main/java/org/springframework/orm/hibernate3/support/OpenSessionInViewFilter.java
index d5fe421631..fbcac64f64 100644
--- a/spring-orm/src/main/java/org/springframework/orm/hibernate3/support/OpenSessionInViewFilter.java
+++ b/spring-orm/src/main/java/org/springframework/orm/hibernate3/support/OpenSessionInViewFilter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2009 the original author or authors.
+ * Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,6 +31,8 @@ import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.orm.hibernate3.SessionHolder;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.web.context.WebApplicationContext;
+import org.springframework.web.context.request.async.AbstractDelegatingCallable;
+import org.springframework.web.context.request.async.AsyncExecutionChain;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.filter.OncePerRequestFilter;
@@ -168,6 +170,8 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter {
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
+ AsyncExecutionChain chain = AsyncExecutionChain.getForCurrentRequest(request);
+
SessionFactory sessionFactory = lookupSessionFactory(request);
boolean participate = false;
@@ -180,7 +184,10 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter {
else {
logger.debug("Opening single Hibernate Session in OpenSessionInViewFilter");
Session session = getSession(sessionFactory);
- TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
+ SessionHolder sessionHolder = new SessionHolder(session);
+ TransactionSynchronizationManager.bindResource(sessionFactory, sessionHolder);
+
+ chain.addDelegatingCallable(getAsyncCallable(request, sessionFactory, sessionHolder));
}
}
else {
@@ -204,6 +211,9 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter {
// single session mode
SessionHolder sessionHolder =
(SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory);
+ if (chain.isAsyncStarted()) {
+ return;
+ }
logger.debug("Closing single Hibernate Session in OpenSessionInViewFilter");
closeSession(sessionHolder.getSession(), sessionFactory);
}
@@ -279,4 +289,28 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter {
SessionFactoryUtils.closeSession(session);
}
+ /**
+ * Create a Callable to extend the use of the open Hibernate Session to the
+ * async thread completing the request.
+ */
+ private AbstractDelegatingCallable getAsyncCallable(final HttpServletRequest request,
+ final SessionFactory sessionFactory, final SessionHolder sessionHolder) {
+
+ return new AbstractDelegatingCallable() {
+ public Object call() throws Exception {
+ TransactionSynchronizationManager.bindResource(sessionFactory, sessionHolder);
+ try {
+ getNextCallable().call();
+ }
+ finally {
+ SessionHolder sessionHolder =
+ (SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory);
+ logger.debug("Closing Hibernate Session in OpenSessionInViewFilter");
+ SessionFactoryUtils.closeSession(sessionHolder.getSession());
+ }
+ return null;
+ }
+ };
+ }
+
}
diff --git a/spring-orm/src/main/java/org/springframework/orm/hibernate3/support/OpenSessionInViewInterceptor.java b/spring-orm/src/main/java/org/springframework/orm/hibernate3/support/OpenSessionInViewInterceptor.java
index 002f0c8238..7fdd151895 100644
--- a/spring-orm/src/main/java/org/springframework/orm/hibernate3/support/OpenSessionInViewInterceptor.java
+++ b/spring-orm/src/main/java/org/springframework/orm/hibernate3/support/OpenSessionInViewInterceptor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2008 the original author or authors.
+ * Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,7 +18,6 @@ package org.springframework.orm.hibernate3.support;
import org.hibernate.HibernateException;
import org.hibernate.Session;
-
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.HibernateAccessor;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
@@ -26,7 +25,8 @@ import org.springframework.orm.hibernate3.SessionHolder;
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.AbstractDelegatingCallable;
+import org.springframework.web.context.request.async.AsyncWebRequestInterceptor;
/**
* Spring web request interceptor that binds a Hibernate Session
to the
@@ -89,7 +89,7 @@ import org.springframework.web.context.request.WebRequestInterceptor;
* @see org.springframework.orm.hibernate3.SessionFactoryUtils#getSession
* @see org.springframework.transaction.support.TransactionSynchronizationManager
*/
-public class OpenSessionInViewInterceptor extends HibernateAccessor implements WebRequestInterceptor {
+public class OpenSessionInViewInterceptor extends HibernateAccessor implements AsyncWebRequestInterceptor {
/**
* Suffix that gets appended to the SessionFactory
@@ -155,7 +155,8 @@ public class OpenSessionInViewInterceptor extends HibernateAccessor implements W
Session session = SessionFactoryUtils.getSession(
getSessionFactory(), getEntityInterceptor(), getJdbcExceptionTranslator());
applyFlushMode(session, false);
- TransactionSynchronizationManager.bindResource(getSessionFactory(), new SessionHolder(session));
+ SessionHolder sessionHolder = new SessionHolder(session);
+ TransactionSynchronizationManager.bindResource(getSessionFactory(), sessionHolder);
}
else {
// deferred close mode
@@ -164,6 +165,39 @@ public class OpenSessionInViewInterceptor extends HibernateAccessor implements W
}
}
+ /**
+ * Create a Callable
to bind the Hibernate
session
+ * to the async request thread.
+ */
+ public AbstractDelegatingCallable getAsyncCallable(WebRequest request) {
+ String attributeName = getParticipateAttributeName();
+ if ((request.getAttribute(attributeName, WebRequest.SCOPE_REQUEST) != null) || !isSingleSession()) {
+ return null;
+ }
+
+ final SessionHolder sessionHolder =
+ (SessionHolder) TransactionSynchronizationManager.getResource(getSessionFactory());
+
+ return new AbstractDelegatingCallable() {
+ public Object call() throws Exception {
+ TransactionSynchronizationManager.bindResource(getSessionFactory(), sessionHolder);
+ getNextCallable().call();
+ return null;
+ }
+ };
+ }
+
+ /**
+ * Unbind the Hibernate Session
from the main thread but leave
+ * the Session
open for further use from the async thread.
+ */
+ public void postHandleAsyncStarted(WebRequest request) {
+ String attributeName = getParticipateAttributeName();
+ if ((request.getAttribute(attributeName, WebRequest.SCOPE_REQUEST) == null) && isSingleSession()) {
+ TransactionSynchronizationManager.unbindResource(getSessionFactory());
+ }
+ }
+
/**
* Flush the Hibernate Session
before view rendering, if necessary.
*
Note that this just applies in {@link #isSingleSession() single session mode}!
diff --git a/spring-orm/src/main/java/org/springframework/orm/hibernate4/support/OpenSessionInViewFilter.java b/spring-orm/src/main/java/org/springframework/orm/hibernate4/support/OpenSessionInViewFilter.java
index 0e76d8c35f..c8dd223406 100644
--- a/spring-orm/src/main/java/org/springframework/orm/hibernate4/support/OpenSessionInViewFilter.java
+++ b/spring-orm/src/main/java/org/springframework/orm/hibernate4/support/OpenSessionInViewFilter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2011 the original author or authors.
+ * Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,6 +32,8 @@ import org.springframework.orm.hibernate4.SessionFactoryUtils;
import org.springframework.orm.hibernate4.SessionHolder;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.web.context.WebApplicationContext;
+import org.springframework.web.context.request.async.AbstractDelegatingCallable;
+import org.springframework.web.context.request.async.AsyncExecutionChain;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.filter.OncePerRequestFilter;
@@ -102,6 +104,8 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter {
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
+ AsyncExecutionChain chain = AsyncExecutionChain.getForCurrentRequest(request);
+
SessionFactory sessionFactory = lookupSessionFactory(request);
boolean participate = false;
@@ -112,7 +116,10 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter {
else {
logger.debug("Opening Hibernate Session in OpenSessionInViewFilter");
Session session = openSession(sessionFactory);
- TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
+ SessionHolder sessionHolder = new SessionHolder(session);
+ TransactionSynchronizationManager.bindResource(sessionFactory, sessionHolder);
+
+ chain.addDelegatingCallable(getAsyncCallable(request, sessionFactory, sessionHolder));
}
try {
@@ -123,6 +130,9 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter {
if (!participate) {
SessionHolder sessionHolder =
(SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory);
+ if (chain.isAsyncStarted()) {
+ return;
+ }
logger.debug("Closing Hibernate Session in OpenSessionInViewFilter");
SessionFactoryUtils.closeSession(sessionHolder.getSession());
}
@@ -177,4 +187,28 @@ public class OpenSessionInViewFilter extends OncePerRequestFilter {
}
}
+ /**
+ * Create a Callable to extend the use of the open Hibernate Session to the
+ * async thread completing the request.
+ */
+ private AbstractDelegatingCallable getAsyncCallable(final HttpServletRequest request,
+ final SessionFactory sessionFactory, final SessionHolder sessionHolder) {
+
+ return new AbstractDelegatingCallable() {
+ public Object call() throws Exception {
+ TransactionSynchronizationManager.bindResource(sessionFactory, sessionHolder);
+ try {
+ getNextCallable().call();
+ }
+ finally {
+ SessionHolder sessionHolder =
+ (SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory);
+ logger.debug("Closing Hibernate Session in OpenSessionInViewFilter");
+ SessionFactoryUtils.closeSession(sessionHolder.getSession());
+ }
+ return null;
+ }
+ };
+ }
+
}
diff --git a/spring-orm/src/main/java/org/springframework/orm/hibernate4/support/OpenSessionInViewInterceptor.java b/spring-orm/src/main/java/org/springframework/orm/hibernate4/support/OpenSessionInViewInterceptor.java
index 1c15bd3b8c..66ceffaaf7 100644
--- a/spring-orm/src/main/java/org/springframework/orm/hibernate4/support/OpenSessionInViewInterceptor.java
+++ b/spring-orm/src/main/java/org/springframework/orm/hibernate4/support/OpenSessionInViewInterceptor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2011 the original author or authors.
+ * Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,7 +22,6 @@ import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
-
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.orm.hibernate4.SessionFactoryUtils;
@@ -30,7 +29,9 @@ import org.springframework.orm.hibernate4.SessionHolder;
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.AbstractDelegatingCallable;
+import org.springframework.web.context.request.async.AsyncExecutionChain;
+import org.springframework.web.context.request.async.AsyncWebRequestInterceptor;
/**
* Spring web request interceptor that binds a Hibernate Session
to the
@@ -72,7 +73,7 @@ import org.springframework.web.context.request.WebRequestInterceptor;
* @see org.springframework.orm.hibernate4.HibernateTransactionManager
* @see org.springframework.transaction.support.TransactionSynchronizationManager
*/
-public class OpenSessionInViewInterceptor implements WebRequestInterceptor {
+public class OpenSessionInViewInterceptor implements AsyncWebRequestInterceptor {
/**
* Suffix that gets appended to the SessionFactory
@@ -112,13 +113,47 @@ public class OpenSessionInViewInterceptor implements WebRequestInterceptor {
else {
logger.debug("Opening Hibernate Session in OpenSessionInViewInterceptor");
Session session = openSession();
- TransactionSynchronizationManager.bindResource(getSessionFactory(), new SessionHolder(session));
+ SessionHolder sessionHolder = new SessionHolder(session);
+ TransactionSynchronizationManager.bindResource(getSessionFactory(), sessionHolder);
}
}
public void postHandle(WebRequest request, ModelMap model) {
}
+ /**
+ * Create a Callable
to bind the Hibernate
session
+ * to the async request thread.
+ */
+ public AbstractDelegatingCallable getAsyncCallable(WebRequest request) {
+ String attributeName = getParticipateAttributeName();
+ if (request.getAttribute(attributeName, WebRequest.SCOPE_REQUEST) != null) {
+ return null;
+ }
+
+ final SessionHolder sessionHolder =
+ (SessionHolder) TransactionSynchronizationManager.getResource(getSessionFactory());
+
+ return new AbstractDelegatingCallable() {
+ public Object call() throws Exception {
+ TransactionSynchronizationManager.bindResource(getSessionFactory(), sessionHolder);
+ getNextCallable().call();
+ return null;
+ }
+ };
+ }
+
+ /**
+ * Unbind the Hibernate Session
from the main thread leaving
+ * it open for further use from an async thread.
+ */
+ public void postHandleAsyncStarted(WebRequest request) {
+ String attributeName = getParticipateAttributeName();
+ if (request.getAttribute(attributeName, WebRequest.SCOPE_REQUEST) == null) {
+ TransactionSynchronizationManager.unbindResource(getSessionFactory());
+ }
+ }
+
/**
* Unbind the Hibernate Session
from the thread and close it).
* @see org.springframework.transaction.support.TransactionSynchronizationManager
diff --git a/spring-orm/src/test/java/org/springframework/orm/hibernate3/support/OpenSessionInViewTests.java b/spring-orm/src/test/java/org/springframework/orm/hibernate3/support/OpenSessionInViewTests.java
index 21b4e2289d..82779fc09b 100644
--- a/spring-orm/src/test/java/org/springframework/orm/hibernate3/support/OpenSessionInViewTests.java
+++ b/spring-orm/src/test/java/org/springframework/orm/hibernate3/support/OpenSessionInViewTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2011 the original author or authors.
+ * Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,23 +16,35 @@
package org.springframework.orm.hibernate3.support;
+import static org.easymock.EasyMock.createStrictMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.reset;
+import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
import java.io.IOException;
import java.sql.Connection;
+import java.util.concurrent.Callable;
+
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.transaction.TransactionManager;
-import junit.framework.TestCase;
-import org.easymock.MockControl;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.classic.Session;
import org.hibernate.engine.SessionFactoryImplementor;
-
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.mock.web.MockFilterConfig;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
@@ -47,307 +59,341 @@ import org.springframework.transaction.support.DefaultTransactionDefinition;
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.AbstractDelegatingCallable;
+import org.springframework.web.context.request.async.AsyncExecutionChain;
+import org.springframework.web.context.request.async.AsyncWebRequest;
import org.springframework.web.context.support.StaticWebApplicationContext;
+
/**
* @author Juergen Hoeller
+ * @author Rossen Stoyanchev
* @since 05.03.2005
*/
-public class OpenSessionInViewTests extends TestCase {
+public class OpenSessionInViewTests {
+
+ private MockServletContext sc;
+
+ private MockHttpServletRequest request;
+
+ private MockHttpServletResponse response;
+ private ServletWebRequest webRequest;
+
+
+ @Before
+ public void setup() {
+ this.sc = new MockServletContext();
+ this.request = new MockHttpServletRequest(sc);
+ this.response = new MockHttpServletResponse();
+ this.webRequest = new ServletWebRequest(this.request);
+ }
+
+ @Test
public void testOpenSessionInViewInterceptorWithSingleSession() throws Exception {
-
- //SessionFactory sf = createMock(SessionFactory.class);
- //Session session = createMock(Session.class);
-
- MockControl sfControl = MockControl.createControl(SessionFactory.class);
- final SessionFactory sf = (SessionFactory) sfControl.getMock();
- MockControl sessionControl = MockControl.createControl(Session.class);
- Session session = (Session) sessionControl.getMock();
-
+
+ SessionFactory sf = createStrictMock(SessionFactory.class);
+ Session session = createStrictMock(Session.class);
+
OpenSessionInViewInterceptor interceptor = new OpenSessionInViewInterceptor();
interceptor.setSessionFactory(sf);
- MockServletContext sc = new MockServletContext();
- MockHttpServletRequest request = new MockHttpServletRequest(sc);
+ expect(sf.openSession()).andReturn(session);
+ expect(session.getSessionFactory()).andReturn(sf);
+ session.setFlushMode(FlushMode.MANUAL);
+ expect(session.getSessionFactory()).andReturn(sf);
+ expect(session.isOpen()).andReturn(true);
+ replay(sf);
+ replay(session);
+
+ interceptor.preHandle(this.webRequest);
+ assertTrue(TransactionSynchronizationManager.hasResource(sf));
+
+ // check that further invocations simply participate
+ interceptor.preHandle(this.webRequest);
+ assertEquals(session, SessionFactoryUtils.getSession(sf, false));
+
+ interceptor.preHandle(this.webRequest);
+ interceptor.postHandle(this.webRequest, null);
+ interceptor.afterCompletion(this.webRequest, null);
- //expect(mockStorage.size()).andReturn(expectedValue);
+ interceptor.postHandle(this.webRequest, null);
+ interceptor.afterCompletion(this.webRequest, null);
- //expect(sf.openSession()).andReturn(session);
- sf.openSession();
- sfControl.setReturnValue(session, 1);
- session.getSessionFactory();
- sessionControl.setReturnValue(sf, 2);
- session.isOpen();
- sessionControl.setReturnValue(true, 1);
+ interceptor.preHandle(this.webRequest);
+ interceptor.postHandle(this.webRequest, null);
+ interceptor.afterCompletion(this.webRequest, null);
+
+ verify(sf);
+ verify(session);
+ reset(sf);
+ reset(session);
+ replay(sf);
+ replay(session);
+
+ interceptor.postHandle(this.webRequest, null);
+ assertTrue(TransactionSynchronizationManager.hasResource(sf));
+
+ verify(sf);
+ verify(session);
+ reset(sf);
+ reset(session);
+ expect(session.close()).andReturn(null);
+ replay(sf);
+ replay(session);
+
+ interceptor.afterCompletion(this.webRequest, null);
+ assertFalse(TransactionSynchronizationManager.hasResource(sf));
+
+ verify(sf);
+ verify(session);
+ }
+
+ @Test
+ public void testOpenSessionInViewInterceptorAsyncScenario() throws Exception {
+
+ final SessionFactory sf = createStrictMock(SessionFactory.class);
+ Session session = createStrictMock(Session.class);
+
+ OpenSessionInViewInterceptor interceptor = new OpenSessionInViewInterceptor();
+ interceptor.setSessionFactory(sf);
+
+ expect(sf.openSession()).andReturn(session);
+ expect(session.getSessionFactory()).andReturn(sf);
session.setFlushMode(FlushMode.MANUAL);
- sessionControl.setVoidCallable(1);
- sfControl.replay();
- sessionControl.replay();
- interceptor.preHandle(new ServletWebRequest(request));
+ replay(sf);
+ replay(session);
+
+ interceptor.preHandle(this.webRequest);
assertTrue(TransactionSynchronizationManager.hasResource(sf));
- // check that further invocations simply participate
- interceptor.preHandle(new ServletWebRequest(request));
+ AbstractDelegatingCallable asyncCallable = interceptor.getAsyncCallable(this.webRequest);
+ assertNotNull(asyncCallable);
- assertEquals(session, SessionFactoryUtils.getSession(sf, false));
+ interceptor.postHandleAsyncStarted(this.webRequest);
+ assertFalse(TransactionSynchronizationManager.hasResource(sf));
- interceptor.preHandle(new ServletWebRequest(request));
- interceptor.postHandle(new ServletWebRequest(request), null);
- interceptor.afterCompletion(new ServletWebRequest(request), null);
+ verify(sf);
+ verify(session);
- interceptor.postHandle(new ServletWebRequest(request), null);
- interceptor.afterCompletion(new ServletWebRequest(request), null);
+ asyncCallable.setNextCallable(new Callable