clean up JSF flow executor argument handler logic
javadoc
This commit is contained in:
@@ -34,37 +34,34 @@ import org.springframework.webflow.executor.RequestParameterInputMapper;
|
||||
import org.springframework.webflow.executor.support.FlowExecutorArgumentExtractor;
|
||||
|
||||
/**
|
||||
* An implementation of a JSF <code>NavigationHandler</code> 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 <code>NavigationHandler</code> 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.
|
||||
* <p>
|
||||
* Specifically, the following navigation handler algorithm is implemented:
|
||||
* The following navigation handler algorithm is implemented by default:
|
||||
* </p>
|
||||
* <p>
|
||||
* If a flow execution has been restored in the current request:
|
||||
* <ul>
|
||||
* <li>If a flow execution is <strong>not</strong> currently in progress:
|
||||
* <li>Resume the flow execution by signaling the JSF action outcome as an event against the current state.
|
||||
* <li>Once event processing completes expose the selected view as the "current" {@link ViewSelection}.
|
||||
* </ul>
|
||||
* </p>
|
||||
* <p>
|
||||
* If a flow execution has not been restored in the current request:
|
||||
* <ul>
|
||||
* <li>If the specified logical outcome <strong>is</strong> of the form
|
||||
* <em>flowId:xxx</em>, look up the corresponding
|
||||
* {@link org.springframework.webflow.engine.Flow} definition with that id and
|
||||
* launch a new flow execution in the starting state. Expose information to
|
||||
* indicate that this flow is in progress and render the starting
|
||||
* {@link ViewSelection}.</li>
|
||||
* <li>If the specified logical outcome is <strong>not</strong> of the form
|
||||
* <em>flowId:xxx</em>, simply delegate to the standard
|
||||
* <code>NavigationHandler</code> implementation and return.</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* <li>If a flow execution <strong>is</strong> currently in progress:
|
||||
* <ul>
|
||||
* <li>Load the reference to the current in-progress flow execution using the
|
||||
* submitted <em>_flowExecutionKey</em> parameter.</li>
|
||||
* <li>Resume the flow execution by signaling what action outcome (aka event)
|
||||
* the user took in the current state.
|
||||
* <li>Once state event processing to complete, render the
|
||||
* <code>ViewSelection</code> returned.</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* <li>If the specified logical outcome is of the form <em>flowId:xxx</em> 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.
|
||||
* <li>If the specified logical outcome is not of the form <em>flowId:xxx</em>, simply delegate to the standard
|
||||
* <code>NavigationHandler</code> implementation and return.
|
||||
* </ul>
|
||||
* </p>
|
||||
* 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.
|
||||
* <p>
|
||||
* This allows developers to control what attributes are made available in
|
||||
* the <code>inputMap</code> 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 <code>inputMap</code> to new
|
||||
* top-level flow executions. The starting execution may then choose to map that available input into its own local
|
||||
* scope.
|
||||
* <p>
|
||||
* 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 <code>NavigationHandler</code>
|
||||
* we are wrapping
|
||||
* Create a new {@link FlowNavigationHandler}, wrapping the specified standard navigation handler implementation.
|
||||
* @param originalNavigationHandler Standard <code>NavigationHandler</code> 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.
|
||||
* <p>
|
||||
* 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());
|
||||
}
|
||||
|
||||
@@ -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 <code>flowId:${flowId}</code>
|
||||
public String extractFlowId(ExternalContext context) throws FlowExecutorArgumentExtractionException {
|
||||
// extract the flowId from a JSF outcome in format <code>${flowIdPrefix}${flowId}</code>
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user