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.LogFactory;
import org.springframework.binding.mapping.AttributeMapper;
import org.springframework.web.jsf.DecoratingNavigationHandler;
import org.springframework.webflow.context.ExternalContext;
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.registry.FlowDefinitionLocator;
import org.springframework.webflow.execution.FlowExecution;
import org.springframework.webflow.execution.FlowExecutionFactory;
import org.springframework.webflow.execution.ViewSelection;
import org.springframework.webflow.executor.RequestParameterInputMapper;
import org.springframework.webflow.executor.support.FlowExecutorArgumentExtractor;
/**
@@ -80,6 +83,21 @@ public class FlowNavigationHandler extends DecoratingNavigationHandler {
*/
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.
*/
@@ -110,7 +128,26 @@ public class FlowNavigationHandler extends DecoratingNavigationHandler {
public void setArgumentExtractor(FlowExecutorArgumentExtractor 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,
NavigationHandler originalNavigationHandler) {
JsfExternalContext context = new JsfExternalContext(facesContext, fromAction, outcome);
@@ -135,7 +172,7 @@ public class FlowNavigationHandler extends DecoratingNavigationHandler {
FlowExecution flowExecution = getFactory(context).createFlowExecution(flowDefinition);
FlowExecutionHolder holder = new FlowExecutionHolder(flowExecution);
FlowExecutionHolderUtils.setFlowExecutionHolder(holder, facesContext);
ViewSelection selectedView = flowExecution.start(createInput(flowExecution, context), context);
ViewSelection selectedView = flowExecution.start(createInput(context), context);
holder.setViewSelection(selectedView);
}
else {
@@ -155,12 +192,19 @@ public class FlowNavigationHandler extends DecoratingNavigationHandler {
/**
* Factory method that creates the input attribute map for a newly created
* {@link FlowExecution}. TODO - add support for input mappings here
* @param flowExecution the new flow execution (yet to be started)
* {@link FlowExecution}. This implementation uses the registered input mapper,
* if any.
* @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) {
return null;
protected MutableAttributeMap createInput(ExternalContext context) {
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.LogFactory;
import org.springframework.binding.mapping.AttributeMapper;
import org.springframework.webflow.context.ExternalContext;
import org.springframework.webflow.context.ExternalContextHolder;
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.registry.FlowDefinitionLocator;
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.FlowDefinitionRedirect;
import org.springframework.webflow.execution.support.FlowExecutionRedirect;
import org.springframework.webflow.executor.RequestParameterInputMapper;
import org.springframework.webflow.executor.ResponseInstruction;
import org.springframework.webflow.executor.support.FlowExecutorArgumentHandler;
import org.springframework.webflow.executor.support.RequestParameterFlowExecutorArgumentHandler;
@@ -76,12 +79,27 @@ public class FlowPhaseListener implements PhaseListener {
* Logger, usable by subclasses.
*/
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();
/**
* 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.
*/
@@ -101,6 +119,25 @@ public class FlowPhaseListener implements PhaseListener {
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.
*/
@@ -142,7 +179,9 @@ public class FlowPhaseListener implements PhaseListener {
saveFlowExecution(getCurrentContext(), holder);
}
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
.extractFlowExecutionKey(context));
FlowExecutionLock lock = repository.getLock(flowExecutionKey);
lock.lock();
FlowExecution flowExecution = repository.getFlowExecution(flowExecutionKey);
if (logger.isDebugEnabled()) {
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);
FlowExecutionHolder holder = new FlowExecutionHolder(flowExecution);
FlowExecutionHolderUtils.setFlowExecutionHolder(holder, facesContext);
ViewSelection selectedView = flowExecution.start(createInput(flowExecution, context), context);
ViewSelection selectedView = flowExecution.start(createInput(context), context);
if (logger.isDebugEnabled()) {
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
* {@link FlowExecution}. TODO - add support for input mappings here
* @param flowExecution the new flow execution (yet to be started)
* {@link FlowExecution}. This implementation uses the registered input mapper,
* if any.
* @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) {
return null;
protected MutableAttributeMap createInput(ExternalContext context) {
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) {

View File

@@ -29,10 +29,6 @@ import org.springframework.webflow.execution.FlowExecution;
/**
* Custom property resolve that resolves active flow session scope properties
* 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
*/