diff --git a/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowNavigationHandler.java b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowNavigationHandler.java
index aa256806..1feca90b 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowNavigationHandler.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowNavigationHandler.java
@@ -34,37 +34,34 @@ import org.springframework.webflow.executor.RequestParameterInputMapper;
import org.springframework.webflow.executor.support.FlowExecutorArgumentExtractor;
/**
- * An implementation of a JSF NavigationHandler that provides
- * integration with Spring Web Flow. It delegates handling to the standard
- * NavigationHandler implementation when a navigation request does not pertain
- * to a flow execution.
+ * An implementation of a JSF NavigationHandler that provides integration with Spring Web Flow.
+ * Responsible for delegating to Spring Web Flow to launch and resume flow executions, treating JSF action outcomes
+ * (like a command button click) as web flow events.
+ *
+ * This class delegates to the standard NavigationHandler implementation when a navigation request does not pertain to a
+ * flow execution.
*
- * Specifically, the following navigation handler algorithm is implemented: + * The following navigation handler algorithm is implemented by default: + *
+ *+ * If a flow execution has been restored in the current request: *
+ * If a flow execution has not been restored in the current request: *
NavigationHandler implementation and return.ViewSelection returned.NavigationHandler implementation and return.
*
+ *
+ * How the flowId and eventId arguments are extracted can be customized by setting a custom
+ * {@link #setArgumentExtractor(FlowExecutorArgumentExtractor) argument extractor}.
*
* @author Craig McClanahan
* @author Colin Sampaleanu
@@ -78,26 +75,22 @@ public class FlowNavigationHandler extends DecoratingNavigationHandler {
protected final Log logger = LogFactory.getLog(getClass());
/**
- * A helper for extracting parameters needed by this flow navigation
- * handler.
+ * A helper for extracting parameters needed by this flow navigation handler.
*/
private FlowExecutorArgumentExtractor argumentExtractor = new FlowNavigationHandlerArgumentExtractor();
/**
- * The service responsible for mapping attributes of an
- * {@link ExternalContext} to a new {@link FlowExecution} during the
- * {@link #launch(String, ExternalContext) launch flow} operation.
+ * The service responsible for mapping attributes of an {@link ExternalContext} to a new {@link FlowExecution}
+ * during the {@link #launch(String, ExternalContext) launch flow} operation.
*
- * This allows developers to control what attributes are made available in
- * the inputMap to new top-level flow executions. The
- * starting execution may then choose to map that available input into its
- * own local scope.
+ * This allows developers to control what attributes are made available in the inputMap to new
+ * top-level flow executions. The starting execution may then choose to map that available input into its own local
+ * scope.
*
- * The default implementation simply exposes all request parameters as flow
- * execution input attributes. May be null.
+ * The default implementation simply exposes all request parameters as flow execution input attributes. May be null.
*/
private AttributeMapper inputMapper = new RequestParameterInputMapper();
-
+
/**
* Create a new {@link FlowNavigationHandler} using the default constructor.
*/
@@ -106,10 +99,8 @@ public class FlowNavigationHandler extends DecoratingNavigationHandler {
}
/**
- * Create a new {@link FlowNavigationHandler}, wrapping the specified
- * standard navigation handler implementation.
- * @param originalNavigationHandler Standard NavigationHandler
- * we are wrapping
+ * Create a new {@link FlowNavigationHandler}, wrapping the specified standard navigation handler implementation.
+ * @param originalNavigationHandler Standard NavigationHandler we are wrapping
*/
public FlowNavigationHandler(NavigationHandler originalNavigationHandler) {
super(originalNavigationHandler);
@@ -123,12 +114,13 @@ public class FlowNavigationHandler extends DecoratingNavigationHandler {
}
/**
- * Sets the argument extractor to use.
+ * Sets the argument extractor to use by this navigation handler. Call to customize how flow id and event id
+ * arguments are extracted.
*/
public void setArgumentExtractor(FlowExecutorArgumentExtractor argumentExtractor) {
this.argumentExtractor = argumentExtractor;
}
-
+
/**
* Returns the configured flow execution input mapper.
*/
@@ -137,17 +129,16 @@ public class FlowNavigationHandler extends DecoratingNavigationHandler {
}
/**
- * Sets the service responsible for mapping attributes of an
- * {@link ExternalContext} to a new {@link FlowExecution} during a launch flow operation.
+ * Sets the service responsible for mapping attributes of an {@link ExternalContext} to a new {@link FlowExecution}
+ * during a launch flow operation.
*
- * The default implementation simply exposes all request parameters as flow
- * execution input attributes. May be null.
+ * The default implementation simply exposes all request parameters as flow execution input attributes. May be null.
* @see RequestParameterInputMapper
*/
public void setInputMapper(AttributeMapper inputMapper) {
this.inputMapper = inputMapper;
}
-
+
public void handleNavigation(FacesContext facesContext, String fromAction, String outcome,
NavigationHandler originalNavigationHandler) {
JsfExternalContext context = new JsfExternalContext(facesContext, fromAction, outcome);
@@ -181,9 +172,8 @@ public class FlowNavigationHandler extends DecoratingNavigationHandler {
}
/**
- * Factory method that creates the input attribute map for a newly created
- * {@link FlowExecution}. This implementation uses the registered input mapper,
- * if any.
+ * Factory method that creates the input attribute map for a newly created {@link FlowExecution}. This
+ * implementation uses the registered input mapper, if any.
* @param context the external context
* @return the input map, or null if no input
*/
@@ -197,13 +187,13 @@ public class FlowNavigationHandler extends DecoratingNavigationHandler {
return null;
}
}
-
+
// helpers
-
+
private FlowDefinitionLocator getLocator(JsfExternalContext context) {
return FlowFacesUtils.getDefinitionLocator(context.getFacesContext());
}
-
+
private FlowExecutionFactory getFactory(JsfExternalContext context) {
return FlowFacesUtils.getExecutionFactory(context.getFacesContext());
}
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowNavigationHandlerArgumentExtractor.java b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowNavigationHandlerArgumentExtractor.java
index a02212b8..3025a705 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowNavigationHandlerArgumentExtractor.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowNavigationHandlerArgumentExtractor.java
@@ -19,27 +19,27 @@ import org.springframework.util.StringUtils;
import org.springframework.webflow.context.ExternalContext;
import org.springframework.webflow.executor.support.FlowExecutorArgumentExtractionException;
import org.springframework.webflow.executor.support.FlowExecutorArgumentExtractor;
-import org.springframework.webflow.executor.support.RequestParameterFlowExecutorArgumentHandler;
/**
- * An {@link FlowExecutorArgumentExtractor} that is aware of JSF
- * outcomes that communicate requests to launch flow executions and
- * signal event in existing flow executions.
+ * An {@link FlowExecutorArgumentExtractor} that is aware of JSF outcomes that communicate requests to launch flow
+ * executions and signal event in existing flow executions. Designed to be used wih a {@link FlowNavigationHandler}.
+ *
+ * Note: this class only implements flow id and event id extraction methods. A FlowNavigationHandler is not expected to
+ * extract a flow execution key, as flow execution restoration is fully handled by the {@link FlowPhaseListener} and the
+ * JSF restore view phase.
*
* @author Keith Donald
*/
-public class FlowNavigationHandlerArgumentExtractor extends RequestParameterFlowExecutorArgumentHandler {
-
- /*
- * Implementation note: subclasses an FlowExecutorArgumentHandler but is really
- * just a FlowExecutorArgumentExtractor.
- */
+public class FlowNavigationHandlerArgumentExtractor implements FlowExecutorArgumentExtractor {
/**
* The default prefix of a outcome string that indicates a new flow should be launched.
*/
private static final String FLOW_ID_PREFIX = "flowId:";
+ /**
+ * The prefix for JSF outcome strings indicating a new flow should be launched.
+ */
private String flowIdPrefix = FLOW_ID_PREFIX;
/**
@@ -56,54 +56,52 @@ public class FlowNavigationHandlerArgumentExtractor extends RequestParameterFlow
this.flowIdPrefix = flowIdPrefix;
}
- public boolean isEventIdPresent(ExternalContext context) {
- return StringUtils.hasText(getOutcome(context)) || super.isEventIdPresent(context);
- }
-
- // overidden to return the eventId from the action outcome string.
- public String extractEventId(ExternalContext context) throws FlowExecutorArgumentExtractionException {
- String outcome = getOutcome(context);
- if (StringUtils.hasText(outcome)) {
- return outcome;
- }
- else {
- return super.extractEventId(context);
- }
- }
-
public boolean isFlowIdPresent(ExternalContext context) throws FlowExecutorArgumentExtractionException {
String outcome = getOutcome(context);
if (outcome != null && outcome.startsWith(getFlowIdPrefix())) {
return true;
}
else {
- return super.isFlowIdPresent(context);
+ return false;
}
}
- // overidden to return the flowId from a JSF outcome in format flowId:${flowId}
public String extractFlowId(ExternalContext context) throws FlowExecutorArgumentExtractionException {
+ // extract the flowId from a JSF outcome in format ${flowIdPrefix}${flowId}
String outcome = getOutcome(context);
- if (StringUtils.hasText(outcome)) {
- int index = outcome.indexOf(getFlowIdPrefix());
- if (index == -1) {
- throw new FlowExecutorArgumentExtractionException(
- "Unable to extract flow id; make sure the JSF outcome is prefixed with '" + getFlowIdPrefix()
- + "' to launch a new flow execution");
- }
- String flowId = outcome.substring(getFlowIdPrefix().length());
- if (!StringUtils.hasText(flowId)) {
- throw new FlowExecutorArgumentExtractionException(
- "Unable to extract flow id; make sure the flow id is provided in the outcome string");
- }
- return flowId;
+ int index = outcome.indexOf(getFlowIdPrefix());
+ if (index == -1) {
+ throw new FlowExecutorArgumentExtractionException(
+ "Unable to extract flow id; make sure the JSF outcome is prefixed with '" + getFlowIdPrefix()
+ + "' to launch a new flow execution");
}
- else {
- return super.extractFlowId(context);
+ String flowId = outcome.substring(getFlowIdPrefix().length());
+ if (!StringUtils.hasText(flowId)) {
+ throw new FlowExecutorArgumentExtractionException(
+ "Unable to extract flow id; make sure the flow id is provided in the outcome string");
}
+ return flowId;
}
+ public boolean isEventIdPresent(ExternalContext context) {
+ return StringUtils.hasText(getOutcome(context));
+ }
+
+ public String extractEventId(ExternalContext context) throws FlowExecutorArgumentExtractionException {
+ // treat the action outcome string as the event id
+ return getOutcome(context);
+ }
+
+ public boolean isFlowExecutionKeyPresent(ExternalContext context) {
+ throw new UnsupportedOperationException("Should not be called by a FlowNavigationHandler");
+ }
+
+ public String extractFlowExecutionKey(ExternalContext context) throws FlowExecutorArgumentExtractionException {
+ throw new UnsupportedOperationException("Should not be called by a FlowNavigationHandler");
+ }
+
+ // helpers
private String getOutcome(ExternalContext context) {
- return ((JsfExternalContext)context).getOutcome();
+ return ((JsfExternalContext) context).getOutcome();
}
}
\ No newline at end of file
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowPhaseListener.java b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowPhaseListener.java
index f317cea9..eee12e50 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowPhaseListener.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowPhaseListener.java
@@ -71,7 +71,7 @@ import org.springframework.webflow.executor.support.ResponseInstructionHandler;
*
* @author Colin Sampaleanu
* @author Keith Donald
- * @author Jeremy Grelle
+ * @author Jeremy Grelle
*/
public class FlowPhaseListener implements PhaseListener {
@@ -255,8 +255,7 @@ public class FlowPhaseListener implements PhaseListener {
}
protected void handleFlowDefinitionRedirect(FlowDefinitionRedirect redirect) throws Exception {
- String url = argumentHandler.createFlowDefinitionUrl(
- (FlowDefinitionRedirect) holder.getViewSelection(), context);
+ String url = argumentHandler.createFlowDefinitionUrl(redirect, context);
sendRedirect(url, context);
}
@@ -269,8 +268,7 @@ public class FlowPhaseListener implements PhaseListener {
protected void handleExternalRedirect(ExternalRedirect redirect) throws Exception {
String flowExecutionKey = holder.getFlowExecution().isActive() ? holder.getFlowExecutionKey()
.toString() : null;
- String url = argumentHandler.createExternalUrl((ExternalRedirect) holder.getViewSelection(),
- flowExecutionKey, context);
+ String url = argumentHandler.createExternalUrl(redirect, flowExecutionKey, context);
sendRedirect(url, context);
}
@@ -287,12 +285,12 @@ public class FlowPhaseListener implements PhaseListener {
* @param holder the holder of the current flow execution
*/
protected void prepareApplicationView(FacesContext facesContext, FlowExecutionHolder holder) {
- ApplicationView forward = (ApplicationView) holder.getViewSelection();
- if (forward != null) {
+ ApplicationView view = (ApplicationView) holder.getViewSelection();
+ if (view != null) {
// expose the view's "model map" in the request map
- putInto(facesContext.getExternalContext().getRequestMap(), forward.getModel());
+ putInto(facesContext.getExternalContext().getRequestMap(), view.getModel());
// update the root component if necessary
- updateViewRoot(facesContext, viewIdMapper.mapViewId(forward.getViewName()));
+ updateViewRoot(facesContext, viewIdMapper.mapViewId(view.getViewName()));
}
String flowExecutionKey = holder.getFlowExecution().isActive() ? holder.getFlowExecutionKey().toString() : null;
if (flowExecutionKey != null) {
@@ -349,14 +347,15 @@ public class FlowPhaseListener implements PhaseListener {
}
/**
- * Saves the flow execution key in a component in the view root for restoration on subsequent RESTORE_VIEW operations.
+ * Saves the flow execution key in a component in the view root for restoration on subsequent RESTORE_VIEW
+ * operations.
* @param facesContext the faces context exposing the view root
* @param flowExecutionKey the flow execution key
*/
private void saveInViewRoot(FacesContext facesContext, String flowExecutionKey) {
// search for key holder in the component tree
- FlowExecutionKeyStateHolder keyHolder = (FlowExecutionKeyStateHolder) facesContext.getViewRoot()
- .findComponent(FlowExecutionKeyStateHolder.COMPONENT_ID);
+ FlowExecutionKeyStateHolder keyHolder = (FlowExecutionKeyStateHolder) facesContext.getViewRoot().findComponent(
+ FlowExecutionKeyStateHolder.COMPONENT_ID);
if (keyHolder == null) {
keyHolder = new FlowExecutionKeyStateHolder();
// expose as the first component in the view root for preservation in the tree