diff --git a/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/AbstractFlowExecutionPropertyResolver.java b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/AbstractFlowExecutionPropertyResolver.java new file mode 100644 index 00000000..b324b6b8 --- /dev/null +++ b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/AbstractFlowExecutionPropertyResolver.java @@ -0,0 +1,171 @@ +/* + * Copyright 2004-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.webflow.executor.jsf; + +import javax.faces.el.EvaluationException; +import javax.faces.el.PropertyNotFoundException; +import javax.faces.el.PropertyResolver; +import javax.faces.el.ReferenceSyntaxException; + +import org.springframework.webflow.execution.FlowExecution; + +/** + * Base class for property resolvers that set flow execution attributes. + * + * @author Keith Donald + */ +public abstract class AbstractFlowExecutionPropertyResolver extends PropertyResolver { + + /** + * The standard property resolver to delegate to if this one doesn't apply. + */ + private final PropertyResolver resolverDelegate; + + /** + * Creates a new flow executon property resolver + * @param resolverDelegate the resolver to delegate to when the property is not a flow execution attribute + */ + public AbstractFlowExecutionPropertyResolver(PropertyResolver resolverDelegate) { + this.resolverDelegate = resolverDelegate; + } + + /** + * Returns the property resolver this resolver delegates to if necessary. + */ + protected final PropertyResolver getResolverDelegate() { + return resolverDelegate; + } + + public Class getType(Object base, Object property) throws EvaluationException, PropertyNotFoundException { + if (base instanceof FlowExecution) { + FlowExecution execution = (FlowExecution) base; + assertPropertyNameValid(property); + return doGetAttributeType(execution, (String) property); + } + else { + return resolverDelegate.getType(base, property); + } + } + + public Class getType(Object base, int index) throws EvaluationException, PropertyNotFoundException { + if (base instanceof FlowExecution) { + // can't access flow execution by index so we cannot determine type. Return null per JSF spec + return null; + } + else { + return resolverDelegate.getType(base, index); + } + } + + public Object getValue(Object base, Object property) throws EvaluationException, PropertyNotFoundException { + if (base instanceof FlowExecution) { + FlowExecution execution = (FlowExecution) base; + assertPropertyNameValid(property); + return doGetAttribute(execution, (String) property); + } + else { + return resolverDelegate.getValue(base, property); + } + } + + public Object getValue(Object base, int index) throws EvaluationException, PropertyNotFoundException { + if (!(base instanceof FlowExecution)) { + return resolverDelegate.getValue(base, index); + } + else { + throw new ReferenceSyntaxException("Cannot apply an index value to a flow execution"); + } + } + + public boolean isReadOnly(Object base, Object property) throws EvaluationException, PropertyNotFoundException { + if (base instanceof FlowExecution) { + return false; + } + else { + return resolverDelegate.isReadOnly(base, property); + } + } + + public boolean isReadOnly(Object base, int index) throws EvaluationException, PropertyNotFoundException { + if (base instanceof FlowExecution) { + return false; + } + else { + return resolverDelegate.isReadOnly(base, index); + } + } + + public void setValue(Object base, Object property, Object value) throws EvaluationException, + PropertyNotFoundException { + if ((base instanceof FlowExecution)) { + FlowExecution execution = (FlowExecution) base; + assertPropertyNameValid(property); + doSetAttribute(execution, (String) property, value); + } + else { + resolverDelegate.setValue(base, property, value); + } + } + + public void setValue(Object base, int index, Object value) throws EvaluationException, PropertyNotFoundException { + if (base instanceof FlowExecution) { + throw new ReferenceSyntaxException("Cannot apply an index value to flow scope"); + } + else { + resolverDelegate.setValue(base, index, value); + } + } + + // helpers + + private void assertPropertyNameValid(Object property) { + if (property == null) { + throw new PropertyNotFoundException("The name of the flow execution attribute cannot be null"); + } + if (!(property instanceof String)) { + throw new PropertyNotFoundException("Flow execution attribute names must be strings but " + property + + " was not"); + } + if (((String) property).length() == 0) { + throw new PropertyNotFoundException("The name of the flow execution attribute cannot be blank"); + } + } + + /** + * Gets the type of value returned by the flow execution attribute. + * @param execution the flow execution + * @param attributeName the name of the attribute + * @return the type of value returned by the attribute + */ + protected abstract Class doGetAttributeType(FlowExecution execution, String attributeName); + + /** + * Gets the value of the flow execution attribute. + * @param execution the flow execution + * @param attributeName the name of the attribute + * @return the attribute value + */ + protected abstract Object doGetAttribute(FlowExecution execution, String attributeName); + + /** + * Sets the value of the flow execution attribute. + * @param execution the flow execution + * @param attributeName the name of the attribute + * @param attributeValue the attribute value + */ + protected abstract void doSetAttribute(FlowExecution execution, String attributeName, Object attributeValue); + +} \ No newline at end of file diff --git a/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/DelegatingFlowVariableResolver.java b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/DelegatingFlowVariableResolver.java index e9dd0eba..ab029256 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/DelegatingFlowVariableResolver.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/DelegatingFlowVariableResolver.java @@ -19,15 +19,19 @@ import javax.faces.context.FacesContext; import javax.faces.el.EvaluationException; import javax.faces.el.VariableResolver; +import org.springframework.web.jsf.DelegatingVariableResolver; import org.springframework.webflow.execution.FlowExecution; /** - * Custom variable resolver that searches the current flow execution for variables to resolve. - * The search algorithm looks in flash scope first, then flow scope, then conversation scope. - * If no variable is found, this resolver delegates to the next resolver in the chain. + * Custom variable resolver that searches the current flow execution for variables to resolve. The search algorithm + * looks in flash scope first, then flow scope, then conversation scope. If no variable is found, this resolver + * delegates to the next resolver in the chain. * - * Suitable for use along side other variable resolvers to support EL binding expressions like - * {#bean.property} where "bean" could be a property in any supported scope. + * Suitable for use along side other variable resolvers to support EL binding expressions like {#bean.property} where + * "bean" could be a property in any supported scope. + * + * Consider combining use of this class with a Spring {@link DelegatingVariableResolver} to also support lazy-initialized + * binding variables managed by a Spring application context using custom bean scopes. * * @author Keith Donald */ diff --git a/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowExecutionPropertyResolver.java b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowExecutionPropertyResolver.java index 675b2a4a..e055b0b9 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowExecutionPropertyResolver.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowExecutionPropertyResolver.java @@ -17,10 +17,8 @@ package org.springframework.webflow.executor.jsf; import java.util.Map; -import javax.faces.el.EvaluationException; import javax.faces.el.PropertyNotFoundException; import javax.faces.el.PropertyResolver; -import javax.faces.el.ReferenceSyntaxException; import org.springframework.webflow.execution.FlowExecution; @@ -32,7 +30,7 @@ import org.springframework.webflow.execution.FlowExecution; * * @author Keith Donald */ -public class FlowExecutionPropertyResolver extends PropertyResolver { +public class FlowExecutionPropertyResolver extends AbstractFlowExecutionPropertyResolver { /** * The name of the special flash scope execution property. @@ -50,53 +48,24 @@ public class FlowExecutionPropertyResolver extends PropertyResolver { private static final String CONVERSATION_SCOPE_PROPERTY = "conversationScope"; /** - * The standard property resolver to delegate to if this one doesn't apply. - */ - private final PropertyResolver resolverDelegate; - - /** - * Create a new FlowExecutionPropertyResolver using the original PropertyResolver. - *
- * A JSF implementation will automatically pass its original resolver into the constructor of a configured resolver, - * provided that there is a corresponding constructor argument. - * - * @param resolverDelegate the original VariableResolver + * Creates a new flow executon property resolver that resolves flash, flow, and conversation scope attributes. + * @param resolverDelegate the resolver to delegate to when the property is not a flow execution attribute */ public FlowExecutionPropertyResolver(PropertyResolver resolverDelegate) { - this.resolverDelegate = resolverDelegate; + super(resolverDelegate); } - public Class getType(Object base, int index) throws EvaluationException, PropertyNotFoundException { - if (!(base instanceof FlowExecution)) { - return resolverDelegate.getType(base, index); - } - else { - // can't access flow execution property by index - return null - return null; - } - } - - public Class getType(Object base, Object property) throws EvaluationException, PropertyNotFoundException { - if (!(base instanceof FlowExecution)) { - return resolverDelegate.getType(base, property); - } - if (property == null) { - throw new PropertyNotFoundException("Unable to get value from flow execution - property (key) is null"); - } - if (!(property instanceof String)) { - throw new PropertyNotFoundException("Unable to get value from flow execution - key is non-String"); - } - if (FLASH_SCOPE_PROPERTY.equals(property)) { + protected Class doGetAttributeType(FlowExecution execution, String attributeName) { + if (FLASH_SCOPE_PROPERTY.equals(attributeName)) { return Map.class; - } else if (FLOW_SCOPE_PROPERTY.equals(property)) { + } else if (FLOW_SCOPE_PROPERTY.equals(attributeName)) { return Map.class; - } else if (CONVERSATION_SCOPE_PROPERTY.equals(property)) { + } else if (CONVERSATION_SCOPE_PROPERTY.equals(attributeName)) { return Map.class; } else { // perform an attribute search - FlowExecution execution = (FlowExecution)base; - String attributeName = (String)property; - // try flash scope + + // try flash scope first Object value = execution.getActiveSession().getFlashMap().get(attributeName); if (value != null) { return value.getClass(); @@ -111,36 +80,21 @@ public class FlowExecutionPropertyResolver extends PropertyResolver { if (value != null) { return value.getClass(); } + // cannot determine return null; } } - public Object getValue(Object base, int index) throws EvaluationException, PropertyNotFoundException { - if (!(base instanceof FlowExecution)) { - return resolverDelegate.getValue(base, index); - } - else { - throw new ReferenceSyntaxException("Cannot apply an index value to flow execution"); - } - } - - public Object getValue(Object base, Object property) throws EvaluationException, PropertyNotFoundException { - if (!(base instanceof FlowExecution)) { - return resolverDelegate.getValue(base, property); - } - if (!(property instanceof String)) { - throw new PropertyNotFoundException("Unable to get value from flow execution - key is non-String"); - } - FlowExecution execution = (FlowExecution) base; - if (FLASH_SCOPE_PROPERTY.equals(property)) { + protected Object doGetAttribute(FlowExecution execution, String attributeName) { + if (FLASH_SCOPE_PROPERTY.equals(attributeName)) { return execution.getActiveSession().getFlashMap().asMap(); - } else if (FLOW_SCOPE_PROPERTY.equals(property)) { + } else if (FLOW_SCOPE_PROPERTY.equals(attributeName)) { return execution.getActiveSession().getScope().asMap(); - } else if (CONVERSATION_SCOPE_PROPERTY.equals(property)) { + } else if (CONVERSATION_SCOPE_PROPERTY.equals(attributeName)) { return execution.getConversationScope().asMap(); } else { // perform an attribute search - String attributeName = (String)property; + // try flash scope Object value = execution.getActiveSession().getFlashMap().get(attributeName); if (value != null) { @@ -156,54 +110,23 @@ public class FlowExecutionPropertyResolver extends PropertyResolver { if (value != null) { return value; } - throw new PropertyNotFoundException("Cannot resolve flow execution property '" + property + "'"); - } + // cannot resolve as expected + throw new PropertyNotFoundException("Readable flow execution attribute '" + attributeName + "' not found in any scope (flash, flow, or conversation)"); + } } - public boolean isReadOnly(Object base, int index) throws EvaluationException, PropertyNotFoundException { - if (!(base instanceof FlowExecution)) { - return resolverDelegate.isReadOnly(base, index); - } - return false; - } - - public boolean isReadOnly(Object base, Object property) throws EvaluationException, PropertyNotFoundException { - if (!(base instanceof FlowExecution)) { - return resolverDelegate.isReadOnly(base, property); - } - return false; - } - - public void setValue(Object base, int index, Object value) throws EvaluationException, PropertyNotFoundException { - if (!(base instanceof FlowExecution)) { - resolverDelegate.setValue(base, index, value); - } - throw new ReferenceSyntaxException("Cannot apply an index value to a flow execution"); - } - - public void setValue(Object base, Object property, Object value) throws EvaluationException, - PropertyNotFoundException { - if (!(base instanceof FlowExecution)) { - resolverDelegate.setValue(base, property, value); - return; - } - if (property == null || !(property instanceof String) - || ((String)property).length() == 0) { - throw new PropertyNotFoundException( - "Attempt to set flow execution attribute with null name, empty name, or non-String name"); - } - FlowExecution execution = (FlowExecution)base; - String attributeName = (String)property; - // perform a search: flash, flow, conversation + protected void doSetAttribute(FlowExecution execution, String attributeName, Object attributeValue) { + // perform a search if (execution.getActiveSession().getFlashMap().contains(attributeName)) { - execution.getActiveSession().getFlashMap().put(attributeName, value); + execution.getActiveSession().getFlashMap().put(attributeName, attributeValue); } else if (execution.getActiveSession().getScope().contains(attributeName)) { - execution.getActiveSession().getScope().put(attributeName, value); + execution.getActiveSession().getScope().put(attributeName, attributeValue); } else if (execution.getConversationScope().contains(attributeName)) { - execution.getConversationScope().put(attributeName, value); + execution.getConversationScope().put(attributeName, attributeValue); } else { - throw new PropertyNotFoundException("Settable flow execution property '" + property + "' not found"); - } + // cannot resolve as expected + throw new PropertyNotFoundException("Settable flow execution attribute '" + attributeName + "' not found in any scope (flash, flow, or conversation)"); + } } } \ No newline at end of file diff --git a/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowExecutionVariableResolver.java b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowExecutionVariableResolver.java index 7b2799e5..1a215940 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowExecutionVariableResolver.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowExecutionVariableResolver.java @@ -46,33 +46,26 @@ public class FlowExecutionVariableResolver extends VariableResolver { private VariableResolver resolverDelegate; /** - * Create a new FlowExecutionVariableResolver, using the given original VariableResolver. - *
- * A JSF implementation will automatically pass its original resolver into the constructor of a configured resolver, - * provided that there is a corresponding constructor argument. - * - * @param resolverDelegate the original VariableResolver + * Creates a new flow executon variable resolver that resolves the current FlowExecution object. + * @param resolverDelegate the resolver to delegate to when the variable is not named "flowExecution". */ public FlowExecutionVariableResolver(VariableResolver resolverDelegate) { this.resolverDelegate = resolverDelegate; } /** - * Return the original VariableResolver that this resolver delegates to. + * Returns the variable resolver this resolver delegates to if necessary. */ protected final VariableResolver getResolverDelegate() { return resolverDelegate; } - /** - * Check for the special "flow" variable first, then delegate to the original VariableResolver. - */ public Object resolveVariable(FacesContext context, String name) throws EvaluationException { - if (!FLOW_EXECUTION_VARIABLE_NAME.equals(name)) { - return resolverDelegate.resolveVariable(context, name); + if (FLOW_EXECUTION_VARIABLE_NAME.equals(name)) { + return FlowExecutionHolderUtils.getRequiredCurrentFlowExecution(context); } else { - return FlowExecutionHolderUtils.getRequiredCurrentFlowExecution(context); + return resolverDelegate.resolveVariable(context, name); } } } \ No newline at end of file diff --git a/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowPropertyResolver.java b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowPropertyResolver.java index 69b7ba9b..c8ce5345 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowPropertyResolver.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowPropertyResolver.java @@ -16,65 +16,42 @@ package org.springframework.webflow.executor.jsf; import javax.faces.context.FacesContext; -import javax.faces.el.EvaluationException; -import javax.faces.el.PropertyNotFoundException; import javax.faces.el.PropertyResolver; -import javax.faces.el.ReferenceSyntaxException; +import org.springframework.beans.factory.BeanFactory; import org.springframework.util.Assert; import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.jsf.DelegatingVariableResolver; import org.springframework.web.jsf.FacesContextUtils; import org.springframework.webflow.execution.FlowExecution; /** - * Custom property resolve that resolves active flow session scope properties - * from a thread bound flow execution. + * Custom property resolve that resolves flow session scope attributes from the current flow execution. This resolver + * will also create and set the attribute value to a bean from the root Spring Web Application Context if the value does + * not already exist, allowing for lazy-initialized binding variables. + * + * Designed mainly to be used with the {@link FlowVariableResolver}. This is the original property resolver implemented + * with Spring Web Flow 1.0. In general, prefer {@link DelegatingFlowVariableResolver} or + * {@link FlowExecutionVariableResolver} over use of this class. Also, consider use of the + * {@link DelegatingVariableResolver} as an alternative to accessing lazy-initialized binding variables managed by a + * Spring application context that uses custom bean scopes. * * @author Colin Sampaleanu + * @author Keith Donald */ -public class FlowPropertyResolver extends PropertyResolver { +public class FlowPropertyResolver extends AbstractFlowExecutionPropertyResolver { /** - * The standard property resolver to delegate to if this one doesn't apply. - */ - private final PropertyResolver resolverDelegate; - - /** - * Create a new PropertyResolver, using the given original PropertyResolver. - *
- * A JSF implementation will automatically pass its original resolver into - * the constructor of a configured resolver, provided that there is a - * corresponding constructor argument. - * - * @param resolverDelegate the original VariableResolver + * Creates a new flow execution property resolver that resolves flow scope attributes. + * @param resolverDelegate the resolver to delegate to when the property is not a flow execution attribute */ public FlowPropertyResolver(PropertyResolver resolverDelegate) { - this.resolverDelegate = resolverDelegate; + super(resolverDelegate); } - public Class getType(Object base, int index) throws EvaluationException, PropertyNotFoundException { - if (!(base instanceof FlowExecution)) { - return resolverDelegate.getType(base, index); - } - else { - // can't access flow scope by index, so can't determine type. Return null per JSF spec - return null; - } - } - - public Class getType(Object base, Object property) throws EvaluationException, PropertyNotFoundException { - if (!(base instanceof FlowExecution)) { - return resolverDelegate.getType(base, property); - } - if (property == null) { - throw new PropertyNotFoundException("Unable to get value from Flow, as property (key) is null"); - } - if (!(property instanceof String)) { - throw new PropertyNotFoundException("Unable to get value from Flow map, as key is non-String"); - } - FlowExecution execution = (FlowExecution)base; + protected Class doGetAttributeType(FlowExecution execution, String attributeName) { // we want to access flow scope of the active session (conversation) - Object value = execution.getActiveSession().getScope().get((String)property); + Object value = execution.getActiveSession().getScope().get(attributeName); // note that MyFaces returns Object.class for a null value here, but // as I read the JSF spec, null should be returned when the object // type can not be determined this certainly seems to be the case @@ -82,80 +59,29 @@ public class FlowPropertyResolver extends PropertyResolver { return (value == null) ? null : value.getClass(); } - public Object getValue(Object base, int index) throws EvaluationException, PropertyNotFoundException { - if (!(base instanceof FlowExecution)) { - return resolverDelegate.getValue(base, index); - } - else { - throw new ReferenceSyntaxException("Cannot apply an index value to flow scope map"); - } - } - - public Object getValue(Object base, Object property) throws EvaluationException, PropertyNotFoundException { - if (!(base instanceof FlowExecution)) { - return resolverDelegate.getValue(base, property); - } - if (!(property instanceof String)) { - throw new PropertyNotFoundException("Unable to get value from flow scope map - key is non-String"); - } - FlowExecution execution = (FlowExecution)base; - String attributeName = (String)property; + protected Object doGetAttribute(FlowExecution execution, String attributeName) { Object value = execution.getActiveSession().getScope().get(attributeName); if (value == null) { - FacesContext context = FacesContext.getCurrentInstance(); - Assert.notNull(context, "FacesContext must exist during property resolution stage"); - WebApplicationContext wac = getWebApplicationContext(context); - if (wac.containsBean(attributeName)) { - // note: this resolver doesn't care, but this should normally be - // either a stateless singleton bean, or a stateful/stateless prototype - value = wac.getBean(attributeName); + FacesContext facesContext = FacesContext.getCurrentInstance(); + Assert.notNull(facesContext, "The current FacesContext must be present during property resolution stage"); + BeanFactory beanFactory = getWebApplicationContext(facesContext); + if (beanFactory.containsBean(attributeName)) { + // note: this resolver doesn't care, but this should be + // a stateless bean with singleton scope or a stateful bean with prototype scope + value = beanFactory.getBean(attributeName); execution.getActiveSession().getScope().put(attributeName, value); } } return value; } - public boolean isReadOnly(Object base, int index) throws EvaluationException, PropertyNotFoundException { - if (!(base instanceof FlowExecution)) { - return resolverDelegate.isReadOnly(base, index); - } - return false; - } - - public boolean isReadOnly(Object base, Object property) throws EvaluationException, PropertyNotFoundException { - if (!(base instanceof FlowExecution)) { - return resolverDelegate.isReadOnly(base, property); - } - return false; - } - - public void setValue(Object base, int index, Object value) throws EvaluationException, PropertyNotFoundException { - if (!(base instanceof FlowExecution)) { - resolverDelegate.setValue(base, index, value); - } - throw new ReferenceSyntaxException("Cannot apply an index value to flow scope"); - } - - public void setValue(Object base, Object property, Object value) throws EvaluationException, - PropertyNotFoundException { - if (!(base instanceof FlowExecution)) { - resolverDelegate.setValue(base, property, value); - return; - } - if (property == null || !(property instanceof String) - || ((String)property).length() == 0) { - throw new PropertyNotFoundException( - "Attempt to set flow attribute with null name, empty name, or non-String name"); - } - FlowExecution execution = (FlowExecution)base; - execution.getActiveSession().getScope().put((String)property, value); + protected void doSetAttribute(FlowExecution execution, String attributeName, Object attributeValue) { + execution.getActiveSession().getScope().put(attributeName, attributeValue); } /** - * Retrieve the web application context to delegate bean name resolution to. - *
- * Default implementation delegates to FacesContextUtils.
- *
+ * Retrieve the web application context to delegate bean name resolution to. Default implementation delegates to
+ * FacesContextUtils.
* @param facesContext the current JSF context
* @return the Spring web application context (never null)
* @see FacesContextUtils#getRequiredWebApplicationContext
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowVariableResolver.java b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowVariableResolver.java
index 12c78f08..e2e7c603 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowVariableResolver.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowVariableResolver.java
@@ -20,9 +20,16 @@ import javax.faces.el.EvaluationException;
import javax.faces.el.VariableResolver;
/**
- * Custom variable resolver that resolves to a thread-bound FlowExecution object
- * for binding expressions prefixed with {@link #FLOW_SCOPE_VARIABLE}. For instance
- * "flowScope.myBean.myProperty".
+ * Custom variable resolver that resolves the current FlowExecution object for binding expressions prefixed with
+ * {@link #FLOW_SCOPE_VARIABLE}. For instance "flowScope.myBean.myProperty". Designed to be used in conjunction with
+ * {@link FlowPropertyResolver} only.
+ *
+ * This class is the original flow execution variable resolver implementation introduced in Spring Web Flow's JSF
+ * support available since 1.0. In general, prefer use of {@link DelegatingFlowVariableResolver} or
+ * {@link FlowExecutionVariableResolver} to this implementation as they are both considerably more flexible.
+ *
+ * This resolver should only be used with the {@link FlowPropertyResolver} which can only resolve flow-scoped variables.
+ * May be deprecated in a future release of Spring Web Flow.
*
* @author Colin Sampaleanu
*/
@@ -39,12 +46,10 @@ public class FlowVariableResolver extends VariableResolver {
private VariableResolver resolverDelegate;
/**
- * Create a new FlowVariableResolver, using the given original
- * VariableResolver.
+ * Create a new FlowVariableResolver, using the given original VariableResolver.
*
- * A JSF implementation will automatically pass its original resolver into - * the constructor of a configured resolver, provided that there is a - * corresponding constructor argument. + * A JSF implementation will automatically pass its original resolver into the constructor of a configured resolver, + * provided that there is a corresponding constructor argument. * * @param resolverDelegate the original VariableResolver */ @@ -59,16 +64,12 @@ public class FlowVariableResolver extends VariableResolver { return resolverDelegate; } - /** - * Check for the special "flow" variable first, then delegate to the - * original VariableResolver. - */ public Object resolveVariable(FacesContext context, String name) throws EvaluationException { - if (!FLOW_SCOPE_VARIABLE.equals(name)) { - return resolverDelegate.resolveVariable(context, name); + if (FLOW_SCOPE_VARIABLE.equals(name)) { + return FlowExecutionHolderUtils.getRequiredCurrentFlowExecution(context); } else { - return FlowExecutionHolderUtils.getRequiredCurrentFlowExecution(context); + return resolverDelegate.resolveVariable(context, name); } } } \ No newline at end of file