support for commit on conversation end

This commit is contained in:
Keith Donald
2007-08-01 18:37:25 +00:00
parent 8f993446de
commit a2ac2bc857
2 changed files with 78 additions and 43 deletions

View File

@@ -18,10 +18,13 @@ package org.springframework.webflow.support.persistence;
import org.hibernate.FlushMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.springframework.orm.hibernate3.SessionHolder;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.webflow.core.collection.AttributeMap;
import org.springframework.webflow.execution.FlowExecutionException;
import org.springframework.webflow.execution.FlowExecutionListener;
@@ -51,47 +54,58 @@ public class HibernateSessionPerConversationListener extends FlowExecutionListen
private static final String HIBERNATE_SESSION_ATTRIBUTE = "hibernate.session";
private TransactionTemplate transactionTemplate;
private SessionFactory sessionFactory;
/**
* Create a new Session-per-Conversation listener using giving Hibernate session factory.
* @param sessionFactory the session factory to use
*/
public HibernateSessionPerConversationListener(SessionFactory sessionFactory) {
public HibernateSessionPerConversationListener(SessionFactory sessionFactory,
PlatformTransactionManager transactionManager) {
this.sessionFactory = sessionFactory;
this.transactionTemplate = new TransactionTemplate(transactionManager);
}
public void sessionCreated(RequestContext context, FlowSession session) {
if (session.isRoot() && session.getDefinition().getAttributes().contains("persistenceContext")) {
Session hibernateSession = createSession(context);
context.getConversationScope().put(HIBERNATE_SESSION_ATTRIBUTE, hibernateSession);
bind(hibernateSession, context, session);
bind(hibernateSession, context);
}
}
public void resumed(RequestContext context) {
bind(getHibernateSession(context), context, context.getFlowExecutionContext().getActiveSession());
bind(getHibernateSession(context), context);
}
public void paused(RequestContext context, ViewSelection selectedView) {
unbind(getHibernateSession(context), context, context.getFlowExecutionContext().getActiveSession());
unbind(getHibernateSession(context), context);
}
public void sessionEnded(RequestContext context, FlowSession session, AttributeMap output) {
if (session.isRoot()) {
Session hibernateSession = (Session) context.getConversationScope().remove(HIBERNATE_SESSION_ATTRIBUTE);
final Session hibernateSession = (Session) context.getConversationScope().remove(
HIBERNATE_SESSION_ATTRIBUTE);
Boolean commitStatus = session.getState().getAttributes().getBoolean("commit");
if (commitStatus == null || commitStatus.equals(Boolean.TRUE)) {
// assume a commit by default and when 'commit' attribute = true
hibernateSession.flush();
hibernateSession.close();
if (Boolean.TRUE.equals(commitStatus)) {
// this is a commit end state - start a new transaction that quickly commits
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
sessionFactory.getCurrentSession();
// nothing to do - a flush will happen on commit automatically as this is a read-write
// transaction
}
});
}
unbind(hibernateSession, context, session);
hibernateSession.close();
unbind(hibernateSession, context);
}
}
public void exceptionThrown(RequestContext context, FlowExecutionException exception) {
unbind(getHibernateSession(context), context, context.getFlowExecutionContext().getActiveSession());
unbind(getHibernateSession(context), context);
}
// internal helpers
@@ -106,19 +120,11 @@ public class HibernateSessionPerConversationListener extends FlowExecutionListen
return (Session) context.getConversationScope().get(HIBERNATE_SESSION_ATTRIBUTE);
}
private void bind(Session session, RequestContext context, FlowSession flowSession) {
SessionHolder sessionHolder = new SessionHolder(session);
if (flowSession.getDefinition().getAttributes().contains("transactional")) {
Transaction tx = session.beginTransaction();
sessionHolder.setTransaction(tx);
}
TransactionSynchronizationManager.bindResource(sessionFactory, sessionHolder);
private void bind(Session session, RequestContext context) {
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
}
private void unbind(Session session, RequestContext context, FlowSession flowSession) {
if (flowSession.getDefinition().getAttributes().contains("transactional")) {
session.getTransaction().commit();
}
private void unbind(Session session, RequestContext context) {
TransactionSynchronizationManager.unbindResource(sessionFactory);
}
}

View File

@@ -31,6 +31,7 @@ import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.orm.hibernate3.HibernateTransactionManager;
import org.springframework.orm.hibernate3.LocalSessionFactoryBean;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.webflow.engine.EndState;
@@ -61,7 +62,8 @@ public class HibernateSessionPerConversationListenerTests extends TestCase {
sessionFactory = getSessionFactory(dataSource);
hibernateTemplate = new HibernateTemplate(sessionFactory);
hibernateTemplate.setCheckWriteOperations(false);
listener = new HibernateSessionPerConversationListener(sessionFactory);
HibernateTransactionManager tm = new HibernateTransactionManager(sessionFactory);
listener = new HibernateSessionPerConversationListener(sessionFactory, tm);
}
public void testSameSession() {
@@ -98,7 +100,7 @@ public class HibernateSessionPerConversationListenerTests extends TestCase {
assertSessionNotBound();
}
public void testFlowEndsInSingleRequest() {
public void testFlowCommitsInSingleRequest() {
assertEquals("Table should only have one row", 1, jdbcTemplate.queryForInt("select count(*) from T_BEAN"));
MockRequestContext context = new MockRequestContext();
MockFlowSession flowSession = new MockFlowSession();
@@ -110,13 +112,17 @@ public class HibernateSessionPerConversationListenerTests extends TestCase {
hibernateTemplate.save(bean);
assertEquals("Table should still only have one row", 1, jdbcTemplate.queryForInt("select count(*) from T_BEAN"));
EndState endState = new EndState(flowSession.getDefinitionInternal(), "success");
endState.getAttributeMap().put("commit", Boolean.TRUE);
flowSession.setState(endState);
listener.sessionEnded(context, flowSession, null);
assertEquals("Table should only have two rows", 2, jdbcTemplate.queryForInt("select count(*) from T_BEAN"));
assertSessionNotBound();
assertFalse(flowSession.getScope().contains("hibernate.session"));
}
public void testFlowSpansMultipleRequests() {
public void testFlowCommitsAfterMultipleRequests() {
assertEquals("Table should only have one row", 1, jdbcTemplate.queryForInt("select count(*) from T_BEAN"));
MockRequestContext context = new MockRequestContext();
MockFlowSession flowSession = new MockFlowSession();
@@ -136,6 +142,10 @@ public class HibernateSessionPerConversationListenerTests extends TestCase {
assertEquals("Table should still only have one row", 1, jdbcTemplate.queryForInt("select count(*) from T_BEAN"));
assertSessionBound();
EndState endState = new EndState(flowSession.getDefinitionInternal(), "success");
endState.getAttributeMap().put("commit", Boolean.TRUE);
flowSession.setState(endState);
listener.sessionEnded(context, flowSession, null);
assertEquals("Table should only have three rows", 3, jdbcTemplate.queryForInt("select count(*) from T_BEAN"));
assertFalse(flowSession.getScope().contains("hibernate.session"));
@@ -145,23 +155,6 @@ public class HibernateSessionPerConversationListenerTests extends TestCase {
}
public void testExceptionThrown() {
assertEquals("Table should only have one row", 1, jdbcTemplate.queryForInt("select count(*) from T_BEAN"));
MockRequestContext context = new MockRequestContext();
MockFlowSession flowSession = new MockFlowSession();
flowSession.getDefinitionInternal().getAttributeMap().put("persistenceContext", "true");
listener.sessionCreated(context, flowSession);
assertSessionBound();
TestBean bean1 = new TestBean("Keith Donald");
hibernateTemplate.save(bean1);
assertEquals("Table should still only have one row", 1, jdbcTemplate.queryForInt("select count(*) from T_BEAN"));
listener.exceptionThrown(context, new FlowExecutionException("bla", "bla", "bla"));
assertEquals("Table should still only have one row", 1, jdbcTemplate.queryForInt("select count(*) from T_BEAN"));
assertSessionNotBound();
}
public void testCancelEndState() {
assertEquals("Table should only have one row", 1, jdbcTemplate.queryForInt("select count(*) from T_BEAN"));
MockRequestContext context = new MockRequestContext();
@@ -183,6 +176,42 @@ public class HibernateSessionPerConversationListenerTests extends TestCase {
assertFalse(flowSession.getScope().contains("hibernate.session"));
}
public void testNoCommitAttributeSetOnEndState() {
assertEquals("Table should only have one row", 1, jdbcTemplate.queryForInt("select count(*) from T_BEAN"));
MockRequestContext context = new MockRequestContext();
MockFlowSession flowSession = new MockFlowSession();
flowSession.getDefinitionInternal().getAttributeMap().put("persistenceContext", "true");
listener.sessionCreated(context, flowSession);
assertSessionBound();
EndState endState = new EndState(flowSession.getDefinitionInternal(), "cancel");
flowSession.setState(endState);
listener.sessionEnded(context, flowSession, null);
assertEquals("Table should only have three rows", 1, jdbcTemplate.queryForInt("select count(*) from T_BEAN"));
assertFalse(flowSession.getScope().contains("hibernate.session"));
assertSessionNotBound();
assertFalse(flowSession.getScope().contains("hibernate.session"));
}
public void testExceptionThrown() {
assertEquals("Table should only have one row", 1, jdbcTemplate.queryForInt("select count(*) from T_BEAN"));
MockRequestContext context = new MockRequestContext();
MockFlowSession flowSession = new MockFlowSession();
flowSession.getDefinitionInternal().getAttributeMap().put("persistenceContext", "true");
listener.sessionCreated(context, flowSession);
assertSessionBound();
TestBean bean1 = new TestBean("Keith Donald");
hibernateTemplate.save(bean1);
assertEquals("Table should still only have one row", 1, jdbcTemplate.queryForInt("select count(*) from T_BEAN"));
listener.exceptionThrown(context, new FlowExecutionException("bla", "bla", "bla"));
assertEquals("Table should still only have one row", 1, jdbcTemplate.queryForInt("select count(*) from T_BEAN"));
assertSessionNotBound();
}
private DataSource getDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");