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 691a4b46..2802017e 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
@@ -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();
}
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 81d1584c..d02d8745 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
@@ -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()));
diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/impl/exceptionHandlingFlow.xml b/spring-webflow/src/test/java/org/springframework/webflow/engine/impl/exceptionHandlingFlow.xml
index 19aa440a..d8299477 100644
--- a/spring-webflow/src/test/java/org/springframework/webflow/engine/impl/exceptionHandlingFlow.xml
+++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/impl/exceptionHandlingFlow.xml
@@ -17,9 +17,27 @@
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file