SWF-333 review

This commit is contained in:
Keith Donald
2007-06-14 04:24:00 +00:00
parent 18315fc1cf
commit 47bd11782a
3 changed files with 91 additions and 30 deletions

View File

@@ -192,11 +192,8 @@ 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();
}
String flowId = getCurrentFlow().getId();
String stateId = getCurrentStateId();
FlowExecutionException flowException = new FlowExecutionException(flowId, stateId,
"Exception thrown in state '" + stateId + "' of flow '" + flowId + "'", e);
return pause(context, handleException(flowException, context));
@@ -226,8 +223,8 @@ 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();
String flowId = getCurrentFlow().getId();
String stateId = getCurrentStateId();
FlowExecutionException flowException = new FlowExecutionException(flowId, stateId,
"Exception thrown in state '" + stateId + "' of flow '" + flowId + "'", e);
return pause(context, handleException(flowException, context));
@@ -259,8 +256,8 @@ 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();
String flowId = getCurrentFlow().getId();
String stateId = getCurrentStateId();
FlowExecutionException flowException = new FlowExecutionException(flowId, stateId,
"Exception thrown in state '" + stateId + "' of flow '" + flowId + "'", e);
return pause(context, handleException(flowException, context));
@@ -332,16 +329,29 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable {
// the state could be null if the flow was attempting a start operation
ViewSelection selectedView = tryStateHandlers(exception, context);
if (selectedView != null) {
if (logger.isDebugEnabled()) {
logger.debug("State '" + exception.getStateId() + "' handled exception");
}
return selectedView;
}
selectedView = tryFlowHandlers(exception, context);
if (selectedView != null) {
if (logger.isDebugEnabled()) {
logger.debug("Flow '" + flow.getId() + "' handled exception");
}
return selectedView;
}
}
catch (FlowExecutionException newException) {
// exception handling resulted in a new FlowExecutionException, try to handle it
return handleException(newException, context);
} catch (Exception e) {
// a lower-level exception occured, wrap it in a flow execution exception and try to handle it
String flowId = getCurrentFlow().getId();
String stateId = getCurrentStateId();
FlowExecutionException flowException = new FlowExecutionException(flowId, stateId,
"Exception thrown in state '" + stateId + "' of flow '" + flowId + "'", e);
return handleException(flowException, context);
}
if (logger.isDebugEnabled()) {
logger.debug("Rethrowing unhandled flow execution exception");
@@ -354,16 +364,11 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable {
* at the state level. Returns null if no handler handled the exception.
*/
private ViewSelection tryStateHandlers(FlowExecutionException exception, RequestControlContext context) {
ViewSelection selectedView = null;
if (exception.getStateId() != null) {
selectedView = getActiveFlow().getStateInstance(exception.getStateId()).handleException(exception, context);
if (selectedView != null) {
if (logger.isDebugEnabled()) {
logger.debug("State '" + exception.getStateId() + "' handled exception");
}
}
if (isActive() && exception.getStateId() != null) {
return getActiveFlow().getStateInstance(exception.getStateId()).handleException(exception, context);
} else {
return null;
}
return selectedView;
}
/**
@@ -371,13 +376,7 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable {
* at the flow level. Returns null if no handler handled the exception.
*/
private ViewSelection tryFlowHandlers(FlowExecutionException exception, RequestControlContext context) {
ViewSelection selectedView = getActiveFlow().handleException(exception, context);
if (selectedView != null) {
if (logger.isDebugEnabled()) {
logger.debug("Flow '" + exception.getFlowId() + "' handled exception");
}
}
return selectedView;
return getCurrentFlow().handleException(exception, context);
}
// internal helpers
@@ -476,17 +475,41 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable {
}
}
/**
* Returns the "current flow": which is the active flow if this execution is active, else the top-level flow definition.
*/
private Flow getCurrentFlow() {
return isActive() ? getActiveFlow() : this.flow;
}
/**
* Returns the id of the "current" state: a valid state identifier if the flow is active and in a state; null if the flow
* is not active or has not yet entered a state.
*/
private String getCurrentStateId() {
if (isActive()) {
State state = getCurrentState();
if (state != null) {
return state.getId();
} else {
return null;
}
} else {
return null;
}
}
/**
* Returns the currently active flow.
*/
private Flow getActiveFlow() {
private Flow getActiveFlow() throws IllegalStateException {
return (Flow)getActiveSessionInternal().getDefinition();
}
/**
* Returns the current state of this flow execution.
*/
private State getCurrentState() {
private State getCurrentState() throws IllegalStateException {
return (State)getActiveSessionInternal().getState();
}

View File

@@ -24,6 +24,8 @@ import org.springframework.core.io.ClassPathResource;
import org.springframework.webflow.action.AbstractAction;
import org.springframework.webflow.core.DefaultExpressionParserFactory;
import org.springframework.webflow.core.collection.LocalAttributeMap;
import org.springframework.webflow.core.collection.MutableAttributeMap;
import org.springframework.webflow.definition.FlowDefinition;
import org.springframework.webflow.engine.ActionState;
import org.springframework.webflow.engine.EndState;
import org.springframework.webflow.engine.Flow;
@@ -253,6 +255,24 @@ public class FlowExecutionImplTests extends TestCase {
execution.signalEvent("view", context);
}
public void testUnhandledExceptionThrownBeforeSessionStartup() {
FlowBuilder flowBuilder = new XmlFlowBuilder(new ClassPathResource("runtime-exception.xml",
getClass()));
Flow flow = new FlowAssembler("runtime-exception", flowBuilder).assembleFlow();
FlowExecutionListener listener = new FlowExecutionListenerAdapter() {
public void sessionStarting(RequestContext context, FlowDefinition definition, MutableAttributeMap input) {
throw new IllegalStateException("Cannot proceed");
}
};
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 testExceptionFromInputMapper() {
FlowBuilder flowBuilder = new XmlFlowBuilder(new ClassPathResource("runtime-exception.xml",
getClass()));

View File

@@ -17,9 +17,27 @@
<action bean="testAction">
<attribute name="exceptionType" value="java.sql.SQLException"/>
</action>
<transition on-exception="java.sql.SQLException" to="failed"/>
<transition on-exception="java.sql.SQLException" to="thirdTry"/>
</action-state>
<end-state id="failed" view="failed"/>
<action-state id="thirdTry">
<action bean="testAction">
<attribute name="exceptionType" value="java.sql.SQLException"/>
</action>
<transition on-exception="java.sql.SQLException" to="failed"/>
</action-state>
<end-state id="failed" view="failed">
<output-mapper>
<!-- will generate an exception as foo doesn't exist -->
<mapping source="flowScope.foo" target="foo" required="true"/>
</output-mapper>
</end-state>
<end-state id="reallyFailed" view="failed"/>
<global-transitions>
<transition on-exception="org.springframework.binding.mapping.RequiredMappingException" to="reallyFailed"/>
</global-transitions>
</flow>