Ensures that all exceptions (including RuntimeExceptions) are handled properly by the FlowExcution (SWF-333)
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<?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">
|
||||
|
||||
<input-mapper>
|
||||
<input-attribute name="inputAttribute" required="true"/>
|
||||
</input-mapper>
|
||||
|
||||
<start-state idref="start" />
|
||||
|
||||
<end-state id="start"/>
|
||||
|
||||
</flow>
|
||||
Reference in New Issue
Block a user