This commit is contained in:
Keith Donald
2007-04-02 03:52:39 +00:00
parent 19cf073bbb
commit b85f550bbd
3 changed files with 106 additions and 19 deletions

View File

@@ -20,14 +20,17 @@ import javax.faces.context.FacesContext;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.binding.mapping.AttributeMapper;
import org.springframework.web.jsf.DecoratingNavigationHandler; import org.springframework.web.jsf.DecoratingNavigationHandler;
import org.springframework.webflow.context.ExternalContext; import org.springframework.webflow.context.ExternalContext;
import org.springframework.webflow.core.collection.LocalAttributeMap;
import org.springframework.webflow.core.collection.MutableAttributeMap; import org.springframework.webflow.core.collection.MutableAttributeMap;
import org.springframework.webflow.definition.FlowDefinition; import org.springframework.webflow.definition.FlowDefinition;
import org.springframework.webflow.definition.registry.FlowDefinitionLocator; import org.springframework.webflow.definition.registry.FlowDefinitionLocator;
import org.springframework.webflow.execution.FlowExecution; import org.springframework.webflow.execution.FlowExecution;
import org.springframework.webflow.execution.FlowExecutionFactory; import org.springframework.webflow.execution.FlowExecutionFactory;
import org.springframework.webflow.execution.ViewSelection; import org.springframework.webflow.execution.ViewSelection;
import org.springframework.webflow.executor.RequestParameterInputMapper;
import org.springframework.webflow.executor.support.FlowExecutorArgumentExtractor; import org.springframework.webflow.executor.support.FlowExecutorArgumentExtractor;
/** /**
@@ -80,6 +83,21 @@ public class FlowNavigationHandler extends DecoratingNavigationHandler {
*/ */
private FlowExecutorArgumentExtractor argumentExtractor = new FlowNavigationHandlerArgumentExtractor(); 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.
* <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.
* <p>
* 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. * Create a new {@link FlowNavigationHandler} using the default constructor.
*/ */
@@ -110,7 +128,26 @@ public class FlowNavigationHandler extends DecoratingNavigationHandler {
public void setArgumentExtractor(FlowExecutorArgumentExtractor argumentExtractor) { public void setArgumentExtractor(FlowExecutorArgumentExtractor argumentExtractor) {
this.argumentExtractor = argumentExtractor; this.argumentExtractor = argumentExtractor;
} }
/**
* Returns the configured flow execution input mapper.
*/
public AttributeMapper getInputMapper() {
return inputMapper;
}
/**
* 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.
* @see RequestParameterInputMapper
*/
public void setInputMapper(AttributeMapper inputMapper) {
this.inputMapper = inputMapper;
}
public void handleNavigation(FacesContext facesContext, String fromAction, String outcome, public void handleNavigation(FacesContext facesContext, String fromAction, String outcome,
NavigationHandler originalNavigationHandler) { NavigationHandler originalNavigationHandler) {
JsfExternalContext context = new JsfExternalContext(facesContext, fromAction, outcome); JsfExternalContext context = new JsfExternalContext(facesContext, fromAction, outcome);
@@ -135,7 +172,7 @@ public class FlowNavigationHandler extends DecoratingNavigationHandler {
FlowExecution flowExecution = getFactory(context).createFlowExecution(flowDefinition); FlowExecution flowExecution = getFactory(context).createFlowExecution(flowDefinition);
FlowExecutionHolder holder = new FlowExecutionHolder(flowExecution); FlowExecutionHolder holder = new FlowExecutionHolder(flowExecution);
FlowExecutionHolderUtils.setFlowExecutionHolder(holder, facesContext); FlowExecutionHolderUtils.setFlowExecutionHolder(holder, facesContext);
ViewSelection selectedView = flowExecution.start(createInput(flowExecution, context), context); ViewSelection selectedView = flowExecution.start(createInput(context), context);
holder.setViewSelection(selectedView); holder.setViewSelection(selectedView);
} }
else { else {
@@ -155,12 +192,19 @@ public class FlowNavigationHandler extends DecoratingNavigationHandler {
/** /**
* Factory method that creates the input attribute map for a newly created * Factory method that creates the input attribute map for a newly created
* {@link FlowExecution}. TODO - add support for input mappings here * {@link FlowExecution}. This implementation uses the registered input mapper,
* @param flowExecution the new flow execution (yet to be started) * if any.
* @param context the external context * @param context the external context
* @return the input map * @return the input map, or null if no input
*/ */
protected MutableAttributeMap createInput(FlowExecution flowExecution, ExternalContext context) { protected MutableAttributeMap createInput(ExternalContext context) {
return null; if (inputMapper != null) {
MutableAttributeMap inputMap = new LocalAttributeMap();
inputMapper.map(context, inputMap, null);
return inputMap;
}
else {
return null;
}
} }
} }

View File

@@ -28,9 +28,11 @@ import javax.faces.event.PhaseListener;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.binding.mapping.AttributeMapper;
import org.springframework.webflow.context.ExternalContext; import org.springframework.webflow.context.ExternalContext;
import org.springframework.webflow.context.ExternalContextHolder; import org.springframework.webflow.context.ExternalContextHolder;
import org.springframework.webflow.core.collection.LocalAttributeMap; import org.springframework.webflow.core.collection.LocalAttributeMap;
import org.springframework.webflow.core.collection.MutableAttributeMap;
import org.springframework.webflow.definition.FlowDefinition; import org.springframework.webflow.definition.FlowDefinition;
import org.springframework.webflow.definition.registry.FlowDefinitionLocator; import org.springframework.webflow.definition.registry.FlowDefinitionLocator;
import org.springframework.webflow.execution.FlowExecution; import org.springframework.webflow.execution.FlowExecution;
@@ -43,6 +45,7 @@ import org.springframework.webflow.execution.support.ApplicationView;
import org.springframework.webflow.execution.support.ExternalRedirect; import org.springframework.webflow.execution.support.ExternalRedirect;
import org.springframework.webflow.execution.support.FlowDefinitionRedirect; import org.springframework.webflow.execution.support.FlowDefinitionRedirect;
import org.springframework.webflow.execution.support.FlowExecutionRedirect; import org.springframework.webflow.execution.support.FlowExecutionRedirect;
import org.springframework.webflow.executor.RequestParameterInputMapper;
import org.springframework.webflow.executor.ResponseInstruction; import org.springframework.webflow.executor.ResponseInstruction;
import org.springframework.webflow.executor.support.FlowExecutorArgumentHandler; import org.springframework.webflow.executor.support.FlowExecutorArgumentHandler;
import org.springframework.webflow.executor.support.RequestParameterFlowExecutorArgumentHandler; import org.springframework.webflow.executor.support.RequestParameterFlowExecutorArgumentHandler;
@@ -76,12 +79,27 @@ public class FlowPhaseListener implements PhaseListener {
* Logger, usable by subclasses. * Logger, usable by subclasses.
*/ */
protected final Log logger = LogFactory.getLog(getClass()); protected final Log logger = LogFactory.getLog(getClass());
/** /**
* A helper for handling arguments needed by this phase listener. * A helper for handling arguments needed by this phase listener to resume and launch flow executions.
*/ */
private FlowExecutorArgumentHandler argumentHandler = new RequestParameterFlowExecutorArgumentHandler(); private FlowExecutorArgumentHandler argumentHandler = new RequestParameterFlowExecutorArgumentHandler();
/**
* 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.
* <p>
* The default implementation simply exposes all request parameters as flow
* execution input attributes. May be null.
*/
private AttributeMapper inputMapper = new RequestParameterInputMapper();
/** /**
* Resolves selected Web Flow view names to JSF view ids. * Resolves selected Web Flow view names to JSF view ids.
*/ */
@@ -101,6 +119,25 @@ public class FlowPhaseListener implements PhaseListener {
this.argumentHandler = argumentHandler; this.argumentHandler = argumentHandler;
} }
/**
* Returns the configured flow execution input mapper.
*/
public AttributeMapper getInputMapper() {
return inputMapper;
}
/**
* 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.
* @see RequestParameterInputMapper
*/
public void setInputMapper(AttributeMapper inputMapper) {
this.inputMapper = inputMapper;
}
/** /**
* Returns the JSF view id resolver used by this phase listener. * Returns the JSF view id resolver used by this phase listener.
*/ */
@@ -142,7 +179,9 @@ public class FlowPhaseListener implements PhaseListener {
saveFlowExecution(getCurrentContext(), holder); saveFlowExecution(getCurrentContext(), holder);
} }
finally { finally {
holder.getFlowExecutionLock().unlock(); if (holder.getFlowExecutionLock() != null) {
holder.getFlowExecutionLock().unlock();
}
} }
} }
} }
@@ -167,6 +206,7 @@ public class FlowPhaseListener implements PhaseListener {
FlowExecutionKey flowExecutionKey = repository.parseFlowExecutionKey(argumentHandler FlowExecutionKey flowExecutionKey = repository.parseFlowExecutionKey(argumentHandler
.extractFlowExecutionKey(context)); .extractFlowExecutionKey(context));
FlowExecutionLock lock = repository.getLock(flowExecutionKey); FlowExecutionLock lock = repository.getLock(flowExecutionKey);
lock.lock();
FlowExecution flowExecution = repository.getFlowExecution(flowExecutionKey); FlowExecution flowExecution = repository.getFlowExecution(flowExecutionKey);
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Loaded existing flow execution from repository with id '" + flowExecutionKey + "'"); logger.debug("Loaded existing flow execution from repository with id '" + flowExecutionKey + "'");
@@ -182,7 +222,7 @@ public class FlowPhaseListener implements PhaseListener {
FlowExecution flowExecution = getFactory(context).createFlowExecution(flowDefinition); FlowExecution flowExecution = getFactory(context).createFlowExecution(flowDefinition);
FlowExecutionHolder holder = new FlowExecutionHolder(flowExecution); FlowExecutionHolder holder = new FlowExecutionHolder(flowExecution);
FlowExecutionHolderUtils.setFlowExecutionHolder(holder, facesContext); FlowExecutionHolderUtils.setFlowExecutionHolder(holder, facesContext);
ViewSelection selectedView = flowExecution.start(createInput(flowExecution, context), context); ViewSelection selectedView = flowExecution.start(createInput(context), context);
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Started new flow execution"); logger.debug("Started new flow execution");
} }
@@ -192,13 +232,20 @@ public class FlowPhaseListener implements PhaseListener {
/** /**
* Factory method that creates the input attribute map for a newly created * Factory method that creates the input attribute map for a newly created
* {@link FlowExecution}. TODO - add support for input mappings here * {@link FlowExecution}. This implementation uses the registered input mapper,
* @param flowExecution the new flow execution (yet to be started) * if any.
* @param context the external context * @param context the external context
* @return the input map * @return the input map, or null if no input
*/ */
protected LocalAttributeMap createInput(FlowExecution flowExecution, ExternalContext context) { protected MutableAttributeMap createInput(ExternalContext context) {
return null; if (inputMapper != null) {
MutableAttributeMap inputMap = new LocalAttributeMap();
inputMapper.map(context, inputMap, null);
return inputMap;
}
else {
return null;
}
} }
protected void prepareResponse(final JsfExternalContext context, final FlowExecutionHolder holder) { protected void prepareResponse(final JsfExternalContext context, final FlowExecutionHolder holder) {

View File

@@ -29,10 +29,6 @@ import org.springframework.webflow.execution.FlowExecution;
/** /**
* Custom property resolve that resolves active flow session scope properties * Custom property resolve that resolves active flow session scope properties
* from a thread bound flow execution. * from a thread bound flow execution.
* <p>
* TODO: this class probably needs to delegate to a strategy object pulled out
* of the appcontext, to provide ability to override and configure, as JSF
* provides no other way to customize and configure this instance.
* *
* @author Colin Sampaleanu * @author Colin Sampaleanu
*/ */