From 3b51c7410edce2475f8dc984715e15206389417e Mon Sep 17 00:00:00 2001 From: Keith Donald Date: Mon, 9 Apr 2007 15:13:56 +0000 Subject: [PATCH] clean up JSF flow executor argument handler logic javadoc --- .../executor/jsf/FlowNavigationHandler.java | 102 ++++++++---------- ...lowNavigationHandlerArgumentExtractor.java | 84 +++++++-------- .../executor/jsf/FlowPhaseListener.java | 23 ++-- 3 files changed, 98 insertions(+), 111 deletions(-) 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: *

- * - *
  • If a flow execution is currently in progress: - * - *
  • + *
  • If the specified logical outcome is of the form flowId:xxx look up the corresponding + * {@link FlowDefinition} with that id and launch a new flow execution in the starting state. Expose the new execution + * as the "current" flow execution for this request. Expose the first selected view as the "current" view selection. + *
  • If the specified logical outcome is not of the form flowId:xxx, simply delegate to the standard + * 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