jpa tests, hibernate bug fix

This commit is contained in:
Keith Donald
2007-08-03 18:57:41 +00:00
parent 1255cec8a4
commit 2dbb3a8820
13 changed files with 532 additions and 294 deletions

View File

@@ -35,8 +35,9 @@
<classpathentry kind="lib" path="lib/test/spring-mock.jar" sourcepath="/spring/mock"/>
<classpathentry kind="lib" path="lib/global/el-api.jar"/>
<classpathentry kind="lib" path="lib/buildtime/persistence-api.jar"/>
<classpathentry kind="lib" path="lib/buildtime/spring-jpa.jar"/>
<classpathentry kind="lib" path="lib/test/shale-test.jar"/>
<classpathentry kind="lib" path="lib/global/jboss-el.jar"/>
<classpathentry kind="lib" path="lib/test/toplink-essentials.jar"/>
<classpathentry kind="lib" path="lib/buildtime/spring-jpa.jar"/>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -60,9 +60,7 @@
<dependency org="org.springframework" name="spring-dao" rev="2.0.6" conf="buildtime->default" />
<dependency org="org.springframework" name="spring-hibernate3" rev="2.0.6" conf="buildtime->default" />
<dependency org="javax.persistence" name="persistence-api" rev="1.0b" conf="buildtime->default"/>
<dependency org="toplink.essentials" name="toplink-essentials" rev="2.0-56" conf="buildtime->default" />
<dependency org="org.springframework" name="spring-jpa" rev="2.0.6" conf="buildtime->default" />
<!-- test time only dependencies -->
@@ -72,7 +70,11 @@
<dependency org="com.cenqua.clover" name="clover" rev="1.3.12" conf="test->default" />
<dependency org="org.springframework" name="spring-jdbc" rev="2.0.6" conf="test->default" />
<dependency org="org.springframework" name="spring-mock" rev="2.0.6" conf="test->default" />
<dependency org="org.springframework" name="spring-agent" rev="2.0.6" conf="test->default" />
<dependency org="org.apache.shale" name="shale-test" rev="1.0.4" conf="test->default" />
<dependency org="toplink.essentials" name="toplink-essentials" rev="2.0-56" conf="test->default" />
<dependency org="toplink.essentials" name="toplink-essentials-agent" rev="2.0-56" conf="test->default" />
</dependencies>
</ivy-module>

View File

@@ -16,6 +16,7 @@
package org.springframework.webflow.support.persistence;
import org.hibernate.FlushMode;
import org.hibernate.Interceptor;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.orm.hibernate3.SessionHolder;
@@ -72,12 +73,14 @@ import org.springframework.webflow.execution.ViewSelection;
*/
public class HibernateFlowExecutionListener extends FlowExecutionListenerAdapter {
private static final String HIBERNATE_SESSION_ATTRIBUTE = "hibernate.session";
private static final String HIBERNATE_SESSION_ATTRIBUTE = "session";
private TransactionTemplate transactionTemplate;
private SessionFactory sessionFactory;
private Interceptor entityInterceptor;
/**
* Create a new Session-per-Conversation listener using giving Hibernate session factory.
* @param sessionFactory the session factory to use
@@ -87,6 +90,14 @@ public class HibernateFlowExecutionListener extends FlowExecutionListenerAdapter
this.transactionTemplate = new TransactionTemplate(transactionManager);
}
/**
* Sets the entity interceptor to attach to sessions opened by this listener.
* @param entityInterceptor the entity interceptor
*/
public void setEntityInterceptor(Interceptor entityInterceptor) {
this.entityInterceptor = entityInterceptor;
}
public void sessionCreated(RequestContext context, FlowSession session) {
if (session.isRoot() && session.getDefinition().getAttributes().contains("persistenceContext")) {
Session hibernateSession = createSession(context);
@@ -130,7 +141,8 @@ public class HibernateFlowExecutionListener extends FlowExecutionListenerAdapter
// internal helpers
private Session createSession(RequestContext context) {
Session session = sessionFactory.openSession();
Session session = (entityInterceptor != null ? sessionFactory.openSession(entityInterceptor) : sessionFactory
.openSession());
session.setFlushMode(FlushMode.MANUAL);
return session;
}
@@ -144,6 +156,8 @@ public class HibernateFlowExecutionListener extends FlowExecutionListenerAdapter
}
private void unbind(Session session, RequestContext context) {
TransactionSynchronizationManager.unbindResource(sessionFactory);
if (TransactionSynchronizationManager.hasResource(sessionFactory)) {
TransactionSynchronizationManager.unbindResource(sessionFactory);
}
}
}

View File

@@ -74,7 +74,7 @@ import org.springframework.webflow.execution.ViewSelection;
*/
public class JpaFlowExecutionListener extends FlowExecutionListenerAdapter {
private static final String ENTITY_MANAGER_ATTRIBUTE = "jpa.entityManager";
private static final String ENTITY_MANAGER_ATTRIBUTE = "entityManager";
private EntityManagerFactory entityManagerFactory;
@@ -114,8 +114,12 @@ public class JpaFlowExecutionListener extends FlowExecutionListenerAdapter {
// this is a commit end state - start a new transaction that quickly commits
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
// nothing to do - a flush will happen on commit automatically as this is a read-write
// transaction
// necessary for JTA to enlist the entity manager in the transaction
try {
em.joinTransaction();
} catch (IllegalStateException e) {
// won't be necessary once Spring 2.0.7 is released
}
}
});
}
@@ -139,6 +143,8 @@ public class JpaFlowExecutionListener extends FlowExecutionListenerAdapter {
}
private void unbind(EntityManager em) {
TransactionSynchronizationManager.unbindResource(entityManagerFactory);
if (TransactionSynchronizationManager.hasResource(entityManagerFactory)) {
TransactionSynchronizationManager.unbindResource(entityManagerFactory);
}
}
}

View File

@@ -68,304 +68,311 @@ import org.springframework.webflow.test.MockFlowServiceLocator;
* @author Ben Hale
*/
public class FlowExecutionImplTests extends TestCase {
public void testExceptionHandlingWithEvaluateAction() {
FlowBuilder flowBuilder = new XmlFlowBuilder(new ClassPathResource("fooFlow.xml", getClass()));
Flow flow = new FlowAssembler("fooFlow", flowBuilder).assembleFlow();
FlowExecution flowExecution = new FlowExecutionImpl(flow);
ViewSelection view = flowExecution.start(null, new MockExternalContext());
assertEquals("showFooException", ((ApplicationView)view).getViewName());
assertFalse(flowExecution.isActive());
}
public void testExceptionWhileHandlingException() {
MockFlowServiceLocator serviceLocator = new MockFlowServiceLocator();
serviceLocator.registerBean("testAction", new ExceptionThrowingAction());
XmlFlowBuilder flowBuilder =
new XmlFlowBuilder(new ClassPathResource("exceptionHandlingFlow.xml", this.getClass()));
flowBuilder.setFlowServiceLocator(serviceLocator);
Flow flow = new FlowAssembler("flow", flowBuilder).assembleFlow();
FlowExecution flowExecution = new FlowExecutionImpl(flow);
ViewSelection view = flowExecution.start(null, new MockExternalContext());
assertEquals("failed", ((ApplicationView)view).getViewName());
assertFalse(flowExecution.isActive());
public void testExceptionHandlingWithGlobalTransitionExceptionHandler() {
FlowBuilder flowBuilder = new XmlFlowBuilder(new ClassPathResource("globalTransitionExceptionHandler-flow.xml",
getClass()));
Flow flow = new FlowAssembler("test", flowBuilder).assembleFlow();
FlowExecution flowExecution = new FlowExecutionImpl(flow);
ViewSelection view = flowExecution.start(null, new MockExternalContext());
assertEquals("showFooException", ((ApplicationView) view).getViewName());
assertFalse(flowExecution.isActive());
}
public void testExceptionHandlingWithEvaluateAction() {
FlowBuilder flowBuilder = new XmlFlowBuilder(new ClassPathResource("fooFlow.xml", getClass()));
Flow flow = new FlowAssembler("fooFlow", flowBuilder).assembleFlow();
FlowExecution flowExecution = new FlowExecutionImpl(flow);
ViewSelection view = flowExecution.start(null, new MockExternalContext());
assertEquals("showFooException", ((ApplicationView) view).getViewName());
assertFalse(flowExecution.isActive());
}
public void testExceptionWhileHandlingException() {
MockFlowServiceLocator serviceLocator = new MockFlowServiceLocator();
serviceLocator.registerBean("testAction", new ExceptionThrowingAction());
XmlFlowBuilder flowBuilder = new XmlFlowBuilder(new ClassPathResource("exceptionHandlingFlow.xml", this
.getClass()));
flowBuilder.setFlowServiceLocator(serviceLocator);
Flow flow = new FlowAssembler("flow", flowBuilder).assembleFlow();
FlowExecution flowExecution = new FlowExecutionImpl(flow);
ViewSelection view = flowExecution.start(null, new MockExternalContext());
assertEquals("failed", ((ApplicationView) view).getViewName());
assertFalse(flowExecution.isActive());
}
public void testFlowExecutionListener() {
Flow flow = new Flow("myFlow");
DefaultAttributeMapper inputMapper = new DefaultAttributeMapper();
MappingBuilder mapping = new MappingBuilder(DefaultExpressionParserFactory.getExpressionParser());
inputMapper.addMapping(mapping.source("name").target("flowScope.name").value());
flow.setInputMapper(inputMapper);
ActionState actionState = new ActionState(flow, "actionState");
actionState.getActionList().add(new TestAction());
actionState.getTransitionSet().add(new Transition(onEvent("success"), toState("viewState")));
ViewState viewState = new ViewState(flow, "viewState");
viewState.setViewSelector(selectView("myView"));
viewState.getTransitionSet().add(new Transition(onEvent("submit"), toState("subFlowState")));
Flow subFlow = new Flow("mySubFlow");
ViewState state1 = new ViewState(subFlow, "subFlowViewState");
state1.setViewSelector(selectView("mySubFlowViewName"));
state1.getTransitionSet().add(new Transition(onEvent("submit"), toState("finish")));
new EndState(subFlow, "finish");
SubflowState subflowState = new SubflowState(flow, "subFlowState", subFlow);
subflowState.getTransitionSet().add(new Transition(onEvent("finish"), toState("finish")));
EndState endState = new EndState(flow, "finish");
endState.getEntryActionList().add(new AbstractAction() {
protected Event doExecute(RequestContext context) throws Exception {
throw new IllegalStateException("Whoops!");
}
});
TransitionExecutingStateExceptionHandler handler = new TransitionExecutingStateExceptionHandler();
handler.add(Exception.class, "error");
endState.getExceptionHandlerSet().add(handler);
new EndState(flow, "error");
MockFlowExecutionListener flowExecutionListener = new MockFlowExecutionListener();
FlowExecutionImpl flowExecution = new FlowExecutionImpl(flow,
new FlowExecutionListener[] { flowExecutionListener }, null);
LocalAttributeMap input = new LocalAttributeMap();
input.put("name", "value");
assertTrue(!flowExecutionListener.isStarted());
flowExecution.start(input, new MockExternalContext());
assertTrue(flowExecutionListener.isStarted());
assertTrue(flowExecutionListener.isPaused());
assertTrue(!flowExecutionListener.isExecuting());
assertEquals(1, flowExecutionListener.getEventsSignaledCount());
assertEquals(0, flowExecutionListener.getFlowNestingLevel());
assertEquals(2, flowExecutionListener.getTransitionCount());
assertEquals("value", flowExecution.getActiveSession().getScope().getString("name"));
flowExecution.signalEvent("submit", new MockExternalContext());
assertTrue(!flowExecutionListener.isExecuting());
assertEquals(2, flowExecutionListener.getEventsSignaledCount());
assertEquals(1, flowExecutionListener.getFlowNestingLevel());
assertEquals(4, flowExecutionListener.getTransitionCount());
flowExecution.signalEvent("submit", new MockExternalContext());
assertTrue(!flowExecutionListener.isExecuting());
assertEquals(0, flowExecutionListener.getFlowNestingLevel());
assertEquals(4, flowExecutionListener.getEventsSignaledCount());
assertEquals(7, flowExecutionListener.getTransitionCount());
assertEquals(1, flowExecutionListener.getExceptionsThrown());
assertTrue(!flowExecution.isActive());
}
public void testLoopInFlow() throws Exception {
AbstractFlowBuilder builder = new AbstractFlowBuilder() {
public void buildStates() throws FlowBuilderException {
addViewState("viewState", "viewName", new Transition[] { transition(on(submit()), to("viewState")),
transition(on(finish()), to("endState")) });
addEndState("endState");
}
};
Flow flow = new FlowAssembler("flow", builder).assembleFlow();
FlowExecution flowExecution = new FlowExecutionImpl(flow);
ApplicationView view = (ApplicationView) flowExecution.start(null, new MockExternalContext());
assertNotNull(view);
assertEquals("viewName", view.getViewName());
for (int i = 0; i < 10; i++) {
view = (ApplicationView) flowExecution.signalEvent("submit", new MockExternalContext());
assertEquals("viewName", view.getViewName());
}
assertTrue(flowExecution.isActive());
flowExecution.signalEvent("finish", new MockExternalContext());
assertFalse(flowExecution.isActive());
}
public void testFlowExecutionListener() {
Flow flow = new Flow("myFlow");
DefaultAttributeMapper inputMapper = new DefaultAttributeMapper();
MappingBuilder mapping = new MappingBuilder(DefaultExpressionParserFactory.getExpressionParser());
inputMapper.addMapping(mapping.source("name").target("flowScope.name").value());
flow.setInputMapper(inputMapper);
ActionState actionState = new ActionState(flow, "actionState");
actionState.getActionList().add(new TestAction());
actionState.getTransitionSet().add(new Transition(onEvent("success"), toState("viewState")));
public void testLoopInFlowWithSubFlow() throws Exception {
AbstractFlowBuilder childBuilder = new AbstractFlowBuilder() {
public void buildStates() throws FlowBuilderException {
addActionState("doOtherStuff", new AbstractAction() {
private int executionCount = 0;
ViewState viewState = new ViewState(flow, "viewState");
viewState.setViewSelector(selectView("myView"));
viewState.getTransitionSet().add(new Transition(onEvent("submit"), toState("subFlowState")));
Flow subFlow = new Flow("mySubFlow");
ViewState state1 = new ViewState(subFlow, "subFlowViewState");
state1.setViewSelector(selectView("mySubFlowViewName"));
state1.getTransitionSet().add(new Transition(onEvent("submit"), toState("finish")));
new EndState(subFlow, "finish");
SubflowState subflowState = new SubflowState(flow, "subFlowState", subFlow);
subflowState.getTransitionSet().add(new Transition(onEvent("finish"), toState("finish")));
EndState endState = new EndState(flow, "finish");
endState.getEntryActionList().add(new AbstractAction() {
protected Event doExecute(RequestContext context) throws Exception {
throw new IllegalStateException("Whoops!");
protected Event doExecute(RequestContext context) throws Exception {
executionCount++;
if (executionCount < 2) {
return success();
}
});
TransitionExecutingStateExceptionHandler handler = new TransitionExecutingStateExceptionHandler();
handler.add(Exception.class, "error");
endState.getExceptionHandlerSet().add(handler);
return error();
}
},
new Transition[] { transition(on(success()), to(finish())),
transition(on(error()), to("stopTest")) });
addEndState(finish());
addEndState("stopTest");
}
};
final Flow childFlow = new FlowAssembler("flow", childBuilder).assembleFlow();
AbstractFlowBuilder parentBuilder = new AbstractFlowBuilder() {
public void buildStates() throws FlowBuilderException {
addActionState("doStuff", new AbstractAction() {
protected Event doExecute(RequestContext context) throws Exception {
return success();
}
}, transition(on(success()), to("startSubFlow")));
addSubflowState("startSubFlow", childFlow, null, new Transition[] {
transition(on(finish()), to("startSubFlow")), transition(on("stopTest"), to("stopTest")) });
addEndState("stopTest");
}
};
Flow parentFlow = new FlowAssembler("parentFlow", parentBuilder).assembleFlow();
new EndState(flow, "error");
FlowExecution flowExecution = new FlowExecutionImpl(parentFlow);
flowExecution.start(null, new MockExternalContext());
assertFalse(flowExecution.isActive());
}
MockFlowExecutionListener flowExecutionListener = new MockFlowExecutionListener();
FlowExecutionImpl flowExecution = new FlowExecutionImpl(flow,
new FlowExecutionListener[] { flowExecutionListener }, null);
LocalAttributeMap input = new LocalAttributeMap();
input.put("name", "value");
assertTrue(!flowExecutionListener.isStarted());
flowExecution.start(input, new MockExternalContext());
assertTrue(flowExecutionListener.isStarted());
assertTrue(flowExecutionListener.isPaused());
assertTrue(!flowExecutionListener.isExecuting());
assertEquals(1, flowExecutionListener.getEventsSignaledCount());
assertEquals(0, flowExecutionListener.getFlowNestingLevel());
assertEquals(2, flowExecutionListener.getTransitionCount());
assertEquals("value", flowExecution.getActiveSession().getScope().getString("name"));
flowExecution.signalEvent("submit", new MockExternalContext());
assertTrue(!flowExecutionListener.isExecuting());
assertEquals(2, flowExecutionListener.getEventsSignaledCount());
assertEquals(1, flowExecutionListener.getFlowNestingLevel());
assertEquals(4, flowExecutionListener.getTransitionCount());
flowExecution.signalEvent("submit", new MockExternalContext());
assertTrue(!flowExecutionListener.isExecuting());
assertEquals(0, flowExecutionListener.getFlowNestingLevel());
assertEquals(4, flowExecutionListener.getEventsSignaledCount());
assertEquals(7, flowExecutionListener.getTransitionCount());
assertEquals(1, flowExecutionListener.getExceptionsThrown());
assertTrue(!flowExecution.isActive());
public void testExtensiveFlowNavigationScenario1() {
XmlFlowBuilder builder = new XmlFlowBuilder(new ClassPathResource("testFlow1.xml", XmlFlowBuilderTests.class),
new TestFlowServiceLocator());
FlowAssembler assembler = new FlowAssembler("testFlow1", builder);
FlowExecution execution = new FlowExecutionImpl(assembler.assembleFlow());
MockExternalContext context = new MockExternalContext();
execution.start(null, context);
assertEquals("viewState1", execution.getActiveSession().getState().getId());
assertNotNull(execution.getActiveSession().getScope().get("items"));
execution.signalEvent("event1", context);
assertTrue(!execution.isActive());
}
public void testExtensiveFlowNavigationScenario2() {
XmlFlowBuilder builder = new XmlFlowBuilder(new ClassPathResource("testFlow1.xml", XmlFlowBuilderTests.class),
new TestFlowServiceLocator());
LocalAttributeMap attributes = new LocalAttributeMap();
attributes.put("scenario2", Boolean.TRUE);
FlowAssembler assembler = new FlowAssembler("testFlow1", attributes, builder);
FlowExecution execution = new FlowExecutionImpl(assembler.assembleFlow());
MockExternalContext context = new MockExternalContext();
execution.start(null, context);
assertEquals("viewState2", execution.getActiveSession().getState().getId());
assertNotNull(execution.getActiveSession().getScope().get("items"));
execution.signalEvent("event2", context);
assertTrue(!execution.isActive());
}
public void testFlashScope() {
FlowExecution execution = new FlowExecutionImpl(new FlashScopeFlow());
MockExternalContext context = new MockExternalContext();
execution.start(null, context);
execution.refresh(context);
execution.refresh(context);
execution.signalEvent("view", context);
}
public void testExceptionFromInputMapper() {
FlowBuilder flowBuilder = new XmlFlowBuilder(new ClassPathResource("runtime-exception.xml", getClass()));
Flow flow = new FlowAssembler("runtime-exception", flowBuilder).assembleFlow();
FlowExecutionImpl flowExecution = new FlowExecutionImpl(flow);
try {
flowExecution.start(new LocalAttributeMap(), new MockExternalContext());
fail("Should have thrown a FlowExecutionException, not any other type");
} catch (FlowExecutionException e) {
}
}
public void testExceptionWithListener() {
FlowBuilder flowBuilder = new XmlFlowBuilder(new ClassPathResource("runtime-exception.xml", getClass()));
Flow flow = new FlowAssembler("runtime-exception", flowBuilder).assembleFlow();
FlowExceptionListener listener = new FlowExceptionListener();
FlowExecutionImpl flowExecution = new FlowExecutionImpl(flow);
flowExecution.setListeners(new FlowExecutionListeners(new FlowExecutionListener[] { listener }));
try {
flowExecution.start(new LocalAttributeMap(), new MockExternalContext());
fail("Should have thrown a FlowExecutionException, not any other type");
} catch (FlowExecutionException e) {
}
public void testLoopInFlow() throws Exception {
AbstractFlowBuilder builder = new AbstractFlowBuilder() {
public void buildStates() throws FlowBuilderException {
addViewState("viewState", "viewName", new Transition[] { transition(on(submit()), to("viewState")),
transition(on(finish()), to("endState")) });
addEndState("endState");
}
};
Flow flow = new FlowAssembler("flow", builder).assembleFlow();
FlowExecution flowExecution = new FlowExecutionImpl(flow);
ApplicationView view = (ApplicationView)flowExecution.start(null, new MockExternalContext());
assertNotNull(view);
assertEquals("viewName", view.getViewName());
for (int i = 0; i < 10; i++) {
view = (ApplicationView)flowExecution.signalEvent("submit", new MockExternalContext());
assertEquals("viewName", view.getViewName());
assertTrue("Listener should have been called on exception", listener.getExceptionFired());
}
public void testExceptionWithHandler() {
FlowBuilder flowBuilder = new XmlFlowBuilder(new ClassPathResource("runtime-exception.xml", getClass()));
Flow flow = new FlowAssembler("runtime-exception", flowBuilder).assembleFlow();
FlowExceptionHandler handler = new FlowExceptionHandler();
flow.getExceptionHandlerSet().add(handler);
FlowExecutionImpl flowExecution = new FlowExecutionImpl(flow);
flowExecution.start(new LocalAttributeMap(), new MockExternalContext());
assertTrue("Handler should have been called on exception", handler.getExceptionHandled());
}
public static TransitionCriteria onEvent(String event) {
return new EventIdTransitionCriteria(event);
}
protected TargetStateResolver toState(String stateId) {
return new DefaultTargetStateResolver(stateId);
}
public static ViewSelector selectView(String viewName) {
return new ApplicationViewSelector(new StaticExpression(viewName));
}
private class FlashScopeFlow extends Flow {
public FlashScopeFlow() {
super("flashScopeFlow");
ActionState state1 = new ActionState(this, "action");
state1.getActionList().add(new Action() {
public Event execute(RequestContext context) throws Exception {
context.getFlashScope().put("flashScopedValue", "flashScopedValue");
return new Event(this, "success");
}
assertTrue(flowExecution.isActive());
flowExecution.signalEvent("finish", new MockExternalContext());
assertFalse(flowExecution.isActive());
}
});
state1.getTransitionSet().add(new Transition(toState("view")));
public void testLoopInFlowWithSubFlow() throws Exception {
AbstractFlowBuilder childBuilder = new AbstractFlowBuilder() {
public void buildStates() throws FlowBuilderException {
addActionState("doOtherStuff", new AbstractAction() {
private int executionCount = 0;
protected Event doExecute(RequestContext context) throws Exception {
executionCount++;
if (executionCount < 2) {
return success();
}
return error();
}
},
new Transition[] { transition(on(success()), to(finish())),
transition(on(error()), to("stopTest")) });
addEndState(finish());
addEndState("stopTest");
}
};
final Flow childFlow = new FlowAssembler("flow", childBuilder).assembleFlow();
AbstractFlowBuilder parentBuilder = new AbstractFlowBuilder() {
public void buildStates() throws FlowBuilderException {
addActionState("doStuff", new AbstractAction() {
protected Event doExecute(RequestContext context) throws Exception {
return success();
}
}, transition(on(success()), to("startSubFlow")));
addSubflowState("startSubFlow", childFlow, null, new Transition[] {
transition(on(finish()), to("startSubFlow")), transition(on("stopTest"), to("stopTest")) });
addEndState("stopTest");
}
};
Flow parentFlow = new FlowAssembler("parentFlow", parentBuilder).assembleFlow();
FlowExecution flowExecution = new FlowExecutionImpl(parentFlow);
flowExecution.start(null, new MockExternalContext());
assertFalse(flowExecution.isActive());
}
public void testExtensiveFlowNavigationScenario1() {
XmlFlowBuilder builder = new XmlFlowBuilder(new ClassPathResource("testFlow1.xml", XmlFlowBuilderTests.class),
new TestFlowServiceLocator());
FlowAssembler assembler = new FlowAssembler("testFlow1", builder);
FlowExecution execution = new FlowExecutionImpl(assembler.assembleFlow());
MockExternalContext context = new MockExternalContext();
execution.start(null, context);
assertEquals("viewState1", execution.getActiveSession().getState().getId());
assertNotNull(execution.getActiveSession().getScope().get("items"));
execution.signalEvent("event1", context);
assertTrue(!execution.isActive());
}
public void testExtensiveFlowNavigationScenario2() {
XmlFlowBuilder builder = new XmlFlowBuilder(new ClassPathResource("testFlow1.xml", XmlFlowBuilderTests.class),
new TestFlowServiceLocator());
LocalAttributeMap attributes = new LocalAttributeMap();
attributes.put("scenario2", Boolean.TRUE);
FlowAssembler assembler = new FlowAssembler("testFlow1", attributes, builder);
FlowExecution execution = new FlowExecutionImpl(assembler.assembleFlow());
MockExternalContext context = new MockExternalContext();
execution.start(null, context);
assertEquals("viewState2", execution.getActiveSession().getState().getId());
assertNotNull(execution.getActiveSession().getScope().get("items"));
execution.signalEvent("event2", context);
assertTrue(!execution.isActive());
}
public void testFlashScope() {
FlowExecution execution = new FlowExecutionImpl(new FlashScopeFlow());
MockExternalContext context = new MockExternalContext();
execution.start(null, context);
execution.refresh(context);
execution.refresh(context);
execution.signalEvent("view", context);
}
public void testExceptionFromInputMapper() {
FlowBuilder flowBuilder = new XmlFlowBuilder(new ClassPathResource("runtime-exception.xml",
getClass()));
Flow flow = new FlowAssembler("runtime-exception", flowBuilder).assembleFlow();
FlowExecutionImpl flowExecution = new FlowExecutionImpl(flow);
try {
flowExecution.start(new LocalAttributeMap(), new MockExternalContext());
fail("Should have thrown a FlowExecutionException, not any other type");
} catch (FlowExecutionException e) {
ViewState state2 = new ViewState(this, "view");
state2.getEntryActionList().add(new Action() {
public Event execute(RequestContext context) throws Exception {
assertTrue(context.getFlashScope().contains("flashScopedValue"));
return new Event(this, "success");
}
}
public void testExceptionWithListener() {
FlowBuilder flowBuilder = new XmlFlowBuilder(new ClassPathResource("runtime-exception.xml",
getClass()));
Flow flow = new FlowAssembler("runtime-exception", flowBuilder).assembleFlow();
FlowExceptionListener listener = new FlowExceptionListener();
FlowExecutionImpl flowExecution = new FlowExecutionImpl(flow);
flowExecution.setListeners(new FlowExecutionListeners(new FlowExecutionListener[] { listener }));
try {
flowExecution.start(new LocalAttributeMap(), new MockExternalContext());
fail("Should have thrown a FlowExecutionException, not any other type");
} catch (FlowExecutionException e) {
});
state2.getTransitionSet().add(new Transition(toState("end")));
EndState state3 = new EndState(this, "end");
state3.getEntryActionList().add(new Action() {
public Event execute(RequestContext context) throws Exception {
assertFalse(context.getFlashScope().contains("flashScopedValue"));
return new Event(this, "success");
}
assertTrue("Listener should have been called on exception", listener.getExceptionFired());
});
}
public void testExceptionWithHandler() {
FlowBuilder flowBuilder = new XmlFlowBuilder(new ClassPathResource("runtime-exception.xml",
getClass()));
Flow flow = new FlowAssembler("runtime-exception", flowBuilder).assembleFlow();
FlowExceptionHandler handler = new FlowExceptionHandler();
flow.getExceptionHandlerSet().add(handler);
FlowExecutionImpl flowExecution = new FlowExecutionImpl(flow);
flowExecution.start(new LocalAttributeMap(), new MockExternalContext());
assertTrue("Handler should have been called on exception", handler.getExceptionHandled());
}
private class FlowExceptionListener extends FlowExecutionListenerAdapter {
private boolean exceptionFired = false;
public boolean getExceptionFired() {
return exceptionFired;
}
public static TransitionCriteria onEvent(String event) {
return new EventIdTransitionCriteria(event);
public void exceptionThrown(RequestContext context, FlowExecutionException exception) {
exceptionFired = true;
}
}
private class FlowExceptionHandler implements FlowExecutionExceptionHandler {
private boolean exceptionHandled = false;
public boolean getExceptionHandled() {
return exceptionHandled;
}
protected TargetStateResolver toState(String stateId) {
return new DefaultTargetStateResolver(stateId);
}
public static ViewSelector selectView(String viewName) {
return new ApplicationViewSelector(new StaticExpression(viewName));
public ViewSelection handle(FlowExecutionException exception, RequestControlContext context) {
exceptionHandled = true;
return ViewSelection.NULL_VIEW;
}
private class FlashScopeFlow extends Flow {
public FlashScopeFlow() {
super("flashScopeFlow");
ActionState state1 = new ActionState(this, "action");
state1.getActionList().add(new Action() {
public Event execute(RequestContext context) throws Exception {
context.getFlashScope().put("flashScopedValue", "flashScopedValue");
return new Event(this, "success");
}
});
state1.getTransitionSet().add(new Transition(toState("view")));
ViewState state2 = new ViewState(this, "view");
state2.getEntryActionList().add(new Action() {
public Event execute(RequestContext context) throws Exception {
assertTrue(context.getFlashScope().contains("flashScopedValue"));
return new Event(this, "success");
}
});
state2.getTransitionSet().add(new Transition(toState("end")));
EndState state3 = new EndState(this, "end");
state3.getEntryActionList().add(new Action() {
public Event execute(RequestContext context) throws Exception {
assertFalse(context.getFlashScope().contains("flashScopedValue"));
return new Event(this, "success");
}
});
}
public boolean handles(FlowExecutionException exception) {
return true;
}
private class FlowExceptionListener extends FlowExecutionListenerAdapter {
private boolean exceptionFired = false;
public boolean getExceptionFired() {
return exceptionFired;
}
public void exceptionThrown(RequestContext context, FlowExecutionException exception) {
exceptionFired = true;
}
}
private class FlowExceptionHandler implements FlowExecutionExceptionHandler {
private boolean exceptionHandled = false;
public boolean getExceptionHandled() {
return exceptionHandled;
}
public ViewSelection handle(FlowExecutionException exception, RequestControlContext context) {
exceptionHandled = true;
return ViewSelection.NULL_VIEW;
}
public boolean handles(FlowExecutionException exception) {
return true;
}
}
}
}

View File

@@ -13,9 +13,9 @@
<transition on-exception="org.springframework.webflow.engine.impl.FooException" to="fooException" />
<transition on-exception="ognl.MethodFailedException" to="methodFailedException" />
</action-state>
<end-state id="fooException" view="showFooException" />
<end-state id="methodFailedException" view="showMethodFailedException" />
</flow>

View File

@@ -0,0 +1,23 @@
<?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-1.0.xsd">
<var name="flowAction" scope="flow" class="org.springframework.webflow.engine.impl.FooFlowAction" />
<start-state idref="action1" />
<action-state id="action1">
<evaluate-action expression="flowScope.flowAction.action1()" />
</action-state>
<end-state id="fooException" view="showFooException" />
<end-state id="methodFailedException" view="showMethodFailedException" />
<global-transitions>
<transition on-exception="org.springframework.webflow.engine.impl.FooException" to="fooException" />
</global-transitions>
</flow>

View File

@@ -74,7 +74,7 @@ public class HibernateFlowExecutionListenerTests extends TestCase {
assertSessionBound();
// Session created and bound to conversation
final Session hibSession = (Session) context.getConversationScope().get("hibernate.session");
final Session hibSession = (Session) context.getConversationScope().get("session");
assertNotNull("Should have been populated", hibSession);
listener.paused(context, ViewSelection.NULL_VIEW);
assertSessionNotBound();
@@ -212,6 +212,16 @@ public class HibernateFlowExecutionListenerTests extends TestCase {
}
public void testExceptionThrownWithNothingBound() {
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");
assertSessionNotBound();
listener.exceptionThrown(context, new FlowExecutionException("foo", "bar", "test"));
assertSessionNotBound();
}
private DataSource getDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");

View File

@@ -0,0 +1,119 @@
package org.springframework.webflow.support.persistence;
import java.sql.Connection;
import java.sql.SQLException;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import junit.framework.TestCase;
import org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver;
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.TopLinkJpaVendorAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.webflow.engine.EndState;
import org.springframework.webflow.test.MockFlowSession;
import org.springframework.webflow.test.MockRequestContext;
public class JpaFlowExecutionListenerTests extends TestCase {
private EntityManagerFactory entityManagerFactory;
private JpaFlowExecutionListener jpaListener;
private JdbcTemplate jdbcTemplate;
private JpaTemplate jpaTemplate;
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);
}
public void testFlowNotAPersistenceContext() {
MockRequestContext context = new MockRequestContext();
MockFlowSession flowSession = new MockFlowSession();
jpaListener.sessionCreated(context, flowSession);
assertSessionNotBound();
}
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();
flowSession.getDefinitionInternal().getAttributeMap().put("persistenceContext", "true");
jpaListener.sessionCreated(context, flowSession);
assertSessionBound();
TestBean bean = new TestBean(1, "Keith Donald");
jpaTemplate.persist(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);
jpaListener.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"));
}
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_BEAN if exists;");
connection.createStatement().execute(
"create table T_BEAN (ID integer primary key, NAME varchar(50) not null);");
connection.createStatement().execute("insert into T_BEAN (ID, NAME) values (0, 'Ben Hale');");
} 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/support/persistence/persistence.xml");
TopLinkJpaVendorAdapter toplink = new TopLinkJpaVendorAdapter();
factory.setJpaVendorAdapter(toplink);
factory.setLoadTimeWeaver(new InstrumentationLoadTimeWeaver());
factory.afterPropertiesSet();
return (EntityManagerFactory) factory.getObject();
}
private void assertSessionNotBound() {
assertNull(TransactionSynchronizationManager.getResource(entityManagerFactory));
}
private void assertSessionBound() {
assertNotNull(TransactionSynchronizationManager.getResource(entityManagerFactory));
}
}

View File

@@ -3,7 +3,7 @@
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.springframework.webflow.support.persistence" default-access="field" default-lazy="false">
<hibernate-mapping package="org.springframework.webflow.support.persistence" default-lazy="false">
<class name="TestBean" table="T_BEAN">
<id name="entityId" column="ID">
<generator class="increment" />

View File

@@ -17,14 +17,35 @@ package org.springframework.webflow.support.persistence;
public class TestBean {
private long entityId;
private Long entityId;
private String name;
public TestBean() {
}
public TestBean(String name) {
this.name = name;
}
public TestBean(long id, String name) {
this.entityId = new Long(id);
this.name = name;
}
public Long getEntityId() {
return entityId;
}
public void setEntityId(Long entityId) {
this.entityId = entityId;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"
version="1.0">
<entity class="org.springframework.webflow.support.persistence.TestBean">
<table name="T_BEAN"/>
<attributes>
<id name="entityId">
<column name="ID"/>
</id>
<basic name="name">
<column name="NAME"/>
</basic>
</attributes>
</entity>
</entity-mappings>

View File

@@ -0,0 +1,16 @@
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="webflow" transaction-type="RESOURCE_LOCAL">
<!-- Explicitly define mapping file path,-->
<mapping-file>org/springframework/webflow/support/persistence/orm.xml</mapping-file>
<!-- Prevent annotation scanning. In this app we are purely driven by orm.xml -->
<exclude-unlisted-classes>true</exclude-unlisted-classes>
</persistence-unit>
</persistence>