From a2ac2bc857f44fac64a802db8fb9cf781a596794 Mon Sep 17 00:00:00 2001 From: Keith Donald Date: Wed, 1 Aug 2007 18:37:25 +0000 Subject: [PATCH] support for commit on conversation end --- ...bernateSessionPerConversationListener.java | 52 +++++++------- ...teSessionPerConversationListenerTests.java | 69 +++++++++++++------ 2 files changed, 78 insertions(+), 43 deletions(-) diff --git a/spring-webflow/src/main/java/org/springframework/webflow/support/persistence/HibernateSessionPerConversationListener.java b/spring-webflow/src/main/java/org/springframework/webflow/support/persistence/HibernateSessionPerConversationListener.java index 47021e0e..e2b20cbd 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/support/persistence/HibernateSessionPerConversationListener.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/support/persistence/HibernateSessionPerConversationListener.java @@ -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); } } \ No newline at end of file diff --git a/spring-webflow/src/test/java/org/springframework/webflow/support/persistence/HibernateSessionPerConversationListenerTests.java b/spring-webflow/src/test/java/org/springframework/webflow/support/persistence/HibernateSessionPerConversationListenerTests.java index 3ef6415e..f142f88f 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/support/persistence/HibernateSessionPerConversationListenerTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/support/persistence/HibernateSessionPerConversationListenerTests.java @@ -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");