Keith Donald
2008-04-19 18:10:24 +00:00
parent 0d32d4a518
commit 8cba9f13d8
7 changed files with 354 additions and 15 deletions

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.binding.expression;
import java.io.Serializable;
import org.springframework.core.style.ToStringCreator;
/**
@@ -22,7 +24,7 @@ import org.springframework.core.style.ToStringCreator;
*
* @author Keith Donald
*/
public class EvaluationAttempt {
public class EvaluationAttempt implements Serializable {
private Expression expression;

View File

@@ -105,6 +105,12 @@ public class HibernateFlowExecutionListener extends FlowExecutionListenerAdapter
public void sessionStarting(RequestContext context, FlowSession session, MutableAttributeMap input) {
if (isPersistenceContext(session.getDefinition())) {
if (!session.isRoot()) {
FlowSession parent = session.getParent();
if (isPersistenceContext(parent.getDefinition())) {
unbind(getHibernateSession(parent));
}
}
Session hibernateSession = createSession(context);
session.getScope().put(HIBERNATE_SESSION_ATTRIBUTE, hibernateSession);
bind(hibernateSession);
@@ -113,7 +119,7 @@ public class HibernateFlowExecutionListener extends FlowExecutionListenerAdapter
public void paused(RequestContext context) {
if (isPersistenceContext(context.getActiveFlow())) {
Session session = getHibernateSession(context);
Session session = getHibernateSession(context.getFlowExecutionContext().getActiveSession());
unbind(session);
session.disconnect();
}
@@ -121,7 +127,7 @@ public class HibernateFlowExecutionListener extends FlowExecutionListenerAdapter
public void resuming(RequestContext context) {
if (isPersistenceContext(context.getActiveFlow())) {
bind(getHibernateSession(context));
bind(getHibernateSession(context.getFlowExecutionContext().getActiveSession()));
}
}
@@ -130,7 +136,6 @@ public class HibernateFlowExecutionListener extends FlowExecutionListenerAdapter
final Session hibernateSession = (Session) session.getScope().remove(HIBERNATE_SESSION_ATTRIBUTE);
Boolean commitStatus = session.getState().getAttributes().getBoolean("commit");
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();
@@ -142,12 +147,18 @@ public class HibernateFlowExecutionListener extends FlowExecutionListenerAdapter
unbind(hibernateSession);
hibernateSession.close();
}
if (!session.isRoot()) {
FlowSession parent = session.getParent();
if (isPersistenceContext(parent.getDefinition())) {
bind(getHibernateSession(parent));
}
}
}
public void exceptionThrown(RequestContext context, FlowExecutionException exception) {
if (context.getFlowExecutionContext().isActive()) {
if (isPersistenceContext(context.getActiveFlow())) {
unbind(getHibernateSession(context));
unbind(getHibernateSession(context.getFlowExecutionContext().getActiveSession()));
}
}
}
@@ -165,8 +176,8 @@ public class HibernateFlowExecutionListener extends FlowExecutionListenerAdapter
return session;
}
private Session getHibernateSession(RequestContext context) {
return (Session) context.getFlowScope().get(HIBERNATE_SESSION_ATTRIBUTE);
private Session getHibernateSession(FlowSession session) {
return (Session) session.getScope().get(HIBERNATE_SESSION_ATTRIBUTE);
}
private void bind(Session session) {

View File

@@ -95,6 +95,12 @@ public class JpaFlowExecutionListener extends FlowExecutionListenerAdapter {
public void sessionStarting(RequestContext context, FlowSession session, MutableAttributeMap input) {
if (isPersistenceContext(session.getDefinition())) {
if (!session.isRoot()) {
FlowSession parent = session.getParent();
if (isPersistenceContext(parent.getDefinition())) {
unbind(getEntityManager(parent));
}
}
EntityManager em = entityManagerFactory.createEntityManager();
session.getScope().put(ENTITY_MANAGER_ATTRIBUTE, em);
bind(em);
@@ -103,13 +109,13 @@ public class JpaFlowExecutionListener extends FlowExecutionListenerAdapter {
public void paused(RequestContext context) {
if (isPersistenceContext(context.getActiveFlow())) {
unbind(getEntityManager(context));
unbind(getEntityManager(context.getFlowExecutionContext().getActiveSession()));
}
}
public void resuming(RequestContext context) {
if (isPersistenceContext(context.getActiveFlow())) {
bind(getEntityManager(context));
bind(getEntityManager(context.getFlowExecutionContext().getActiveSession()));
}
}
@@ -118,7 +124,6 @@ public class JpaFlowExecutionListener extends FlowExecutionListenerAdapter {
final EntityManager em = (EntityManager) session.getScope().remove(ENTITY_MANAGER_ATTRIBUTE);
Boolean commitStatus = session.getState().getAttributes().getBoolean("commit");
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) {
em.joinTransaction();
@@ -128,12 +133,18 @@ public class JpaFlowExecutionListener extends FlowExecutionListenerAdapter {
unbind(em);
em.close();
}
if (!session.isRoot()) {
FlowSession parent = session.getParent();
if (isPersistenceContext(parent.getDefinition())) {
bind(getEntityManager(parent));
}
}
}
public void exceptionThrown(RequestContext context, FlowExecutionException exception) {
if (context.getFlowExecutionContext().isActive()) {
if (isPersistenceContext(context.getActiveFlow())) {
unbind(getEntityManager(context));
unbind(getEntityManager(context.getFlowExecutionContext().getActiveSession()));
}
}
}
@@ -144,8 +155,8 @@ public class JpaFlowExecutionListener extends FlowExecutionListenerAdapter {
return flow.getAttributes().contains(PERSISTENCE_CONTEXT_ATTRIBUTE);
}
private EntityManager getEntityManager(RequestContext context) {
return (EntityManager) context.getFlowScope().get(ENTITY_MANAGER_ATTRIBUTE);
private EntityManager getEntityManager(FlowSession session) {
return (EntityManager) session.getScope().get(ENTITY_MANAGER_ATTRIBUTE);
}
private void bind(EntityManager em) {

View File

@@ -84,12 +84,12 @@ public class HibernateFlowExecutionListenerTests extends TestCase {
hibernateListener.resuming(context);
assertSessionBound();
hibernateTemplate.execute(new HibernateCallback() {
hibernateTemplate.executeWithNativeSession(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException, SQLException {
assertSame("Should have been original instance", hibSession, session);
return null;
}
}, true);
});
hibernateListener.paused(context);
assertSessionNotBound();
}

View File

@@ -0,0 +1,142 @@
package org.springframework.webflow.persistence;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import junit.framework.TestCase;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
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.Flow;
import org.springframework.webflow.engine.builder.FlowAssembler;
import org.springframework.webflow.engine.builder.model.FlowModelFlowBuilder;
import org.springframework.webflow.engine.impl.FlowExecutionImplFactory;
import org.springframework.webflow.engine.model.builder.xml.XmlFlowModelBuilder;
import org.springframework.webflow.engine.model.registry.DefaultFlowModelHolder;
import org.springframework.webflow.execution.Action;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.FlowExecution;
import org.springframework.webflow.execution.RequestContext;
import org.springframework.webflow.execution.factory.StaticFlowExecutionListenerLoader;
import org.springframework.webflow.test.MockExternalContext;
import org.springframework.webflow.test.MockFlowBuilderContext;
public class HibernateFlowManagedPersistenceIntegrationTests extends TestCase {
private SessionFactory sessionFactory;
private JdbcTemplate jdbcTemplate;
private HibernateTemplate hibernateTemplate;
private HibernateFlowExecutionListener hibernateListener;
private FlowExecution flowExecution;
protected void setUp() throws Exception {
DataSource dataSource = getDataSource();
populateDataBase(dataSource);
jdbcTemplate = new JdbcTemplate(dataSource);
sessionFactory = getSessionFactory(dataSource);
hibernateTemplate = new HibernateTemplate(sessionFactory);
hibernateTemplate.setCheckWriteOperations(false);
HibernateTransactionManager tm = new HibernateTransactionManager(sessionFactory);
hibernateListener = new HibernateFlowExecutionListener(sessionFactory, tm);
ClassPathResource res = new ClassPathResource("flow-managed-persistence.xml", getClass());
DefaultFlowModelHolder holder = new DefaultFlowModelHolder(new XmlFlowModelBuilder(res));
FlowModelFlowBuilder builder = new FlowModelFlowBuilder(holder);
MockFlowBuilderContext context = new MockFlowBuilderContext("foo");
FlowAssembler assembler = new FlowAssembler(builder, context);
Flow flow = assembler.assembleFlow();
context.registerSubflow(flow);
context.registerBean("loadTestBean", new Action() {
public Event execute(RequestContext context) throws Exception {
assertSessionBound();
Session session = (Session) context.getFlowScope().get("session");
TestBean bean = (TestBean) session.get(TestBean.class, new Long(0));
assertNotNull(bean);
context.getFlowScope().put("testBean", bean);
return new Event(this, "success");
}
});
FlowExecutionImplFactory factory = new FlowExecutionImplFactory();
factory.setExecutionListenerLoader(new StaticFlowExecutionListenerLoader(hibernateListener));
flowExecution = factory.createFlowExecution(flow);
}
public void testFlowWithSubflow() {
MockExternalContext context = new MockExternalContext();
flowExecution.start(null, context);
context.setEventId("subflow");
flowExecution.resume(context);
context.setEventId("finish");
flowExecution.resume(context);
}
private DataSource getDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
dataSource.setUrl("jdbc:hsqldb:mem:jpa");
dataSource.setUsername("sa");
dataSource.setPassword("");
return dataSource;
}
private void populateDataBase(DataSource dataSource) {
Connection connection = null;
try {
connection = dataSource.getConnection();
connection.createStatement().execute("drop table T_ADDRESS if exists;");
connection.createStatement().execute("drop table T_BEAN if exists;");
connection.createStatement().execute(
"create table T_BEAN (ID integer primary key, NAME varchar(50) not null);");
connection.createStatement().execute(
"create table T_ADDRESS (ID integer primary key, BEAN_ID integer, VALUE varchar(50) not null);");
connection
.createStatement()
.execute(
"alter table T_ADDRESS add constraint FK_BEAN_ADDRESS foreign key (BEAN_ID) references T_BEAN(ID) on delete cascade");
connection.createStatement().execute("insert into T_BEAN (ID, NAME) values (0, 'Ben Hale');");
connection.createStatement().execute(
"insert into T_ADDRESS (ID, BEAN_ID, VALUE) values (0, 0, 'Melbourne')");
} catch (SQLException e) {
throw new RuntimeException("SQL exception occurred acquiring connection", e);
} finally {
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
}
}
}
}
private SessionFactory getSessionFactory(DataSource dataSource) throws Exception {
LocalSessionFactoryBean factory = new LocalSessionFactoryBean();
factory.setDataSource(dataSource);
factory.setMappingLocations(new Resource[] {
new ClassPathResource("org/springframework/webflow/persistence/TestBean.hbm.xml"),
new ClassPathResource("org/springframework/webflow/persistence/TestAddress.hbm.xml") });
factory.afterPropertiesSet();
return (SessionFactory) factory.getObject();
}
private void assertSessionNotBound() {
assertNull(TransactionSynchronizationManager.getResource(sessionFactory));
}
private void assertSessionBound() {
assertNotNull(TransactionSynchronizationManager.getResource(sessionFactory));
}
}

View File

@@ -0,0 +1,141 @@
package org.springframework.webflow.persistence;
import java.sql.Connection;
import java.sql.SQLException;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import junit.framework.TestCase;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTemplate;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.OpenJpaVendorAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.webflow.engine.Flow;
import org.springframework.webflow.engine.builder.FlowAssembler;
import org.springframework.webflow.engine.builder.model.FlowModelFlowBuilder;
import org.springframework.webflow.engine.impl.FlowExecutionImplFactory;
import org.springframework.webflow.engine.model.builder.xml.XmlFlowModelBuilder;
import org.springframework.webflow.engine.model.registry.DefaultFlowModelHolder;
import org.springframework.webflow.execution.Action;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.FlowExecution;
import org.springframework.webflow.execution.RequestContext;
import org.springframework.webflow.execution.factory.StaticFlowExecutionListenerLoader;
import org.springframework.webflow.test.MockExternalContext;
import org.springframework.webflow.test.MockFlowBuilderContext;
public class JpaFlowManagedPersistenceIntegrationTests extends TestCase {
private EntityManagerFactory entityManagerFactory;
private JpaFlowExecutionListener jpaListener;
private JdbcTemplate jdbcTemplate;
private JpaTemplate jpaTemplate;
private FlowExecution flowExecution;
protected void setUp() throws Exception {
DataSource dataSource = getDataSource();
populateDataBase(dataSource);
jdbcTemplate = new JdbcTemplate(dataSource);
entityManagerFactory = getEntityManagerFactory(dataSource);
JpaTransactionManager tm = new JpaTransactionManager(entityManagerFactory);
jpaListener = new JpaFlowExecutionListener(entityManagerFactory, tm);
jpaTemplate = new JpaTemplate(entityManagerFactory);
ClassPathResource res = new ClassPathResource("flow-managed-persistence.xml", getClass());
DefaultFlowModelHolder holder = new DefaultFlowModelHolder(new XmlFlowModelBuilder(res));
FlowModelFlowBuilder builder = new FlowModelFlowBuilder(holder);
MockFlowBuilderContext context = new MockFlowBuilderContext("foo");
FlowAssembler assembler = new FlowAssembler(builder, context);
Flow flow = assembler.assembleFlow();
context.registerSubflow(flow);
context.registerBean("loadTestBean", new Action() {
public Event execute(RequestContext context) throws Exception {
assertSessionBound();
EntityManager em = (EntityManager) context.getFlowScope().get("entityManager");
TestBean bean = (TestBean) em.getReference(TestBean.class, new Integer(0));
assertNotNull(bean);
context.getFlowScope().put("testBean", bean);
return new Event(this, "success");
}
});
FlowExecutionImplFactory factory = new FlowExecutionImplFactory();
factory.setExecutionListenerLoader(new StaticFlowExecutionListenerLoader(jpaListener));
flowExecution = factory.createFlowExecution(flow);
}
public void testFlowWithSubflow() {
MockExternalContext context = new MockExternalContext();
flowExecution.start(null, context);
context.setEventId("subflow");
flowExecution.resume(context);
context.setEventId("finish");
flowExecution.resume(context);
}
private DataSource getDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
dataSource.setUrl("jdbc:hsqldb:mem:jpa");
dataSource.setUsername("sa");
dataSource.setPassword("");
return dataSource;
}
private void populateDataBase(DataSource dataSource) {
Connection connection = null;
try {
connection = dataSource.getConnection();
connection.createStatement().execute("drop table T_ADDRESS if exists;");
connection.createStatement().execute("drop table T_BEAN if exists;");
connection.createStatement().execute(
"create table T_BEAN (ID integer primary key, NAME varchar(50) not null);");
connection.createStatement().execute(
"create table T_ADDRESS (ID integer primary key, BEAN_ID integer, VALUE varchar(50) not null);");
connection
.createStatement()
.execute(
"alter table T_ADDRESS add constraint FK_BEAN_ADDRESS foreign key (BEAN_ID) references T_BEAN(ID) on delete cascade");
connection.createStatement().execute("insert into T_BEAN (ID, NAME) values (0, 'Ben Hale');");
connection.createStatement().execute(
"insert into T_ADDRESS (ID, BEAN_ID, VALUE) values (0, 0, 'Melbourne')");
} catch (SQLException e) {
throw new RuntimeException("SQL exception occurred acquiring connection", e);
} finally {
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
}
}
}
}
private EntityManagerFactory getEntityManagerFactory(DataSource dataSource) throws Exception {
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setDataSource(dataSource);
factory.setPersistenceXmlLocation("classpath:org/springframework/webflow/persistence/persistence.xml");
OpenJpaVendorAdapter openJpa = new OpenJpaVendorAdapter();
factory.setJpaVendorAdapter(openJpa);
factory.afterPropertiesSet();
return factory.getObject();
}
private void assertSessionNotBound() {
assertNull(TransactionSynchronizationManager.getResource(entityManagerFactory));
}
private void assertSessionBound() {
assertNotNull(TransactionSynchronizationManager.getResource(entityManagerFactory));
}
}

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns="http://www.springframework.org/schema/webflow"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
<persistence-context/>
<on-start>
<evaluate expression="loadTestBean" />
</on-start>
<view-state id="view">
<transition on="view2" to="view2"/>
<transition on="subflow" to="subflow"/>
<transition on="cancel" to="cancel"/>
<transition on="finish" to="finish"/>
</view-state>
<subflow-state id="subflow" subflow="foo">
<transition on="finish" to="view2">
<evaluate expression="loadTestBean" />
</transition>
</subflow-state>
<view-state id="view2">
<transition on="finish" to="finish"/>
</view-state>
<end-state id="finish" commit="true"/>
<end-state id="cancel"/>
</flow>