From da9de998a56d6965f44f28560a6a618f5ba33cd3 Mon Sep 17 00:00:00 2001 From: Ben Hale Date: Thu, 14 Jun 2007 02:24:36 +0000 Subject: [PATCH] Ensures that all exceptions (including RuntimeExceptions) are handled properly by the FlowExcution (SWF-333) --- .../engine/impl/FlowExecutionImpl.java | 21 ++++++ .../engine/impl/FlowExecutionImplTests.java | 75 +++++++++++++++++++ .../engine/impl/MiscFlowExecutionTests.java | 7 +- .../webflow/engine/impl/runtime-exception.xml | 15 ++++ 4 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 spring-webflow/src/test/java/org/springframework/webflow/engine/impl/runtime-exception.xml diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionImpl.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionImpl.java index 918aea9e..691a4b46 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionImpl.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionImpl.java @@ -191,6 +191,15 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable { } catch (FlowExecutionException e) { return pause(context, handleException(e, context)); + } catch (Exception e) { + String flowId = context.getActiveFlow().getId(); + String stateId = null; + if(context.getCurrentState() != null) { + stateId = context.getCurrentState().getId(); + } + FlowExecutionException flowException = new FlowExecutionException(flowId, stateId, + "Exception thrown in state '" + stateId + "' of flow '" + flowId + "'", e); + return pause(context, handleException(flowException, context)); } } finally { @@ -216,6 +225,12 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable { } catch (FlowExecutionException e) { return pause(context, handleException(e, context)); + } catch (Exception e) { + String flowId = context.getActiveFlow().getId(); + String stateId = context.getCurrentState().getId(); + FlowExecutionException flowException = new FlowExecutionException(flowId, stateId, + "Exception thrown in state '" + stateId + "' of flow '" + flowId + "'", e); + return pause(context, handleException(flowException, context)); } } finally { @@ -243,6 +258,12 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable { } catch (FlowExecutionException e) { return pause(context, handleException(e, context)); + } catch (Exception e) { + String flowId = context.getActiveFlow().getId(); + String stateId = context.getCurrentState().getId(); + FlowExecutionException flowException = new FlowExecutionException(flowId, stateId, + "Exception thrown in state '" + stateId + "' of flow '" + flowId + "'", e); + return pause(context, handleException(flowException, context)); } } finally { diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/impl/FlowExecutionImplTests.java b/spring-webflow/src/test/java/org/springframework/webflow/engine/impl/FlowExecutionImplTests.java index a7e54008..81d1584c 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/engine/impl/FlowExecutionImplTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/impl/FlowExecutionImplTests.java @@ -27,6 +27,8 @@ import org.springframework.webflow.core.collection.LocalAttributeMap; import org.springframework.webflow.engine.ActionState; import org.springframework.webflow.engine.EndState; import org.springframework.webflow.engine.Flow; +import org.springframework.webflow.engine.FlowExecutionExceptionHandler; +import org.springframework.webflow.engine.RequestControlContext; import org.springframework.webflow.engine.SubflowState; import org.springframework.webflow.engine.TargetStateResolver; import org.springframework.webflow.engine.Transition; @@ -47,7 +49,9 @@ import org.springframework.webflow.engine.support.TransitionExecutingStateExcept import org.springframework.webflow.execution.Action; import org.springframework.webflow.execution.Event; import org.springframework.webflow.execution.FlowExecution; +import org.springframework.webflow.execution.FlowExecutionException; import org.springframework.webflow.execution.FlowExecutionListener; +import org.springframework.webflow.execution.FlowExecutionListenerAdapter; import org.springframework.webflow.execution.MockFlowExecutionListener; import org.springframework.webflow.execution.RequestContext; import org.springframework.webflow.execution.TestAction; @@ -248,6 +252,45 @@ public class FlowExecutionImplTests extends TestCase { 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) { + } + + 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); @@ -293,4 +336,36 @@ public class FlowExecutionImplTests extends TestCase { }); } } + + 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; + } + + } } \ No newline at end of file diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/impl/MiscFlowExecutionTests.java b/spring-webflow/src/test/java/org/springframework/webflow/engine/impl/MiscFlowExecutionTests.java index 8125095f..37955844 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/engine/impl/MiscFlowExecutionTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/impl/MiscFlowExecutionTests.java @@ -30,6 +30,7 @@ import org.springframework.webflow.engine.builder.xml.XmlFlowBuilder; import org.springframework.webflow.engine.support.ApplicationViewSelector; import org.springframework.webflow.execution.Event; import org.springframework.webflow.execution.FlowExecution; +import org.springframework.webflow.execution.FlowExecutionException; import org.springframework.webflow.execution.RequestContext; import org.springframework.webflow.execution.support.ApplicationView; import org.springframework.webflow.test.MockExternalContext; @@ -75,8 +76,10 @@ public class MiscFlowExecutionTests extends TestCase { FlowExecutionImpl execution = new FlowExecutionImpl(flow); try { execution.start(null, new MockExternalContext()); - } catch (RequiredMappingException e) { - + fail("Should have thrown a FlowExecutionException"); + } catch (FlowExecutionException e) { + assertTrue("Root cause should have been a RequiredMappingException", + e.getRootCause() instanceof RequiredMappingException); } } diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/impl/runtime-exception.xml b/spring-webflow/src/test/java/org/springframework/webflow/engine/impl/runtime-exception.xml new file mode 100644 index 00000000..e736bc28 --- /dev/null +++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/impl/runtime-exception.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + +