Merging of SWF-367 back into trunk. This has all of the SWF 2.0 goodies.
This commit is contained in:
@@ -2,49 +2,49 @@
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="target/classes" path="src/main/java"/>
|
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/spring-binding"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/commons-beanutils.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/commons-digester.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/concurrent.jar"/>
|
||||
<classpathentry kind="lib" path="lib/test/easymock.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/hibernate.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/junit.jar"/>
|
||||
<classpathentry kind="lib" path="lib/global/ognl.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/portlet-api.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/servlet-api.jar"/>
|
||||
<classpathentry kind="lib" path="lib/global/spring-beans.jar" sourcepath="/spring/src"/>
|
||||
<classpathentry kind="lib" path="lib/global/spring-context.jar" sourcepath="/spring/src"/>
|
||||
<classpathentry kind="lib" path="lib/global/spring-core.jar" sourcepath="/spring/src"/>
|
||||
<classpathentry kind="lib" path="lib/global/spring-web.jar" sourcepath="/spring/src"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/spring-webmvc.jar" sourcepath="/spring/src"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/struts.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/commons-collections.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/dom4j.jar"/>
|
||||
<classpathentry kind="lib" path="lib/test/hsqldb.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/jta.jar"/>
|
||||
<classpathentry kind="lib" path="lib/test/spring-jdbc.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/persistence-api.jar"/>
|
||||
<classpathentry kind="lib" path="lib/test/clover.jar"/>
|
||||
<classpathentry kind="lib" path="lib/test/hibernate-annotations.jar"/>
|
||||
<classpathentry kind="lib" path="lib/test/hibernate-commons-annotations.jar"/>
|
||||
<classpathentry kind="lib" path="lib/test/hibernate-entitymanager.jar"/>
|
||||
<classpathentry kind="lib" path="lib/test/javassist.jar"/>
|
||||
<classpathentry kind="lib" path="lib/test/jboss-common-core.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/spring-orm.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/antlr.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/asm.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/asm-attrs.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/cglib.jar"/>
|
||||
<classpathentry kind="lib" path="lib/test/clover.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/commons-beanutils.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/commons-collections.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/commons-digester.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/commons-fileupload.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/commons-logging.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/commons-validator.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/concurrent.jar"/>
|
||||
<classpathentry kind="lib" path="lib/test/easymock.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/dom4j.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/ehcache.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/el-api.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/hibernate.jar"/>
|
||||
<classpathentry kind="lib" path="lib/test/hibernate-annotations.jar"/>
|
||||
<classpathentry kind="lib" path="lib/test/hibernate-commons-annotations.jar"/>
|
||||
<classpathentry kind="lib" path="lib/test/hibernate-entitymanager.jar"/>
|
||||
<classpathentry kind="lib" path="lib/test/hsqldb.jar"/>
|
||||
<classpathentry kind="lib" path="lib/test/javassist.jar"/>
|
||||
<classpathentry kind="lib" path="lib/test/jboss-common-core.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/jta.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/junit.jar"/>
|
||||
<classpathentry kind="lib" path="lib/global/ognl.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/oro.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/spring-tx.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/spring-webmvc-portlet.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/xalan.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/persistence-api.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/portlet-api.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/servlet-api.jar"/>
|
||||
<classpathentry kind="lib" path="lib/global/spring-beans.jar"/>
|
||||
<classpathentry kind="lib" path="lib/global/spring-binding.jar"/>
|
||||
<classpathentry kind="lib" path="lib/global/spring-context.jar"/>
|
||||
<classpathentry kind="lib" path="lib/global/spring-core.jar"/>
|
||||
<classpathentry kind="lib" path="lib/test/spring-jdbc.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/spring-orm.jar"/>
|
||||
<classpathentry kind="lib" path="lib/test/spring-test.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/spring-webmvc-struts.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/spring-tx.jar"/>
|
||||
<classpathentry kind="lib" path="lib/global/spring-web.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/spring-webmvc.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/spring-webmvc-portlet.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/struts.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/xalan.jar"/>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
<!-- build time only dependencies -->
|
||||
<dependency org="javax.servlet" name="servlet-api" rev="2.4" conf="buildtime->default" />
|
||||
<dependency org="javax.portlet" name="portlet-api" rev="1.0" conf="buildtime->default" />
|
||||
<dependency org="javax.el" name="el-api" rev="1.0" conf="buildtime->default" />
|
||||
<dependency org="org.hibernate" name="hibernate" rev="3.2.4.ga" conf="buildtime->default" />
|
||||
<dependency org="org.springframework" name="spring-orm" rev="2.1-m4" conf="buildtime->default" />
|
||||
<dependency org="org.springframework" name="spring-tx" rev="2.1-m4" conf="buildtime->default" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
SPRING WEB FLOW 2.0-m2 (October 2007)
|
||||
SPRING WEB FLOW 2.0-M2 (October 2007)
|
||||
----------------------------------
|
||||
http://www.springframework.org/webflow
|
||||
http://forum.springframework.org
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
http\://www.springframework.org/schema/webflow-config/spring-webflow-config-1.0.xsd=org/springframework/webflow/config/spring-webflow-config-1.0.xsd
|
||||
http\://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd=org/springframework/webflow/config/spring-webflow-config-2.0.xsd
|
||||
@@ -24,7 +24,6 @@ import org.springframework.webflow.core.collection.AttributeMap;
|
||||
import org.springframework.webflow.execution.Action;
|
||||
import org.springframework.webflow.execution.Event;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
import org.springframework.webflow.execution.support.EventFactorySupport;
|
||||
|
||||
/**
|
||||
* Base action that provides assistance commonly needed by action implementations. This includes:
|
||||
@@ -62,7 +61,7 @@ public abstract class AbstractAction implements Action, InitializingBean {
|
||||
}
|
||||
|
||||
/**
|
||||
* Action initializing callback, may be overriden by subclasses to perform custom initialization logic.
|
||||
* Action initializing callback, may be overridden by subclasses to perform custom initialization logic.
|
||||
* <p>
|
||||
* Keep in mind that this hook will only be invoked when this action is deployed in a Spring application context
|
||||
* since it uses the Spring {@link InitializingBean} mechanism to trigger action initialisation.
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package org.springframework.webflow.action;
|
||||
|
||||
import org.springframework.binding.expression.EvaluationContext;
|
||||
import org.springframework.binding.expression.Expression;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.webflow.execution.Event;
|
||||
@@ -70,20 +69,10 @@ public class EvaluateAction extends AbstractAction {
|
||||
}
|
||||
|
||||
protected Event doExecute(RequestContext context) throws Exception {
|
||||
Object result = expression.evaluate(context, getEvaluationContext(context));
|
||||
Object result = expression.getValue(context);
|
||||
if (evaluationResultExposer != null) {
|
||||
evaluationResultExposer.exposeResult(result, context);
|
||||
}
|
||||
return resultEventFactorySelector.forResult(result).createResultEvent(this, result, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method subclasses may override to customize the expressin evaluation context. This implementation
|
||||
* returns null.
|
||||
* @param context the request context
|
||||
* @return the evaluation context
|
||||
*/
|
||||
protected EvaluationContext getEvaluationContext(RequestContext context) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.webflow.execution.support;
|
||||
package org.springframework.webflow.action;
|
||||
|
||||
import org.springframework.webflow.core.collection.AttributeMap;
|
||||
import org.springframework.webflow.core.collection.CollectionUtils;
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.springframework.webflow.action;
|
||||
|
||||
import org.springframework.binding.expression.Expression;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.webflow.execution.Event;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
|
||||
public class ExternalRedirectAction extends AbstractAction {
|
||||
|
||||
private Expression resourceUri;
|
||||
|
||||
public ExternalRedirectAction(Expression resourceUri) {
|
||||
Assert.notNull(resourceUri, "The URI of the resource to redirect to is required");
|
||||
this.resourceUri = resourceUri;
|
||||
}
|
||||
|
||||
protected Event doExecute(RequestContext context) throws Exception {
|
||||
String resourceUri = (String) this.resourceUri.getValue(context);
|
||||
context.getExternalContext().sendExternalRedirect(resourceUri);
|
||||
return success();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package org.springframework.webflow.action;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.binding.expression.Expression;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.webflow.context.FlowDefinitionRequestInfo;
|
||||
import org.springframework.webflow.context.RequestPath;
|
||||
import org.springframework.webflow.core.collection.LocalParameterMap;
|
||||
import org.springframework.webflow.core.collection.ParameterMap;
|
||||
import org.springframework.webflow.execution.Event;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
|
||||
public class FlowDefinitionRedirectAction extends AbstractAction {
|
||||
private Expression flowId;
|
||||
private Expression[] requestElements;
|
||||
private Map requestParameters;
|
||||
|
||||
public FlowDefinitionRedirectAction(Expression flowId, Expression[] requestElements, Map requestParameters) {
|
||||
Assert.notNull(flowId, "The flow id to redirect to is required");
|
||||
this.flowId = flowId;
|
||||
this.requestElements = requestElements;
|
||||
this.requestParameters = requestParameters;
|
||||
}
|
||||
|
||||
protected Event doExecute(RequestContext context) throws Exception {
|
||||
String flowId = (String) this.flowId.getValue(context);
|
||||
RequestPath requestPath = evaluateRequestPath(context);
|
||||
ParameterMap requestParameters = evaluateRequestParameters(context);
|
||||
context.getExternalContext().sendFlowDefinitionRedirect(
|
||||
new FlowDefinitionRequestInfo(flowId, requestPath, requestParameters, null));
|
||||
return success();
|
||||
}
|
||||
|
||||
private RequestPath evaluateRequestPath(RequestContext context) {
|
||||
if (this.requestElements == null || this.requestElements.length == 0) {
|
||||
return null;
|
||||
}
|
||||
String[] requestElements = new String[this.requestElements.length];
|
||||
for (int i = 0; i < this.requestElements.length; i++) {
|
||||
Expression element = this.requestElements[i];
|
||||
requestElements[i] = (String) element.getValue(context);
|
||||
}
|
||||
return RequestPath.valueOf(requestElements);
|
||||
}
|
||||
|
||||
private ParameterMap evaluateRequestParameters(RequestContext context) {
|
||||
if (this.requestParameters == null) {
|
||||
return null;
|
||||
} else {
|
||||
Map requestParameters = new HashMap();
|
||||
for (Iterator it = this.requestParameters.entrySet().iterator(); it.hasNext();) {
|
||||
Map.Entry entry = (Map.Entry) it.next();
|
||||
Expression name = (Expression) entry.getKey();
|
||||
Expression value = (Expression) entry.getValue();
|
||||
String paramName = (String) name.getValue(context);
|
||||
String paramValue = (String) value.getValue(context);
|
||||
requestParameters.put(paramName, paramValue);
|
||||
}
|
||||
return new LocalParameterMap(requestParameters);
|
||||
}
|
||||
}
|
||||
|
||||
public static FlowDefinitionRedirectAction create(String encodedFlowRedirect) {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package org.springframework.webflow.action;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.PropertyEditorRegistrar;
|
||||
import org.springframework.beans.PropertyEditorRegistry;
|
||||
@@ -26,7 +24,7 @@ import org.springframework.core.style.StylerUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.DataBinder;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.validation.MessageCodesResolver;
|
||||
@@ -36,7 +34,6 @@ import org.springframework.webflow.execution.Event;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
import org.springframework.webflow.execution.ScopeType;
|
||||
import org.springframework.webflow.util.DispatchMethodInvoker;
|
||||
import org.springframework.webflow.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Multi-action that implements common logic dealing with input forms. This class leverages the Spring Web data binding
|
||||
@@ -242,20 +239,6 @@ import org.springframework.webflow.util.ReflectionUtils;
|
||||
*/
|
||||
public class FormAction extends MultiAction implements InitializingBean {
|
||||
|
||||
/*
|
||||
* Implementation note: Uses deprecated DataBinder.getErrors() to remain compatible with Spring 1.2.x.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Implementation note: Introspects BindException at class init time to preserve 1.2.x compatability.
|
||||
*/
|
||||
private static boolean hasPropertyEditorRegistryAccessor;
|
||||
|
||||
static {
|
||||
hasPropertyEditorRegistryAccessor = ClassUtils
|
||||
.hasMethod(BindException.class, "getPropertyEditorRegistry", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* The default form object name ("formObject").
|
||||
*/
|
||||
@@ -510,7 +493,7 @@ public class FormAction extends MultiAction implements InitializingBean {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Executing validation");
|
||||
}
|
||||
doValidate(context, formObject, binder.getErrors());
|
||||
doValidate(context, formObject, binder.getBindingResult());
|
||||
} else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
if (getValidator() == null) {
|
||||
@@ -520,8 +503,8 @@ public class FormAction extends MultiAction implements InitializingBean {
|
||||
}
|
||||
}
|
||||
}
|
||||
putFormErrors(context, binder.getErrors());
|
||||
return binder.getErrors().hasErrors() ? error() : success();
|
||||
putFormErrors(context, binder.getBindingResult());
|
||||
return binder.getBindingResult().hasErrors() ? error() : success();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -542,8 +525,8 @@ public class FormAction extends MultiAction implements InitializingBean {
|
||||
Object formObject = getFormObject(context);
|
||||
DataBinder binder = createBinder(context, formObject);
|
||||
doBind(context, binder);
|
||||
putFormErrors(context, binder.getErrors());
|
||||
return binder.getErrors().hasErrors() ? error() : success();
|
||||
putFormErrors(context, binder.getBindingResult());
|
||||
return binder.getBindingResult().hasErrors() ? error() : success();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -635,7 +618,7 @@ public class FormAction extends MultiAction implements InitializingBean {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Creating new form errors for object with name '" + getFormObjectName() + "'");
|
||||
}
|
||||
Errors errors = createBinder(context, formObject).getErrors();
|
||||
Errors errors = createBinder(context, formObject).getBindingResult();
|
||||
putFormErrors(context, errors);
|
||||
return errors;
|
||||
}
|
||||
@@ -691,8 +674,8 @@ public class FormAction extends MultiAction implements InitializingBean {
|
||||
*/
|
||||
private boolean formErrorsValid(RequestContext context, Object formObject) {
|
||||
Errors errors = getFormObjectAccessor(context).getFormErrors(getFormObjectName(), getFormErrorsScope());
|
||||
if (errors instanceof BindException) {
|
||||
BindException be = (BindException) errors;
|
||||
if (errors instanceof BindingResult) {
|
||||
BindingResult be = (BindingResult) errors;
|
||||
if (be.getTarget() != formObject) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Inconsistency detected: the Errors instance in '" + getFormErrorsScope()
|
||||
@@ -715,29 +698,9 @@ public class FormAction extends MultiAction implements InitializingBean {
|
||||
* @param context the flow execution request context
|
||||
*/
|
||||
private void reinstallPropertyEditors(RequestContext context) {
|
||||
BindException errors = (BindException) getFormObjectAccessor(context).getFormErrors(getFormObjectName(),
|
||||
BindingResult errors = (BindingResult) getFormObjectAccessor(context).getFormErrors(getFormObjectName(),
|
||||
getFormErrorsScope());
|
||||
registerPropertyEditors(context, getPropertyEditorRegistry(errors));
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a property editor registry from given bind exception (errors instance).
|
||||
*/
|
||||
private PropertyEditorRegistry getPropertyEditorRegistry(BindException errors) {
|
||||
Method accessor;
|
||||
try {
|
||||
if (hasPropertyEditorRegistryAccessor) {
|
||||
accessor = errors.getClass().getMethod("getPropertyEditorRegistry", null);
|
||||
} else {
|
||||
// only way to get at the registry in 1.2.8 or <.
|
||||
accessor = errors.getClass().getDeclaredMethod("getBeanWrapper", null);
|
||||
accessor.setAccessible(true);
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new IllegalStateException(
|
||||
"Unable to resolve property editor registry accessor method as expected - this should not happen");
|
||||
}
|
||||
return (PropertyEditorRegistry) ReflectionUtils.invokeMethod(accessor, errors);
|
||||
registerPropertyEditors(context, errors.getPropertyEditorRegistry());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -852,8 +815,8 @@ public class FormAction extends MultiAction implements InitializingBean {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Binding completed for form object with name '" + binder.getObjectName()
|
||||
+ "', post-bind formObject toString = " + binder.getTarget());
|
||||
logger.debug("There are [" + binder.getErrors().getErrorCount() + "] errors, details: "
|
||||
+ binder.getErrors().getAllErrors());
|
||||
logger.debug("There are [" + binder.getBindingResult().getErrorCount() + "] errors, details: "
|
||||
+ binder.getBindingResult().getAllErrors());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package org.springframework.webflow.action;
|
||||
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
import org.springframework.webflow.execution.ScopeType;
|
||||
@@ -24,10 +24,10 @@ import org.springframework.webflow.execution.ScopeType;
|
||||
* Convenience helper that encapsulates logic on how to retrieve and expose form objects and associated errors to and
|
||||
* from a flow execution request context.
|
||||
* <p>
|
||||
* <b>Note</b>: The form object available under the well known attribute name {@link #CURRENT_FORM_OBJECT_ATTRIBUTE}
|
||||
* will be the last ("current") form object set in the request context. The same is true for the associated errors
|
||||
* object. This implies that special care should be taken when accessing the form object using this alias if there are
|
||||
* multiple form objects available in the flow execution request context!
|
||||
* <b>Note</b>: The form object available under the well known attribute name will be the last ("current") form object
|
||||
* set in the request context. The same is true for the associated errors object. This implies that special care should
|
||||
* be taken when accessing the form object using this alias if there are multiple form objects available in the flow
|
||||
* execution request context!
|
||||
*
|
||||
* @see org.springframework.webflow.execution.RequestContext
|
||||
* @see org.springframework.validation.Errors
|
||||
@@ -53,8 +53,7 @@ public class FormObjectAccessor {
|
||||
/**
|
||||
* The errors prefix.
|
||||
*/
|
||||
// use deprecated API to remain compatible with Spring 1.2.x
|
||||
private static final String ERRORS_PREFIX = BindException.ERROR_KEY_PREFIX;
|
||||
private static final String ERRORS_PREFIX = BindingResult.MODEL_KEY_PREFIX;
|
||||
|
||||
/**
|
||||
* The wrapped request context.
|
||||
@@ -86,8 +85,7 @@ public class FormObjectAccessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the form object from the context, using the well-known attribute name {@link #CURRENT_FORM_OBJECT_ATTRIBUTE}.
|
||||
* Will try all scopes.
|
||||
* Gets the form object from the context, using the well-known attribute name. Will try all scopes.
|
||||
* @return the form object, or null if not found
|
||||
*/
|
||||
public Object getCurrentFormObject() {
|
||||
@@ -107,7 +105,7 @@ public class FormObjectAccessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the form object from the context, using the well-known attribute name {@link #CURRENT_FORM_OBJECT_ATTRIBUTE}.
|
||||
* Gets the form object from the context, using the well-known attribute name.
|
||||
* @param scopeType the scope to obtain the form object from
|
||||
* @return the form object, or null if not found
|
||||
*/
|
||||
@@ -116,8 +114,7 @@ public class FormObjectAccessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose given form object using the well known alias {@link #CURRENT_FORM_OBJECT_ATTRIBUTE} in the specified
|
||||
* scope.
|
||||
* Expose given form object using the well known alias in the specified scope.
|
||||
* @param formObject the form object
|
||||
* @param scopeType the scope in which to expose the form object
|
||||
*/
|
||||
@@ -160,8 +157,8 @@ public class FormObjectAccessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the form object <code>Errors</code> tracker from the context, using the form object name
|
||||
* {@link #CURRENT_FORM_OBJECT_ATTRIBUTE}. This method will search all scopes.
|
||||
* Gets the form object <code>Errors</code> tracker from the context, using the form object name. This method will
|
||||
* search all scopes.
|
||||
* @return the form object Errors tracker, or null if not found
|
||||
*/
|
||||
public Errors getCurrentFormErrors() {
|
||||
@@ -181,8 +178,7 @@ public class FormObjectAccessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the form object <code>Errors</code> tracker from the context, using the form object name
|
||||
* {@link #CURRENT_FORM_OBJECT_ATTRIBUTE}.
|
||||
* Gets the form object <code>Errors</code> tracker from the context, using the form object name.
|
||||
* @param scopeType the scope to obtain the errors from
|
||||
* @return the form object Errors tracker, or null if not found
|
||||
*/
|
||||
@@ -191,8 +187,7 @@ public class FormObjectAccessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose given errors instance using the well known alias {@link #CURRENT_FORM_OBJECT_ATTRIBUTE} in the specified
|
||||
* scope.
|
||||
* Expose given errors instance using the well known alias in the specified scope.
|
||||
* @param errors the errors instance
|
||||
* @param scopeType the scope in which to expose the errors instance
|
||||
*/
|
||||
@@ -203,7 +198,7 @@ public class FormObjectAccessor {
|
||||
/**
|
||||
* Gets the form object <code>Errors</code> tracker from the context, using the specified form object name.
|
||||
* @param formObjectName the name of the Errors object, which will be prefixed with
|
||||
* {@link BindException#ERROR_KEY_PREFIX}
|
||||
* {@link BindingResult#MODEL_KEY_PREFIX}
|
||||
* @param scopeType the scope to obtain the errors from
|
||||
* @return the form object errors instance, or null if not found
|
||||
*/
|
||||
|
||||
@@ -19,7 +19,6 @@ import org.springframework.core.JdkVersion;
|
||||
import org.springframework.core.enums.LabeledEnum;
|
||||
import org.springframework.webflow.execution.Event;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
import org.springframework.webflow.execution.support.EventFactorySupport;
|
||||
|
||||
/**
|
||||
* Result object-to-event adapter interface that tries to do a sensible conversion of the result object into a web flow
|
||||
@@ -31,13 +30,13 @@ import org.springframework.webflow.execution.support.EventFactorySupport;
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>null</td>
|
||||
* <td>{@link org.springframework.webflow.execution.support.EventFactorySupport#getNullEventId()}</td>
|
||||
* <td>{@link org.springframework.webflow.action.EventFactorySupport#getNullEventId()}</td>
|
||||
* <td> </td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>{@link java.lang.Boolean} or boolean</td>
|
||||
* <td>{@link org.springframework.webflow.execution.support.EventFactorySupport#getYesEventId()}/
|
||||
* {@link org.springframework.webflow.execution.support.EventFactorySupport#getNoEventId()}</td>
|
||||
* <td>{@link org.springframework.webflow.action.EventFactorySupport#getYesEventId()}/
|
||||
* {@link org.springframework.webflow.action.EventFactorySupport#getNoEventId()}</td>
|
||||
* <td> </td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
|
||||
@@ -15,9 +15,7 @@
|
||||
*/
|
||||
package org.springframework.webflow.action;
|
||||
|
||||
import org.springframework.binding.expression.EvaluationContext;
|
||||
import org.springframework.binding.expression.Expression;
|
||||
import org.springframework.binding.expression.SettableExpression;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.webflow.core.collection.MutableAttributeMap;
|
||||
import org.springframework.webflow.execution.Event;
|
||||
@@ -34,7 +32,7 @@ public class SetAction extends AbstractAction {
|
||||
/**
|
||||
* The expression for setting the scoped attribute value.
|
||||
*/
|
||||
private SettableExpression attributeExpression;
|
||||
private Expression attributeExpression;
|
||||
|
||||
/**
|
||||
* The target scope.
|
||||
@@ -52,7 +50,7 @@ public class SetAction extends AbstractAction {
|
||||
* @param scope the target scope of the attribute
|
||||
* @param valueExpression the evaluatable attribute value expression
|
||||
*/
|
||||
public SetAction(SettableExpression attributeExpression, ScopeType scope, Expression valueExpression) {
|
||||
public SetAction(Expression attributeExpression, ScopeType scope, Expression valueExpression) {
|
||||
Assert.notNull(attributeExpression, "The attribute expression is required");
|
||||
Assert.notNull(scope, "The scope type is required");
|
||||
Assert.notNull(valueExpression, "The value expression is required");
|
||||
@@ -62,20 +60,9 @@ public class SetAction extends AbstractAction {
|
||||
}
|
||||
|
||||
protected Event doExecute(RequestContext context) throws Exception {
|
||||
EvaluationContext evaluationContext = getEvaluationContext(context);
|
||||
Object value = valueExpression.evaluate(context, evaluationContext);
|
||||
Object value = valueExpression.getValue(context);
|
||||
MutableAttributeMap scopeMap = scope.getScope(context);
|
||||
attributeExpression.evaluateToSet(scopeMap, value, evaluationContext);
|
||||
attributeExpression.setValue(scopeMap, value);
|
||||
return success();
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method subclasses may override to customize the expression evaluation context. This implementation
|
||||
* returns null.
|
||||
* @param context the request context
|
||||
* @return the evaluation context
|
||||
*/
|
||||
protected EvaluationContext getEvaluationContext(RequestContext context) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,6 @@ package org.springframework.webflow.action;
|
||||
|
||||
import org.springframework.webflow.execution.Event;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
import org.springframework.webflow.execution.support.EventFactorySupport;
|
||||
|
||||
/**
|
||||
* Default implementation of the resultObject-to-event mapping interface. Always returns the "success" event.
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* 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.config;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.config.MapFactoryBean;
|
||||
import org.springframework.beans.factory.config.TypedStringValue;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.ManagedMap;
|
||||
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
import org.springframework.webflow.engine.support.ApplicationViewSelector;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* {@link BeanDefinitionParser} for the <code><execution-attributes></code> tag.
|
||||
*
|
||||
* @author Ben Hale
|
||||
*/
|
||||
class ExecutionAttributesBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
|
||||
|
||||
// elements and attributes
|
||||
|
||||
private static final String ATTRIBUTE_ELEMENT = "attribute";
|
||||
|
||||
private static final String NAME_ATTRIBUTE = "name";
|
||||
|
||||
private static final String TYPE_ATTRIBUTE = "type";
|
||||
|
||||
private static final String VALUE_ATTRIBUTE = "value";
|
||||
|
||||
// properties
|
||||
|
||||
private static final String SOURCE_MAP_PROPERTY = "sourceMap";
|
||||
|
||||
protected Class getBeanClass(Element element) {
|
||||
return MapFactoryBean.class;
|
||||
}
|
||||
|
||||
protected void doParse(Element element, BeanDefinitionBuilder definitionBuilder) {
|
||||
List attributeElements = DomUtils.getChildElementsByTagName(element, ATTRIBUTE_ELEMENT);
|
||||
Map attributeMap = new ManagedMap(attributeElements.size());
|
||||
putAttributes(attributeMap, attributeElements);
|
||||
putSpecialAttributes(attributeMap, element);
|
||||
definitionBuilder.addPropertyValue(SOURCE_MAP_PROPERTY, attributeMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all attributes defined in given list of attribute elements to given map.
|
||||
*/
|
||||
private void putAttributes(Map attributeMap, List attributeElements) {
|
||||
for (Iterator i = attributeElements.iterator(); i.hasNext();) {
|
||||
Element attributeElement = (Element) i.next();
|
||||
String type = attributeElement.getAttribute(TYPE_ATTRIBUTE);
|
||||
Object value;
|
||||
if (StringUtils.hasText(type)) {
|
||||
value = new TypedStringValue(attributeElement.getAttribute(VALUE_ATTRIBUTE), type);
|
||||
} else {
|
||||
value = attributeElement.getAttribute(VALUE_ATTRIBUTE);
|
||||
}
|
||||
attributeMap.put(attributeElement.getAttribute(NAME_ATTRIBUTE), value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all non-generic (special) attributes defined in given element to given map.
|
||||
*/
|
||||
private void putSpecialAttributes(Map attributeMap, Element element) {
|
||||
putAlwaysRedirectOnPauseAttribute(attributeMap, DomUtils.getChildElementByTagName(element,
|
||||
ApplicationViewSelector.ALWAYS_REDIRECT_ON_PAUSE_ATTRIBUTE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the "alwaysRedirectOnPause" attribute from given element and add it to given map.
|
||||
*/
|
||||
private void putAlwaysRedirectOnPauseAttribute(Map attributeMap, Element element) {
|
||||
if (element != null) {
|
||||
Boolean value = Boolean.valueOf(element.getAttribute(VALUE_ATTRIBUTE));
|
||||
attributeMap.put(ApplicationViewSelector.ALWAYS_REDIRECT_ON_PAUSE_ATTRIBUTE, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package org.springframework.webflow.config;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A low-level pointer to a flow definition that will be registered in a flow registry and built by a concrete flow
|
||||
* builder implementation class.
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
class FlowBuilderInfo {
|
||||
|
||||
/**
|
||||
* The id to assign to the flow definition.
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* The fully-qualified flow builder implementation class.
|
||||
*/
|
||||
private String className;
|
||||
|
||||
/**
|
||||
* Attributes to assign to the flow definition.
|
||||
*/
|
||||
private Set attributes;
|
||||
|
||||
public FlowBuilderInfo(String id, String className, Set attributes) {
|
||||
Assert.hasText(className, "The fully-qualified FlowBuilder class name is required");
|
||||
this.className = className;
|
||||
setId(id);
|
||||
this.attributes = (attributes != null ? attributes : Collections.EMPTY_SET);
|
||||
}
|
||||
|
||||
private void setId(String id) {
|
||||
if (StringUtils.hasText(id)) {
|
||||
this.id = id;
|
||||
} else {
|
||||
this.id = StringUtils.uncapitalize(StringUtils.delete(ClassUtils.getShortName(className), "FlowBuilder"));
|
||||
}
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
return className;
|
||||
}
|
||||
|
||||
public Set getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package org.springframework.webflow.config;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.webflow.core.collection.AttributeMap;
|
||||
|
||||
/**
|
||||
* An abstract representation of an externalized flow definition resource. Holds the data necessary to build a flow
|
||||
* definition from an external file, and register the flow definition in a flow definition registry.
|
||||
*
|
||||
* Flow definition resources are created by a {@link FlowDefinitionResourceFactory}.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @see FlowDefinitionResource
|
||||
*/
|
||||
public class FlowDefinitionResource {
|
||||
|
||||
private String id;
|
||||
|
||||
private Resource path;
|
||||
|
||||
private AttributeMap attributes;
|
||||
|
||||
FlowDefinitionResource(String flowId, Resource path, AttributeMap attributes) {
|
||||
this.id = flowId;
|
||||
this.path = path;
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the identifier to be assigned to the flow definition.
|
||||
* @return the flow definition identifier
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to the flow definition resource.
|
||||
* @return the path location
|
||||
*/
|
||||
public Resource getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns attributes to assign the flow definition.
|
||||
* @return flow definition attributes
|
||||
*/
|
||||
public AttributeMap getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return path.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package org.springframework.webflow.config;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.webflow.core.collection.AttributeMap;
|
||||
|
||||
public class FlowDefinitionResourceFactory {
|
||||
private ResourceLoader resourceLoader;
|
||||
|
||||
public FlowDefinitionResourceFactory() {
|
||||
this.resourceLoader = new DefaultResourceLoader();
|
||||
}
|
||||
|
||||
public FlowDefinitionResourceFactory(ResourceLoader resourceLoader) {
|
||||
Assert.notNull(resourceLoader, "The resource loader cannot be null");
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
public FlowDefinitionResource createResource(String path) {
|
||||
return createResource(path, null, null);
|
||||
}
|
||||
|
||||
public FlowDefinitionResource createResource(String path, AttributeMap attributes) {
|
||||
return createResource(path, attributes, null);
|
||||
}
|
||||
|
||||
public FlowDefinitionResource createResource(String path, AttributeMap attributes, String flowId) {
|
||||
Resource resource = resourceLoader.getResource(path);
|
||||
if (flowId == null || flowId.length() == 0) {
|
||||
flowId = getFlowId(resource);
|
||||
}
|
||||
return new FlowDefinitionResource(flowId, resource, attributes);
|
||||
}
|
||||
|
||||
public FlowDefinitionResource[] createResources(String pattern) throws IOException {
|
||||
if (resourceLoader instanceof ResourcePatternResolver) {
|
||||
ResourcePatternResolver resolver = (ResourcePatternResolver) resourceLoader;
|
||||
Resource[] resources = resolver.getResources(pattern);
|
||||
FlowDefinitionResource[] flowResources = new FlowDefinitionResource[resources.length];
|
||||
for (int i = 0; i < resources.length; i++) {
|
||||
Resource resource = resources[i];
|
||||
flowResources[i] = new FlowDefinitionResource(getFlowId(resource), resource, null);
|
||||
}
|
||||
return flowResources;
|
||||
} else {
|
||||
throw new IllegalStateException(
|
||||
"Cannot create flow definition resources from patterns without a ResourceLoader configured that is a ResourcePatternResolver");
|
||||
}
|
||||
}
|
||||
|
||||
public FlowDefinitionResource createFileResource(String path) {
|
||||
Resource resource = new FileSystemResource(new File(path));
|
||||
return new FlowDefinitionResource(getFlowId(resource), resource, null);
|
||||
}
|
||||
|
||||
public FlowDefinitionResource createClassPathResource(String path, Class clazz) {
|
||||
Resource resource = new ClassPathResource(path, clazz);
|
||||
return new FlowDefinitionResource(getFlowId(resource), resource, null);
|
||||
}
|
||||
|
||||
private String getFlowId(Resource flowResource) {
|
||||
String fileName = flowResource.getFilename();
|
||||
int extensionIndex = fileName.lastIndexOf('.');
|
||||
if (extensionIndex != -1) {
|
||||
return fileName.substring(0, extensionIndex);
|
||||
} else {
|
||||
return fileName;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.config;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A low-level definition of a attribute describing a flow artifact.
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
class FlowElementAttribute {
|
||||
|
||||
/**
|
||||
* The name of the attribute.
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* The value of the attribute before type-conversion.
|
||||
*/
|
||||
private String value;
|
||||
|
||||
/**
|
||||
* The attribute type, optional, but necessary for type conversion.
|
||||
*/
|
||||
private String type;
|
||||
|
||||
public FlowElementAttribute(String name, String value, String type) {
|
||||
Assert.hasText(name, "The name is required");
|
||||
Assert.hasText(value, "The value is required");
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public boolean needsTypeConversion() {
|
||||
return type != null && type.length() > 0;
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,6 @@ import org.springframework.beans.factory.support.ManagedMap;
|
||||
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
import org.springframework.webflow.execution.factory.ConditionalFlowExecutionListenerLoader;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
@@ -33,22 +32,22 @@ import org.w3c.dom.Element;
|
||||
*
|
||||
* @author Ben Hale
|
||||
*/
|
||||
class ExecutionListenersBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
|
||||
class FlowExecutionListenerLoaderBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
|
||||
|
||||
// elements and attributes
|
||||
|
||||
private static final String LISTENER_ELEMENT = "listener";
|
||||
|
||||
// properties
|
||||
|
||||
private static final String LISTENERS_PROPERTY = "listeners";
|
||||
|
||||
private static final String CRITERIA_ATTRIBUTE = "criteria";
|
||||
|
||||
private static final String REF_ATTRIBUTE = "ref";
|
||||
|
||||
// properties
|
||||
|
||||
private static final String LISTENERS_PROPERTY = "listeners";
|
||||
|
||||
protected Class getBeanClass(Element element) {
|
||||
return ConditionalFlowExecutionListenerLoader.class;
|
||||
return FlowExecutionListenerLoaderFactoryBean.class;
|
||||
}
|
||||
|
||||
protected void doParse(Element element, BeanDefinitionBuilder definitionBuilder) {
|
||||
@@ -0,0 +1,68 @@
|
||||
package org.springframework.webflow.config;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.webflow.execution.FlowExecutionListener;
|
||||
import org.springframework.webflow.execution.factory.ConditionalFlowExecutionListenerLoader;
|
||||
import org.springframework.webflow.execution.factory.FlowExecutionListenerCriteriaFactory;
|
||||
import org.springframework.webflow.execution.factory.FlowExecutionListenerLoader;
|
||||
|
||||
/**
|
||||
* A factory for a flow execution listener loader. Is a Spring FactoryBean, for provision by the flow execution listener
|
||||
* loader bean definition parser. Is package-private, as people should not be using this class directly, but rather
|
||||
* through the higher-level webflow-config Spring 2.x configuration namespace.
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
class FlowExecutionListenerLoaderFactoryBean implements FactoryBean, InitializingBean {
|
||||
|
||||
/**
|
||||
* The configured execution listeners and the criteria determining when they apply.
|
||||
*/
|
||||
private Map listenersWithCriteria;
|
||||
|
||||
/**
|
||||
* The listener loader created by this factory. Is conditional, allowing listeners to apply to flow executions
|
||||
* selectively based on some criteria expression.
|
||||
*/
|
||||
private ConditionalFlowExecutionListenerLoader listenerLoader;
|
||||
|
||||
/**
|
||||
* A helper factory for converting string-encoded listener criteria to a FlowExecutionListenerCriteria object.
|
||||
*/
|
||||
private FlowExecutionListenerCriteriaFactory listenerCriteriaFactory = new FlowExecutionListenerCriteriaFactory();
|
||||
|
||||
/**
|
||||
* Sets the listeners eligible for loading, and the criteria for when they should be loaded.
|
||||
* @param listenersWithCriteria the listener-to-criteria map
|
||||
*/
|
||||
public void setListeners(Map listenersWithCriteria) {
|
||||
this.listenersWithCriteria = listenersWithCriteria;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
listenerLoader = new ConditionalFlowExecutionListenerLoader();
|
||||
Iterator it = listenersWithCriteria.entrySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
Map.Entry entry = (Map.Entry) it.next();
|
||||
FlowExecutionListener listener = (FlowExecutionListener) entry.getKey();
|
||||
String criteria = (String) entry.getValue();
|
||||
listenerLoader.addListener(listener, listenerCriteriaFactory.getListenerCriteria(criteria));
|
||||
}
|
||||
}
|
||||
|
||||
public Object getObject() throws Exception {
|
||||
return listenerLoader;
|
||||
}
|
||||
|
||||
public Class getObjectType() {
|
||||
return FlowExecutionListenerLoader.class;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -16,9 +16,6 @@
|
||||
package org.springframework.webflow.config;
|
||||
|
||||
import org.springframework.core.enums.StaticLabeledEnum;
|
||||
import org.springframework.webflow.execution.repository.continuation.ClientContinuationFlowExecutionRepository;
|
||||
import org.springframework.webflow.execution.repository.continuation.ContinuationFlowExecutionRepository;
|
||||
import org.springframework.webflow.execution.repository.support.SimpleFlowExecutionRepository;
|
||||
|
||||
/**
|
||||
* Type-safe enumeration of logical flow execution repository types.
|
||||
@@ -27,37 +24,32 @@ import org.springframework.webflow.execution.repository.support.SimpleFlowExecut
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public class RepositoryType extends StaticLabeledEnum {
|
||||
public class FlowExecutionRepositoryType extends StaticLabeledEnum {
|
||||
|
||||
/**
|
||||
* The 'simple' flow execution repository type.
|
||||
* @see SimpleFlowExecutionRepository
|
||||
*/
|
||||
public static final RepositoryType SIMPLE = new RepositoryType(0, "Simple");
|
||||
public static final FlowExecutionRepositoryType SIMPLE = new FlowExecutionRepositoryType(0, "Simple");
|
||||
|
||||
/**
|
||||
* The 'continuation' flow execution repository type.
|
||||
* @see ContinuationFlowExecutionRepository
|
||||
*/
|
||||
public static final RepositoryType CONTINUATION = new RepositoryType(1, "Continuation");
|
||||
public static final FlowExecutionRepositoryType CONTINUATION = new FlowExecutionRepositoryType(1, "Continuation");
|
||||
|
||||
/**
|
||||
* The 'client' (continuation) flow execution repository type.
|
||||
* @see ClientContinuationFlowExecutionRepository
|
||||
*/
|
||||
public static final RepositoryType CLIENT = new RepositoryType(2, "Client");
|
||||
public static final FlowExecutionRepositoryType CLIENT = new FlowExecutionRepositoryType(2, "Client");
|
||||
|
||||
/**
|
||||
* The 'singleKey' flow execution repository type.
|
||||
* @see SimpleFlowExecutionRepository
|
||||
* @see SimpleFlowExecutionRepository#setAlwaysGenerateNewNextKey(boolean)
|
||||
*/
|
||||
public static final RepositoryType SINGLEKEY = new RepositoryType(3, "Single Key");
|
||||
public static final FlowExecutionRepositoryType SINGLEKEY = new FlowExecutionRepositoryType(3, "Single Key");
|
||||
|
||||
/**
|
||||
* Private constructor because this is a typesafe enum!
|
||||
*/
|
||||
private RepositoryType(int code, String label) {
|
||||
private FlowExecutionRepositoryType(int code, String label) {
|
||||
super(code, label);
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,11 @@
|
||||
*/
|
||||
package org.springframework.webflow.config;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
|
||||
@@ -25,55 +30,60 @@ import org.springframework.util.xml.DomUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* {@link BeanDefinitionParser} for the <code><executor></code> tag.
|
||||
* {@link BeanDefinitionParser} for the <code><flow-executor></code> tag.
|
||||
*
|
||||
* @author Ben Hale
|
||||
* @author Christian Dupuis
|
||||
* @author Keith Donald
|
||||
*/
|
||||
class ExecutorBeanDefinitionParser extends AbstractBeanDefinitionParser {
|
||||
class FlowExecutorBeanDefinitionParser extends AbstractBeanDefinitionParser {
|
||||
|
||||
// elements and attributes
|
||||
|
||||
private static final String CONVERSATION_MANAGER_REF_ATTRIBUTE = "conversation-manager-ref";
|
||||
private static final String CONVERSATION_MANAGER_REF_ATTRIBUTE = "conversation-manager";
|
||||
|
||||
private static final String EXECUTION_ATTRIBUTES_ELEMENT = "execution-attributes";
|
||||
private static final String EXECUTION_ATTRIBUTES_ELEMENT = "flow-execution-attributes";
|
||||
|
||||
private static final String EXECUTION_LISTENERS_ELEMENT = "execution-listeners";
|
||||
private static final String ALWAYS_REDIRECT_ON_PAUSE_ELEMENT = "alwaysRedirectOnPause";
|
||||
|
||||
private static final String ATTRIBUTE_ELEMENT = "attribute";
|
||||
|
||||
private static final String NAME_ATTRIBUTE = "name";
|
||||
|
||||
private static final String VALUE_ATTRIBUTE = "value";
|
||||
|
||||
private static final String TYPE_ATTRIBUTE = "type";
|
||||
|
||||
private static final String EXECUTION_LISTENERS_ELEMENT = "flow-execution-listeners";
|
||||
|
||||
private static final String MAX_CONTINUATIONS_ATTRIBUTE = "max-continuations";
|
||||
|
||||
private static final String MAX_CONVERSATIONS_ATTRIBUTE = "max-conversations";
|
||||
|
||||
private static final String REGISTRY_REF_ATTRIBUTE = "registry-ref";
|
||||
private static final String REGISTRY_REF_ATTRIBUTE = "flow-registry";
|
||||
|
||||
private static final String REPOSITORY_ELEMENT = "repository";
|
||||
|
||||
private static final String REPOSITORY_TYPE_ATTRIBUTE = "repository-type";
|
||||
|
||||
private static final String TYPE_ATTRIBUTE = "type";
|
||||
private static final String REPOSITORY_ELEMENT = "flow-execution-repository";
|
||||
|
||||
// properties
|
||||
|
||||
private static final String CONVERSATION_MANAGER_PROPERTY = "conversationManager";
|
||||
|
||||
private static final String DEFINITION_LOCATOR_PROPERTY = "definitionLocator";
|
||||
private static final String DEFINITION_LOCATOR_PROPERTY = "flowDefinitionLocator";
|
||||
|
||||
private static final String EXECUTION_ATTRIBUTES_PROPERTY = "executionAttributes";
|
||||
private static final String REPOSITORY_TYPE_PROPERTY = "flowExecutionRepositoryType";
|
||||
|
||||
private static final String EXECUTION_LISTENER_LOADER_PROPERTY = "executionListenerLoader";
|
||||
private static final String EXECUTION_ATTRIBUTES_PROPERTY = "flowExecutionAttributes";
|
||||
|
||||
private static final String EXECUTION_LISTENER_LOADER_PROPERTY = "flowExecutionListenerLoader";
|
||||
|
||||
private static final String MAX_CONTINUATIONS_PROPERTY = "maxContinuations";
|
||||
|
||||
private static final String MAX_CONVERSATIONS_PROPERTY = "maxConversations";
|
||||
|
||||
private static final String REPOSITORY_TYPE_PROPERTY = "repositoryType";
|
||||
|
||||
protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(FlowExecutorFactoryBean.class);
|
||||
definitionBuilder.setSource(parserContext.extractSource(element));
|
||||
definitionBuilder.addPropertyReference(DEFINITION_LOCATOR_PROPERTY, getRegistryRef(element, parserContext));
|
||||
addExecutionAttributes(element, parserContext, definitionBuilder);
|
||||
definitionBuilder.addPropertyValue(EXECUTION_ATTRIBUTES_PROPERTY, parseAttributes(element));
|
||||
addExecutionListenerLoader(element, parserContext, definitionBuilder);
|
||||
configureRepository(element, definitionBuilder, parserContext);
|
||||
return definitionBuilder.getBeanDefinition();
|
||||
@@ -88,18 +98,10 @@ class ExecutorBeanDefinitionParser extends AbstractBeanDefinitionParser {
|
||||
private void configureRepository(Element element, BeanDefinitionBuilder definitionBuilder,
|
||||
ParserContext parserContext) {
|
||||
Element repositoryElement = DomUtils.getChildElementByTagName(element, REPOSITORY_ELEMENT);
|
||||
String repositoryTypeAttribute = getRepositoryType(element);
|
||||
if (repositoryElement != null) {
|
||||
if (StringUtils.hasText(repositoryTypeAttribute)) {
|
||||
parserContext.getReaderContext().error(
|
||||
"The 'repositoryType' attribute of the 'executor' element must "
|
||||
+ "not have a value if there is a 'repository' element", element);
|
||||
}
|
||||
definitionBuilder.addPropertyValue(REPOSITORY_TYPE_PROPERTY, getType(repositoryElement));
|
||||
configureContinuations(repositoryElement, definitionBuilder, parserContext);
|
||||
configureConversationManager(repositoryElement, definitionBuilder, parserContext);
|
||||
} else if (StringUtils.hasText(repositoryTypeAttribute)) {
|
||||
definitionBuilder.addPropertyValue(REPOSITORY_TYPE_PROPERTY, repositoryTypeAttribute);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,20 +157,11 @@ class ExecutorBeanDefinitionParser extends AbstractBeanDefinitionParser {
|
||||
String registryRef = element.getAttribute(REGISTRY_REF_ATTRIBUTE);
|
||||
if (!StringUtils.hasText(registryRef)) {
|
||||
parserContext.getReaderContext().error(
|
||||
"The 'registry-ref' attribute of the 'executor' element must have a value", element);
|
||||
"The 'registry-ref' attribute of the 'flow-executor' element must have a value", element);
|
||||
}
|
||||
return registryRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the repository type enum field detailed in the bean definition.
|
||||
* @param element the element to extract the repository type from
|
||||
* @return the type of the repository
|
||||
*/
|
||||
private String getRepositoryType(Element element) {
|
||||
return element.getAttribute(REPOSITORY_TYPE_ATTRIBUTE).toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the repository type enum field detailed in the bean definition.
|
||||
* @param element the element to extract the repository type from
|
||||
@@ -205,18 +198,6 @@ class ExecutorBeanDefinitionParser extends AbstractBeanDefinitionParser {
|
||||
return element.getAttribute(CONVERSATION_MANAGER_REF_ATTRIBUTE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse execution attribute definitions contained in given element.
|
||||
*/
|
||||
private void addExecutionAttributes(Element element, ParserContext parserContext,
|
||||
BeanDefinitionBuilder definitionBuilder) {
|
||||
Element attributesElement = DomUtils.getChildElementByTagName(element, EXECUTION_ATTRIBUTES_ELEMENT);
|
||||
if (attributesElement != null) {
|
||||
definitionBuilder.addPropertyValue(EXECUTION_ATTRIBUTES_PROPERTY, parserContext.getDelegate()
|
||||
.parseCustomElement(attributesElement, definitionBuilder.getBeanDefinition()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse execution listener definitions contained in given element.
|
||||
*/
|
||||
@@ -228,4 +209,28 @@ class ExecutorBeanDefinitionParser extends AbstractBeanDefinitionParser {
|
||||
.parseCustomElement(listenersElement, definitionBuilder.getBeanDefinition()));
|
||||
}
|
||||
}
|
||||
|
||||
private Set parseAttributes(Element element) {
|
||||
Element executionAttributesElement = DomUtils.getChildElementByTagName(element, EXECUTION_ATTRIBUTES_ELEMENT);
|
||||
if (executionAttributesElement != null) {
|
||||
HashSet attributes = new HashSet();
|
||||
Element redirectElement = DomUtils.getChildElementByTagName(executionAttributesElement,
|
||||
ALWAYS_REDIRECT_ON_PAUSE_ELEMENT);
|
||||
if (redirectElement != null) {
|
||||
String value = redirectElement.getAttribute(VALUE_ATTRIBUTE);
|
||||
attributes.add(new FlowElementAttribute("alwaysRedirectOnPause", value, "boolean"));
|
||||
}
|
||||
List attributeElements = DomUtils.getChildElementsByTagName(executionAttributesElement, ATTRIBUTE_ELEMENT);
|
||||
for (Iterator it = attributeElements.iterator(); it.hasNext();) {
|
||||
Element attributeElement = (Element) it.next();
|
||||
String name = attributeElement.getAttribute(NAME_ATTRIBUTE);
|
||||
String value = attributeElement.getAttribute(VALUE_ATTRIBUTE);
|
||||
String type = attributeElement.getAttribute(TYPE_ATTRIBUTE);
|
||||
attributes.add(new FlowElementAttribute(name, value, type));
|
||||
}
|
||||
return attributes;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,13 +15,15 @@
|
||||
*/
|
||||
package org.springframework.webflow.config;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.binding.mapping.AttributeMapper;
|
||||
import org.springframework.binding.convert.ConversionExecutor;
|
||||
import org.springframework.binding.convert.ConversionService;
|
||||
import org.springframework.binding.convert.support.DefaultConversionService;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.webflow.context.ExternalContext;
|
||||
import org.springframework.webflow.conversation.ConversationManager;
|
||||
import org.springframework.webflow.conversation.impl.SessionBindingConversationManager;
|
||||
import org.springframework.webflow.core.collection.AttributeMap;
|
||||
@@ -31,16 +33,13 @@ import org.springframework.webflow.definition.registry.FlowDefinitionLocator;
|
||||
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
|
||||
import org.springframework.webflow.engine.impl.FlowExecutionImplFactory;
|
||||
import org.springframework.webflow.engine.impl.FlowExecutionImplStateRestorer;
|
||||
import org.springframework.webflow.execution.FlowExecution;
|
||||
import org.springframework.webflow.execution.FlowExecutionFactory;
|
||||
import org.springframework.webflow.execution.FlowExecutionListener;
|
||||
import org.springframework.webflow.execution.FlowExecutionKeyFactory;
|
||||
import org.springframework.webflow.execution.factory.FlowExecutionListenerLoader;
|
||||
import org.springframework.webflow.execution.factory.StaticFlowExecutionListenerLoader;
|
||||
import org.springframework.webflow.execution.repository.FlowExecutionRepository;
|
||||
import org.springframework.webflow.execution.repository.continuation.ClientContinuationFlowExecutionRepository;
|
||||
import org.springframework.webflow.execution.repository.continuation.ContinuationFlowExecutionRepository;
|
||||
import org.springframework.webflow.execution.repository.impl.ClientFlowExecutionRepository;
|
||||
import org.springframework.webflow.execution.repository.impl.DefaultFlowExecutionRepository;
|
||||
import org.springframework.webflow.execution.repository.support.FlowExecutionStateRestorer;
|
||||
import org.springframework.webflow.execution.repository.support.SimpleFlowExecutionRepository;
|
||||
import org.springframework.webflow.executor.FlowExecutor;
|
||||
import org.springframework.webflow.executor.FlowExecutorImpl;
|
||||
|
||||
@@ -51,8 +50,7 @@ import org.springframework.webflow.executor.FlowExecutorImpl;
|
||||
* This factory encapsulates the construction and assembly of a {@link FlowExecutor}, including the provision of its
|
||||
* {@link FlowExecutionRepository} strategy.
|
||||
* <p>
|
||||
* The {@link #setDefinitionLocator(FlowDefinitionLocator) definition locator} property is required, all other
|
||||
* properties are optional.
|
||||
* The definition locator property is required, all other properties are optional.
|
||||
* <p>
|
||||
* This class has been designed with subclassing in mind. If you want to do advanced Spring Web Flow customization, e.g.
|
||||
* using a custom {@link org.springframework.webflow.executor.FlowExecutor} implementation, consider subclassing this
|
||||
@@ -61,22 +59,22 @@ import org.springframework.webflow.executor.FlowExecutorImpl;
|
||||
* @author Keith Donald
|
||||
* @author Erwin Vervaet
|
||||
*/
|
||||
public class FlowExecutorFactoryBean implements FactoryBean, InitializingBean {
|
||||
class FlowExecutorFactoryBean implements FactoryBean, InitializingBean {
|
||||
|
||||
/**
|
||||
* The locator the executor will use to access flow definitions registered in a central registry. Required.
|
||||
*/
|
||||
private FlowDefinitionLocator definitionLocator;
|
||||
private FlowDefinitionLocator flowDefinitionLocator;
|
||||
|
||||
/**
|
||||
* Execution attributes to apply.
|
||||
*/
|
||||
private MutableAttributeMap executionAttributes;
|
||||
private Set flowExecutionAttributes;
|
||||
|
||||
/**
|
||||
* The loader that will determine which listeners to attach to flow definition executions.
|
||||
*/
|
||||
private FlowExecutionListenerLoader executionListenerLoader;
|
||||
private FlowExecutionListenerLoader flowExecutionListenerLoader;
|
||||
|
||||
/**
|
||||
* The conversation manager to be used by the flow execution repository to store state associated with conversations
|
||||
@@ -93,67 +91,40 @@ public class FlowExecutorFactoryBean implements FactoryBean, InitializingBean {
|
||||
* The type of execution repository to configure with executors created by this factory. Optional. Will fallback to
|
||||
* default value if not set.
|
||||
*/
|
||||
private RepositoryType repositoryType;
|
||||
private FlowExecutionRepositoryType flowExecutionRepositoryType;
|
||||
|
||||
/**
|
||||
* The maximum number of allowed continuations for a single conversation. Only used when the repository type is
|
||||
* {@link RepositoryType#CONTINUATION}.
|
||||
* {@link FlowExecutionRepositoryType#CONTINUATION}.
|
||||
*/
|
||||
private Integer maxContinuations;
|
||||
|
||||
/**
|
||||
* A custom attribute mapper to use for mapping attributes of an {@link ExternalContext} to a new
|
||||
* {@link FlowExecution} during the {@link FlowExecutor#launch(String, ExternalContext) launch flow} operation.
|
||||
* The conversion service to use for type conversion of flow execution attribute values.
|
||||
*/
|
||||
private AttributeMapper inputMapper;
|
||||
private ConversionService conversionService = new DefaultConversionService();
|
||||
|
||||
/**
|
||||
* The flow executor this factory bean creates.
|
||||
*/
|
||||
private FlowExecutor flowExecutor;
|
||||
|
||||
/**
|
||||
* Spring Web Flow executor system defaults.
|
||||
*/
|
||||
private FlowSystemDefaults defaults = new FlowSystemDefaults();
|
||||
|
||||
/**
|
||||
* Sets the flow definition locator that will locate flow definitions needed for execution. Typically also a
|
||||
* {@link FlowDefinitionRegistry}. Required.
|
||||
* @param definitionLocator the flow definition locator (registry)
|
||||
* @param flowDefinitionLocator the flow definition locator (registry)
|
||||
*/
|
||||
public void setDefinitionLocator(FlowDefinitionLocator definitionLocator) {
|
||||
this.definitionLocator = definitionLocator;
|
||||
public void setFlowDefinitionLocator(FlowDefinitionLocator flowDefinitionLocator) {
|
||||
this.flowDefinitionLocator = flowDefinitionLocator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the system attributes that apply to flow executions launched by the executor created by this factory.
|
||||
* Execution attributes may affect flow execution behavior.
|
||||
* <p>
|
||||
* Note: this method simply accepts a generic <code>java.util.Map</code> to allow for easy configuration by
|
||||
* Spring. The map entries should consist of non-null String keys with object values.
|
||||
* @param executionAttributes the flow execution system attributes
|
||||
* @param flowExecutionAttributes the flow execution system attributes
|
||||
*/
|
||||
public void setExecutionAttributes(Map executionAttributes) {
|
||||
this.executionAttributes = new LocalAttributeMap(executionAttributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience setter that sets a single listener that always applies to flow executions launched by the executor
|
||||
* created by this factory.
|
||||
* @param executionListener the flow execution listener
|
||||
*/
|
||||
public void setExecutionListener(FlowExecutionListener executionListener) {
|
||||
setExecutionListeners(new FlowExecutionListener[] { executionListener });
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience setter that sets a list of listeners that always apply to flow executions launched by the executor
|
||||
* created by this factory.
|
||||
* @param executionListeners the flow execution listeners
|
||||
*/
|
||||
public void setExecutionListeners(FlowExecutionListener[] executionListeners) {
|
||||
setExecutionListenerLoader(new StaticFlowExecutionListenerLoader(executionListeners));
|
||||
public void setFlowExecutionAttributes(Set flowExecutionAttributes) {
|
||||
this.flowExecutionAttributes = flowExecutionAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,8 +132,8 @@ public class FlowExecutorFactoryBean implements FactoryBean, InitializingBean {
|
||||
* control over what listeners should apply to executions of a flow definition launched by the executor created by
|
||||
* this factory.
|
||||
*/
|
||||
public void setExecutionListenerLoader(FlowExecutionListenerLoader executionListenerLoader) {
|
||||
this.executionListenerLoader = executionListenerLoader;
|
||||
public void setFlowExecutionListenerLoader(FlowExecutionListenerLoader flowExecutionListenerLoader) {
|
||||
this.flowExecutionListenerLoader = flowExecutionListenerLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -171,28 +142,25 @@ public class FlowExecutorFactoryBean implements FactoryBean, InitializingBean {
|
||||
* provided type.
|
||||
* @param repositoryType the flow execution repository type
|
||||
*/
|
||||
public void setRepositoryType(RepositoryType repositoryType) {
|
||||
this.repositoryType = repositoryType;
|
||||
public void setFlowExecutionRepositoryType(FlowExecutionRepositoryType repositoryType) {
|
||||
this.flowExecutionRepositoryType = repositoryType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum number of continuation snapshots allowed for a single conversation when using the
|
||||
* {@link RepositoryType#CONTINUATION continuation} flow execution repository.
|
||||
* @see ContinuationFlowExecutionRepository#setMaxContinuations(int)
|
||||
* @since 1.0.1
|
||||
*/
|
||||
public void setMaxContinuations(int maxContinuations) {
|
||||
this.maxContinuations = new Integer(maxContinuations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configured maximum number of continuation snapshots allowed for a single conversation when using the
|
||||
* {@link RepositoryType#CONTINUATION continuation} flow execution repository.
|
||||
* @return the configured value or null if the user did not explicitly specify a value and wants to use the default
|
||||
* @since 1.0.1
|
||||
* Set the maximum number of allowed concurrent conversations in the session. This is a convenience setter to allow
|
||||
* easy configuration of the maxConversations property of the default {@link SessionBindingConversationManager}. Do
|
||||
* not use this when an explicit conversation manager is configured.
|
||||
* @see SessionBindingConversationManager#setMaxConversations(int)
|
||||
*/
|
||||
protected Integer getMaxContinuations() {
|
||||
return maxContinuations;
|
||||
public void setMaxConversations(int maxConversations) {
|
||||
this.maxConversations = new Integer(maxConversations);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -202,122 +170,67 @@ public class FlowExecutorFactoryBean implements FactoryBean, InitializingBean {
|
||||
* The conversation manager is used by the flow execution repository subsystem to begin and end new conversations
|
||||
* that store execution state.
|
||||
* <p>
|
||||
* By default, a {@link SessionBindingConversationManager} is used. Do not use {@link #setMaxConversations(int)}
|
||||
* when using this method.
|
||||
* By default, a {@link SessionBindingConversationManager} is used. Do not use setMaxConversations when using this
|
||||
* method.
|
||||
*/
|
||||
public void setConversationManager(ConversationManager conversationManager) {
|
||||
this.conversationManager = conversationManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum number of allowed concurrent conversations in the session. This is a convenience setter to allow
|
||||
* easy configuration of the maxConversations property of the default {@link SessionBindingConversationManager}. Do
|
||||
* not use this when using {@link #setConversationManager(ConversationManager)}.
|
||||
* @see SessionBindingConversationManager#setMaxConversations(int)
|
||||
* @since 1.0.1
|
||||
* Sets the conversion service for converting string-encoded flow execution attributes to typed values.
|
||||
* @param conversionService the conversion service
|
||||
*/
|
||||
public void setMaxConversations(int maxConversations) {
|
||||
this.maxConversations = new Integer(maxConversations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configured maximum number of allowed concurrent conversations in the session. Will only be used when
|
||||
* using the default conversation manager, e.g. when no explicit conversation manager has been configured using
|
||||
* {@link #setConversationManager(ConversationManager)}.
|
||||
* @return the configured value or null if the user did not explicitly specify a value and wants to use the default
|
||||
* @since 1.0.1
|
||||
*/
|
||||
protected Integer getMaxConversations() {
|
||||
return maxConversations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the service responsible for mapping attributes of an {@link ExternalContext} to a new {@link FlowExecution}
|
||||
* during the {@link FlowExecutor#launch(String, ExternalContext) launch flow} operation.
|
||||
* <p>
|
||||
* This is optional. If not set, a default implementation will be used that simply exposes all request parameters as
|
||||
* flow execution input attributes.
|
||||
*/
|
||||
public void setInputMapper(AttributeMapper inputMapper) {
|
||||
this.inputMapper = inputMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configured input mapper.
|
||||
*/
|
||||
protected AttributeMapper getInputMapper() {
|
||||
return inputMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set system defaults that should be used.
|
||||
* @param defaults the defaults to use.
|
||||
*/
|
||||
public void setDefaults(FlowSystemDefaults defaults) {
|
||||
this.defaults = defaults;
|
||||
public void setConversionService(ConversionService conversionService) {
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
// implementing InitializingBean
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(definitionLocator, "The flow definition locator is required");
|
||||
Assert.notNull(flowDefinitionLocator, "The flow definition locator property is required");
|
||||
|
||||
// apply defaults
|
||||
executionAttributes = defaults.applyExecutionAttributes(executionAttributes);
|
||||
repositoryType = defaults.applyIfNecessary(repositoryType);
|
||||
FlowExecutorSystemDefaults defaults = new FlowExecutorSystemDefaults();
|
||||
|
||||
MutableAttributeMap executionAttributes = defaults.applyExecutionAttributes(createExecutionAttributeMap());
|
||||
flowExecutionRepositoryType = defaults.applyIfNecessary(flowExecutionRepositoryType);
|
||||
|
||||
// pass all available parameters to the hook methods so that they
|
||||
// can participate in the construction process
|
||||
|
||||
// a factory for flow executions
|
||||
FlowExecutionFactory executionFactory = createFlowExecutionFactory(executionAttributes, executionListenerLoader);
|
||||
|
||||
// a strategy to restore deserialized flow executions
|
||||
FlowExecutionStateRestorer executionStateRestorer = createFlowExecutionStateRestorer(definitionLocator,
|
||||
executionAttributes, executionListenerLoader);
|
||||
FlowExecutionStateRestorer executionStateRestorer = createFlowExecutionStateRestorer(flowDefinitionLocator,
|
||||
executionAttributes, flowExecutionListenerLoader);
|
||||
|
||||
// a repository to store flow executions
|
||||
FlowExecutionRepository executionRepository = createExecutionRepository(repositoryType, executionStateRestorer,
|
||||
conversationManager);
|
||||
FlowExecutionRepository executionRepository = createFlowExecutionRepository(flowExecutionRepositoryType,
|
||||
executionStateRestorer, conversationManager);
|
||||
|
||||
// a factory for flow executions
|
||||
FlowExecutionFactory executionFactory = createFlowExecutionFactory(executionAttributes,
|
||||
flowExecutionListenerLoader, (FlowExecutionKeyFactory) executionRepository);
|
||||
|
||||
// combine all pieces of the puzzle to get an operational flow executor
|
||||
flowExecutor = createFlowExecutor(definitionLocator, executionFactory, executionRepository);
|
||||
flowExecutor = createFlowExecutor(flowDefinitionLocator, executionFactory, executionRepository);
|
||||
}
|
||||
|
||||
// implementing FactoryBean
|
||||
|
||||
public Class getObjectType() {
|
||||
return FlowExecutor.class;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public Object getObject() throws Exception {
|
||||
return flowExecutor;
|
||||
}
|
||||
|
||||
// subclassing hook methods
|
||||
|
||||
/**
|
||||
* Create the conversation manager to be used in the default case, e.g. when no explicit conversation manager has
|
||||
* been configured using {@link #setConversationManager(ConversationManager)}. This implementation return a
|
||||
* {@link SessionBindingConversationManager}.
|
||||
* @return the default conversation manager
|
||||
*/
|
||||
protected ConversationManager createDefaultConversationManager() {
|
||||
SessionBindingConversationManager conversationManager = new SessionBindingConversationManager();
|
||||
if (getMaxConversations() != null) {
|
||||
conversationManager.setMaxConversations(getMaxConversations().intValue());
|
||||
}
|
||||
return conversationManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the flow execution factory to be used by the executor produced by this factory bean. Configure the
|
||||
* execution factory appropriately. Subclasses may override if they which to use a custom execution factory, e.g. to
|
||||
* use a custom FlowExecution implementation.
|
||||
* @param executionAttributes execution attributes to apply to created executions
|
||||
* @param executionListenerLoader decides which listeners to apply to created executions
|
||||
* @return a new flow execution factory instance
|
||||
*/
|
||||
protected FlowExecutionFactory createFlowExecutionFactory(AttributeMap executionAttributes,
|
||||
FlowExecutionListenerLoader executionListenerLoader) {
|
||||
FlowExecutionImplFactory executionFactory = new FlowExecutionImplFactory();
|
||||
executionFactory.setExecutionAttributes(executionAttributes);
|
||||
if (executionListenerLoader != null) {
|
||||
executionFactory.setExecutionListenerLoader(executionListenerLoader);
|
||||
}
|
||||
return executionFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the flow execution state restorer to be used by the executor produced by this factory bean. Configure the
|
||||
* state restorer appropriately. Subclasses may override if they which to use a custom state restorer
|
||||
@@ -346,15 +259,15 @@ public class FlowExecutorFactoryBean implements FactoryBean, InitializingBean {
|
||||
* default conversation manager should be used
|
||||
* @return a new flow execution repository instance
|
||||
*/
|
||||
protected FlowExecutionRepository createExecutionRepository(RepositoryType repositoryType,
|
||||
protected FlowExecutionRepository createFlowExecutionRepository(FlowExecutionRepositoryType repositoryType,
|
||||
FlowExecutionStateRestorer executionStateRestorer, ConversationManager conversationManager) {
|
||||
if (repositoryType == RepositoryType.CLIENT) {
|
||||
if (repositoryType == FlowExecutionRepositoryType.CLIENT) {
|
||||
if (conversationManager == null) {
|
||||
// use the default no-op conversation manager
|
||||
return new ClientContinuationFlowExecutionRepository(executionStateRestorer);
|
||||
return new ClientFlowExecutionRepository(executionStateRestorer);
|
||||
} else {
|
||||
// use the conversation manager specified by the user
|
||||
return new ClientContinuationFlowExecutionRepository(executionStateRestorer, conversationManager);
|
||||
return new ClientFlowExecutionRepository(conversationManager, executionStateRestorer);
|
||||
}
|
||||
} else {
|
||||
// determine the conversation manager to use
|
||||
@@ -362,19 +275,21 @@ public class FlowExecutorFactoryBean implements FactoryBean, InitializingBean {
|
||||
if (conversationManagerToUse == null) {
|
||||
conversationManagerToUse = createDefaultConversationManager();
|
||||
}
|
||||
|
||||
if (repositoryType == RepositoryType.SIMPLE) {
|
||||
return new SimpleFlowExecutionRepository(executionStateRestorer, conversationManagerToUse);
|
||||
} else if (repositoryType == RepositoryType.CONTINUATION) {
|
||||
ContinuationFlowExecutionRepository repository = new ContinuationFlowExecutionRepository(
|
||||
executionStateRestorer, conversationManagerToUse);
|
||||
if (getMaxContinuations() != null) {
|
||||
repository.setMaxContinuations(getMaxContinuations().intValue());
|
||||
if (repositoryType == FlowExecutionRepositoryType.SIMPLE) {
|
||||
DefaultFlowExecutionRepository repository = new DefaultFlowExecutionRepository(
|
||||
conversationManagerToUse, executionStateRestorer);
|
||||
repository.setMaxContinuations(1);
|
||||
return repository;
|
||||
} else if (repositoryType == FlowExecutionRepositoryType.CONTINUATION) {
|
||||
DefaultFlowExecutionRepository repository = new DefaultFlowExecutionRepository(
|
||||
conversationManagerToUse, executionStateRestorer);
|
||||
if (maxContinuations != null) {
|
||||
repository.setMaxContinuations(maxContinuations.intValue());
|
||||
}
|
||||
return repository;
|
||||
} else if (repositoryType == RepositoryType.SINGLEKEY) {
|
||||
SimpleFlowExecutionRepository repository = new SimpleFlowExecutionRepository(executionStateRestorer,
|
||||
conversationManagerToUse);
|
||||
} else if (repositoryType == FlowExecutionRepositoryType.SINGLEKEY) {
|
||||
DefaultFlowExecutionRepository repository = new DefaultFlowExecutionRepository(
|
||||
conversationManagerToUse, executionStateRestorer);
|
||||
repository.setAlwaysGenerateNewNextKey(false);
|
||||
return repository;
|
||||
} else {
|
||||
@@ -384,6 +299,38 @@ public class FlowExecutorFactoryBean implements FactoryBean, InitializingBean {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the conversation manager to be used in the default case, e.g. when no explicit conversation manager has
|
||||
* been configured. This implementation return a {@link SessionBindingConversationManager}.
|
||||
* @return the default conversation manager
|
||||
*/
|
||||
protected ConversationManager createDefaultConversationManager() {
|
||||
SessionBindingConversationManager conversationManager = new SessionBindingConversationManager();
|
||||
if (maxConversations != null) {
|
||||
conversationManager.setMaxConversations(maxConversations.intValue());
|
||||
}
|
||||
return conversationManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the flow execution factory to be used by the executor produced by this factory bean. Configure the
|
||||
* execution factory appropriately. Subclasses may override if they which to use a custom execution factory, e.g. to
|
||||
* use a custom FlowExecution implementation.
|
||||
* @param executionAttributes execution attributes to apply to created executions
|
||||
* @param executionListenerLoader decides which listeners to apply to created executions
|
||||
* @return a new flow execution factory instance
|
||||
*/
|
||||
protected FlowExecutionFactory createFlowExecutionFactory(AttributeMap executionAttributes,
|
||||
FlowExecutionListenerLoader executionListenerLoader, FlowExecutionKeyFactory keyFactory) {
|
||||
FlowExecutionImplFactory executionFactory = new FlowExecutionImplFactory();
|
||||
executionFactory.setExecutionAttributes(executionAttributes);
|
||||
if (executionListenerLoader != null) {
|
||||
executionFactory.setExecutionListenerLoader(executionListenerLoader);
|
||||
}
|
||||
executionFactory.setExecutionKeyFactory(keyFactory);
|
||||
return executionFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the flow executor instance created by this factory bean and configure it appropriately. Subclasses may
|
||||
* override if they which to use a custom executor implementation.
|
||||
@@ -394,32 +341,27 @@ public class FlowExecutorFactoryBean implements FactoryBean, InitializingBean {
|
||||
*/
|
||||
protected FlowExecutor createFlowExecutor(FlowDefinitionLocator definitionLocator,
|
||||
FlowExecutionFactory executionFactory, FlowExecutionRepository executionRepository) {
|
||||
FlowExecutorImpl flowExecutor = new FlowExecutorImpl(definitionLocator, executionFactory, executionRepository);
|
||||
if (getInputMapper() != null) {
|
||||
flowExecutor.setInputMapper(inputMapper);
|
||||
return new FlowExecutorImpl(definitionLocator, executionFactory, executionRepository);
|
||||
}
|
||||
|
||||
private MutableAttributeMap createExecutionAttributeMap() {
|
||||
LocalAttributeMap executionAttributes = new LocalAttributeMap();
|
||||
if (flowExecutionAttributes != null) {
|
||||
for (Iterator it = flowExecutionAttributes.iterator(); it.hasNext();) {
|
||||
FlowElementAttribute attribute = (FlowElementAttribute) it.next();
|
||||
executionAttributes.put(attribute.getName(), getConvertedValue(attribute));
|
||||
}
|
||||
}
|
||||
return flowExecutor;
|
||||
return executionAttributes;
|
||||
}
|
||||
|
||||
// implementing FactoryBean
|
||||
|
||||
public Class getObjectType() {
|
||||
return FlowExecutor.class;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public Object getObject() throws Exception {
|
||||
return getFlowExecutor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the flow executor constructed by the factory bean.
|
||||
* @since 1.0.2
|
||||
*/
|
||||
public FlowExecutor getFlowExecutor() {
|
||||
return flowExecutor;
|
||||
private Object getConvertedValue(FlowElementAttribute attribute) {
|
||||
if (attribute.needsTypeConversion()) {
|
||||
ConversionExecutor converter = conversionService.getConversionExecutorByTargetAlias(String.class, attribute
|
||||
.getType());
|
||||
return converter.execute(attribute.getValue());
|
||||
} else {
|
||||
return attribute.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,6 @@ import java.io.Serializable;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.webflow.core.collection.LocalAttributeMap;
|
||||
import org.springframework.webflow.core.collection.MutableAttributeMap;
|
||||
import org.springframework.webflow.engine.support.ApplicationViewSelector;
|
||||
|
||||
/**
|
||||
* Encapsulates overall flow system configuration defaults. Allows for centralized application of, and if necessary,
|
||||
@@ -28,7 +27,7 @@ import org.springframework.webflow.engine.support.ApplicationViewSelector;
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public class FlowSystemDefaults implements Serializable {
|
||||
class FlowExecutorSystemDefaults implements Serializable {
|
||||
|
||||
/**
|
||||
* The default 'alwaysRedirectOnPause' execution attribute value.
|
||||
@@ -38,12 +37,11 @@ public class FlowSystemDefaults implements Serializable {
|
||||
/**
|
||||
* The default flow execution repository type.
|
||||
*/
|
||||
private RepositoryType repositoryType = RepositoryType.CONTINUATION;
|
||||
private FlowExecutionRepositoryType repositoryType = FlowExecutionRepositoryType.CONTINUATION;
|
||||
|
||||
/**
|
||||
* Overrides the alwaysRedirectOnPause execution attribute default. Defaults to "true".
|
||||
* @param alwaysRedirectOnPause the new default value
|
||||
* @see ApplicationViewSelector#ALWAYS_REDIRECT_ON_PAUSE_ATTRIBUTE
|
||||
*/
|
||||
public void setAlwaysRedirectOnPause(boolean alwaysRedirectOnPause) {
|
||||
this.alwaysRedirectOnPause = alwaysRedirectOnPause;
|
||||
@@ -53,7 +51,7 @@ public class FlowSystemDefaults implements Serializable {
|
||||
* Overrides the default repository type.
|
||||
* @param repositoryType the new default value
|
||||
*/
|
||||
public void setRepositoryType(RepositoryType repositoryType) {
|
||||
public void setRepositoryType(FlowExecutionRepositoryType repositoryType) {
|
||||
this.repositoryType = repositoryType;
|
||||
}
|
||||
|
||||
@@ -67,9 +65,8 @@ public class FlowSystemDefaults implements Serializable {
|
||||
if (executionAttributes == null) {
|
||||
executionAttributes = new LocalAttributeMap(1, 1);
|
||||
}
|
||||
if (!executionAttributes.contains(ApplicationViewSelector.ALWAYS_REDIRECT_ON_PAUSE_ATTRIBUTE)) {
|
||||
executionAttributes.put(ApplicationViewSelector.ALWAYS_REDIRECT_ON_PAUSE_ATTRIBUTE, new Boolean(
|
||||
alwaysRedirectOnPause));
|
||||
if (!executionAttributes.contains("alwaysRedirectOnPause")) {
|
||||
executionAttributes.put("alwaysRedirectOnPause", new Boolean(alwaysRedirectOnPause));
|
||||
}
|
||||
return executionAttributes;
|
||||
}
|
||||
@@ -79,7 +76,7 @@ public class FlowSystemDefaults implements Serializable {
|
||||
* @param selectedType the selected repository type (may be null if no selection was made)
|
||||
* @return the repository type, with the default applied if necessary
|
||||
*/
|
||||
public RepositoryType applyIfNecessary(RepositoryType selectedType) {
|
||||
public FlowExecutionRepositoryType applyIfNecessary(FlowExecutionRepositoryType selectedType) {
|
||||
if (selectedType == null) {
|
||||
return repositoryType;
|
||||
} else {
|
||||
@@ -0,0 +1,49 @@
|
||||
package org.springframework.webflow.config;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A low-level pointer to a flow definition that will be registered in a registry and built from an external file
|
||||
* resource.
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
class FlowLocation {
|
||||
|
||||
/**
|
||||
* The id to assign to the flow definition.
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* The string-encoded path to the flow definition file resource.
|
||||
*/
|
||||
private String path;
|
||||
|
||||
/**
|
||||
* Attributes to assign to the flow definition.
|
||||
*/
|
||||
private Set attributes;
|
||||
|
||||
public FlowLocation(String id, String path, Set attributes) {
|
||||
Assert.hasText(path, "The path is required");
|
||||
this.id = id;
|
||||
this.path = path;
|
||||
this.attributes = (attributes != null ? attributes : Collections.EMPTY_SET);
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public Set getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* 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.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* {@link BeanDefinitionParser} for the flow <code><registry></code> tag.
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
class FlowRegistryBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
|
||||
|
||||
private static final String FLOW_BUILDER_SERVICES_ATTRIBUTE = "flow-builder-services";
|
||||
|
||||
private static final String FLOW_LOCATION_ELEMENT = "flow-location";
|
||||
|
||||
private static final String FLOW_BUILDER_ELEMENT = "flow-builder";
|
||||
|
||||
private static final String ID_ATTRIBUTE = "id";
|
||||
|
||||
private static final String PATH_ATTRIBUTE = "path";
|
||||
|
||||
private static final String CLASS_ATTRIBUTE = "class";
|
||||
|
||||
private static final String DEFINITION_ATTRIBUTES_ELEMENT = "flow-definition-attributes";
|
||||
|
||||
private static final String ATTRIBUTE_ELEMENT = "attribute";
|
||||
|
||||
private static final String NAME_ATTRIBUTE = "name";
|
||||
|
||||
private static final String VALUE_ATTRIBUTE = "value";
|
||||
|
||||
private static final String TYPE_ATTRIBUTE = "type";
|
||||
|
||||
private static final String FLOW_LOCATIONS_PROPERTY = "flowLocations";
|
||||
|
||||
private static final String FLOW_BUILDERS_PROPERTY = "flowBuilders";
|
||||
|
||||
private static final String FLOW_BUILDER_SERVICES_PROPERTY = "flowBuilderServices";
|
||||
|
||||
protected Class getBeanClass(Element element) {
|
||||
return FlowRegistryFactoryBean.class;
|
||||
}
|
||||
|
||||
protected void doParse(Element element, BeanDefinitionBuilder definitionBuilder) {
|
||||
String flowBuilderServices = getFlowBuilderServicesAttribute(element);
|
||||
if (StringUtils.hasText(flowBuilderServices)) {
|
||||
definitionBuilder.addPropertyReference(FLOW_BUILDER_SERVICES_PROPERTY, flowBuilderServices);
|
||||
}
|
||||
definitionBuilder.addPropertyValue(FLOW_LOCATIONS_PROPERTY, parseLocations(element));
|
||||
definitionBuilder.addPropertyValue(FLOW_BUILDERS_PROPERTY, parseFlowBuilders(element));
|
||||
}
|
||||
|
||||
private List parseLocations(Element element) {
|
||||
List locationElements = DomUtils.getChildElementsByTagName(element, FLOW_LOCATION_ELEMENT);
|
||||
if (locationElements.isEmpty()) {
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
List locations = new ArrayList(locationElements.size());
|
||||
for (Iterator it = locationElements.iterator(); it.hasNext();) {
|
||||
Element locationElement = (Element) it.next();
|
||||
String id = locationElement.getAttribute(ID_ATTRIBUTE);
|
||||
String path = locationElement.getAttribute(PATH_ATTRIBUTE);
|
||||
locations.add(new FlowLocation(id, path, parseAttributes(locationElement)));
|
||||
}
|
||||
return locations;
|
||||
}
|
||||
|
||||
private Set parseAttributes(Element element) {
|
||||
Element definitionAttributesElement = DomUtils.getChildElementByTagName(element, DEFINITION_ATTRIBUTES_ELEMENT);
|
||||
if (definitionAttributesElement != null) {
|
||||
List attributeElements = DomUtils.getChildElementsByTagName(definitionAttributesElement, ATTRIBUTE_ELEMENT);
|
||||
HashSet attributes = new HashSet(attributeElements.size());
|
||||
for (Iterator it = attributeElements.iterator(); it.hasNext();) {
|
||||
Element attributeElement = (Element) it.next();
|
||||
String name = attributeElement.getAttribute(NAME_ATTRIBUTE);
|
||||
String value = attributeElement.getAttribute(VALUE_ATTRIBUTE);
|
||||
String type = attributeElement.getAttribute(TYPE_ATTRIBUTE);
|
||||
attributes.add(new FlowElementAttribute(name, value, type));
|
||||
}
|
||||
return attributes;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private List parseFlowBuilders(Element element) {
|
||||
List builderElements = DomUtils.getChildElementsByTagName(element, FLOW_BUILDER_ELEMENT);
|
||||
if (builderElements.isEmpty()) {
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
List builders = new ArrayList(builderElements.size());
|
||||
for (Iterator it = builderElements.iterator(); it.hasNext();) {
|
||||
Element builderElement = (Element) it.next();
|
||||
String id = builderElement.getAttribute(ID_ATTRIBUTE);
|
||||
String className = builderElement.getAttribute(CLASS_ATTRIBUTE);
|
||||
builders.add(new FlowBuilderInfo(id, className, parseAttributes(builderElement)));
|
||||
}
|
||||
return builders;
|
||||
}
|
||||
|
||||
private String getFlowBuilderServicesAttribute(Element element) {
|
||||
return element.getAttribute(FLOW_BUILDER_SERVICES_ATTRIBUTE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
package org.springframework.webflow.config;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.binding.convert.ConversionExecutor;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.webflow.core.collection.AttributeMap;
|
||||
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.FlowDefinitionConstructionException;
|
||||
import org.springframework.webflow.definition.registry.FlowDefinitionHolder;
|
||||
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
|
||||
import org.springframework.webflow.definition.registry.FlowDefinitionRegistryImpl;
|
||||
import org.springframework.webflow.engine.builder.FlowAssembler;
|
||||
import org.springframework.webflow.engine.builder.FlowBuilder;
|
||||
import org.springframework.webflow.engine.builder.FlowBuilderContext;
|
||||
import org.springframework.webflow.engine.builder.RefreshableFlowDefinitionHolder;
|
||||
import org.springframework.webflow.engine.builder.support.FlowBuilderContextImpl;
|
||||
import org.springframework.webflow.engine.builder.support.FlowBuilderServices;
|
||||
import org.springframework.webflow.engine.builder.xml.XmlFlowBuilder;
|
||||
|
||||
/**
|
||||
* A factory for a flow definition registry. Is a Spring FactoryBean, for provision by the flow definition registry bean
|
||||
* definition parser. Is package-private, as people should not be using this class directly, but rather through the
|
||||
* higher-level webflow-config Spring 2.x configuration namespace.
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
class FlowRegistryFactoryBean implements FactoryBean, ResourceLoaderAware, BeanFactoryAware, InitializingBean {
|
||||
|
||||
/**
|
||||
* The definition registry produced by this factory bean.
|
||||
*/
|
||||
private FlowDefinitionRegistry flowRegistry;
|
||||
|
||||
/**
|
||||
* Flow definitions defined in external files that should be registered in the registry produced by this factory
|
||||
* bean.
|
||||
*/
|
||||
private FlowLocation[] flowLocations;
|
||||
|
||||
/**
|
||||
* Java {@link FlowBuilder flow builder} classes that should be registered in the registry produced by this factory
|
||||
* bean.
|
||||
*/
|
||||
private FlowBuilderInfo[] flowBuilders;
|
||||
|
||||
/**
|
||||
* The holder for services needed to build flow definitions registered in this registry.
|
||||
*/
|
||||
private FlowBuilderServices flowBuilderServices;
|
||||
|
||||
/**
|
||||
* A helper for creating abstract representation of externalized flow definition resources.
|
||||
*/
|
||||
private FlowDefinitionResourceFactory flowResourceFactory;
|
||||
|
||||
/**
|
||||
* The container's resource loader.
|
||||
*/
|
||||
private ResourceLoader resourceLoader;
|
||||
|
||||
/**
|
||||
* The containing bean factory this factory bean was deployed in.
|
||||
*/
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
public void setFlowLocations(FlowLocation[] flowLocations) {
|
||||
this.flowLocations = flowLocations;
|
||||
}
|
||||
|
||||
public void setFlowBuilders(FlowBuilderInfo[] flowBuilders) {
|
||||
this.flowBuilders = flowBuilders;
|
||||
}
|
||||
|
||||
public void setFlowBuilderServices(FlowBuilderServices flowBuilderServices) {
|
||||
this.flowBuilderServices = flowBuilderServices;
|
||||
}
|
||||
|
||||
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (flowBuilderServices == null) {
|
||||
initFlowBuilderServices();
|
||||
}
|
||||
flowResourceFactory = new FlowDefinitionResourceFactory(resourceLoader);
|
||||
flowRegistry = new FlowDefinitionRegistryImpl();
|
||||
registerFlowLocations();
|
||||
registerFlowBuilders();
|
||||
}
|
||||
|
||||
public Object getObject() throws Exception {
|
||||
return flowRegistry;
|
||||
}
|
||||
|
||||
public Class getObjectType() {
|
||||
return FlowDefinitionRegistry.class;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void registerFlowLocations() {
|
||||
if (flowLocations != null) {
|
||||
for (int i = 0; i < flowLocations.length; i++) {
|
||||
FlowLocation location = flowLocations[i];
|
||||
flowRegistry.registerFlowDefinition(createFlowDefinitionHolder(location));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void registerFlowBuilders() {
|
||||
if (flowBuilders != null) {
|
||||
for (int i = 0; i < flowBuilders.length; i++) {
|
||||
FlowBuilderInfo builder = flowBuilders[i];
|
||||
flowRegistry.registerFlowDefinition(createFlowDefinitionHolder(builder));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private FlowDefinitionHolder createFlowDefinitionHolder(FlowLocation location) {
|
||||
FlowDefinitionResource flowResource = createResource(location);
|
||||
FlowBuilder builder = createFlowBuilder(flowResource);
|
||||
FlowBuilderContext builderContext = new FlowBuilderContextImpl(flowResource.getId(), flowResource
|
||||
.getAttributes(), flowRegistry, flowBuilderServices);
|
||||
FlowAssembler assembler = new FlowAssembler(builder, builderContext);
|
||||
return new RefreshableFlowDefinitionHolder(assembler);
|
||||
}
|
||||
|
||||
private FlowDefinitionResource createResource(FlowLocation location) {
|
||||
AttributeMap flowAttributes = getFlowAttributes(location.getAttributes());
|
||||
return flowResourceFactory.createResource(location.getPath(), flowAttributes, location.getId());
|
||||
}
|
||||
|
||||
private AttributeMap getFlowAttributes(Set attributes) {
|
||||
MutableAttributeMap flowAttributes = null;
|
||||
if (!attributes.isEmpty()) {
|
||||
flowAttributes = new LocalAttributeMap();
|
||||
for (Iterator it = attributes.iterator(); it.hasNext();) {
|
||||
FlowElementAttribute attribute = (FlowElementAttribute) it.next();
|
||||
flowAttributes.put(attribute.getName(), getConvertedValue(attribute));
|
||||
}
|
||||
}
|
||||
return flowAttributes;
|
||||
}
|
||||
|
||||
private FlowBuilder createFlowBuilder(FlowDefinitionResource resource) {
|
||||
if (isXml(resource.getPath())) {
|
||||
return new XmlFlowBuilder(resource.getPath());
|
||||
} else {
|
||||
throw new IllegalArgumentException(resource
|
||||
+ " is not a supported resource type; supported types are [.xml]");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isXml(Resource flowResource) {
|
||||
return flowResource.getFilename().endsWith(".xml");
|
||||
}
|
||||
|
||||
private Object getConvertedValue(FlowElementAttribute attribute) {
|
||||
if (attribute.needsTypeConversion()) {
|
||||
ConversionExecutor converter = flowBuilderServices.getConversionService()
|
||||
.getConversionExecutorByTargetAlias(String.class, attribute.getType());
|
||||
return converter.execute(attribute.getValue());
|
||||
} else {
|
||||
return attribute.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
private FlowDefinitionHolder createFlowDefinitionHolder(FlowBuilderInfo builder) {
|
||||
ClassLoader classLoader = ClassUtils.getDefaultClassLoader();
|
||||
AttributeMap flowAttributes = getFlowAttributes(builder.getAttributes());
|
||||
FlowBuilderContext builderContext = new FlowBuilderContextImpl(builder.getId(), flowAttributes, flowRegistry,
|
||||
flowBuilderServices);
|
||||
return new FlowBuilderCreatingFlowDefinitionHolder(builder.getClassName(), classLoader, builderContext);
|
||||
}
|
||||
|
||||
private void initFlowBuilderServices() {
|
||||
flowBuilderServices = new FlowBuilderServices();
|
||||
flowBuilderServices.setResourceLoader(resourceLoader);
|
||||
flowBuilderServices.setBeanFactory(beanFactory);
|
||||
}
|
||||
|
||||
class FlowBuilderCreatingFlowDefinitionHolder implements FlowDefinitionHolder {
|
||||
|
||||
private String flowBuilderClassName;
|
||||
|
||||
private ClassLoader classLoader;
|
||||
|
||||
private FlowBuilderContext builderContext;
|
||||
|
||||
public FlowBuilderCreatingFlowDefinitionHolder(String flowBuilderClassName, ClassLoader classLoader,
|
||||
FlowBuilderContext builderContext) {
|
||||
this.flowBuilderClassName = flowBuilderClassName;
|
||||
this.classLoader = classLoader;
|
||||
this.builderContext = builderContext;
|
||||
}
|
||||
|
||||
public String getFlowDefinitionId() {
|
||||
return builderContext.getFlowId();
|
||||
}
|
||||
|
||||
public FlowDefinition getFlowDefinition() throws FlowDefinitionConstructionException {
|
||||
try {
|
||||
Class flowBuilderClass = classLoader.loadClass(flowBuilderClassName);
|
||||
FlowBuilder builder = (FlowBuilder) flowBuilderClass.newInstance();
|
||||
FlowAssembler assembler = new FlowAssembler(builder, builderContext);
|
||||
return assembler.assembleFlow();
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new FlowDefinitionConstructionException(getFlowDefinitionId(), e);
|
||||
} catch (InstantiationException e) {
|
||||
throw new FlowDefinitionConstructionException(getFlowDefinitionId(), e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new FlowDefinitionConstructionException(getFlowDefinitionId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public void refresh() throws FlowDefinitionConstructionException {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
* 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.config;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||
import org.springframework.core.io.support.ResourcePatternUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
import org.springframework.webflow.definition.registry.FlowDefinitionResource;
|
||||
import org.springframework.webflow.engine.builder.FlowRegistryFactoryBean;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* {@link BeanDefinitionParser} for the <code><registry></code> tag.
|
||||
*
|
||||
* @author Ben Hale
|
||||
*/
|
||||
class RegistryBeanDefinitionParser extends AbstractBeanDefinitionParser {
|
||||
|
||||
// elements
|
||||
private static final String LOCATION_ELEMENT = "location";
|
||||
|
||||
private static final String NAMESPACE_ELEMENT = "namespace";
|
||||
|
||||
// attributes
|
||||
private static final String ID_ATTRIBUTE = "id";
|
||||
|
||||
private static final String NAME_ATTRIBUTE = "name";
|
||||
|
||||
private static final String PATH_ATTRIBUTE = "path";
|
||||
|
||||
// Properties
|
||||
private static final String XML_NAMESPACE_FLOW_MAPPINGS_PROPERTY = "xmlNamespaceFlowMappings";
|
||||
|
||||
protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(FlowRegistryFactoryBean.class);
|
||||
definitionBuilder.setSource(parserContext.extractSource(element));
|
||||
parseXml(element, definitionBuilder, parserContext);
|
||||
return definitionBuilder.getBeanDefinition();
|
||||
}
|
||||
|
||||
public void parseXml(Element element, BeanDefinitionBuilder definitionBuilder, ParserContext parserContext) {
|
||||
Map xmlNamespaceFlowMappings = new HashMap();
|
||||
xmlNamespaceFlowMappings.put("", parseXmlElements(
|
||||
DomUtils.getChildElementsByTagName(element, LOCATION_ELEMENT), parserContext));
|
||||
List namespaceElements = DomUtils.getChildElementsByTagName(element, NAMESPACE_ELEMENT);
|
||||
for (Iterator it = namespaceElements.iterator(); it.hasNext();) {
|
||||
Element namespaceElement = (Element) it.next();
|
||||
String namespace = namespaceElement.getAttribute(NAME_ATTRIBUTE);
|
||||
xmlNamespaceFlowMappings.put(namespace, parseXmlElements(DomUtils.getChildElementsByTagName(
|
||||
namespaceElement, LOCATION_ELEMENT), parserContext));
|
||||
}
|
||||
definitionBuilder.addPropertyValue(XML_NAMESPACE_FLOW_MAPPINGS_PROPERTY, xmlNamespaceFlowMappings);
|
||||
}
|
||||
|
||||
private Set parseXmlElements(List locationElements, ParserContext parserContext) {
|
||||
ResourcePatternResolver resolver = ResourcePatternUtils.getResourcePatternResolver(parserContext
|
||||
.getReaderContext().getResourceLoader());
|
||||
Set resources = new HashSet();
|
||||
for (Iterator it = locationElements.iterator(); it.hasNext();) {
|
||||
Element locationElement = (Element) it.next();
|
||||
try {
|
||||
Resource[] locations = resolver.getResources(locationElement.getAttribute(PATH_ATTRIBUTE));
|
||||
if (locationElement.hasAttribute(ID_ATTRIBUTE)) {
|
||||
if (locations.length != 1) {
|
||||
parserContext.getReaderContext().error(
|
||||
"The 'path' attribute of the 'location' element must point to a single value "
|
||||
+ "flow definition if an id has been specified", locationElement);
|
||||
} else {
|
||||
resources.add(new FlowDefinitionResource(locationElement.getAttribute(ID_ATTRIBUTE),
|
||||
locations[0]));
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < locations.length; i++) {
|
||||
resources.add(new FlowDefinitionResource(locations[i]));
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
parserContext.getReaderContext().error(
|
||||
"The 'path' attribute of the 'location' element must point to a valid flow definition "
|
||||
+ "or definitions", locationElement);
|
||||
}
|
||||
}
|
||||
return resources;
|
||||
}
|
||||
}
|
||||
@@ -15,51 +15,18 @@
|
||||
*/
|
||||
package org.springframework.webflow.config;
|
||||
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
|
||||
|
||||
/**
|
||||
* <code>NamespaceHandler</code> for the <code>webflow-config</code> namespace.
|
||||
* <p>
|
||||
* Provides {@link BeanDefinitionParser bean definition parsers} for the <code><executor></code> and
|
||||
* <code><registry></code> tags. An <code>executor</code> tag can include an <code>execution-listeners</code>
|
||||
* tag and a <code>registry</code> tag can include <code>location</code> tags.
|
||||
* <p>
|
||||
* Using the <code>executor</code> tag you can configure a {@link FlowExecutorFactoryBean} that creates a
|
||||
* {@link org.springframework.webflow.executor.FlowExecutor}. The <code>executor</code> tag allows you to specify the
|
||||
* repository type and a reference to a registry.
|
||||
*
|
||||
* <pre class="code">
|
||||
* <flow:executor id="registry" registry-ref="registry" repository-type="continuation" >
|
||||
* <flow:execution-listeners>
|
||||
* <flow:listener ref="listener1" />
|
||||
* <flow:listener ref="listener2" ref="*" />
|
||||
* <flow:listener ref="listener3" ref="flow1, flow2, flow3" />
|
||||
* <flow:execution-listeners />
|
||||
* </flow:executor>
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* Using the <code>registry</code> tag you can configure an
|
||||
* {@link org.springframework.webflow.engine.builder.xml.XmlFlowRegistryFactoryBean} to create a registry for use by any
|
||||
* number of <code>executor</code>s. The <code>registry</code> tag supports in-line flow definition locations.
|
||||
*
|
||||
* <pre class="code">
|
||||
* <flow:registry id="registry">
|
||||
* <flow:location path="/path/to/flow.xml" />
|
||||
* <flow:location path="/path/with/wildcards/*-flow.xml" />
|
||||
* </flow:registry>
|
||||
* </pre>
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Ben Hale
|
||||
*/
|
||||
public class WebFlowConfigNamespaceHandler extends NamespaceHandlerSupport {
|
||||
|
||||
public void init() {
|
||||
registerBeanDefinitionParser("execution-attributes", new ExecutionAttributesBeanDefinitionParser());
|
||||
registerBeanDefinitionParser("execution-listeners", new ExecutionListenersBeanDefinitionParser());
|
||||
registerBeanDefinitionParser("executor", new ExecutorBeanDefinitionParser());
|
||||
registerBeanDefinitionParser("registry", new RegistryBeanDefinitionParser());
|
||||
registerBeanDefinitionParser("enable-scopes", new EnableScopesBeanDefinitionParser());
|
||||
registerBeanDefinitionParser("flow-executor", new FlowExecutorBeanDefinitionParser());
|
||||
registerBeanDefinitionParser("flow-execution-listeners", new FlowExecutionListenerLoaderBeanDefinitionParser());
|
||||
registerBeanDefinitionParser("flow-registry", new FlowRegistryBeanDefinitionParser());
|
||||
}
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
/*
|
||||
* 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.config.scope;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.beans.factory.config.Scope;
|
||||
import org.springframework.webflow.core.collection.MutableAttributeMap;
|
||||
import org.springframework.webflow.execution.FlowExecutionContext;
|
||||
import org.springframework.webflow.execution.FlowExecutionContextHolder;
|
||||
import org.springframework.webflow.execution.FlowSession;
|
||||
|
||||
/**
|
||||
* Base class for {@link Scope} implementations that access a Web Flow scope from the current thread-bound
|
||||
* {@link FlowExecutionContext} object.
|
||||
* <p>
|
||||
* Subclasses simply need to implement {@link #getScope()} to return the {@link MutableAttributeMap scope map} to
|
||||
* access.
|
||||
* <p>
|
||||
* Relies on a thread-bound
|
||||
* @{link FlowExecutionContext} instance located through the
|
||||
* @{link FlowExecutionContextHolder}.
|
||||
*
|
||||
* @see FlowExecutionContext
|
||||
* @see FlowExecutionContextHolder
|
||||
*
|
||||
* @author Ben Hale
|
||||
*/
|
||||
public abstract class AbstractWebFlowScope implements Scope {
|
||||
|
||||
/**
|
||||
* Logger, usable by subclasses.
|
||||
*/
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
public Object get(String name, ObjectFactory objectFactory) {
|
||||
MutableAttributeMap scope = getScope();
|
||||
Object scopedObject = scope.get(name);
|
||||
if (scopedObject == null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("No scoped instance '" + name + "' found; creating new instance");
|
||||
}
|
||||
scopedObject = objectFactory.getObject();
|
||||
scope.put(name, scopedObject);
|
||||
} else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Returning scoped instance '" + name + "'");
|
||||
}
|
||||
}
|
||||
return scopedObject;
|
||||
}
|
||||
|
||||
public Object remove(String name) {
|
||||
return getScope().remove(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method that returns the target scope map.
|
||||
* @return the target scope map
|
||||
* @see FlowExecutionContext#getConversationScope()
|
||||
* @see FlowExecutionContext#getActiveSession()
|
||||
* @see FlowSession#getFlashMap()
|
||||
* @see FlowSession#getScope()
|
||||
* @throws IllegalStateException if the scope could not be accessed
|
||||
*/
|
||||
protected abstract MutableAttributeMap getScope() throws IllegalStateException;
|
||||
|
||||
/**
|
||||
* Always returns <code>null</code> as most Spring Web Flow scopes do not have obvious conversation ids.
|
||||
* Subclasses should override this method where conversation ids can be intelligently returned.
|
||||
* @return always returns <code>null</code>
|
||||
*/
|
||||
public String getConversationId() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will not register a destruction callback as Spring Web Flow does not support destruction of scoped beans.
|
||||
* Subclasses should override this method where where destruction can adequately be accomplished.
|
||||
* @param name the name of the bean to register the callback for
|
||||
* @param callback the callback to execute
|
||||
*/
|
||||
public void registerDestructionCallback(String name, Runnable callback) {
|
||||
logger.warn("Destruction callback for '" + name + "' was not registered. Spring Web Flow does not "
|
||||
+ "support destruction of scoped beans.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current flow execution context. Used by subclasses to easily get access to the thread-bound flow
|
||||
* execution context.
|
||||
* @return the current thread-bound flow execution context
|
||||
* @throws IllegalStateException if the current flow execution context is not bound
|
||||
*/
|
||||
protected FlowExecutionContext getFlowExecutionContext() throws IllegalStateException {
|
||||
return FlowExecutionContextHolder.getFlowExecutionContext();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* 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.config.scope;
|
||||
|
||||
import org.springframework.beans.factory.config.Scope;
|
||||
import org.springframework.webflow.core.collection.MutableAttributeMap;
|
||||
import org.springframework.webflow.execution.FlowExecution;
|
||||
|
||||
/**
|
||||
* Conversation {@link Scope scope} implementation.
|
||||
*
|
||||
* @see FlowExecution#getConversationScope()
|
||||
*
|
||||
* @author Ben Hale
|
||||
*/
|
||||
public class ConversationScope extends AbstractWebFlowScope {
|
||||
protected MutableAttributeMap getScope() {
|
||||
return getFlowExecutionContext().getConversationScope();
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* 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.config.scope;
|
||||
|
||||
import org.springframework.beans.factory.config.Scope;
|
||||
import org.springframework.webflow.core.collection.MutableAttributeMap;
|
||||
import org.springframework.webflow.execution.FlowExecutionContext;
|
||||
|
||||
/**
|
||||
* Flash {@link Scope scope} implementation.
|
||||
*
|
||||
* @see FlowExecutionContext#getFlashScope()
|
||||
*
|
||||
* @author Ben Hale
|
||||
*/
|
||||
public class FlashScope extends AbstractWebFlowScope {
|
||||
protected MutableAttributeMap getScope() {
|
||||
return getFlowExecutionContext().getFlashScope();
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* 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.config.scope;
|
||||
|
||||
import org.springframework.beans.factory.config.Scope;
|
||||
import org.springframework.webflow.core.collection.MutableAttributeMap;
|
||||
import org.springframework.webflow.execution.FlowSession;
|
||||
|
||||
/**
|
||||
* Flow {@link Scope scope} implementation.
|
||||
*
|
||||
* @see FlowSession#getScope()
|
||||
*
|
||||
* @author Ben Hale
|
||||
*/
|
||||
public class FlowScope extends AbstractWebFlowScope {
|
||||
protected MutableAttributeMap getScope() {
|
||||
return getFlowExecutionContext().getActiveSession().getScope();
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* 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.config.scope;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.Scope;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.webflow.execution.ScopeType;
|
||||
|
||||
/**
|
||||
* Registers the Spring Web Flow bean scopes with a
|
||||
* @{link ConfigurableListableBeanFactory}.
|
||||
*
|
||||
* @author Ben Hale
|
||||
* @see Scope
|
||||
*/
|
||||
public class ScopeRegistrar implements BeanFactoryPostProcessor, Ordered {
|
||||
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||
beanFactory.registerScope(ScopeType.FLASH.getLabel().toLowerCase(), new FlashScope());
|
||||
beanFactory.registerScope(ScopeType.FLOW.getLabel().toLowerCase(), new FlowScope());
|
||||
beanFactory.registerScope(ScopeType.CONVERSATION.getLabel().toLowerCase(), new ConversationScope());
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return Ordered.LOWEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
<html>
|
||||
<body>
|
||||
Support code to allow access to the Spring Web Flow scopes (conversation, flow, flash)
|
||||
from a Spring ApplicationContext.
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,345 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<xsd:schema
|
||||
xmlns="http://www.springframework.org/schema/webflow-config"
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
targetNamespace="http://www.springframework.org/schema/webflow-config"
|
||||
elementFormDefault="qualified" attributeFormDefault="unqualified"
|
||||
version="1.0.1">
|
||||
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
Spring Web Flow Configuration Schema
|
||||
Authors: Keith Donald, Ben Hale
|
||||
<br>
|
||||
Provides an easy way to configure a flow executor and an XML flow definition registry.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
|
||||
<xsd:import namespace="http://www.springframework.org/schema/beans"
|
||||
schemaLocation="http://www.springframework.org/schema/beans/spring-beans-2.0.xsd" />
|
||||
|
||||
<xsd:element name="registry">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
Deploys a flow definition registy. A flow definition registry contains a set of flow definitions.
|
||||
A flow definition is typically built from externalized resource location such as a .xml file.
|
||||
<br>
|
||||
Each flow definition registered in this registry is assigned a unique identifier. By default,
|
||||
this identifier is the name of the externalized resource minus its file extension. For example,
|
||||
a registry containing flow definitions built from the files "orderitem-flow.xml" and "shipping-flow.xml"
|
||||
would index those definitions by "orderitem-flow" and "shipping-flow" by default.
|
||||
<br>
|
||||
A flow registry is used by a flow executor at runtime to launch new executions of flow definitions.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="beans:identifiedType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="location" type="locationType" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
Specifies a path to a externalized flow definition resource. The flow definition built from this
|
||||
resource will be registered in this registry.
|
||||
<br>
|
||||
Individual paths such as:
|
||||
<pre>
|
||||
/WEB-INF/flows/orderitem-flow.xml
|
||||
</pre>
|
||||
... are supported as well as wildcard paths such as:
|
||||
<pre>
|
||||
/WEB-INF/flows/**/*-flow.xml
|
||||
</pre>
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:complexType name="locationType">
|
||||
<xsd:attribute name="path" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The path to the externalized flow definition resource. May be a path to a single resource or
|
||||
a ANT-style path expression that matches multiple resources.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:element name="executor">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
Deploys a flow executor. A flow executor executes flow definitions. More specifically, an executor
|
||||
is responsible for launching new flow executions as well as resuming paused flow executions.
|
||||
A flow execution that spans more than one request is said to have 'paused' and is stored in
|
||||
an execution repository.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="beans:identifiedType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="repository" type="repositoryType" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
Explicit repository configuration for this executor. This element is used if configuration needs
|
||||
to be more fine grained than the repositoryType attribute on executor.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="execution-attributes" type="execution-attributesType" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
Execution attributes to associate with new flow executions launched by this executor.
|
||||
These attributes may influence execution behavior.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="execution-listeners" type="execution-listenersType" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The listeners eligible for observing the lifecycle of executions launched by this executor.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="repository-type" type="repositoryTypeAttribute">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The type of execution repository to use. The repository is responsible for managing flow execution
|
||||
persistence between requests.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="registry-ref" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The idref to the registry this executor will use to locate flow definitions for execution.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:simpleType name="repositoryTypeAttribute">
|
||||
<xsd:restriction base="xsd:string">
|
||||
<xsd:enumeration value="continuation">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The continuation repository type. Use this to snapshot flow execution state server-side to support use
|
||||
of the browser back button.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:enumeration>
|
||||
<xsd:enumeration value="simple">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The simple repository type. Use of this strategy incurs minimal storage overhead but explicitly prevents
|
||||
resubmits using the browser back button.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:enumeration>
|
||||
<xsd:enumeration value="client">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The client continuation repository type. Use this to snapshot flow execution state client-side to support use of the browser
|
||||
back button in a stateless server environment.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:enumeration>
|
||||
<xsd:enumeration value="singlekey">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The "single key" repository type. Use of this strategy assigns a single key per flow execution that remains
|
||||
constant throughout the life of the execution.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:enumeration>
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
|
||||
<xsd:complexType name="repositoryType">
|
||||
<xsd:attribute name="type" type="repositoryTypeAttribute" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The type of flow execution repository to use. The repository is responsible for managing flow execution
|
||||
persistence between requests.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="max-conversations" type="xsd:integer">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The maximum number of concurrent conversations allowed by this repository. It is illegal to set this
|
||||
attribute if you are also setting the 'conversation-manager-ref' attribute.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="max-continuations" type="xsd:integer">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The maximum number of flow execution continuations (snapshots) allowed by this repository per conversation.
|
||||
This attribute is only relevant when the repository type is 'continuation'.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="conversation-manager-ref" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The idref of the conversation manager this repository should use. Setting this attribute
|
||||
allows full control over the conversation manager implementation and configuration.
|
||||
When used, any value for the 'max-conversations' attribute is ignored.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="execution-listenersType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="listener" type="listenerType" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
Deploys a single flow execution listener that will observe the execution lifecycle of one or more
|
||||
flow definitions. The flow definitions this listener applies to may be restricted by specifying criteria.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="listenerType">
|
||||
<xsd:attribute name="ref" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The idref to your flow execution listener.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="criteria" type="xsd:string" default="*">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The flow definitions your listener should apply to, delimited by commas or '*' for "all".
|
||||
Example: 'flow1,flow2,flow3'.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="execution-attributesType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="alwaysRedirectOnPause" type="alwaysRedirectOnPauseType" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
Sets the 'alwaysRedirectOnPause' execution attribute value. 'alwaysRedirectOnPause' allows
|
||||
control over whether each time a flow execution pauses a browser redirect is performed. If
|
||||
not specified the default value is 'true' unless explicitly set otherwise.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="attribute" type="attributeType" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
A single attribute describing an element. Attributes have string keys and object values.
|
||||
An attribute's type may be specified using the 'type' attribute.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="alwaysRedirectOnPauseType">
|
||||
<xsd:attribute name="value" type="xsd:boolean" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
true = always redirect on pause; false = do not, only redirect when explicitly instructed by the flow definition.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="attributeType">
|
||||
<xsd:attribute name="name" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The name of the attribute.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="type" type="xsd:string" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The attribute's type, used to perform a from-string type conversion if specified.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="value" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The attribute value, subject to type conversion if the 'type' attribute is defined.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
</xsd:schema>
|
||||
@@ -18,21 +18,9 @@ Provides an easy way to configure a flow executor and an XML flow definition reg
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
|
||||
<xsd:import namespace="http://www.springframework.org/schema/beans"
|
||||
schemaLocation="http://www.springframework.org/schema/beans/spring-beans-2.0.xsd" />
|
||||
<xsd:import namespace="http://www.springframework.org/schema/beans" schemaLocation="http://www.springframework.org/schema/beans/spring-beans-2.0.xsd" />
|
||||
|
||||
<xsd:element name="enable-scopes">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
Allows access to the Spring Web Flow scopes (conversation, flow, flash) from a Spring ApplicationContext.
|
||||
Only one of these elements is required per ApplicationContext.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="registry">
|
||||
<xsd:element name="flow-registry">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
@@ -48,11 +36,11 @@ A flow registry is used by a flow executor at runtime to launch new executions o
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="beans:identifiedType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="location" type="locationType" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="beans:identifiedType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="flow-location" type="flowLocationType" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
@@ -61,7 +49,7 @@ resource will be registered in this registry.
|
||||
<br>
|
||||
Individual paths such as:
|
||||
<pre>
|
||||
/WEB-INF/flows/orderitem-flow.xml
|
||||
/WEB-INF/booking.xml
|
||||
</pre>
|
||||
... are supported as well as wildcard paths such as:
|
||||
<pre>
|
||||
@@ -70,55 +58,43 @@ Individual paths such as:
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="class" type="classType" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
Specifies a class containing a flow definition. The flow definition built from this
|
||||
class will be registered in this registry.
|
||||
<br>
|
||||
Classes must be declared using a fully qualified path name such as
|
||||
<pre>
|
||||
com.interface21.SellItemFlow
|
||||
</pre>
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="crud" type="crudType" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
Specifies a domain entity class to have crud flows built for. This will register flows for Creating, Reading, Updating,
|
||||
and Deleting the domain entity in a namespace that is equal to the short name of the class.
|
||||
<br>
|
||||
Entities must be declared using a fully qualified path name such as
|
||||
<pre>
|
||||
com.interface21.Account
|
||||
</pre>
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="namespace" type="namespaceType" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
Specifies a namespace to register nested flow definitions in. This namespace is a course-grained grouping for uniquely
|
||||
identifying a flow. Since some flows define a namespace implicitly (CRUD flows for example), not all flow types are
|
||||
allowed to be nested in the namespace tag.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
</xsd:extension>
|
||||
</xsd:element>
|
||||
<xsd:element name="flow-builder" type="flowBuilderType" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
Specifies a flow builder implementation to register in the registry.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="flow-builder-services" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The idref of the flow builder services instance this flow definition registry should use.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:complexType name="locationType">
|
||||
<xsd:complexType name="flowLocationType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="flow-definition-attributes" type="flowDefinitionAttributesType" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
Attributes to assign to the flow definition that may influence flow execution.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="id" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
@@ -140,82 +116,42 @@ that matches multiple resources.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="classType">
|
||||
<xsd:attribute name="name" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The name of the java based flow definition.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="crudType">
|
||||
<xsd:attribute name="entity" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The name of a domain entity class to have crud flows created for.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="namespaceType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="location" type="locationType" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
Specifies a path to a externalized flow definition resource. The flow definition built from this
|
||||
resource will be registered in this registry.
|
||||
<br>
|
||||
Individual paths such as:
|
||||
<pre>
|
||||
/WEB-INF/flows/orderitem-flow.xml
|
||||
</pre>
|
||||
... are supported as well as wildcard paths such as:
|
||||
<pre>
|
||||
/WEB-INF/flows/**/*-flow.xml
|
||||
</pre>
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="class" type="classType" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
Specifies a class containing a flow definition. The flow definition built from this
|
||||
class will be registered in this registry.
|
||||
<br>
|
||||
Classes must be declared using a fully qualified path name such as
|
||||
<pre>
|
||||
com.interface21.SellItemFlow
|
||||
</pre>
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The name of the namespace to group all child flow definitions in.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:element name="executor">
|
||||
|
||||
<xsd:complexType name="flowBuilderType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="flow-definition-attributes" type="flowDefinitionAttributesType" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
Attributes to assign to the flow definition that may influence flow execution.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="id" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The id to the externalized flow definition built by this builder.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="class" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The fully qualified class name of the flow builder implementation.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:element name="flow-executor">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
@@ -230,7 +166,7 @@ an execution repository.
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="beans:identifiedType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="repository" type="repositoryType" minOccurs="0">
|
||||
<xsd:element name="flow-execution-repository" type="flowExecutionRepositoryType" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
@@ -240,7 +176,7 @@ to be more fine grained than the repositoryType attribute on executor.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="execution-attributes" type="execution-attributesType" minOccurs="0">
|
||||
<xsd:element name="flow-execution-attributes" type="flowExecutionAttributesType" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
@@ -250,7 +186,7 @@ These attributes may influence execution behavior.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="execution-listeners" type="execution-listenersType" minOccurs="0">
|
||||
<xsd:element name="flow-execution-listeners" type="flowExecutionListenersType" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
@@ -260,17 +196,7 @@ The listeners eligible for observing the lifecycle of executions launched by thi
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="repository-type" type="repositoryTypeAttribute">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The type of execution repository to use. The repository is responsible for managing flow execution
|
||||
persistence between requests.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="registry-ref" type="xsd:string" use="required">
|
||||
<xsd:attribute name="flow-registry" type="xsd:string" default="flowRegistry">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
@@ -283,54 +209,9 @@ The idref to the registry this executor will use to locate flow definitions for
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:simpleType name="repositoryTypeAttribute">
|
||||
<xsd:restriction base="xsd:string">
|
||||
<xsd:enumeration value="continuation">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The continuation repository type. Use this to snapshot flow execution state server-side to support use
|
||||
of the browser back button.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:enumeration>
|
||||
<xsd:enumeration value="simple">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The simple repository type. Use of this strategy incurs minimal storage overhead but explicitly prevents
|
||||
resubmits using the browser back button.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:enumeration>
|
||||
<xsd:enumeration value="client">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The client continuation repository type. Use this to snapshot flow execution state client-side to support use of the browser
|
||||
back button in a stateless server environment.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:enumeration>
|
||||
<xsd:enumeration value="singlekey">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The "single key" repository type. Use of this strategy assigns a single key per flow execution that remains
|
||||
constant throughout the life of the execution.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:enumeration>
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
|
||||
<xsd:complexType name="repositoryType">
|
||||
<xsd:attribute name="type" type="repositoryTypeAttribute" use="required">
|
||||
|
||||
<xsd:complexType name="flowExecutionRepositoryType">
|
||||
<xsd:attribute name="type" type="flowExecutionRepositoryTypeAttribute" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
@@ -345,7 +226,7 @@ persistence between requests.
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The maximum number of concurrent conversations allowed by this repository. It is illegal to set this
|
||||
attribute if you are also setting the 'conversation-manager-ref' attribute.
|
||||
attribute if you are also setting the 'conversation-manager' attribute.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
@@ -360,7 +241,7 @@ This attribute is only relevant when the repository type is 'continuation'.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="conversation-manager-ref" type="xsd:string">
|
||||
<xsd:attribute name="conversation-manager" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
@@ -372,10 +253,55 @@ When used, any value for the 'max-conversations' attribute is ignored.
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="execution-listenersType">
|
||||
|
||||
<xsd:simpleType name="flowExecutionRepositoryTypeAttribute">
|
||||
<xsd:restriction base="xsd:string">
|
||||
<xsd:enumeration value="continuation">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The continuation repository type. Use this to snapshot flow execution state server-side to support use
|
||||
of the browser back button.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:enumeration>
|
||||
<xsd:enumeration value="simple">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The simple repository type. Use of this strategy incurs minimal storage overhead but explicitly prevents
|
||||
resubmits using the browser back button.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:enumeration>
|
||||
<xsd:enumeration value="client">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The client continuation repository type. Use this to snapshot flow execution state client-side to support use of the browser
|
||||
back button in a stateless server environment.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:enumeration>
|
||||
<xsd:enumeration value="singlekey">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The "single key" repository type. Use of this strategy assigns a single key per flow execution that remains
|
||||
constant throughout the life of the execution.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:enumeration>
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
|
||||
<xsd:complexType name="flowExecutionListenersType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="listener" type="listenerType" maxOccurs="unbounded">
|
||||
<xsd:element name="listener" type="flowExecutionListenerType" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
@@ -388,7 +314,7 @@ flow definitions. The flow definitions this listener applies to may be restrict
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="listenerType">
|
||||
<xsd:complexType name="flowExecutionListenerType">
|
||||
<xsd:attribute name="ref" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
@@ -410,7 +336,7 @@ Example: 'flow1,flow2,flow3'.
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="execution-attributesType">
|
||||
<xsd:complexType name="flowExecutionAttributesType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="alwaysRedirectOnPause" type="alwaysRedirectOnPauseType" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
@@ -447,7 +373,22 @@ true = always redirect on pause; false = do not, only redirect when explicitly i
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
|
||||
<xsd:complexType name="flowDefinitionAttributesType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="attribute" type="attributeType" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
A single attribute describing an element. Attributes have string keys and object values.
|
||||
An attribute's type may be specified using the 'type' attribute.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="attributeType">
|
||||
<xsd:attribute name="name" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
package org.springframework.webflow.context;
|
||||
|
||||
import org.springframework.webflow.core.collection.ParameterMap;
|
||||
|
||||
/**
|
||||
* An abstract representation request to be sent into the Spring Web Flow system at a later date. Used to request
|
||||
* operations such as flow definition redirects, flow execution redirects, as well as to generate flow definition and
|
||||
* flow execution URLs.
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public abstract class AbstractFlowRequestInfo {
|
||||
|
||||
/**
|
||||
* The flow definition the request should be sent to.
|
||||
*/
|
||||
private String flowDefinitionId;
|
||||
|
||||
/**
|
||||
* Hierarchical data to be sent along in the request.
|
||||
*/
|
||||
private RequestPath requestPath;
|
||||
|
||||
/**
|
||||
* Query parameters to be sent along in the request.
|
||||
*/
|
||||
private ParameterMap requestParameters;
|
||||
|
||||
/**
|
||||
* An anchor fragment to be sent in the request, for interpretation by the agent that initiates the request.
|
||||
*/
|
||||
private String fragment;
|
||||
|
||||
protected AbstractFlowRequestInfo(String flowDefinitionId, RequestPath requestPath, ParameterMap requestParameters,
|
||||
String fragment) {
|
||||
this.flowDefinitionId = flowDefinitionId;
|
||||
this.requestPath = requestPath;
|
||||
this.requestParameters = requestParameters;
|
||||
this.fragment = fragment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the flow definition this request should be sent to.
|
||||
* @return the flow definition identifier
|
||||
*/
|
||||
public String getFlowDefinitionId() {
|
||||
return flowDefinitionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns hierarchical data to be sent along in the request.
|
||||
* @return the request path
|
||||
*/
|
||||
public RequestPath getRequestPath() {
|
||||
return requestPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns query parameters to send along in the request.
|
||||
* @return the request parameters
|
||||
*/
|
||||
public ParameterMap getRequestParameters() {
|
||||
return requestParameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fragment to be sent along in the request. A fragment is a free-form string value that may be
|
||||
* interpreted by the client agent that initiates the request.
|
||||
* @return the fragment
|
||||
*/
|
||||
public String getFragment() {
|
||||
return fragment;
|
||||
}
|
||||
}
|
||||
@@ -15,12 +15,15 @@
|
||||
*/
|
||||
package org.springframework.webflow.context;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
import org.springframework.webflow.core.FlowException;
|
||||
import org.springframework.webflow.core.collection.MutableAttributeMap;
|
||||
import org.springframework.webflow.core.collection.ParameterMap;
|
||||
import org.springframework.webflow.core.collection.SharedAttributeMap;
|
||||
|
||||
/**
|
||||
* A facade that provides normalized access to an external system that has interacted with Spring Web Flow.
|
||||
* A facade that provides normalized access to an external system that has called into the Spring Web Flow system.
|
||||
* <p>
|
||||
* This context object provides a normalized interface for internal web flow artifacts to use to reason on and
|
||||
* manipulate the state of an external actor calling into SWF to execute flows. It represents the context about a
|
||||
@@ -36,22 +39,31 @@ import org.springframework.webflow.core.collection.SharedAttributeMap;
|
||||
public interface ExternalContext {
|
||||
|
||||
/**
|
||||
* Returns the path (or identifier) of the application that is executing.
|
||||
* @return the application context path (e.g. "/myapp")
|
||||
* Returns the unique id of the flow definition invoked by this caller. For a "launch" request, this identifier is
|
||||
* used directly to create a new flow execution. For a "resume" request, this identifier provides additional flow
|
||||
* execution context.
|
||||
* @return the flow definition identifier, never null
|
||||
* @see #getFlowExecutionKey()
|
||||
*/
|
||||
public String getContextPath();
|
||||
public String getFlowId();
|
||||
|
||||
/**
|
||||
* Returns the path (or identifier) of the dispatcher <i>within</i> the application that dispatched this request.
|
||||
* @return the dispatcher path (e.g. "/dispatcher")
|
||||
* Returns the unique key identifying the flow execution this client wishes to resume.
|
||||
* @return the flow execution key, may be null if this is not a resume request
|
||||
*/
|
||||
public String getDispatcherPath();
|
||||
public String getFlowExecutionKey();
|
||||
|
||||
/**
|
||||
* Returns the path info of this external request. Could be null.
|
||||
* @return the request path info (e.g. "/flows.htm")
|
||||
* Returns the type of this client request. For example, this request may be a "GET" request or a "POST" request.
|
||||
* @return the request method
|
||||
*/
|
||||
public String getRequestPathInfo();
|
||||
public String getRequestMethod();
|
||||
|
||||
/**
|
||||
* Returns the path of this request as a ordered list of fields.
|
||||
* @return the elements of the request path
|
||||
*/
|
||||
public RequestPath getRequestPath();
|
||||
|
||||
/**
|
||||
* Provides access to the parameters associated with the user request that led to SWF being called. This map is
|
||||
@@ -92,4 +104,105 @@ public interface ExternalContext {
|
||||
*/
|
||||
public SharedAttributeMap getApplicationMap();
|
||||
|
||||
/**
|
||||
* Provides access to the context object for the current environment.
|
||||
* @return the environment specific context object
|
||||
*/
|
||||
public Object getContext();
|
||||
|
||||
/**
|
||||
* Provides access to the request object for the current environment.
|
||||
* @return the environment specific request object.
|
||||
*/
|
||||
public Object getRequest();
|
||||
|
||||
/**
|
||||
* Provides access to the response object for the current environment.
|
||||
* @return the environment specific response object.
|
||||
*/
|
||||
public Object getResponse();
|
||||
|
||||
/**
|
||||
* Get a writer for writing out a response.
|
||||
* @return the writer
|
||||
*/
|
||||
public PrintWriter getResponseWriter();
|
||||
|
||||
/**
|
||||
* Builds a context-relative flow definition URL, suitable for rendering links that a launch new execution of a flow
|
||||
* definition when accessed.
|
||||
* @param requestInfo data needed to build the flow definition path
|
||||
* @return the generated flow definition URL
|
||||
*/
|
||||
public String buildFlowDefinitionUrl(FlowDefinitionRequestInfo requestInfo);
|
||||
|
||||
/**
|
||||
* Builds a flow execution URL, suitable for rendering links that resume a paused flow execution when accessed.
|
||||
* @param requestInfo data needed to build the flow execution URL
|
||||
* @param contextRelative whether the URL returned should be relative to this external context or absolute.
|
||||
* @return the generated flow execution URL
|
||||
*/
|
||||
public String buildFlowExecutionUrl(FlowExecutionRequestInfo requestInfo, boolean contextRelative);
|
||||
|
||||
/**
|
||||
* Encode the provided string using the encoding scheme of this external context.
|
||||
* @param string the string
|
||||
* @return the encoded string
|
||||
*/
|
||||
public String encode(String string);
|
||||
|
||||
/**
|
||||
* Request that a flow execution redirect be sent as the response. A flow execution redirect tells the caller to
|
||||
* resume a flow execution in a new request. Sets response committed to true.
|
||||
* @param requestInfo data needed to issue the flow execution redirect
|
||||
* @see #isResponseCommitted()
|
||||
*/
|
||||
public void sendFlowExecutionRedirect(FlowExecutionRequestInfo requestInfo);
|
||||
|
||||
/**
|
||||
* Request that a flow definition redirect be sent as the response. A flow definition redirect tells the caller to
|
||||
* start a new execution of the flow definition with the input provided.
|
||||
* @param requestInfo data needed to issue the flow definition redirect
|
||||
*/
|
||||
public void sendFlowDefinitionRedirect(FlowDefinitionRequestInfo requestInfo);
|
||||
|
||||
/**
|
||||
* Request that a external redirect be sent as the response. An external redirect tells the caller to access the
|
||||
* resource at the given resource URL. Sets response committed to true. Note: no special encoding is performed on
|
||||
* the string argument. Callers must perform their own encoding when necessary.
|
||||
* @param resourceUrl the resource URL string
|
||||
* @see #isResponseCommitted()
|
||||
*/
|
||||
public void sendExternalRedirect(String resourceUrl);
|
||||
|
||||
/**
|
||||
* Report that flow execution request processing ended with a "paused" result, indicating the flow execution paused
|
||||
* and will be waiting to resume on a subsequent request.
|
||||
* @param flowExecutionKey the flow execution key
|
||||
*/
|
||||
public void setPausedResult(String flowExecutionKey);
|
||||
|
||||
/**
|
||||
* Report that flow execution request processing ended with a "ended" result, indicating the flow execution
|
||||
* terminated.
|
||||
* @param flowExecutionKey the flow execution key, now invalid or null if never assigned
|
||||
*/
|
||||
public void setEndedResult(String flowExecutionKey);
|
||||
|
||||
/**
|
||||
* Report that flow execution request processing ended with a flow exception.
|
||||
* @param e the flow exception
|
||||
*/
|
||||
public void setExceptionResult(FlowException e);
|
||||
|
||||
/**
|
||||
* Returns true if the current request has already provisioned the response that will be sent back to the calling
|
||||
* system.
|
||||
* @return true if the response has been committed, false otherwise
|
||||
* @see #sendFlowExecutionRedirect(FlowExecutionRequestInfo)
|
||||
* @see #sendFlowDefinitionRedirect(FlowDefinitionRequestInfo)
|
||||
* @see #sendExternalRedirect(String)
|
||||
*/
|
||||
public boolean isResponseCommitted();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.springframework.webflow.context;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.webflow.core.collection.ParameterMap;
|
||||
|
||||
/**
|
||||
* A request to launch a new execution of a flow definition.
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public class FlowDefinitionRequestInfo extends AbstractFlowRequestInfo {
|
||||
|
||||
/**
|
||||
* Creates a new flow definition request.
|
||||
* @param flowDefinitionId the flow definition id (required)
|
||||
* @param requestPath the request path (optional)
|
||||
* @param requestParameters the request parameters (optional)
|
||||
* @param fragment the fragment (optional)
|
||||
*/
|
||||
public FlowDefinitionRequestInfo(String flowDefinitionId, RequestPath requestPath, ParameterMap requestParameters,
|
||||
String fragment) {
|
||||
super(flowDefinitionId, requestPath, requestParameters, fragment);
|
||||
Assert.hasText(flowDefinitionId, "The id of the flow definition to redirect to is required");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package org.springframework.webflow.context;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.webflow.core.collection.ParameterMap;
|
||||
|
||||
/**
|
||||
* A request to resume an existing flow execution that is currently paused.
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public class FlowExecutionRequestInfo extends AbstractFlowRequestInfo {
|
||||
|
||||
/**
|
||||
* The key of the flow execution to resume.
|
||||
*/
|
||||
private String flowExecutionKey;
|
||||
|
||||
/**
|
||||
* Creates a new request to resume a flow execution.
|
||||
* @param flowDefinitionId the definition identifier of the execution (required)
|
||||
* @param flowExecutionKey the key of the flow execution (required)
|
||||
*/
|
||||
public FlowExecutionRequestInfo(String flowDefinitionId, String flowExecutionKey) {
|
||||
this(flowDefinitionId, flowExecutionKey, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new request to resume a flow execution.
|
||||
* @param flowDefinitionId the definition identifier of the execution (required)
|
||||
* @param flowExecutionKey the key of the flow execution (required)
|
||||
* @param requestPath the request path (optional)
|
||||
* @param requestParameters the request parameters (optional)
|
||||
* @param fragment the fragment (optional)
|
||||
*/
|
||||
public FlowExecutionRequestInfo(String flowDefinitionId, String flowExecutionKey, RequestPath requestPath,
|
||||
ParameterMap requestParameters, String fragment) {
|
||||
super(flowDefinitionId, requestPath, requestParameters, fragment);
|
||||
Assert.hasText(flowDefinitionId,
|
||||
"The definition identifier of the flow execution to redirect to cannot be null");
|
||||
Assert.hasText(flowExecutionKey, "The flow execution key to redirect to cannot be null");
|
||||
this.flowExecutionKey = flowExecutionKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unique key of the flow execution to resume.
|
||||
* @return the flow execution key
|
||||
*/
|
||||
public String getFlowExecutionKey() {
|
||||
return flowExecutionKey;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package org.springframework.webflow.context;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A representation of a flow request path. In a flow definition request URI,the request path is the portion after the
|
||||
* flow definition identifier. For example, in a URI of http://host/booking-flow/1 the request path is "/1".
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public class RequestPath {
|
||||
|
||||
private String[] elements;
|
||||
|
||||
public RequestPath(String path) {
|
||||
Assert.notNull(path, "The path string cannot be null");
|
||||
Assert.isTrue(path.startsWith("/"), "The path must start with a '/'");
|
||||
this.elements = path.substring(1).split("/");
|
||||
}
|
||||
|
||||
private RequestPath(String[] elements) {
|
||||
this.elements = elements;
|
||||
}
|
||||
|
||||
public RequestPath pop(int elementCount) {
|
||||
if (elementCount < elements.length) {
|
||||
String[] newElements = new String[elements.length - elementCount];
|
||||
System.arraycopy(elements, elementCount, newElements, 0, newElements.length);
|
||||
return new RequestPath(newElements);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public RequestPath pop() {
|
||||
return pop(1);
|
||||
}
|
||||
|
||||
public int getElementCount() {
|
||||
return elements.length;
|
||||
}
|
||||
|
||||
public String getFirstElement() {
|
||||
return getElement(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the elements of the request path.
|
||||
* @return parsed request path elements
|
||||
*/
|
||||
public String[] getElements() {
|
||||
return elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the path element at the specified index.
|
||||
* @param index the element index
|
||||
* @return the element
|
||||
* @throws IndexOutOfBoundsException if the index is out of bounds
|
||||
*/
|
||||
public String getElement(int index) throws IndexOutOfBoundsException {
|
||||
return elements[index];
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof RequestPath)) {
|
||||
return false;
|
||||
}
|
||||
RequestPath other = (RequestPath) o;
|
||||
return Arrays.equals(this.elements, other.elements);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(elements);
|
||||
}
|
||||
|
||||
public static RequestPath valueOf(String[] elements) {
|
||||
return new RequestPath(elements);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
buffer.append("/");
|
||||
for (int i = 0; i < elements.length; i++) {
|
||||
buffer.append(elements[i]);
|
||||
if (i < (elements.length - 1)) {
|
||||
buffer.append("/");
|
||||
}
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.webflow.context.portlet;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.portlet.PortletContext;
|
||||
@@ -24,6 +25,10 @@ import javax.portlet.PortletSession;
|
||||
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.webflow.context.ExternalContext;
|
||||
import org.springframework.webflow.context.FlowDefinitionRequestInfo;
|
||||
import org.springframework.webflow.context.FlowExecutionRequestInfo;
|
||||
import org.springframework.webflow.context.RequestPath;
|
||||
import org.springframework.webflow.core.FlowException;
|
||||
import org.springframework.webflow.core.collection.LocalAttributeMap;
|
||||
import org.springframework.webflow.core.collection.LocalParameterMap;
|
||||
import org.springframework.webflow.core.collection.LocalSharedAttributeMap;
|
||||
@@ -103,20 +108,6 @@ public class PortletExternalContext implements ExternalContext {
|
||||
this.userInfoMap = userInfo != null ? new LocalAttributeMap(userInfo) : null;
|
||||
}
|
||||
|
||||
public String getContextPath() {
|
||||
return request.getContextPath();
|
||||
}
|
||||
|
||||
public String getDispatcherPath() {
|
||||
// returns null in a portlet environment
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getRequestPathInfo() {
|
||||
// returns null in a portlet environment
|
||||
return null;
|
||||
}
|
||||
|
||||
public ParameterMap getRequestParameterMap() {
|
||||
return requestParameterMap;
|
||||
}
|
||||
@@ -148,25 +139,101 @@ public class PortletExternalContext implements ExternalContext {
|
||||
/**
|
||||
* Returns the wrapped Portlet context.
|
||||
*/
|
||||
public PortletContext getContext() {
|
||||
public Object getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the wrapped Portlet request.
|
||||
*/
|
||||
public PortletRequest getRequest() {
|
||||
public Object getRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the wrapped Portlet response.
|
||||
*/
|
||||
public PortletResponse getResponse() {
|
||||
public Object getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
public String getFlowId() {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Auto-generated method stub");
|
||||
}
|
||||
|
||||
public String getFlowExecutionKey() {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Auto-generated method stub");
|
||||
}
|
||||
|
||||
public RequestPath getRequestPath() {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Auto-generated method stub");
|
||||
}
|
||||
|
||||
public String getRequestMethod() {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Auto-generated method stub");
|
||||
}
|
||||
|
||||
public PrintWriter getResponseWriter() {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Auto-generated method stub");
|
||||
}
|
||||
|
||||
public String buildFlowDefinitionUrl(FlowDefinitionRequestInfo urlInfo) {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Auto-generated method stub");
|
||||
}
|
||||
|
||||
public String buildFlowExecutionUrl(FlowExecutionRequestInfo urlInfo, boolean contextRelative) {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Auto-generated method stub");
|
||||
}
|
||||
|
||||
public String encode(String string) {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Auto-generated method stub");
|
||||
}
|
||||
|
||||
public void sendFlowDefinitionRedirect(FlowDefinitionRequestInfo urlInfo) {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Auto-generated method stub");
|
||||
}
|
||||
|
||||
public void sendFlowExecutionRedirect(FlowExecutionRequestInfo urlInfo) {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Auto-generated method stub");
|
||||
}
|
||||
|
||||
public void sendExternalRedirect(String resourceUrl) {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Auto-generated method stub");
|
||||
}
|
||||
|
||||
public void setPausedResult(String flowExecutionKey) {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Auto-generated method stub");
|
||||
}
|
||||
|
||||
public void setEndedResult(String flowExecutionKey) {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Auto-generated method stub");
|
||||
}
|
||||
|
||||
public void setExceptionResult(FlowException e) {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Auto-generated method stub");
|
||||
}
|
||||
|
||||
public boolean isResponseCommitted() {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Auto-generated method stub");
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("requestParameterMap", getRequestParameterMap()).toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,18 +15,34 @@
|
||||
*/
|
||||
package org.springframework.webflow.context.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.webflow.context.AbstractFlowRequestInfo;
|
||||
import org.springframework.webflow.context.ExternalContext;
|
||||
import org.springframework.webflow.context.ExternalContextHolder;
|
||||
import org.springframework.webflow.context.FlowDefinitionRequestInfo;
|
||||
import org.springframework.webflow.context.FlowExecutionRequestInfo;
|
||||
import org.springframework.webflow.context.RequestPath;
|
||||
import org.springframework.webflow.core.FlowException;
|
||||
import org.springframework.webflow.core.collection.LocalAttributeMap;
|
||||
import org.springframework.webflow.core.collection.LocalParameterMap;
|
||||
import org.springframework.webflow.core.collection.LocalSharedAttributeMap;
|
||||
import org.springframework.webflow.core.collection.MutableAttributeMap;
|
||||
import org.springframework.webflow.core.collection.ParameterMap;
|
||||
import org.springframework.webflow.core.collection.SharedAttributeMap;
|
||||
import org.springframework.webflow.executor.FlowExecutor;
|
||||
|
||||
/**
|
||||
* Provides contextual information about an HTTP Servlet environment that has interacted with Spring Web Flow.
|
||||
@@ -36,6 +52,9 @@ import org.springframework.webflow.core.collection.SharedAttributeMap;
|
||||
*/
|
||||
public class ServletExternalContext implements ExternalContext {
|
||||
|
||||
/** The default encoding scheme: UTF-8 */
|
||||
private static final String DEFAULT_ENCODING_SCHEME = "UTF-8";
|
||||
|
||||
/**
|
||||
* The context.
|
||||
*/
|
||||
@@ -71,32 +90,61 @@ public class ServletExternalContext implements ExternalContext {
|
||||
*/
|
||||
private SharedAttributeMap applicationMap;
|
||||
|
||||
private String flowId;
|
||||
|
||||
private String flowExecutionKey;
|
||||
|
||||
private RequestPath requestPath;
|
||||
|
||||
private String encodingScheme = DEFAULT_ENCODING_SCHEME;
|
||||
|
||||
private FlowExecutionRedirector flowExecutionRedirector;
|
||||
|
||||
private FlowDefinitionRedirector flowDefinitionRedirector;
|
||||
|
||||
private String resourceUri;
|
||||
|
||||
private short result;
|
||||
|
||||
private String processedFlowExecutionKey;
|
||||
|
||||
private FlowException exception;
|
||||
|
||||
/**
|
||||
* Create a new external context wrapping given servlet HTTP request and response and given servlet context.
|
||||
* @param context the servlet context
|
||||
* @param request the HTTP request
|
||||
* @param response the HTTP response
|
||||
* @param request the servlet request
|
||||
* @param response the servlet response
|
||||
*/
|
||||
public ServletExternalContext(ServletContext context, HttpServletRequest request, HttpServletResponse response) {
|
||||
public ServletExternalContext(ServletContext context, ServletRequest request, ServletResponse response) {
|
||||
this.context = context;
|
||||
this.request = request;
|
||||
this.response = response;
|
||||
this.requestParameterMap = new LocalParameterMap(new HttpServletRequestParameterMap(request));
|
||||
this.requestMap = new LocalAttributeMap(new HttpServletRequestMap(request));
|
||||
this.sessionMap = new LocalSharedAttributeMap(new HttpSessionMap(request));
|
||||
try {
|
||||
this.request = (HttpServletRequest) request;
|
||||
this.response = (HttpServletResponse) response;
|
||||
} catch (ClassCastException e) {
|
||||
throw new IllegalArgumentException("Request and response objects must be HTTP objects", e);
|
||||
}
|
||||
this.requestParameterMap = new LocalParameterMap(new HttpServletRequestParameterMap(this.request));
|
||||
this.requestMap = new LocalAttributeMap(new HttpServletRequestMap(this.request));
|
||||
this.sessionMap = new LocalSharedAttributeMap(new HttpSessionMap(this.request));
|
||||
this.applicationMap = new LocalSharedAttributeMap(new HttpServletContextMap(context));
|
||||
parseRequestPathInfo();
|
||||
}
|
||||
|
||||
public String getContextPath() {
|
||||
return request.getContextPath();
|
||||
public String getFlowId() {
|
||||
return flowId;
|
||||
}
|
||||
|
||||
public String getDispatcherPath() {
|
||||
return request.getServletPath();
|
||||
public String getFlowExecutionKey() {
|
||||
return flowExecutionKey;
|
||||
}
|
||||
|
||||
public String getRequestPathInfo() {
|
||||
return request.getPathInfo();
|
||||
public String getRequestMethod() {
|
||||
return request.getMethod();
|
||||
}
|
||||
|
||||
public RequestPath getRequestPath() {
|
||||
return requestPath;
|
||||
}
|
||||
|
||||
public ParameterMap getRequestParameterMap() {
|
||||
@@ -119,28 +167,268 @@ public class ServletExternalContext implements ExternalContext {
|
||||
return applicationMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the wrapped HTTP servlet context.
|
||||
*/
|
||||
public ServletContext getContext() {
|
||||
public Object getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the wrapped HTTP servlet request.
|
||||
*/
|
||||
public HttpServletRequest getRequest() {
|
||||
public Object getRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the wrapped HTTP servlet response.
|
||||
*/
|
||||
public HttpServletResponse getResponse() {
|
||||
public Object getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
public boolean isResponseCommitted() {
|
||||
return flowExecutionRedirector != null || flowDefinitionRedirector != null || resourceUri != null;
|
||||
}
|
||||
|
||||
// response requesters
|
||||
|
||||
public PrintWriter getResponseWriter() {
|
||||
try {
|
||||
return response.getWriter();
|
||||
} catch (IOException e) {
|
||||
// TODO - handle how?
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendFlowExecutionRedirect(FlowExecutionRequestInfo request) {
|
||||
flowExecutionRedirector = new FlowExecutionRedirector(request);
|
||||
}
|
||||
|
||||
public void sendFlowDefinitionRedirect(FlowDefinitionRequestInfo request) {
|
||||
flowDefinitionRedirector = new FlowDefinitionRedirector(request);
|
||||
}
|
||||
|
||||
public void sendExternalRedirect(String resourceUri) {
|
||||
this.resourceUri = resourceUri;
|
||||
}
|
||||
|
||||
// helpers
|
||||
|
||||
public String encode(String string) {
|
||||
try {
|
||||
return URLEncoder.encode(string, encodingScheme);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new IllegalStateException("Unsupported encoding errors should never happen", e);
|
||||
}
|
||||
}
|
||||
|
||||
public String buildFlowDefinitionUrl(FlowDefinitionRequestInfo requestInfo) {
|
||||
return request.getContextPath() + request.getServletPath() + "/" + requestInfo.getFlowDefinitionId()
|
||||
+ requestPath(requestInfo) + requestParameters(requestInfo) + fragment(requestInfo);
|
||||
}
|
||||
|
||||
public String buildFlowExecutionUrl(FlowExecutionRequestInfo requestInfo, boolean contextRelative) {
|
||||
String contextRelativeUrl = request.getContextPath() + request.getServletPath() + "/executions/"
|
||||
+ requestInfo.getFlowDefinitionId() + "/" + requestInfo.getFlowExecutionKey()
|
||||
+ requestPath(requestInfo) + requestParameters(requestInfo) + fragment(requestInfo);
|
||||
if (contextRelative) {
|
||||
return contextRelativeUrl;
|
||||
} else {
|
||||
return request.getScheme() + request.getServerName() + contextRelativeUrl;
|
||||
}
|
||||
}
|
||||
|
||||
// execution processing result setters
|
||||
|
||||
public void setPausedResult(String flowExecutionKey) {
|
||||
result = 0;
|
||||
processedFlowExecutionKey = flowExecutionKey;
|
||||
}
|
||||
|
||||
public void setEndedResult(String flowExecutionKey) {
|
||||
result = 1;
|
||||
processedFlowExecutionKey = flowExecutionKey;
|
||||
}
|
||||
|
||||
public void setExceptionResult(FlowException e) {
|
||||
result = 2;
|
||||
exception = e;
|
||||
processedFlowExecutionKey = flowExecutionKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the flow request lifecycle, including provision of the final response.
|
||||
* @param flowExecutor the flow executor for calling into the Spring Web Flow system
|
||||
* @throws IOException an IOException occurred issuing the response
|
||||
*/
|
||||
public void executeFlowRequest(FlowExecutor flowExecutor) throws IOException {
|
||||
ExternalContextHolder.setExternalContext(this);
|
||||
try {
|
||||
flowExecutor.execute(this);
|
||||
if (isPausedResult()) {
|
||||
if (flowExecutionRedirector != null) {
|
||||
flowExecutionRedirector.issueRedirect();
|
||||
} else if (flowDefinitionRedirector != null) {
|
||||
flowDefinitionRedirector.issueRedirect();
|
||||
} else if (resourceUri != null) {
|
||||
sendRedirect(resourceUri);
|
||||
} else {
|
||||
// commit response?
|
||||
}
|
||||
} else if (isEndResult()) {
|
||||
if (flowExecutionRedirector != null) {
|
||||
if (flowExecutionRedirector.redirectsTo(processedFlowExecutionKey)) {
|
||||
throw new IllegalStateException(
|
||||
"You cannot send a flow execution redirect when this execution has ended - programmer error");
|
||||
} else {
|
||||
flowExecutionRedirector.issueRedirect();
|
||||
}
|
||||
} else if (flowDefinitionRedirector != null) {
|
||||
flowDefinitionRedirector.issueRedirect();
|
||||
} else if (resourceUri != null) {
|
||||
sendRedirect(resourceUri);
|
||||
} else {
|
||||
// commit response?
|
||||
}
|
||||
} else if (isExceptionResult()) {
|
||||
// response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, exception.getMessage());
|
||||
throw exception;
|
||||
}
|
||||
} finally {
|
||||
ExternalContextHolder.setExternalContext(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void parseRequestPathInfo() {
|
||||
String pathInfo = request.getPathInfo();
|
||||
if (pathInfo == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"The requestPathInfo is null: unable to extract flow definition id or flow execution key");
|
||||
}
|
||||
RequestPath path = new RequestPath(pathInfo);
|
||||
if (path.getElement(0).equals("executions")) {
|
||||
flowId = path.getElement(1);
|
||||
flowExecutionKey = path.getElement(2);
|
||||
if (path.getElementCount() > 3) {
|
||||
requestPath = path.pop(3);
|
||||
} else {
|
||||
requestPath = null;
|
||||
}
|
||||
} else {
|
||||
flowId = path.getElement(0);
|
||||
if (path.getElementCount() > 1) {
|
||||
requestPath = path.pop(1);
|
||||
} else {
|
||||
requestPath = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isPausedResult() {
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
private boolean isEndResult() {
|
||||
return result == 1;
|
||||
}
|
||||
|
||||
private boolean isExceptionResult() {
|
||||
return result == 2;
|
||||
}
|
||||
|
||||
private String requestPath(AbstractFlowRequestInfo requestInfo) {
|
||||
if (requestInfo.getRequestPath() == null) {
|
||||
return "";
|
||||
}
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
String[] requestElements = requestInfo.getRequestPath().getElements();
|
||||
for (int i = 0; i < requestElements.length; i++) {
|
||||
buffer.append('/').append(encode(requestElements[i], encodingScheme));
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
private String requestParameters(AbstractFlowRequestInfo requestInfo) {
|
||||
if (requestInfo.getRequestParameters() == null) {
|
||||
return "";
|
||||
}
|
||||
StringBuffer queryString = new StringBuffer();
|
||||
queryString.append('?');
|
||||
ParameterMap requestParameters = requestInfo.getRequestParameters();
|
||||
Iterator it = requestParameters.asMap().entrySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
Map.Entry entry = (Map.Entry) it.next();
|
||||
String parameterName = encode((String) entry.getKey(), encodingScheme);
|
||||
String parameterValue = encode((String) entry.getValue(), encodingScheme);
|
||||
queryString.append(parameterName).append('=').append(parameterValue);
|
||||
if (it.hasNext()) {
|
||||
queryString.append('&');
|
||||
}
|
||||
}
|
||||
return queryString.toString();
|
||||
}
|
||||
|
||||
private String fragment(AbstractFlowRequestInfo requestInfo) {
|
||||
if (requestInfo.getFragment() == null || requestInfo.getFragment().length() == 0) {
|
||||
return "";
|
||||
}
|
||||
return "#" + requestInfo.getFragment();
|
||||
}
|
||||
|
||||
private String encode(String value, String encodingScheme) {
|
||||
try {
|
||||
return URLEncoder.encode(value, encodingScheme);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendRedirect(String targetUrl) throws IOException {
|
||||
response.sendRedirect(response.encodeRedirectURL(targetUrl));
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("requestParameterMap", getRequestParameterMap()).toString();
|
||||
}
|
||||
|
||||
private abstract class FlowRedirector {
|
||||
private AbstractFlowRequestInfo requestInfo;
|
||||
|
||||
public FlowRedirector(AbstractFlowRequestInfo requestInfo) {
|
||||
this.requestInfo = requestInfo;
|
||||
}
|
||||
|
||||
protected AbstractFlowRequestInfo getRequestInfo() {
|
||||
return requestInfo;
|
||||
}
|
||||
|
||||
public abstract void issueRedirect() throws IOException;
|
||||
}
|
||||
|
||||
private class FlowExecutionRedirector extends FlowRedirector {
|
||||
public FlowExecutionRedirector(FlowExecutionRequestInfo requestInfo) {
|
||||
super(requestInfo);
|
||||
}
|
||||
|
||||
public boolean redirectsTo(String flowExecutionKey) {
|
||||
FlowExecutionRequestInfo requestInfo = (FlowExecutionRequestInfo) getRequestInfo();
|
||||
return requestInfo.getFlowExecutionKey().equals(flowExecutionKey);
|
||||
}
|
||||
|
||||
public void issueRedirect() throws IOException {
|
||||
FlowExecutionRequestInfo requestInfo = (FlowExecutionRequestInfo) getRequestInfo();
|
||||
String targetUrl = request.getContextPath() + request.getServletPath() + "/executions/"
|
||||
+ requestInfo.getFlowDefinitionId() + "/" + requestInfo.getFlowExecutionKey()
|
||||
+ requestPath(getRequestInfo()) + requestParameters(getRequestInfo()) + fragment(getRequestInfo());
|
||||
response.sendRedirect(response.encodeRedirectURL(targetUrl));
|
||||
}
|
||||
}
|
||||
|
||||
private class FlowDefinitionRedirector extends FlowRedirector {
|
||||
public FlowDefinitionRedirector(FlowDefinitionRequestInfo redirect) {
|
||||
super(redirect);
|
||||
}
|
||||
|
||||
public void issueRedirect() throws IOException {
|
||||
FlowDefinitionRequestInfo redirect = (FlowDefinitionRequestInfo) getRequestInfo();
|
||||
String targetUrl = request.getContextPath() + request.getServletPath() + "/"
|
||||
+ redirect.getFlowDefinitionId() + requestPath(getRequestInfo())
|
||||
+ requestParameters(getRequestInfo()) + fragment(getRequestInfo());
|
||||
response.sendRedirect(response.encodeRedirectURL(targetUrl));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,12 +13,12 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.webflow.core;
|
||||
package org.springframework.webflow.core.expression;
|
||||
|
||||
import org.springframework.binding.expression.Expression;
|
||||
import org.springframework.binding.expression.ExpressionParser;
|
||||
import org.springframework.binding.expression.ExpressionVariable;
|
||||
import org.springframework.binding.expression.ParserException;
|
||||
import org.springframework.binding.expression.SettableExpression;
|
||||
|
||||
/**
|
||||
* Static helper factory that creates instances of the default expression parser used by Spring Web Flow when requested.
|
||||
@@ -47,20 +47,22 @@ public final class DefaultExpressionParserFactory {
|
||||
*/
|
||||
public static synchronized ExpressionParser getExpressionParser() {
|
||||
// return a wrapper that will lazily load the default expression parser
|
||||
// this prevents the default OGNL-based parser from being intialized until it is actually used
|
||||
// this prevents the default OGNL-based parser from being initialized until it is actually used
|
||||
// which allows OGNL to be an optional dependency if the expression parser wrapper is replaced and never used
|
||||
return new ExpressionParser() {
|
||||
public boolean isDelimitedExpression(String expressionString) {
|
||||
return getDefaultExpressionParser().isDelimitedExpression(expressionString);
|
||||
public boolean isEvalExpressionString(String string) {
|
||||
return getDefaultExpressionParser().isEvalExpressionString(string);
|
||||
}
|
||||
|
||||
public Expression parseExpression(String expressionString) throws ParserException {
|
||||
return getDefaultExpressionParser().parseExpression(expressionString);
|
||||
public String parseEvalExpressionString(String string) throws ParserException {
|
||||
return getDefaultExpressionParser().parseEvalExpressionString(string);
|
||||
}
|
||||
|
||||
public SettableExpression parseSettableExpression(String expressionString) throws ParserException,
|
||||
UnsupportedOperationException {
|
||||
return getDefaultExpressionParser().parseSettableExpression(expressionString);
|
||||
public Expression parseExpression(String expressionString, Class expressionTargetType,
|
||||
Class expectedEvaluationResultType, ExpressionVariable[] expressionVariables)
|
||||
throws ParserException {
|
||||
return getDefaultExpressionParser().parseExpression(expressionString, expressionTargetType,
|
||||
expectedEvaluationResultType, expressionVariables);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -80,7 +82,7 @@ public final class DefaultExpressionParserFactory {
|
||||
* Create the default expression parser.
|
||||
* @return the default expression parser
|
||||
*/
|
||||
private static ExpressionParser createDefaultExpressionParser() {
|
||||
private static ExpressionParser createDefaultExpressionParser() throws IllegalStateException {
|
||||
try {
|
||||
Class.forName("ognl.Ognl");
|
||||
return new WebFlowOgnlExpressionParser();
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.webflow.core;
|
||||
package org.springframework.webflow.core.expression;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package org.springframework.webflow.core.expression.el;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.el.ELContext;
|
||||
import javax.el.ELResolver;
|
||||
import javax.el.PropertyNotWritableException;
|
||||
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
import org.springframework.webflow.execution.RequestContextHolder;
|
||||
|
||||
/**
|
||||
* Custom EL resolver that resolves to a thread-bound RequestContext object for binding expressions prefixed with a
|
||||
* {@link #REQUEST_CONTEXT_VARIABLE_NAME}. For instance "#{requestContext.conversationScope.myProperty}".
|
||||
* @author Jeremy Grelle
|
||||
*/
|
||||
public class RequestContextELResolver extends ELResolver {
|
||||
|
||||
/**
|
||||
* Name of the request context variable.
|
||||
*/
|
||||
public static final String REQUEST_CONTEXT_VARIABLE_NAME = "requestContext";
|
||||
|
||||
public Class getCommonPropertyType(ELContext elContext, Object base) {
|
||||
return Object.class;
|
||||
}
|
||||
|
||||
public Iterator getFeatureDescriptors(ELContext elContext, Object base) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Class getType(ELContext elContext, Object base, Object property) {
|
||||
if (base == null && REQUEST_CONTEXT_VARIABLE_NAME.equals(property)) {
|
||||
elContext.setPropertyResolved(true);
|
||||
return RequestContext.class;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Object getValue(ELContext elContext, Object base, Object property) {
|
||||
if (base == null && REQUEST_CONTEXT_VARIABLE_NAME.equals(property)) {
|
||||
elContext.setPropertyResolved(true);
|
||||
return RequestContextHolder.getRequestContext();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isReadOnly(ELContext elContext, Object base, Object property) {
|
||||
if (base == null && REQUEST_CONTEXT_VARIABLE_NAME.equals(property)) {
|
||||
elContext.setPropertyResolved(true);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void setValue(ELContext elContext, Object base, Object property, Object value) {
|
||||
if (base == null && REQUEST_CONTEXT_VARIABLE_NAME.equals(property)) {
|
||||
elContext.setPropertyResolved(true);
|
||||
throw new PropertyNotWritableException("The RequestContext cannot be set with an expression.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
package org.springframework.webflow.core.expression.el;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.el.ELContext;
|
||||
import javax.el.ELResolver;
|
||||
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
import org.springframework.webflow.execution.RequestContextHolder;
|
||||
|
||||
/**
|
||||
* Custom EL resolver that searches the current request context for variables to resolve. The search algorithm looks in
|
||||
* request scope first, then flash scope, then flow scope, then conversation 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.
|
||||
*
|
||||
* @author Jeremy Grelle
|
||||
*/
|
||||
public class ScopeSearchingELResolver extends ELResolver {
|
||||
|
||||
public Class getCommonPropertyType(ELContext elContext, Object base) {
|
||||
return Object.class;
|
||||
}
|
||||
|
||||
public Iterator getFeatureDescriptors(ELContext elContext, Object base) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Class getType(ELContext elContext, Object base, Object property) {
|
||||
RequestContext requestContext;
|
||||
if (base != null && base instanceof RequestContext) {
|
||||
requestContext = (RequestContext) base;
|
||||
} else if (base == null) {
|
||||
requestContext = RequestContextHolder.getRequestContext();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
if (requestContext == null) {
|
||||
return null;
|
||||
}
|
||||
// flow execution is active: try request/flash/flow/conversation scope
|
||||
String name = property.toString();
|
||||
if (requestContext.getRequestScope().contains(name)) {
|
||||
elContext.setPropertyResolved(true);
|
||||
return requestContext.getRequestScope().get(name).getClass();
|
||||
} else if (requestContext.getFlashScope().contains(name)) {
|
||||
elContext.setPropertyResolved(true);
|
||||
return requestContext.getFlashScope().get(name).getClass();
|
||||
} else if (requestContext.getFlowScope().contains(name)) {
|
||||
elContext.setPropertyResolved(true);
|
||||
return requestContext.getFlowScope().get(name).getClass();
|
||||
} else if (requestContext.getConversationScope().contains(name)) {
|
||||
elContext.setPropertyResolved(true);
|
||||
return requestContext.getConversationScope().get(name).getClass();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Object getValue(ELContext elContext, Object base, Object property) {
|
||||
RequestContext requestContext;
|
||||
if (base != null && base instanceof RequestContext) {
|
||||
requestContext = (RequestContext) base;
|
||||
} else if (base == null) {
|
||||
requestContext = RequestContextHolder.getRequestContext();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
if (requestContext == null) {
|
||||
return null;
|
||||
}
|
||||
String name = property.toString();
|
||||
if (requestContext.getRequestScope().contains(name)) {
|
||||
elContext.setPropertyResolved(true);
|
||||
return requestContext.getRequestScope().get(name);
|
||||
} else if (requestContext.getFlashScope().contains(name)) {
|
||||
elContext.setPropertyResolved(true);
|
||||
return requestContext.getFlashScope().get(name);
|
||||
} else if (requestContext.getFlowScope().contains(name)) {
|
||||
elContext.setPropertyResolved(true);
|
||||
return requestContext.getFlowScope().get(name);
|
||||
} else if (requestContext.getConversationScope().contains(name)) {
|
||||
elContext.setPropertyResolved(true);
|
||||
return requestContext.getConversationScope().get(name);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isReadOnly(ELContext elContext, Object base, Object property) {
|
||||
RequestContext requestContext;
|
||||
if (base != null && base instanceof RequestContext) {
|
||||
requestContext = (RequestContext) base;
|
||||
} else if (base == null) {
|
||||
requestContext = RequestContextHolder.getRequestContext();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
if (requestContext == null) {
|
||||
return false;
|
||||
}
|
||||
// flow execution is active: try request/flash/flow/conversation scope
|
||||
String name = property.toString();
|
||||
if (requestContext.getRequestScope().contains(name)) {
|
||||
elContext.setPropertyResolved(true);
|
||||
return false;
|
||||
} else if (requestContext.getFlashScope().contains(name)) {
|
||||
elContext.setPropertyResolved(true);
|
||||
return false;
|
||||
} else if (requestContext.getFlowScope().contains(name)) {
|
||||
elContext.setPropertyResolved(true);
|
||||
return false;
|
||||
} else if (requestContext.getConversationScope().contains(name)) {
|
||||
elContext.setPropertyResolved(true);
|
||||
return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void setValue(ELContext elContext, Object base, Object property, Object value) {
|
||||
RequestContext requestContext;
|
||||
if (base != null && base instanceof RequestContext) {
|
||||
requestContext = (RequestContext) base;
|
||||
} else if (base == null) {
|
||||
requestContext = RequestContextHolder.getRequestContext();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
if (requestContext == null) {
|
||||
return;
|
||||
}
|
||||
// flow execution is active: try request/flash/flow/conversation scope
|
||||
String name = property.toString();
|
||||
if (requestContext.getRequestScope().contains(name)) {
|
||||
elContext.setPropertyResolved(true);
|
||||
requestContext.getRequestScope().put(name, value);
|
||||
} else if (requestContext.getFlashScope().contains(name)) {
|
||||
elContext.setPropertyResolved(true);
|
||||
requestContext.getFlashScope().put(name, value);
|
||||
} else if (requestContext.getFlowScope().contains(name)) {
|
||||
elContext.setPropertyResolved(true);
|
||||
requestContext.getFlowScope().put(name, value);
|
||||
} else if (requestContext.getConversationScope().contains(name)) {
|
||||
elContext.setPropertyResolved(true);
|
||||
requestContext.getConversationScope().put(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package org.springframework.webflow.core.expression.el;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.el.ELContext;
|
||||
import javax.el.ELResolver;
|
||||
import javax.el.ExpressionFactory;
|
||||
import javax.el.FunctionMapper;
|
||||
import javax.el.VariableMapper;
|
||||
|
||||
import org.springframework.binding.expression.el.DefaultELResolver;
|
||||
import org.springframework.binding.expression.el.ELContextFactory;
|
||||
import org.springframework.binding.expression.el.ELExpressionParser;
|
||||
import org.springframework.webflow.core.collection.MutableAttributeMap;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
|
||||
/**
|
||||
* An ExpressionParser that allows Spring and Web Flow managed beans to be referenced in expressions in the
|
||||
* FlowDefinition.
|
||||
*
|
||||
* @author Jeremy Grelle
|
||||
*/
|
||||
public class WebFlowELExpressionParser extends ELExpressionParser {
|
||||
|
||||
public WebFlowELExpressionParser(ExpressionFactory expressionFactory) {
|
||||
super(expressionFactory);
|
||||
putContextFactory(RequestContext.class, new RequestContextELContextFactory());
|
||||
putContextFactory(MutableAttributeMap.class, new AttributeMapELContextFactory());
|
||||
}
|
||||
|
||||
private static class RequestContextELContextFactory implements ELContextFactory {
|
||||
public ELContext getELContext(Object target, VariableMapper variableMapper) {
|
||||
List customResolvers = new ArrayList();
|
||||
customResolvers.add(new RequestContextELResolver());
|
||||
customResolvers.add(new ScopeSearchingELResolver());
|
||||
ELResolver resolver = new DefaultELResolver(target, customResolvers);
|
||||
return new WebFlowELContext(resolver, variableMapper);
|
||||
}
|
||||
}
|
||||
|
||||
private static class AttributeMapELContextFactory implements ELContextFactory {
|
||||
public ELContext getELContext(Object target, VariableMapper variableMapper) {
|
||||
ELResolver resolver = new DefaultELResolver(target, null);
|
||||
return new WebFlowELContext(resolver, variableMapper);
|
||||
}
|
||||
}
|
||||
|
||||
private static class WebFlowELContext extends ELContext {
|
||||
|
||||
VariableMapper variableMapper;
|
||||
|
||||
ELResolver resolver;
|
||||
|
||||
public WebFlowELContext(ELResolver resolver, VariableMapper variableMapper) {
|
||||
this.resolver = resolver;
|
||||
this.variableMapper = variableMapper;
|
||||
}
|
||||
|
||||
public ELResolver getELResolver() {
|
||||
return resolver;
|
||||
}
|
||||
|
||||
public FunctionMapper getFunctionMapper() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public VariableMapper getVariableMapper() {
|
||||
return variableMapper;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
/*
|
||||
* 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.definition.registry;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.webflow.engine.builder.FlowServiceLocator;
|
||||
|
||||
/**
|
||||
* A flow definition registrar that populates a flow definition registry from flow definitions defined within
|
||||
* externalized resources. Encapsulates registration behavior common to all externalized registrars and is not tied to a
|
||||
* specific flow definition format (e.g. xml).
|
||||
* <p>
|
||||
* Concrete subclasses are expected to derive from this class to provide knowledge about a particular kind of definition
|
||||
* format by implementing the abstract template methods in this class.
|
||||
*
|
||||
* @see org.springframework.webflow.definition.registry.FlowDefinitionResource
|
||||
* @see org.springframework.webflow.definition.registry.FlowDefinitionRegistry
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Ben Hale
|
||||
*/
|
||||
public abstract class ExternalizedFlowDefinitionRegistrar implements FlowDefinitionRegistrar {
|
||||
|
||||
/**
|
||||
* The locator of services needed by flow definitions.
|
||||
*/
|
||||
private FlowServiceLocator flowServiceLocator;
|
||||
|
||||
/**
|
||||
* A set of mappings between a namespace and a set of externalized flow definitions. A map of Strings to Sets
|
||||
* containing {@link FlowDefinitionResource}s
|
||||
*/
|
||||
private Map namespaceFlowMappings;
|
||||
|
||||
/**
|
||||
* The default namespace for flows registered without an explicit namespace.
|
||||
*/
|
||||
private String defaultNamespace = "";
|
||||
|
||||
/**
|
||||
* Creates a new registrar with an empty initial set of namespace to flow mappings.
|
||||
*/
|
||||
public ExternalizedFlowDefinitionRegistrar() {
|
||||
this(new HashMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new registrar with an initial set of namespace to flow mappings.
|
||||
* @param namespaceFlowMappings the initial set of namespace to flow mappings
|
||||
*/
|
||||
public ExternalizedFlowDefinitionRegistrar(Map namespaceFlowMappings) {
|
||||
this.namespaceFlowMappings = namespaceFlowMappings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default namespace to register flows in. If not set the default namespace is "" (an empty string).
|
||||
* @param defaultNamespace the default namespace
|
||||
*/
|
||||
public void setDefaultNamespace(String defaultNamespace) {
|
||||
this.defaultNamespace = defaultNamespace;
|
||||
}
|
||||
|
||||
public void setFlowServiceLocator(FlowServiceLocator flowServiceLocator) {
|
||||
this.flowServiceLocator = flowServiceLocator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the flow service locator for use by subclasses.
|
||||
*/
|
||||
protected FlowServiceLocator getFlowServiceLocator() {
|
||||
return flowServiceLocator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an externalized XML flow definition to be registered in the default namespace.
|
||||
* @param location the resource to register
|
||||
* @see #addLocation(Resource, String)
|
||||
* @see #setDefaultNamespace(String)
|
||||
*/
|
||||
public boolean addLocation(Resource location) {
|
||||
return addLocation(location, defaultNamespace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an externalized XML flow definition to be registered. The resource will be assigned a registry identifier
|
||||
* equal to the filename of the resource, minus the filename extension. For example, an XML-based flow definition
|
||||
* defined in the file <code>flow1.xml</code> will be identified as <code>flow1</code> in the registry created
|
||||
* by this factory bean.
|
||||
* @param location the resource to register
|
||||
* @param namespace the namespace to register the flow definition in
|
||||
*/
|
||||
public boolean addLocation(Resource location, String namespace) {
|
||||
return getFlows(namespace).add(new FlowDefinitionResource(location));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an externalized XML flow definition resource to be registered in the default namespace.
|
||||
* @param resource the flow definition resource to be registered
|
||||
* @see #addResource(FlowDefinitionResource, String)
|
||||
* @see #setDefaultNamespace(String)
|
||||
*/
|
||||
public boolean addResource(FlowDefinitionResource resource) {
|
||||
return addResource(resource, defaultNamespace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an externalized XML flow definition resource to be registered.
|
||||
* @param resource the flow definition resource to be registered
|
||||
* @param namespace the namespace to register the flow definition in
|
||||
*/
|
||||
public boolean addResource(FlowDefinitionResource resource, String namespace) {
|
||||
return getFlows(namespace).add(resource);
|
||||
}
|
||||
|
||||
public void registerFlowDefinitions(FlowDefinitionRegistry registry) {
|
||||
for (Iterator mappings = namespaceFlowMappings.entrySet().iterator(); mappings.hasNext();) {
|
||||
Map.Entry mapping = (Map.Entry) mappings.next();
|
||||
String namespace = (String) mapping.getKey();
|
||||
for (Iterator resources = ((Set) mapping.getValue()).iterator(); resources.hasNext();) {
|
||||
FlowDefinitionResource resource = (FlowDefinitionResource) resources.next();
|
||||
register(resource, namespace, registry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a flow definition resource in a given namespace.
|
||||
* @param resource the resource to register
|
||||
* @param namespace the namespace to register in
|
||||
* @param registry the registry
|
||||
*/
|
||||
private void register(FlowDefinitionResource resource, String namespace, FlowDefinitionRegistry registry) {
|
||||
registry.registerFlowDefinition(createFlowDefinitionHolder(resource), namespace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of flows to be registered in a namespace.
|
||||
* @param namespace The namespace for the collection to be returned
|
||||
*/
|
||||
private Set getFlows(String namespace) {
|
||||
if (!namespaceFlowMappings.containsKey(namespace)) {
|
||||
namespaceFlowMappings.put(namespace, new HashSet());
|
||||
}
|
||||
return (Set) namespaceFlowMappings.get(namespace);
|
||||
}
|
||||
|
||||
// sub-classing hooks
|
||||
|
||||
/**
|
||||
* Template factory method subclasses must override to return the holder for the flow definition to be registered
|
||||
* loaded from the specified resource.
|
||||
* @param resource the externalized resource
|
||||
* @return the flow definition holder
|
||||
*/
|
||||
protected abstract FlowDefinitionHolder createFlowDefinitionHolder(FlowDefinitionResource resource);
|
||||
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("namespaceFlowMappings", namespaceFlowMappings).toString();
|
||||
}
|
||||
}
|
||||
@@ -23,28 +23,28 @@ import org.springframework.webflow.core.FlowException;
|
||||
* @author Keith Donald
|
||||
* @author Erwin Vervaet
|
||||
*/
|
||||
public abstract class FlowDefinitionConstructionException extends FlowException {
|
||||
public class FlowDefinitionConstructionException extends FlowException {
|
||||
|
||||
/**
|
||||
* The id of the flow that could not be constructed.
|
||||
*/
|
||||
private String flowId;
|
||||
private String flowDefinitionId;
|
||||
|
||||
/**
|
||||
* Creates an exception indicating a flow definition could not be constructed.
|
||||
* @param flowId the flow id
|
||||
* @param cause underlying cause of the exception
|
||||
* @param flowDefinitionId the flow definition identifier
|
||||
* @param cause the underlying cause of the exception
|
||||
*/
|
||||
public FlowDefinitionConstructionException(String flowId, Throwable cause) {
|
||||
super("An exception occured constructing the flow with id '" + flowId + "'", cause);
|
||||
this.flowId = flowId;
|
||||
public FlowDefinitionConstructionException(String flowDefinitionId, Throwable cause) {
|
||||
super("An exception occurred constructing the flow '" + flowDefinitionId + "'", cause);
|
||||
this.flowDefinitionId = flowDefinitionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the id of the flow definition that could not be constructed.
|
||||
* @return the flow id
|
||||
*/
|
||||
public String getFlowId() {
|
||||
return flowId;
|
||||
public String getFlowDefinitionId() {
|
||||
return flowDefinitionId;
|
||||
}
|
||||
}
|
||||
@@ -18,10 +18,8 @@ package org.springframework.webflow.definition.registry;
|
||||
import org.springframework.webflow.definition.FlowDefinition;
|
||||
|
||||
/**
|
||||
* A runtime service locator interface for retrieving flow definitions by <code>id</code>.
|
||||
* <p>
|
||||
* Flow locators are needed by flow executors at runtime to retrieve fully-configured flow definitions to support
|
||||
* launching new flow executions.
|
||||
* A runtime service locator interface for retrieving flow definitions by <code>id</code>. Flow locators are needed
|
||||
* by flow executors at runtime to retrieve fully-configured flow definitions to support launching new flow executions.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Erwin Vervaet
|
||||
@@ -29,12 +27,12 @@ import org.springframework.webflow.definition.FlowDefinition;
|
||||
public interface FlowDefinitionLocator {
|
||||
|
||||
/**
|
||||
* Lookup the flow definition with the specified <code>path</code>.
|
||||
* @param flowPath the flow definition path
|
||||
* Lookup the flow definition with the specified id.
|
||||
* @param id the flow definition identifier
|
||||
* @return the flow definition
|
||||
* @throws NoSuchFlowDefinitionException when the flow definition with the specified id does not exist
|
||||
* @throws FlowDefinitionConstructionException if there is a problem constructing the identified flow definition
|
||||
*/
|
||||
public FlowDefinition getFlowDefinition(String flowPath) throws NoSuchFlowDefinitionException,
|
||||
public FlowDefinition getFlowDefinition(String id) throws NoSuchFlowDefinitionException,
|
||||
FlowDefinitionConstructionException;
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* 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.definition.registry;
|
||||
|
||||
|
||||
/**
|
||||
* A strategy to use to populate a flow definition registry with one or more flow definitions.
|
||||
* <p>
|
||||
* Flow definition registrars encapsulate the knowledge about the source of a set of flow definition resources and the
|
||||
* behavior necessary to add those resources to a flow definition registry.
|
||||
* <p>
|
||||
* The typical usage pattern is as follows:
|
||||
* <ol>
|
||||
* <li>Create a new (initially empty) flow definition registry.
|
||||
* <li>Use any number of flow definition registrars to populate the registry by calling
|
||||
* {@link #registerFlowDefinitions(FlowDefinitionRegistry)}.
|
||||
* </ol>
|
||||
* <p>
|
||||
* This design where various registrars populate a generic registry was inspired by Spring's GenericApplicationContext,
|
||||
* which can use any number of BeanDefinitionReaders to drive context population.
|
||||
*
|
||||
* @see FlowDefinitionRegistry
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public interface FlowDefinitionRegistrar {
|
||||
|
||||
/**
|
||||
* Register flow definition resources managed by this registrar in the registry provided.
|
||||
* @param registry the registry to register flow definitions in
|
||||
*/
|
||||
public void registerFlowDefinitions(FlowDefinitionRegistry registry);
|
||||
|
||||
}
|
||||
@@ -16,17 +16,15 @@
|
||||
package org.springframework.webflow.definition.registry;
|
||||
|
||||
/**
|
||||
* A container of flow definitions. Extends the {@link FlowDefinitionRegistryMBean} management interface exposing
|
||||
* registry monitoring and management operations. Also extends {@link FlowDefinitionLocator} for accessing registered
|
||||
* Flow definitions for execution at runtime.
|
||||
* A container of flow definitions. Extends {@link FlowDefinitionLocator} for accessing registered Flow definitions for
|
||||
* execution at runtime.
|
||||
* <p>
|
||||
* Flow definition registries can be configured with a "parent" registry to provide a hook into a larger flow definition
|
||||
* registry hierarchy.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Ben Hale
|
||||
*/
|
||||
public interface FlowDefinitionRegistry extends FlowDefinitionLocator, FlowDefinitionRegistryMBean {
|
||||
public interface FlowDefinitionRegistry extends FlowDefinitionLocator {
|
||||
|
||||
/**
|
||||
* Sets this registry's parent registry. When asked by a client to locate a flow definition this registry will query
|
||||
@@ -39,17 +37,8 @@ public interface FlowDefinitionRegistry extends FlowDefinitionLocator, FlowDefin
|
||||
* Register a flow definition in this registry. Registers a "holder", not the Flow definition itself. This allows
|
||||
* the actual Flow definition to be loaded lazily only when needed, and also rebuilt at runtime when its underlying
|
||||
* resource changes without re-deploy.
|
||||
* @param flowHolder a holder holding the flow definition to register
|
||||
* @param definitionHolder a holder holding the flow definition to register
|
||||
*/
|
||||
public void registerFlowDefinition(FlowDefinitionHolder flowHolder);
|
||||
|
||||
/**
|
||||
* Register a flow definition in this registry under a specific namespace. Registers a "holder", not the Flow
|
||||
* definition itself. This allows the actual Flow definition to be loaded lazily only when needed, and also rebuilt
|
||||
* at runtime when its underlying resource changes without re-deploy.
|
||||
* @param flowHolder a holder holding the flow definition to register
|
||||
* @param namespace the namespace to register the flow definition in
|
||||
*/
|
||||
public void registerFlowDefinition(FlowDefinitionHolder flowHolder, String namespace);
|
||||
public void registerFlowDefinition(FlowDefinitionHolder definitionHolder);
|
||||
|
||||
}
|
||||
@@ -15,9 +15,6 @@
|
||||
*/
|
||||
package org.springframework.webflow.definition.registry;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
@@ -29,13 +26,8 @@ import org.springframework.webflow.definition.FlowDefinition;
|
||||
|
||||
/**
|
||||
* A generic registry implementation for housing one or more flow definitions.
|
||||
* <p>
|
||||
* This registry may be refreshed at runtime to "hot reload" refreshable flow definitions. Note that the refresh will
|
||||
* only reload already registered flow definitions but will not detect any new flow definitions or remove flow
|
||||
* definitions that no longer exist.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Ben Hale
|
||||
*/
|
||||
public class FlowDefinitionRegistryImpl implements FlowDefinitionRegistry {
|
||||
|
||||
@@ -55,83 +47,22 @@ public class FlowDefinitionRegistryImpl implements FlowDefinitionRegistry {
|
||||
flowDefinitions = new TreeMap();
|
||||
}
|
||||
|
||||
// implementing FlowDefinitionRegistryMBean
|
||||
|
||||
public String[] getFlowDefinitionPaths() {
|
||||
List flowPaths = new LinkedList();
|
||||
for (Iterator namespaces = flowDefinitions.entrySet().iterator(); namespaces.hasNext();) {
|
||||
Map.Entry namespaceEntry = (Map.Entry) namespaces.next();
|
||||
String namespaceName = (String) namespaceEntry.getKey();
|
||||
Map namespace = (Map) namespaceEntry.getValue();
|
||||
for (Iterator ids = namespace.keySet().iterator(); ids.hasNext();) {
|
||||
flowPaths.add(FlowPathUtils.buildFlowPath(namespaceName, (String) ids.next()));
|
||||
}
|
||||
}
|
||||
return (String[]) flowPaths.toArray(new String[flowPaths.size()]);
|
||||
}
|
||||
|
||||
public int getFlowDefinitionCount() {
|
||||
int count = 0;
|
||||
for (Iterator namespaces = flowDefinitions.values().iterator(); namespaces.hasNext();) {
|
||||
Map namespace = (Map) namespaces.next();
|
||||
count += namespace.size();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public boolean containsFlowDefinition(String flowPath) {
|
||||
Assert.hasText(flowPath, "The flow path is required");
|
||||
Map namespace = getNamespace(FlowPathUtils.extractFlowNamespace(flowPath));
|
||||
return namespace.containsKey(FlowPathUtils.extractFlowId(flowPath));
|
||||
}
|
||||
|
||||
public void refresh() throws FlowDefinitionConstructionException {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Refreshing flow definition registry '" + this + "'");
|
||||
}
|
||||
for (Iterator namespaces = flowDefinitions.entrySet().iterator(); namespaces.hasNext();) {
|
||||
Map.Entry namespaceEntry = (Map.Entry) namespaces.next();
|
||||
String namespaceName = (String) namespaceEntry.getKey();
|
||||
Map namespace = (Map) namespaceEntry.getValue();
|
||||
for (Iterator ids = namespace.keySet().iterator(); ids.hasNext();) {
|
||||
refresh(FlowPathUtils.buildFlowPath(namespaceName, (String) ids.next()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void refresh(String flowPath) throws NoSuchFlowDefinitionException, FlowDefinitionConstructionException {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Refreshing flow with path '" + flowPath + "'");
|
||||
}
|
||||
ClassLoader loader = Thread.currentThread().getContextClassLoader();
|
||||
try {
|
||||
// workaround for JMX
|
||||
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
|
||||
FlowDefinitionHolder holder = getFlowDefinitionHolder(flowPath);
|
||||
holder.refresh();
|
||||
if (!holder.getFlowDefinitionId().equals(FlowPathUtils.extractFlowId(flowPath))) {
|
||||
reindex(holder, FlowPathUtils.extractFlowNamespace(flowPath), flowPath);
|
||||
}
|
||||
} finally {
|
||||
Thread.currentThread().setContextClassLoader(loader);
|
||||
}
|
||||
}
|
||||
|
||||
// implementing FlowDefinitionLocator
|
||||
|
||||
public FlowDefinition getFlowDefinition(String path) throws NoSuchFlowDefinitionException,
|
||||
public FlowDefinition getFlowDefinition(String id) throws NoSuchFlowDefinitionException,
|
||||
FlowDefinitionConstructionException {
|
||||
Assert.hasText(path,
|
||||
"Unable to load a flow definition: no flow path was provided. Please provide a valid flow path.");
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Getting flow definition with path '" + path + "'");
|
||||
}
|
||||
try {
|
||||
return getFlowDefinitionHolder(path).getFlowDefinition();
|
||||
if (id == null) {
|
||||
throw new IllegalArgumentException("The id of the flow to lookup is required");
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Getting flow definition with id '" + id + "'");
|
||||
}
|
||||
return getFlowDefinitionHolder(id).getFlowDefinition();
|
||||
} catch (NoSuchFlowDefinitionException e) {
|
||||
if (parent != null) {
|
||||
// try parent
|
||||
return parent.getFlowDefinition(path);
|
||||
return parent.getFlowDefinition(id);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
@@ -140,84 +71,32 @@ public class FlowDefinitionRegistryImpl implements FlowDefinitionRegistry {
|
||||
// implementing FlowDefinitionRegistry
|
||||
|
||||
public void setParent(FlowDefinitionRegistry parent) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Setting parent flow definition registry to '" + parent + "'");
|
||||
}
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public void registerFlowDefinition(FlowDefinitionHolder flowHolder) {
|
||||
registerFlowDefinition(flowHolder, "");
|
||||
public void registerFlowDefinition(FlowDefinitionHolder definitionHolder) {
|
||||
Assert.notNull(definitionHolder, "The holder of the flow definition to register is required");
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Registering flow definition " + definitionHolder);
|
||||
}
|
||||
flowDefinitions.put(definitionHolder.getFlowDefinitionId(), definitionHolder);
|
||||
}
|
||||
|
||||
public void registerFlowDefinition(FlowDefinitionHolder flowHolder, String namespace) {
|
||||
Assert.notNull(flowHolder, "The flow definition holder to register is required");
|
||||
Assert.notNull(namespace, "The flow namespace is required");
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Registering flow definition with id '" + flowHolder.getFlowDefinitionId()
|
||||
+ "' in namespace '" + namespace + "'");
|
||||
}
|
||||
index(flowHolder, namespace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove identified flow definition from this registry. If the given id is not known in this registry, nothing will
|
||||
* happen.
|
||||
* @param flowPath the flow definition path
|
||||
*/
|
||||
public void removeFlowDefinition(String flowPath) {
|
||||
Assert.hasText(flowPath, "The flow path is required");
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Removing flow definition with path '" + flowPath + "'");
|
||||
}
|
||||
Map namespace = getNamespace(FlowPathUtils.extractFlowNamespace(flowPath));
|
||||
namespace.remove(FlowPathUtils.extractFlowId(flowPath));
|
||||
public void registerFlowDefinition(FlowDefinition definition) {
|
||||
registerFlowDefinition(new StaticFlowDefinitionHolder(definition));
|
||||
}
|
||||
|
||||
// internal helpers
|
||||
|
||||
/**
|
||||
* Re-index given flow definition.
|
||||
* @param holder the holder holding the flow definition to re-index
|
||||
* @param namespace the namespace to index the new flow in
|
||||
* @param oldFlowPath the flowPath that was previously assigned to given flow definition
|
||||
*/
|
||||
private void reindex(FlowDefinitionHolder holder, String namespace, String oldFlowPath) {
|
||||
removeFlowDefinition(oldFlowPath);
|
||||
index(holder, namespace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Index given flow definition.
|
||||
* @param holder the holder holding the flow definition to index
|
||||
* @param namespaceName the namespace to index the flow definition in
|
||||
*/
|
||||
private void index(FlowDefinitionHolder holder, String namespaceName) {
|
||||
Assert.hasText(holder.getFlowDefinitionId(), "The flow holder to index must return a non-blank flow id");
|
||||
Map namespace = getNamespace(namespaceName);
|
||||
namespace.put(holder.getFlowDefinitionId(), holder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the identified flow definition holder. Throws an exception if it cannot be found.
|
||||
*/
|
||||
private FlowDefinitionHolder getFlowDefinitionHolder(String flowPath) throws NoSuchFlowDefinitionException {
|
||||
Map namespace = getNamespace(FlowPathUtils.extractFlowNamespace(flowPath));
|
||||
FlowDefinitionHolder flowHolder = (FlowDefinitionHolder) namespace.get(FlowPathUtils.extractFlowId(flowPath));
|
||||
if (flowHolder == null) {
|
||||
throw new NoSuchFlowDefinitionException(flowPath, getFlowDefinitionPaths());
|
||||
private FlowDefinitionHolder getFlowDefinitionHolder(String id) throws NoSuchFlowDefinitionException {
|
||||
FlowDefinitionHolder holder = (FlowDefinitionHolder) flowDefinitions.get(id);
|
||||
if (holder == null) {
|
||||
throw new NoSuchFlowDefinitionException(id);
|
||||
}
|
||||
return flowHolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the namespace map for a given namespace. Creates the map if it does not exist.
|
||||
*/
|
||||
private Map getNamespace(String namespace) {
|
||||
if (!flowDefinitions.containsKey(namespace)) {
|
||||
flowDefinitions.put(namespace, new TreeMap());
|
||||
}
|
||||
return (Map) flowDefinitions.get(namespace);
|
||||
return holder;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* 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.definition.registry;
|
||||
|
||||
/**
|
||||
* A management interface for managing flow definition registries at runtime. Provides the ability to query the size and
|
||||
* state of the registry, as well as refresh registered flow definitions at runtime.
|
||||
* <p>
|
||||
* Flow registries that implement this interface may be exposed for management over the JMX protocol. The following is
|
||||
* an example of using Spring's JMX <code>MBeanExporter</code> to export a flow registry to an MBeanServer:
|
||||
*
|
||||
* <pre class="code">
|
||||
* <!-- Creates the registry of flow definitions for this application -->
|
||||
* <bean name="flowRegistry" class="org.springframework.webflow...XmlFlowRegistryFactoryBean">
|
||||
* <property name="locations" value="/WEB-INF/flow1.xml"/>
|
||||
* </bean>
|
||||
*
|
||||
* <!-- Automatically exports the created flowRegistry as an MBean -->
|
||||
* <bean id="mbeanExporter" class="org.springframework.jmx.export.MBeanExporter">
|
||||
* <property name="beans">
|
||||
* <map>
|
||||
* <entry key="spring-webflow:name=flowRegistry" value-ref="flowRegistry"/>
|
||||
* </map>
|
||||
* </property>
|
||||
* <property name="assembler">
|
||||
* <bean class="org.springframework.jmx.export.assembler.InterfaceBasedMBeanInfoAssembler">
|
||||
* <property name="managedInterfaces"
|
||||
* value="org.springframework.webflow.definition.registry.FlowDefinitionRegistryMBean"/>
|
||||
* </bean>
|
||||
* </property>
|
||||
* </bean>
|
||||
* </pre>
|
||||
*
|
||||
* With the above configuration, you may then use any JMX client (such as Sun's jConsole which ships with JDK 1.5) to
|
||||
* refresh flow definitions at runtime.
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public interface FlowDefinitionRegistryMBean {
|
||||
|
||||
/**
|
||||
* Returns the paths of the flow definitions registered in this registry.
|
||||
* @return the flow definition paths
|
||||
*/
|
||||
public String[] getFlowDefinitionPaths();
|
||||
|
||||
/**
|
||||
* Return the number of flow definitions registered in this registry.
|
||||
* @return the flow definition count
|
||||
*/
|
||||
public int getFlowDefinitionCount();
|
||||
|
||||
/**
|
||||
* Queries this registry to determine if a specific flow is contained within it.
|
||||
* @param flowPath the flow definition path
|
||||
* @return true if a flow definition is contained in this registry with the id provided
|
||||
*/
|
||||
public boolean containsFlowDefinition(String flowPath);
|
||||
|
||||
/**
|
||||
* Refresh this flow definition registry, reloading all Flow definitions from their externalized representations.
|
||||
*/
|
||||
public void refresh() throws FlowDefinitionConstructionException;
|
||||
|
||||
/**
|
||||
* Refresh the Flow definition in this registry with the <code>path</code> provided, reloading it from it's
|
||||
* externalized representation.
|
||||
* @param flowPath the path of the flow definition to refresh
|
||||
* @throws NoSuchFlowDefinitionException if a flow with the id provided is not stored in this registry
|
||||
*/
|
||||
public void refresh(String flowPath) throws NoSuchFlowDefinitionException, FlowDefinitionConstructionException;
|
||||
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
/*
|
||||
* 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.definition.registry;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.webflow.core.collection.AttributeMap;
|
||||
import org.springframework.webflow.core.collection.CollectionUtils;
|
||||
|
||||
/**
|
||||
* A pointer to an externalized flow definition resource. Adds assigned identification information about the resource
|
||||
* including the flow id and attributes.
|
||||
*
|
||||
* @see ExternalizedFlowDefinitionRegistrar
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public class FlowDefinitionResource implements Serializable {
|
||||
|
||||
/**
|
||||
* The identifier to assign to the flow definition.
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* Attributes that can be used to affect flow construction.
|
||||
*/
|
||||
private AttributeMap attributes;
|
||||
|
||||
/**
|
||||
* The externalized location of the flow definition resource.
|
||||
*/
|
||||
private Resource location;
|
||||
|
||||
/**
|
||||
* Creates a new externalized flow definition resource. The flow id assigned will be the same name as the
|
||||
* externalized resource's filename, excluding the extension.
|
||||
* @param location the flow resource location
|
||||
*/
|
||||
public FlowDefinitionResource(Resource location) {
|
||||
init(conventionalFlowId(location), location, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new externalized flow definition resource. The flow id assigned will be the same name as the
|
||||
* externalized resource's filename, excluding the extension.
|
||||
* @param location the flow resource location
|
||||
* @param attributes flow definition attributes to be assigned
|
||||
*/
|
||||
public FlowDefinitionResource(Resource location, AttributeMap attributes) {
|
||||
init(conventionalFlowId(location), location, attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new externalized flow definition.
|
||||
* @param id the flow id to be assigned
|
||||
* @param location the flow resource location
|
||||
*/
|
||||
public FlowDefinitionResource(String id, Resource location) {
|
||||
init(id, location, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new externalized flow definition.
|
||||
* @param id the flow id to be assigned
|
||||
* @param location the flow resource location
|
||||
* @param attributes flow definition attributes to be assigned
|
||||
*/
|
||||
public FlowDefinitionResource(String id, Resource location, AttributeMap attributes) {
|
||||
init(id, location, attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the identifier to assign to the flow definition.
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the externalized flow definition resource location.
|
||||
*/
|
||||
public Resource getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns arbitrary flow definition attributes.
|
||||
*/
|
||||
public AttributeMap getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof FlowDefinitionResource)) {
|
||||
return false;
|
||||
}
|
||||
FlowDefinitionResource other = (FlowDefinitionResource) o;
|
||||
return id.equals(other.id) && location.equals(other.location);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return id.hashCode() + location.hashCode();
|
||||
}
|
||||
|
||||
// internal helpers
|
||||
|
||||
/**
|
||||
* Initialize this object.
|
||||
*/
|
||||
private void init(String id, Resource location, AttributeMap attributes) {
|
||||
Assert.hasText(id, "The id of the externalized flow definition is required");
|
||||
Assert.notNull(location, "The location of the externalized flow definition is required");
|
||||
this.id = id;
|
||||
this.location = location;
|
||||
if (attributes != null) {
|
||||
this.attributes = attributes;
|
||||
} else {
|
||||
this.attributes = CollectionUtils.EMPTY_ATTRIBUTE_MAP;
|
||||
}
|
||||
}
|
||||
|
||||
// public utilities
|
||||
|
||||
/**
|
||||
* Returns the flow id assigned to the flow definition contained in given resource. By convention this will be the
|
||||
* filename of the resource, excluding extension.
|
||||
* @see FlowDefinitionResource#FlowDefinitionResource(Resource)
|
||||
* @see FlowDefinitionResource#FlowDefinitionResource(Resource, AttributeMap)
|
||||
* @since 1.0.1
|
||||
*/
|
||||
public static String conventionalFlowId(Resource location) {
|
||||
String fileName = location.getFilename();
|
||||
int extensionIndex = fileName.lastIndexOf('.');
|
||||
if (extensionIndex != -1) {
|
||||
return fileName.substring(0, extensionIndex);
|
||||
} else {
|
||||
return fileName;
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("id", id).append("location", location).append("attributes", attributes)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package org.springframework.webflow.definition.registry;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Simple utility for working with flow paths. Only intended for internal use.
|
||||
*
|
||||
* @author Ben Hale
|
||||
*/
|
||||
class FlowPathUtils {
|
||||
|
||||
private static final String PATH_DELIMITER = "/";
|
||||
|
||||
/**
|
||||
* Parses a flow path and returns the namespace part of the path.
|
||||
* @param flowPath The path to parse
|
||||
* @return The namespace part of the path
|
||||
*/
|
||||
public static String extractFlowNamespace(String flowPath) {
|
||||
Assert.hasText(flowPath, "The flow path must not be empty or null");
|
||||
int index = flowPath.lastIndexOf(PATH_DELIMITER);
|
||||
if (index == -1) {
|
||||
return "";
|
||||
} else {
|
||||
return flowPath.substring(0, index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a flow path and returns the id part of the path.
|
||||
* @param flowPath The path to parse
|
||||
* @return The id part of the path
|
||||
*/
|
||||
public static String extractFlowId(String flowPath) {
|
||||
Assert.hasText(flowPath, "The flow path must not be empty or null");
|
||||
int index = flowPath.lastIndexOf(PATH_DELIMITER);
|
||||
if (index == -1) {
|
||||
return flowPath;
|
||||
} else {
|
||||
return flowPath.substring(index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a flow path based on given namespace and id.
|
||||
* @param namespace The namespace of the path
|
||||
* @param id The id of the path
|
||||
* @return The complete flow path
|
||||
*/
|
||||
public static String buildFlowPath(String namespace, String id) {
|
||||
Assert.notNull(namespace, "namespace must have some value");
|
||||
Assert.hasText(id, "The id must not be empty or null");
|
||||
return namespace + PATH_DELIMITER + id;
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package org.springframework.webflow.definition.registry;
|
||||
|
||||
import org.springframework.core.style.StylerUtils;
|
||||
import org.springframework.webflow.core.FlowException;
|
||||
|
||||
/**
|
||||
@@ -29,23 +28,21 @@ public class NoSuchFlowDefinitionException extends FlowException {
|
||||
/**
|
||||
* The id of the flow definition that could not be located.
|
||||
*/
|
||||
private String flowId;
|
||||
private String flowDefinitionId;
|
||||
|
||||
/**
|
||||
* Creates an exception indicating a flow definition could not be found.
|
||||
* @param flowId the flow id
|
||||
* @param availableFlowIds all flow ids available to the locator generating this exception
|
||||
* @param flowDefinitionId the flow definition id
|
||||
*/
|
||||
public NoSuchFlowDefinitionException(String flowId, String[] availableFlowIds) {
|
||||
super("No such flow definition with id '" + flowId + "' found; the flows available are: "
|
||||
+ StylerUtils.style(availableFlowIds));
|
||||
this.flowId = flowId;
|
||||
public NoSuchFlowDefinitionException(String flowDefinitionId) {
|
||||
super("No flow definition '" + flowDefinitionId + "' found");
|
||||
this.flowDefinitionId = flowDefinitionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the id of the flow definition that could not be found.
|
||||
*/
|
||||
public String getFlowId() {
|
||||
return flowId;
|
||||
public String getFlowDefinitionId() {
|
||||
return flowDefinitionId;
|
||||
}
|
||||
}
|
||||
@@ -1,51 +1,48 @@
|
||||
/*
|
||||
* 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.definition.registry;
|
||||
|
||||
import org.springframework.webflow.definition.FlowDefinition;
|
||||
|
||||
/**
|
||||
* A simple flow definition holder that just holds a constant singleton reference to a flow definition.
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public final class StaticFlowDefinitionHolder implements FlowDefinitionHolder {
|
||||
|
||||
/**
|
||||
* The held flow definition.
|
||||
*/
|
||||
private final FlowDefinition flowDefinition;
|
||||
|
||||
/**
|
||||
* Creates the static flow definition holder.
|
||||
* @param flowDefinition the flow to hold
|
||||
*/
|
||||
public StaticFlowDefinitionHolder(FlowDefinition flowDefinition) {
|
||||
this.flowDefinition = flowDefinition;
|
||||
}
|
||||
|
||||
public String getFlowDefinitionId() {
|
||||
return flowDefinition.getId();
|
||||
}
|
||||
|
||||
public FlowDefinition getFlowDefinition() {
|
||||
return flowDefinition;
|
||||
}
|
||||
|
||||
public void refresh() {
|
||||
// nothing to do
|
||||
}
|
||||
package org.springframework.webflow.definition.registry;
|
||||
|
||||
import org.springframework.webflow.definition.FlowDefinition;
|
||||
|
||||
/**
|
||||
* A simple flow definition holder that just holds a constant singleton reference to a flow definition.
|
||||
* @author Keith Donald
|
||||
*/
|
||||
class StaticFlowDefinitionHolder implements FlowDefinitionHolder {
|
||||
|
||||
/**
|
||||
* The held flow definition.
|
||||
*/
|
||||
private final FlowDefinition flowDefinition;
|
||||
|
||||
/**
|
||||
* Creates the static flow definition holder.
|
||||
* @param flowDefinition the flow to hold
|
||||
*/
|
||||
public StaticFlowDefinitionHolder(FlowDefinition flowDefinition) {
|
||||
this.flowDefinition = flowDefinition;
|
||||
}
|
||||
|
||||
public String getFlowDefinitionId() {
|
||||
return flowDefinition.getId();
|
||||
}
|
||||
|
||||
public FlowDefinition getFlowDefinition() throws FlowDefinitionConstructionException {
|
||||
return flowDefinition;
|
||||
}
|
||||
|
||||
public void refresh() throws FlowDefinitionConstructionException {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof StaticFlowDefinitionHolder)) {
|
||||
return false;
|
||||
}
|
||||
StaticFlowDefinitionHolder other = (StaticFlowDefinitionHolder) o;
|
||||
return flowDefinition.equals(other.flowDefinition);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return flowDefinition.hashCode();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package org.springframework.webflow.engine;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.webflow.execution.Action;
|
||||
import org.springframework.webflow.execution.Event;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
@@ -30,8 +28,6 @@ import org.springframework.webflow.execution.RequestContext;
|
||||
*/
|
||||
public class ActionExecutor {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ActionExecutor.class);
|
||||
|
||||
/**
|
||||
* Private constructor to avoid instantiation.
|
||||
*/
|
||||
@@ -48,14 +44,6 @@ public class ActionExecutor {
|
||||
*/
|
||||
public static Event execute(Action action, RequestContext context) throws ActionExecutionException {
|
||||
try {
|
||||
if (logger.isDebugEnabled()) {
|
||||
if (context.getCurrentState() == null) {
|
||||
logger.debug("Executing start " + action + " for flow '" + context.getActiveFlow().getId() + "'");
|
||||
} else {
|
||||
logger.debug("Executing " + action + " in state '" + context.getCurrentState().getId()
|
||||
+ "' of flow '" + context.getActiveFlow().getId() + "'");
|
||||
}
|
||||
}
|
||||
return action.execute(context);
|
||||
} catch (ActionExecutionException e) {
|
||||
throw e;
|
||||
|
||||
@@ -23,7 +23,6 @@ import org.springframework.webflow.execution.Action;
|
||||
import org.springframework.webflow.execution.Event;
|
||||
import org.springframework.webflow.execution.FlowExecutionException;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
import org.springframework.webflow.execution.ViewSelection;
|
||||
|
||||
/**
|
||||
* A transitionable state that executes one or more actions when entered. When the action(s) are executed this state
|
||||
@@ -123,7 +122,7 @@ public class ActionState extends TransitionableState {
|
||||
|
||||
/*
|
||||
* Overrides getRequiredTransition(RequestContext) to throw a local NoMatchingActionResultTransitionException if a
|
||||
* transition on the occurence of an action result event cannot be matched. Used to facilitate an action invocation
|
||||
* transition on the occurrence of an action result event cannot be matched. Used to facilitate an action invocation
|
||||
* chain. <p>Note that we cannot catch NoMatchingTransitionException since that could lead to unwanted situations
|
||||
* where we're catching an exception that's generated by another state, e.g. because of a configuration error!
|
||||
*/
|
||||
@@ -136,7 +135,7 @@ public class ActionState extends TransitionableState {
|
||||
}
|
||||
|
||||
/**
|
||||
* Specialization of State's <code>doEnter</code> template method that executes behaviour specific to this state
|
||||
* Specialization of State's <code>doEnter</code> template method that executes behavior specific to this state
|
||||
* type in polymorphic fashion.
|
||||
* <p>
|
||||
* This implementation iterates over each configured <code>Action</code> instance and executes it. Execution
|
||||
@@ -144,10 +143,9 @@ public class ActionState extends TransitionableState {
|
||||
* context, or the set of all actions is exhausted.
|
||||
* @param context the control context for the currently executing flow, used by this state to manipulate the flow
|
||||
* execution
|
||||
* @return a view selection signaling that control should be returned to the client and a view rendered
|
||||
* @throws FlowExecutionException if an exception occurs in this state
|
||||
*/
|
||||
protected ViewSelection doEnter(RequestControlContext context) throws FlowExecutionException {
|
||||
protected void doEnter(RequestControlContext context) throws FlowExecutionException {
|
||||
int executionCount = 0;
|
||||
String[] eventIds = new String[actionList.size()];
|
||||
Iterator it = actionList.iterator();
|
||||
@@ -157,8 +155,8 @@ public class ActionState extends TransitionableState {
|
||||
if (event != null) {
|
||||
eventIds[executionCount] = event.getId();
|
||||
try {
|
||||
// will check both local state transitions and global transitions
|
||||
return context.signalEvent(event);
|
||||
context.handleEvent(event);
|
||||
return;
|
||||
} catch (NoMatchingActionResultTransitionException e) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Action execution ["
|
||||
@@ -207,7 +205,6 @@ public class ActionState extends TransitionableState {
|
||||
/**
|
||||
* Local "no transition found" exception used to report that an action result could not be mapped to a state
|
||||
* transition.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Erwin Vervaet
|
||||
*/
|
||||
|
||||
@@ -17,7 +17,6 @@ package org.springframework.webflow.engine;
|
||||
|
||||
import org.springframework.webflow.execution.FlowExecutionException;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
import org.springframework.webflow.execution.ViewSelection;
|
||||
|
||||
/**
|
||||
* A simple transitionable state that when entered will execute the first transition whose matching criteria evaluates
|
||||
@@ -40,17 +39,15 @@ public class DecisionState extends TransitionableState {
|
||||
}
|
||||
|
||||
/**
|
||||
* Specialization of State's <code>doEnter</code> template method that executes behaviour specific to this state
|
||||
* Specialization of State's <code>doEnter</code> template method that executes behavior specific to this state
|
||||
* type in polymorphic fashion.
|
||||
* <p>
|
||||
* Simply looks up the first transition that matches the state of the context and executes it.
|
||||
* @param context the control context for the currently executing flow, used by this state to manipulate the flow
|
||||
* execution
|
||||
* @return a view selection containing model and view information needed to render the results of the state
|
||||
* execution
|
||||
* @throws FlowExecutionException if an exception occurs in this state
|
||||
*/
|
||||
protected ViewSelection doEnter(RequestControlContext context) throws FlowExecutionException {
|
||||
return getRequiredTransition(context).execute(this, context);
|
||||
protected void doEnter(RequestControlContext context) throws FlowExecutionException {
|
||||
getRequiredTransition(context).execute(this, context);
|
||||
}
|
||||
}
|
||||
@@ -16,35 +16,28 @@
|
||||
package org.springframework.webflow.engine;
|
||||
|
||||
import org.springframework.binding.mapping.AttributeMapper;
|
||||
import org.springframework.binding.mapping.MappingContext;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.webflow.core.collection.LocalAttributeMap;
|
||||
import org.springframework.webflow.execution.Action;
|
||||
import org.springframework.webflow.execution.Event;
|
||||
import org.springframework.webflow.execution.FlowExecutionException;
|
||||
import org.springframework.webflow.execution.FlowSession;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
import org.springframework.webflow.execution.ViewSelection;
|
||||
|
||||
/**
|
||||
* A state that ends a flow when entered. More specifically, this state ends the active flow session of the active flow
|
||||
* execution associated with the current request context.
|
||||
* A state that ends a flow when entered. This state ends the active flow session of an ongoing flow execution.
|
||||
* <p>
|
||||
* If the ended session is the "root flow session" the entire flow execution ends, signaling the end of a logical
|
||||
* conversation.
|
||||
* <p>
|
||||
* If the terminated session was acting as a subflow the flow execution continues and control is returned to the parent
|
||||
* flow session. In that case, this state returns an ending result event the resuming parent flow is expected to respond
|
||||
* to.
|
||||
* If the terminated session was acting as a subflow, the flow execution continues and control is returned to the parent
|
||||
* flow session. In that case, this state returns an ending result event the resuming parent flow responds to.
|
||||
* <p>
|
||||
* An end state may optionally be configured with the name of a view to render when entered. This view will be rendered
|
||||
* if the end state terminates the entire flow execution as a kind of flow ending "confirmation page".
|
||||
* <p>
|
||||
* Note: if no <code>viewName</code> property is specified <b>and</b> this end state terminates the entire flow
|
||||
* execution it is expected that some action has already written the response (or else a blank response will result). On
|
||||
* the other hand, if no <code>viewName</code> is specified <b>and</b> this end state relinquishes control back to a
|
||||
* parent flow, view selection responsibility falls on the parent flow.
|
||||
* An end state may be configured with a renderer to render a final response. This renderer will be invoked if the end
|
||||
* state terminates the entire flow execution.
|
||||
*
|
||||
* @see org.springframework.webflow.engine.ViewSelector
|
||||
* @see org.springframework.webflow.engine.SubflowState
|
||||
*
|
||||
* @author Keith Donald
|
||||
@@ -54,14 +47,14 @@ import org.springframework.webflow.execution.ViewSelection;
|
||||
public class EndState extends State {
|
||||
|
||||
/**
|
||||
* The optional view selector that will select a view to render if this end state terminates a root flow session.
|
||||
* The renderer that will render the final response when a flow execution terminates.
|
||||
*/
|
||||
private ViewSelector viewSelector = NullViewSelector.INSTANCE;
|
||||
private Action finalResponseAction = new NullFinalResponseAction();
|
||||
|
||||
/**
|
||||
* Attribute mapper for mapping output attributes exposed by this end state when it is entered.
|
||||
* The attribute mapper for mapping output attributes exposed by this end state when it is entered.
|
||||
*/
|
||||
private AttributeMapper outputMapper;
|
||||
private AttributeMapper outputMapper = new NoOutputMapper();
|
||||
|
||||
/**
|
||||
* Create a new end state with no associated view.
|
||||
@@ -69,7 +62,7 @@ public class EndState extends State {
|
||||
* @param id the state identifier (must be unique to the flow)
|
||||
* @throws IllegalArgumentException when this state cannot be added to given flow, e.g. because the id is not unique
|
||||
* @see State#State(Flow, String)
|
||||
* @see #setViewSelector(ViewSelector)
|
||||
* @see #setFinalResponseAction(Action)
|
||||
* @see #setOutputMapper(AttributeMapper)
|
||||
*/
|
||||
public EndState(Flow flow, String id) throws IllegalArgumentException {
|
||||
@@ -77,58 +70,42 @@ public class EndState extends State {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the strategy used to select the view to render in this end state if it terminates a root flow.
|
||||
* Sets the renderer that will render the final flow execution response.
|
||||
*/
|
||||
public ViewSelector getViewSelector() {
|
||||
return viewSelector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the strategy used to select the view to render when this end state is entered and terminates a root flow.
|
||||
*/
|
||||
public void setViewSelector(ViewSelector viewSelector) {
|
||||
Assert.notNull(viewSelector, "The view selector is required");
|
||||
this.viewSelector = viewSelector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configured attribute mapper for mapping output attributes exposed by this end state when it is
|
||||
* entered.
|
||||
*/
|
||||
public AttributeMapper getOutputMapper() {
|
||||
return outputMapper;
|
||||
public void setFinalResponseAction(Action finalResponseAction) {
|
||||
Assert.notNull(finalResponseAction, "The final response action is required");
|
||||
this.finalResponseAction = finalResponseAction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the attribute mapper to use for mapping output attributes exposed by this end state when it is entered.
|
||||
*/
|
||||
public void setOutputMapper(AttributeMapper outputMapper) {
|
||||
Assert.notNull(outputMapper, "The flow session output mapper is required");
|
||||
this.outputMapper = outputMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specialization of State's <code>doEnter</code> template method that executes behaviour specific to this state
|
||||
* Specialization of State's <code>doEnter</code> template method that executes behavior specific to this state
|
||||
* type in polymorphic fashion.
|
||||
* <p>
|
||||
* This implementation pops the top (active) flow session off the execution stack, ending it, and resumes control in
|
||||
* the parent flow (if neccessary). If the ended session is the root flow, a {@link ViewSelection} is returned.
|
||||
* the parent flow (if necessary). If the ended session is the root flow, a final response is rendered.
|
||||
* @param context the control context for the currently executing flow, used by this state to manipulate the flow
|
||||
* execution
|
||||
* @return a view selection signaling that control should be returned to the client and a view rendered
|
||||
* @throws FlowExecutionException if an exception occurs in this state
|
||||
*/
|
||||
protected ViewSelection doEnter(RequestControlContext context) throws FlowExecutionException {
|
||||
protected void doEnter(final RequestControlContext context) throws FlowExecutionException {
|
||||
FlowSession activeSession = context.getFlowExecutionContext().getActiveSession();
|
||||
if (activeSession.isRoot()) {
|
||||
// entire flow execution is ending, return ending view if applicable
|
||||
ViewSelection selectedView = viewSelector.makeEntrySelection(context);
|
||||
// entire flow execution is ending; issue the final response
|
||||
ActionExecutor.execute(finalResponseAction, context);
|
||||
context.endActiveFlowSession(createSessionOutput(context));
|
||||
return selectedView;
|
||||
} else {
|
||||
// there is a parent flow that will resume (this flow is a subflow)
|
||||
LocalAttributeMap sessionOutput = createSessionOutput(context);
|
||||
context.endActiveFlowSession(sessionOutput);
|
||||
return context.signalEvent(new Event(this, getId(), sessionOutput));
|
||||
context.handleEvent(new Event(this, getId(), sessionOutput));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,13 +115,38 @@ public class EndState extends State {
|
||||
*/
|
||||
protected LocalAttributeMap createSessionOutput(RequestContext context) {
|
||||
LocalAttributeMap outputMap = new LocalAttributeMap();
|
||||
if (outputMapper != null) {
|
||||
outputMapper.map(context, outputMap, null);
|
||||
}
|
||||
outputMapper.map(context, outputMap, null);
|
||||
return outputMap;
|
||||
}
|
||||
|
||||
protected void appendToString(ToStringCreator creator) {
|
||||
creator.append("viewSelector", viewSelector).append("outputMapper", outputMapper);
|
||||
creator.append("finalResponseAction", finalResponseAction).append("outputMapper", outputMapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders no response. The default implementation.
|
||||
*/
|
||||
private class NullFinalResponseAction implements Action {
|
||||
public Event execute(RequestContext context) {
|
||||
logger.debug("Not issuing a final response");
|
||||
return new Event(this, "success");
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "none";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps no output attributes. The default implementation.
|
||||
*/
|
||||
private class NoOutputMapper implements AttributeMapper {
|
||||
public void map(Object source, Object target, MappingContext context) {
|
||||
logger.debug("No output attributes mapped");
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "none";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,17 +21,20 @@ import java.util.Set;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.binding.mapping.AttributeMapper;
|
||||
import org.springframework.binding.mapping.MappingContext;
|
||||
import org.springframework.core.CollectionFactory;
|
||||
import org.springframework.core.style.StylerUtils;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.webflow.context.ExternalContext;
|
||||
import org.springframework.webflow.core.collection.AttributeMap;
|
||||
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.StateDefinition;
|
||||
import org.springframework.webflow.execution.FlowExecutionException;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
import org.springframework.webflow.execution.ViewSelection;
|
||||
|
||||
/**
|
||||
* A single flow definition. A Flow definition is a reusable, self-contained controller module that provides the blue
|
||||
@@ -45,7 +48,7 @@ import org.springframework.webflow.execution.ViewSelection;
|
||||
* <p>
|
||||
* Especially in Intranet applications there are often "controlled navigations" where the user is not free to do what he
|
||||
* or she wants but must follow the guidelines provided by the system to complete a process that is transactional in
|
||||
* nature (the quinessential example would be a 'checkout' flow of a shopping cart application). This is a typical use
|
||||
* nature (the quintessential example would be a 'checkout' flow of a shopping cart application). This is a typical use
|
||||
* case appropriate to model as a flow.
|
||||
* <p>
|
||||
* Structurally a Flow is composed of a set of states. A {@link State} is a point in a flow where a behavior is
|
||||
@@ -55,7 +58,7 @@ import org.springframework.webflow.execution.ViewSelection;
|
||||
* Each {@link TransitionableState} type has one or more transitions that when executed move a flow to another state.
|
||||
* These transitions define the supported paths through the flow.
|
||||
* <p>
|
||||
* A state transition is triggered by the occurence of an event. An event is something that happens the flow should
|
||||
* A state transition is triggered by the occurrence of an event. An event is something that happens the flow should
|
||||
* respond to, for example a user input event like ("submit") or an action execution result event like ("success"). When
|
||||
* an event occurs in a state of a Flow that event drives a state transition that decides what to do next.
|
||||
* <p>
|
||||
@@ -76,14 +79,15 @@ import org.springframework.webflow.execution.ViewSelection;
|
||||
* the ability to design reusable, high-level controller modules that may be executed in <i>any</i> environment.
|
||||
* <p>
|
||||
* Note: flows are singleton definition objects so they should be thread-safe. You can think a flow definition as
|
||||
* analagous somewhat to a Java class, defining all the behavior of an application module. The core behaviors
|
||||
* {@link #start(RequestControlContext, MutableAttributeMap) start}, {@link #onEvent(RequestControlContext) on event},
|
||||
* and {@link #end(RequestControlContext, MutableAttributeMap) end} each accept a {@link RequestContext request context}
|
||||
* that allows for this flow to access execution state in a thread safe manner. A flow execution is what models a
|
||||
* running instance of this flow definition, somewhat analgous to a java object that is an instance of a class.
|
||||
* analogous to a Java class, defining all the behavior of an application module. The core behaviors
|
||||
* {@link #start(RequestControlContext, MutableAttributeMap) start}, {@link #resume(RequestControlContext)},
|
||||
* {@link #handleEvent(RequestControlContext) on event}, {@link #end(RequestControlContext, MutableAttributeMap) end},
|
||||
* and {@link #handleException(FlowExecutionException, RequestControlContext)}. Each method accepts a
|
||||
* {@link RequestContext request context} that allows for this flow to access execution state in a thread safe manner. A
|
||||
* flow execution is what models a running instance of this flow definition, somewhat analogous to a java object that is
|
||||
* an instance of a class.
|
||||
*
|
||||
* @see org.springframework.webflow.engine.State
|
||||
* @see org.springframework.webflow.engine.TransitionableState
|
||||
* @see org.springframework.webflow.engine.ActionState
|
||||
* @see org.springframework.webflow.engine.ViewState
|
||||
* @see org.springframework.webflow.engine.SubflowState
|
||||
@@ -126,7 +130,7 @@ public class Flow extends AnnotatedObject implements FlowDefinition {
|
||||
/**
|
||||
* The mapper to map flow input attributes.
|
||||
*/
|
||||
private AttributeMapper inputMapper;
|
||||
private AttributeMapper inputMapper = new NoInputMapper();
|
||||
|
||||
/**
|
||||
* The list of actions to execute when this flow starts.
|
||||
@@ -149,24 +153,34 @@ public class Flow extends AnnotatedObject implements FlowDefinition {
|
||||
/**
|
||||
* The mapper to map flow output attributes.
|
||||
*/
|
||||
private AttributeMapper outputMapper;
|
||||
private AttributeMapper outputMapper = new NoOutputMapper();
|
||||
|
||||
/**
|
||||
* The set of exception handlers for this flow.
|
||||
*/
|
||||
private FlowExecutionExceptionHandlerSet exceptionHandlerSet = new FlowExecutionExceptionHandlerSet();
|
||||
|
||||
/**
|
||||
* The set of inline flows contained by this flow.
|
||||
*/
|
||||
private Set inlineFlows = CollectionFactory.createLinkedSetIfPossible(3);
|
||||
|
||||
/**
|
||||
* Construct a new flow definition with the given id. The id should be unique among all flows.
|
||||
* @param id the flow identifier
|
||||
*/
|
||||
public Flow(String id) {
|
||||
setId(id);
|
||||
Assert.hasText(id, "This flow must be uniquely identified");
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
// convenient static factory methods
|
||||
|
||||
/**
|
||||
* Create a new flow with the given id and attributes.
|
||||
* @param id the flow id
|
||||
* @param attributes the attributes
|
||||
* @return the flow
|
||||
*/
|
||||
public static Flow create(String id, AttributeMap attributes) {
|
||||
Flow flow = new Flow(id);
|
||||
flow.getAttributeMap().putAll(attributes);
|
||||
return flow;
|
||||
}
|
||||
|
||||
// implementing FlowDefinition
|
||||
@@ -187,14 +201,6 @@ public class Flow extends AnnotatedObject implements FlowDefinition {
|
||||
return getStateInstance(stateId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the unique id of this flow.
|
||||
*/
|
||||
protected void setId(String id) {
|
||||
Assert.hasText(id, "This flow must have a unique, non-blank identifier");
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add given state definition to this flow definition. Marked protected, as this method is to be called by the
|
||||
* (privileged) state definition classes themselves during state construction as part of a FlowBuilder invocation.
|
||||
@@ -358,6 +364,7 @@ public class Flow extends AnnotatedObject implements FlowDefinition {
|
||||
* @param inputMapper the input mapper
|
||||
*/
|
||||
public void setInputMapper(AttributeMapper inputMapper) {
|
||||
Assert.notNull(inputMapper, "The input mapper cannot be null");
|
||||
this.inputMapper = inputMapper;
|
||||
}
|
||||
|
||||
@@ -392,6 +399,7 @@ public class Flow extends AnnotatedObject implements FlowDefinition {
|
||||
* @param outputMapper the output mapper
|
||||
*/
|
||||
public void setOutputMapper(AttributeMapper outputMapper) {
|
||||
Assert.notNull(outputMapper, "The output mapper cannot be null");
|
||||
this.outputMapper = outputMapper;
|
||||
}
|
||||
|
||||
@@ -406,74 +414,6 @@ public class Flow extends AnnotatedObject implements FlowDefinition {
|
||||
return exceptionHandlerSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an inline flow to this flow.
|
||||
* @param flow the inline flow to add
|
||||
*/
|
||||
public void addInlineFlow(Flow flow) {
|
||||
inlineFlows.add(flow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of inline flow ids.
|
||||
* @return a string array of inline flow identifiers
|
||||
*/
|
||||
public String[] getInlineFlowIds() {
|
||||
String[] flowIds = new String[getInlineFlowCount()];
|
||||
int i = 0;
|
||||
Iterator it = inlineFlows.iterator();
|
||||
while (it.hasNext()) {
|
||||
flowIds[i++] = ((Flow) it.next()).getId();
|
||||
}
|
||||
return flowIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of inline flows.
|
||||
* @return the list of inline flows
|
||||
*/
|
||||
public Flow[] getInlineFlows() {
|
||||
return (Flow[]) inlineFlows.toArray(new Flow[inlineFlows.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the count of registered inline flows.
|
||||
* @return the count
|
||||
*/
|
||||
public int getInlineFlowCount() {
|
||||
return inlineFlows.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if this flow contains an in-line flow with the specified id.
|
||||
* @param id the inline flow id
|
||||
* @return true if this flow contains a inline flow with that id, false otherwise
|
||||
*/
|
||||
public boolean containsInlineFlow(String id) {
|
||||
return getInlineFlow(id) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the inline flow with the provided id, or <code>null</code> if no such inline flow exists.
|
||||
* @param id the inline flow id
|
||||
* @return the inline flow
|
||||
* @throws IllegalArgumentException when an invalid flow id is provided
|
||||
*/
|
||||
public Flow getInlineFlow(String id) throws IllegalArgumentException {
|
||||
if (!StringUtils.hasText(id)) {
|
||||
throw new IllegalArgumentException(
|
||||
"The specified inline flowId is invalid: flow identifiers must be non-blank");
|
||||
}
|
||||
Iterator it = inlineFlows.iterator();
|
||||
while (it.hasNext()) {
|
||||
Flow flow = (Flow) it.next();
|
||||
if (flow.getId().equals(id)) {
|
||||
return flow;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of transitions eligible for execution by this flow if no state-level transition is matched. The
|
||||
* returned set is mutable.
|
||||
@@ -499,12 +439,21 @@ public class Flow extends AnnotatedObject implements FlowDefinition {
|
||||
|
||||
// behavioral code, could be overridden in subclasses
|
||||
|
||||
/**
|
||||
* Factory method that creates the initial input map to pass to this flow when a new execution of this flow is
|
||||
* started. Allows this flow to assemble the input map from data in the external context representing the calling
|
||||
* environment.
|
||||
*/
|
||||
public MutableAttributeMap createExecutionInputMap(ExternalContext context) {
|
||||
return new LocalAttributeMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a new session for this flow in its start state. This boils down to the following:
|
||||
* <ol>
|
||||
* <li>Create (setup) all registered flow variables ({@link #addVariable(FlowVariable)}) in flow scope.</li>
|
||||
* <li>Map provided input data into the flow execution control context. Typically data will be mapped into flow
|
||||
* scope using the registered input mapper ({@link #setInputMapper(AttributeMapper)}).</li>
|
||||
* <li>Map provided input data into the flow. Typically data will be mapped into flow scope using the registered
|
||||
* input mapper ({@link #setInputMapper(AttributeMapper)}).</li>
|
||||
* <li>Execute all registered start actions ({@link #getStartActionList()}).</li>
|
||||
* <li>Enter the configured start state ({@link #setStartState(State)})</li>
|
||||
* </ol>
|
||||
@@ -512,31 +461,36 @@ public class Flow extends AnnotatedObject implements FlowDefinition {
|
||||
* @param input eligible input into the session
|
||||
* @throws FlowExecutionException when an exception occurs starting the flow
|
||||
*/
|
||||
public ViewSelection start(RequestControlContext context, MutableAttributeMap input) throws FlowExecutionException {
|
||||
public void start(RequestControlContext context, MutableAttributeMap input) throws FlowExecutionException {
|
||||
assertStartStateSet();
|
||||
createVariables(context);
|
||||
if (inputMapper != null) {
|
||||
inputMapper.map(input, context, null);
|
||||
}
|
||||
inputMapper.map(input, context, null);
|
||||
startActionList.execute(context);
|
||||
return startState.enter(context);
|
||||
startState.enter(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inform this flow definition that an event was signaled in the current state of an active flow execution. The
|
||||
* signaled event is the last event available in given request context ({@link RequestContext#getLastEvent()}).
|
||||
* Resume a paused session for this flow in its current view state.
|
||||
* @param context the flow execution control context
|
||||
* @return the selected view
|
||||
* @throws FlowExecutionException when an exception occurs processing the event
|
||||
* @throws FlowExecutionException when an exception occurs during the resume operation
|
||||
*/
|
||||
public ViewSelection onEvent(RequestControlContext context) throws FlowExecutionException {
|
||||
public void resume(RequestControlContext context) throws FlowExecutionException {
|
||||
getCurrentViewState(context).resume(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the last event that occurred against an active session of this flow.
|
||||
* @param context the flow execution control context
|
||||
*/
|
||||
public void handleEvent(RequestControlContext context) {
|
||||
TransitionableState currentState = getCurrentTransitionableState(context);
|
||||
try {
|
||||
return currentState.onEvent(context);
|
||||
currentState.handleEvent(context);
|
||||
} catch (NoMatchingTransitionException e) {
|
||||
// try the flow level transition set for a match
|
||||
Transition transition = globalTransitionSet.getTransition(context);
|
||||
if (transition != null) {
|
||||
return transition.execute(currentState, context);
|
||||
transition.execute(currentState, context);
|
||||
} else {
|
||||
// no matching global transition => let the original exception
|
||||
// propagate
|
||||
@@ -559,25 +513,28 @@ public class Flow extends AnnotatedObject implements FlowDefinition {
|
||||
*/
|
||||
public void end(RequestControlContext context, MutableAttributeMap output) throws FlowExecutionException {
|
||||
endActionList.execute(context);
|
||||
if (outputMapper != null) {
|
||||
outputMapper.map(context, output, null);
|
||||
}
|
||||
outputMapper.map(context, output, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an exception that occured during an execution of this flow.
|
||||
* @param exception the exception that occured
|
||||
* Handle an exception that occurred during an execution of this flow.
|
||||
* @param exception the exception that occurred
|
||||
* @param context the flow execution control context
|
||||
* @return the selected error view, or <code>null</code> if no handler matched or returned a non-null view
|
||||
* selection
|
||||
*/
|
||||
public ViewSelection handleException(FlowExecutionException exception, RequestControlContext context)
|
||||
public boolean handleException(FlowExecutionException exception, RequestControlContext context)
|
||||
throws FlowExecutionException {
|
||||
return getExceptionHandlerSet().handleException(exception, context);
|
||||
}
|
||||
|
||||
// internal helpers
|
||||
|
||||
private void assertStartStateSet() {
|
||||
if (startState == null) {
|
||||
throw new IllegalStateException("Unable to start flow '" + id
|
||||
+ "'; the start state is not set -- flow builder configuration error?");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create (setup) all known flow variables in flow scope.
|
||||
*/
|
||||
@@ -592,6 +549,18 @@ public class Flow extends AnnotatedObject implements FlowDefinition {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current state and makes sure it is a view state.
|
||||
*/
|
||||
private ViewState getCurrentViewState(RequestControlContext context) {
|
||||
State currentState = (State) context.getCurrentState();
|
||||
if (!(currentState instanceof ViewState)) {
|
||||
throw new IllegalStateException("You can only resume paused view states, and state "
|
||||
+ context.getCurrentState() + " is not a view state - programmer error");
|
||||
}
|
||||
return (ViewState) currentState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current state and makes sure it is transitionable.
|
||||
*/
|
||||
@@ -604,11 +573,39 @@ public class Flow extends AnnotatedObject implements FlowDefinition {
|
||||
return (TransitionableState) currentState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps no input attributes. The default implementation.
|
||||
*/
|
||||
private class NoInputMapper implements AttributeMapper {
|
||||
public void map(Object source, Object target, MappingContext context) {
|
||||
logger.debug("No input attributes mapped");
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "none";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps no input attributes. The default implementation.
|
||||
*/
|
||||
private class NoOutputMapper implements AttributeMapper {
|
||||
public void map(Object source, Object target, MappingContext context) {
|
||||
logger.debug("No output attributes mapped");
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "none";
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("id", id).append("states", states).append("startState", startState)
|
||||
.append("variables", variables).append("inputMapper", inputMapper).append("startActionList",
|
||||
startActionList).append("exceptionHandlerSet", exceptionHandlerSet).append(
|
||||
"globalTransitionSet", globalTransitionSet).append("endActionList", endActionList).append(
|
||||
"outputMapper", outputMapper).append("inlineFlows", inlineFlows).toString();
|
||||
"outputMapper", outputMapper).toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,7 +16,6 @@
|
||||
package org.springframework.webflow.engine;
|
||||
|
||||
import org.springframework.webflow.execution.FlowExecutionException;
|
||||
import org.springframework.webflow.execution.ViewSelection;
|
||||
|
||||
/**
|
||||
* A strategy for handling an exception that occurs at runtime during the execution of a flow definition.
|
||||
@@ -27,18 +26,16 @@ public interface FlowExecutionExceptionHandler {
|
||||
|
||||
/**
|
||||
* Can this handler handle the given exception?
|
||||
* @param exception the exception that occured
|
||||
* @param exception the exception that occurred
|
||||
* @return true if yes, false if no
|
||||
*/
|
||||
public boolean handles(FlowExecutionException exception);
|
||||
public boolean canHandle(FlowExecutionException exception);
|
||||
|
||||
/**
|
||||
* Handle the exception in the context of the current request, optionally making an error view selection that should
|
||||
* be rendered.
|
||||
* @param exception the exception that occured
|
||||
* @param exception the exception that occurred
|
||||
* @param context the execution control context for this request
|
||||
* @return the selected error view that should be displayed (may be null if the handler chooses not to select a
|
||||
* view, in which case other exception handlers may be given a chance to handle the exception)
|
||||
*/
|
||||
public ViewSelection handle(FlowExecutionException exception, RequestControlContext context);
|
||||
public void handle(FlowExecutionException exception, RequestControlContext context);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import java.util.List;
|
||||
import org.springframework.core.style.StylerUtils;
|
||||
import org.springframework.webflow.core.collection.CollectionUtils;
|
||||
import org.springframework.webflow.execution.FlowExecutionException;
|
||||
import org.springframework.webflow.execution.ViewSelection;
|
||||
|
||||
/**
|
||||
* A typed set of state exception handlers, mainly for use internally by artifacts that can apply state exception
|
||||
@@ -98,28 +97,24 @@ public class FlowExecutionExceptionHandlerSet {
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an exception that occured during the context of the current flow execution request.
|
||||
* Handle an exception that occurred during the context of the current flow execution request.
|
||||
* <p>
|
||||
* This implementation iterates over the ordered set of exception handler objects, delegating to each handler in the
|
||||
* set until one handles the exception that occured and selects a non-null error view.
|
||||
* @param exception the exception that occured
|
||||
* set until one handles the exception that occurred.
|
||||
* @param exception the exception that occurred
|
||||
* @param context the flow execution control context
|
||||
* @return the selected error view, or <code>null</code> if no handler matched or returned a non-null view
|
||||
* selection
|
||||
* @return true if the exception was handled
|
||||
*/
|
||||
public ViewSelection handleException(FlowExecutionException exception, RequestControlContext context) {
|
||||
public boolean handleException(FlowExecutionException exception, RequestControlContext context) {
|
||||
Iterator it = exceptionHandlers.iterator();
|
||||
while (it.hasNext()) {
|
||||
FlowExecutionExceptionHandler handler = (FlowExecutionExceptionHandler) it.next();
|
||||
if (handler.handles(exception)) {
|
||||
ViewSelection result = handler.handle(exception, context);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
// else continue with next handler
|
||||
if (handler.canHandle(exception)) {
|
||||
handler.handle(exception, context);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
|
||||
@@ -29,7 +29,7 @@ import org.springframework.webflow.execution.FlowExecutionException;
|
||||
public class NoMatchingTransitionException extends FlowExecutionException {
|
||||
|
||||
/**
|
||||
* The event that occured that could not be matched to a Transition.
|
||||
* The event that occurred that could not be matched to a Transition.
|
||||
*/
|
||||
private Event event;
|
||||
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* 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.engine;
|
||||
|
||||
import java.io.ObjectStreamException;
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
import org.springframework.webflow.execution.ViewSelection;
|
||||
|
||||
/**
|
||||
* Makes a null view selection, indicating no response should be issued.
|
||||
*
|
||||
* @see org.springframework.webflow.execution.ViewSelection#NULL_VIEW
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public final class NullViewSelector implements ViewSelector, Serializable {
|
||||
|
||||
/*
|
||||
* Implementation note: not located in webflow.execution.support package to avoid a cyclic dependency between
|
||||
* webflow.execution and webflow.execution.support.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The shared singleton {@link NullViewSelector} instance.
|
||||
*/
|
||||
public static final ViewSelector INSTANCE = new NullViewSelector();
|
||||
|
||||
/**
|
||||
* Private constructor since this is a singleton.
|
||||
*/
|
||||
private NullViewSelector() {
|
||||
}
|
||||
|
||||
public boolean isEntrySelectionRenderable(RequestContext context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public ViewSelection makeEntrySelection(RequestContext context) {
|
||||
return ViewSelection.NULL_VIEW;
|
||||
}
|
||||
|
||||
public ViewSelection makeRefreshSelection(RequestContext context) {
|
||||
return makeEntrySelection(context);
|
||||
}
|
||||
|
||||
// resolve the singleton instance
|
||||
private Object readResolve() throws ObjectStreamException {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,17 +19,16 @@ import org.springframework.webflow.core.collection.MutableAttributeMap;
|
||||
import org.springframework.webflow.execution.Event;
|
||||
import org.springframework.webflow.execution.FlowExecutionContext;
|
||||
import org.springframework.webflow.execution.FlowExecutionException;
|
||||
import org.springframework.webflow.execution.FlowExecutionKey;
|
||||
import org.springframework.webflow.execution.FlowSession;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
import org.springframework.webflow.execution.ViewSelection;
|
||||
|
||||
/**
|
||||
* Mutable control interface used to manipulate an ongoing flow execution in the context of one client request.
|
||||
* Primarily used internally by the various flow artifacts when they are invoked.
|
||||
* <p>
|
||||
* This interface acts as a facade for core definition constructs such as the central <code>Flow</code> and
|
||||
* <code>State</code> classes, abstracting away details about the runtime execution machine defined in the
|
||||
* {@link org.springframework.webflow.engine.impl execution engine implementation} package.
|
||||
* <code>State</code> classes, abstracting away details about the runtime execution machine.
|
||||
* <p>
|
||||
* Note this type is not the same as the {@link FlowExecutionContext}. Objects of this type are <i>request specific</i>:
|
||||
* they provide a control interface for manipulating exactly one flow execution locally from exactly one request. A
|
||||
@@ -47,12 +46,12 @@ import org.springframework.webflow.execution.ViewSelection;
|
||||
public interface RequestControlContext extends RequestContext {
|
||||
|
||||
/**
|
||||
* Record the last event signaled in the executing flow. This method will be called as part of signaling an event in
|
||||
* a flow to indicate the 'lastEvent' that was signaled.
|
||||
* @param lastEvent the last event signaled
|
||||
* @see Flow#onEvent(RequestControlContext)
|
||||
* Record the current state that has entered in the executing flow. This method will be called as part of entering a
|
||||
* new state by the State type itself.
|
||||
* @param state the current state
|
||||
* @see State#enter(RequestControlContext)
|
||||
*/
|
||||
public void setLastEvent(Event lastEvent);
|
||||
public void setCurrentState(State state);
|
||||
|
||||
/**
|
||||
* Record the last transition that executed in the executing flow. This method will be called as part of executing a
|
||||
@@ -63,12 +62,10 @@ public interface RequestControlContext extends RequestContext {
|
||||
public void setLastTransition(Transition lastTransition);
|
||||
|
||||
/**
|
||||
* Record the current state that has entered in the executing flow. This method will be called as part of entering a
|
||||
* new state by the State type itself.
|
||||
* @param state the current state
|
||||
* @see State#enter(RequestControlContext)
|
||||
* Assign the ongoing flow execution its flow execution key. This method will be called before a state is about to
|
||||
* render a view and pause the flow execution.
|
||||
*/
|
||||
public void setCurrentState(State state);
|
||||
public FlowExecutionKey assignFlowExecutionKey();
|
||||
|
||||
/**
|
||||
* Spawn a new flow session and activate it in the currently executing flow. Also transitions the spawned flow to
|
||||
@@ -77,26 +74,22 @@ public interface RequestControlContext extends RequestContext {
|
||||
* This will start a new flow session in the current flow execution, which is already active.
|
||||
* @param flow the flow to start, its <code>start()</code> method will be called
|
||||
* @param input initial contents of the newly created flow session (may be <code>null</code>, e.g. empty)
|
||||
* @return the selected starting view, which returns control to the client and requests that a view be rendered with
|
||||
* model data
|
||||
* @throws FlowExecutionException if an exception was thrown within a state of the flow during execution of this
|
||||
* start operation
|
||||
* @see Flow#start(RequestControlContext, MutableAttributeMap)
|
||||
*/
|
||||
public ViewSelection start(Flow flow, MutableAttributeMap input) throws FlowExecutionException;
|
||||
public void start(Flow flow, MutableAttributeMap input) throws FlowExecutionException;
|
||||
|
||||
/**
|
||||
* Signals the occurence of an event in the current state of this flow execution request context. This method should
|
||||
* be called by clients that report internal event occurences, such as action states. The <code>onEvent()</code>
|
||||
* method of the flow involved in the flow execution will be called.
|
||||
* @param event the event that occured
|
||||
* @return the next selected view, which returns control to the client and requests that a view be rendered with
|
||||
* model data
|
||||
* Signals the occurrence of an event in the current state of this flow execution request context. This method
|
||||
* should be called by clients that report internal event occurrences, such as action states. The
|
||||
* <code>onEvent()</code> method of the flow involved in the flow execution will be called.
|
||||
* @param event the event that occurred
|
||||
* @throws FlowExecutionException if an exception was thrown within a state of the flow during execution of this
|
||||
* signalEvent operation
|
||||
* @see Flow#onEvent(RequestControlContext)
|
||||
* @see Flow#handleEvent(RequestControlContext)
|
||||
*/
|
||||
public ViewSelection signalEvent(Event event) throws FlowExecutionException;
|
||||
public void handleEvent(Event event) throws FlowExecutionException;
|
||||
|
||||
/**
|
||||
* End the active flow session of the current flow execution. This method should be called by clients that terminate
|
||||
@@ -113,9 +106,21 @@ public interface RequestControlContext extends RequestContext {
|
||||
* Execute this transition out of the current source state. Allows for privileged execution of an arbitrary
|
||||
* transition.
|
||||
* @param transition the transition
|
||||
* @return a new view selection
|
||||
* @see Transition#execute(State, RequestControlContext)
|
||||
*/
|
||||
public ViewSelection execute(Transition transition);
|
||||
public void execute(Transition transition);
|
||||
|
||||
/**
|
||||
* Returns true if the 'always redirect pause' flow execution attribute is set to true, false otherwise.
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean getAlwaysRedirectOnPause();
|
||||
|
||||
/**
|
||||
* Request that a redirect be sent to this flow execution after the current request has processed. The current flow
|
||||
* execution must have its key assigned for this operation to be supported.
|
||||
* @throws IllegalStateException if the flow execution has not yet had its key assigned
|
||||
*/
|
||||
public void sendFlowExecutionRedirect() throws IllegalStateException;
|
||||
|
||||
}
|
||||
@@ -22,7 +22,6 @@ import org.springframework.util.Assert;
|
||||
import org.springframework.webflow.definition.FlowDefinition;
|
||||
import org.springframework.webflow.definition.StateDefinition;
|
||||
import org.springframework.webflow.execution.FlowExecutionException;
|
||||
import org.springframework.webflow.execution.ViewSelection;
|
||||
|
||||
/**
|
||||
* A point in a flow where something happens. What happens is determined by a state's type. Standard types of states
|
||||
@@ -32,8 +31,8 @@ import org.springframework.webflow.execution.ViewSelection;
|
||||
* configuration information needed for a specific kind of state.
|
||||
* <p>
|
||||
* Subclasses should implement the <code>doEnter</code> method to execute the processing that should occur when this
|
||||
* state is entered, acting on its configuration information. The ability to plugin custom state types that execute
|
||||
* different behaviour polymorphically is the classic GoF state pattern.
|
||||
* state is entered, acting on its configuration information. The ability to plug-in custom state types that execute
|
||||
* different behaviors is the classic GoF state pattern.
|
||||
* <p>
|
||||
* Equality: Two states are equal if they have the same id and are part of the same flow.
|
||||
*
|
||||
@@ -178,53 +177,47 @@ public abstract class State extends AnnotatedObject implements StateDefinition {
|
||||
* the entry actions.
|
||||
* @param context the control context for the currently executing flow, used by this state to manipulate the flow
|
||||
* execution
|
||||
* @return a view selection containing model and view information needed to render the results of the state
|
||||
* processing
|
||||
* @throws FlowExecutionException if an exception occurs in this state
|
||||
*/
|
||||
public final ViewSelection enter(RequestControlContext context) throws FlowExecutionException {
|
||||
public final void enter(RequestControlContext context) throws FlowExecutionException {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Entering state '" + getId() + "' of flow '" + getFlow().getId() + "'");
|
||||
}
|
||||
context.setCurrentState(this);
|
||||
entryActionList.execute(context);
|
||||
return doEnter(context);
|
||||
doEnter(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook method to execute custom behaviour as a result of entering this state. By implementing this method
|
||||
* subclasses specialize the behaviour of the state.
|
||||
* Hook method to execute custom behavior as a result of entering this state. By implementing this method subclasses
|
||||
* specialize the behavior of the state.
|
||||
* @param context the control context for the currently executing flow, used by this state to manipulate the flow
|
||||
* execution
|
||||
* @return a view selection containing model and view information needed to render the results of the state
|
||||
* processing
|
||||
* @throws FlowExecutionException if an exception occurs in this state
|
||||
*/
|
||||
protected abstract ViewSelection doEnter(RequestControlContext context) throws FlowExecutionException;
|
||||
protected abstract void doEnter(RequestControlContext context) throws FlowExecutionException;
|
||||
|
||||
/**
|
||||
* Handle an exception that occured in this state during the context of the current flow execution request.
|
||||
* @param exception the exception that occured
|
||||
* Handle an exception that occurred in this state during the context of the current flow execution request.
|
||||
* @param exception the exception that occurred
|
||||
* @param context the flow execution control context
|
||||
* @return the selected error view, or <code>null</code> if no handler matched or returned a non-null view
|
||||
* selection
|
||||
*/
|
||||
public ViewSelection handleException(FlowExecutionException exception, RequestControlContext context) {
|
||||
public boolean handleException(FlowExecutionException exception, RequestControlContext context) {
|
||||
return getExceptionHandlerSet().handleException(exception, context);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
String flowName = (flow == null ? "<not set>" : flow.getId());
|
||||
ToStringCreator creator = new ToStringCreator(this).append("id", getId()).append("flow", flowName).append(
|
||||
ToStringCreator creator = new ToStringCreator(this).append("id", getId()).append("flow", flow.getId()).append(
|
||||
"entryActionList", entryActionList).append("exceptionHandlerSet", exceptionHandlerSet);
|
||||
appendToString(creator);
|
||||
return creator.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses may override this hook method to stringify their internal state. This default implementation does
|
||||
* nothing.
|
||||
* @param creator the toString creator, to stringify properties
|
||||
* Subclasses may override this hook method to print their internal state to a string. This default implementation
|
||||
* does nothing.
|
||||
* @param creator the toString creator, to print properties to string
|
||||
* @see #toString()
|
||||
*/
|
||||
protected void appendToString(ToStringCreator creator) {
|
||||
}
|
||||
|
||||
@@ -18,10 +18,10 @@ package org.springframework.webflow.engine;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.webflow.core.collection.AttributeMap;
|
||||
import org.springframework.webflow.core.collection.LocalAttributeMap;
|
||||
import org.springframework.webflow.core.collection.MutableAttributeMap;
|
||||
import org.springframework.webflow.execution.FlowExecutionException;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
import org.springframework.webflow.execution.ViewSelection;
|
||||
|
||||
/**
|
||||
* A transitionable state that spawns a subflow when executed. When the subflow this state spawns ends, the ending
|
||||
@@ -41,14 +41,14 @@ import org.springframework.webflow.execution.ViewSelection;
|
||||
public class SubflowState extends TransitionableState {
|
||||
|
||||
/**
|
||||
* The subflow that should be spawned when this subflow state is entered.
|
||||
* The subflow that should be spawned when this subflow state is entered. TODO - late binding
|
||||
*/
|
||||
private Flow subflow;
|
||||
|
||||
/**
|
||||
* The attribute mapper that should map attributes from the parent flow down to the spawned subflow and visa versa.
|
||||
*/
|
||||
private FlowAttributeMapper attributeMapper;
|
||||
private FlowAttributeMapper attributeMapper = new NoAttributeMapper();
|
||||
|
||||
/**
|
||||
* Create a new subflow state.
|
||||
@@ -64,15 +64,7 @@ public class SubflowState extends TransitionableState {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the subflow spawned by this state.
|
||||
*/
|
||||
public Flow getSubflow() {
|
||||
return subflow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the subflow that will be spawned by this state.
|
||||
* @param subflow the subflow to spawn
|
||||
* Set the subflow this state will call.
|
||||
*/
|
||||
private void setSubflow(Flow subflow) {
|
||||
Assert.notNull(subflow, "A subflow state must have a subflow; the subflow is required");
|
||||
@@ -80,17 +72,10 @@ public class SubflowState extends TransitionableState {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the attribute mapper used to map data between the parent and child flow, or null if no mapping is needed.
|
||||
*/
|
||||
public FlowAttributeMapper getAttributeMapper() {
|
||||
return attributeMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the attribute mapper used to map model data between the parent and child flow. Can be null if no mapping is
|
||||
* needed.
|
||||
* Set the attribute mapper used to map model data between the parent and child flow.
|
||||
*/
|
||||
public void setAttributeMapper(FlowAttributeMapper attributeMapper) {
|
||||
Assert.notNull(attributeMapper, "The attribute mapper is required");
|
||||
this.attributeMapper = attributeMapper;
|
||||
}
|
||||
|
||||
@@ -101,70 +86,41 @@ public class SubflowState extends TransitionableState {
|
||||
* Entering this state, creates the subflow input map and spawns the subflow in the current flow execution.
|
||||
* @param context the control context for the currently executing flow, used by this state to manipulate the flow
|
||||
* execution
|
||||
* @return a view selection containing model and view information needed to render the results of the state
|
||||
* execution
|
||||
* @throws FlowExecutionException if an exception occurs in this state
|
||||
*/
|
||||
protected ViewSelection doEnter(RequestControlContext context) throws FlowExecutionException {
|
||||
protected void doEnter(RequestControlContext context) throws FlowExecutionException {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Spawning subflow '" + getSubflow().getId() + "' within flow '" + getFlow().getId() + "'");
|
||||
}
|
||||
return context.start(getSubflow(), createSubflowInput(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the input data map for the spawned subflow session. The returned map will be passed to
|
||||
* {@link Flow#start(RequestControlContext, MutableAttributeMap)}.
|
||||
*/
|
||||
protected MutableAttributeMap createSubflowInput(RequestContext context) {
|
||||
if (getAttributeMapper() != null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Messaging the configured attribute mapper to map attributes "
|
||||
+ "down to the spawned subflow for access within the subflow");
|
||||
}
|
||||
return getAttributeMapper().createFlowInput(context);
|
||||
} else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("No attribute mapper configured for this subflow state '" + getId()
|
||||
+ "' -- As a result, no attributes will be passed to the spawned subflow '" + subflow.getId()
|
||||
+ "'");
|
||||
}
|
||||
return null;
|
||||
logger.debug("Calling subflow '" + subflow.getId() + "'");
|
||||
}
|
||||
context.start(subflow, attributeMapper.createFlowInput(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on completion of the subflow to handle the subflow result event as determined by the end state reached by
|
||||
* the subflow.
|
||||
*/
|
||||
public ViewSelection onEvent(RequestControlContext context) {
|
||||
mapSubflowOutput(context.getLastEvent().getAttributes(), context);
|
||||
return super.onEvent(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map the output data produced by the subflow back into the request context (typically flow scope).
|
||||
*/
|
||||
private void mapSubflowOutput(AttributeMap subflowOutput, RequestContext context) {
|
||||
if (getAttributeMapper() != null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger
|
||||
.debug("Messaging the configured attribute mapper to map subflow result attributes to the "
|
||||
+ "resuming parent flow -- It will have access to attributes passed up by the completed subflow");
|
||||
}
|
||||
attributeMapper.mapFlowOutput(subflowOutput, context);
|
||||
} else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger
|
||||
.debug("No attribute mapper is configured for the resuming subflow state '"
|
||||
+ getId()
|
||||
+ "' -- As a result, no attributes of the ending flow will be passed to the resuming parent flow");
|
||||
}
|
||||
}
|
||||
public void handleEvent(RequestControlContext context) {
|
||||
attributeMapper.mapFlowOutput(context.getLastEvent().getAttributes(), context);
|
||||
super.handleEvent(context);
|
||||
}
|
||||
|
||||
protected void appendToString(ToStringCreator creator) {
|
||||
creator.append("subflow", subflow.getId()).append("attributeMapper", attributeMapper);
|
||||
super.appendToString(creator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps no output attributes. The default implementation.
|
||||
*/
|
||||
private class NoAttributeMapper implements FlowAttributeMapper {
|
||||
public MutableAttributeMap createFlowInput(RequestContext context) {
|
||||
logger.debug("No input will be passed to subflow");
|
||||
return new LocalAttributeMap();
|
||||
}
|
||||
|
||||
public void mapFlowOutput(AttributeMap flowOutput, RequestContext context) {
|
||||
logger.debug("No subflow output will be mapped");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,13 +23,12 @@ import org.springframework.webflow.definition.TransitionDefinition;
|
||||
import org.springframework.webflow.execution.Event;
|
||||
import org.springframework.webflow.execution.FlowExecutionException;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
import org.springframework.webflow.execution.ViewSelection;
|
||||
|
||||
/**
|
||||
* A path from one {@link TransitionableState state} to another {@link State state}.
|
||||
* <p>
|
||||
* When executed a transition takes a flow execution from its current state, called the <i>source state</i>, to another
|
||||
* state, called the </i>target state</i>. A transition may become eligible for execution on the occurence of an
|
||||
* state, called the </i>target state</i>. A transition may become eligible for execution on the occurrence of an
|
||||
* {@link Event} from within a transitionable source state.
|
||||
* <p>
|
||||
* When an event occurs within this transition's source <code>TransitionableState</code> the determination of the
|
||||
@@ -163,7 +162,7 @@ public class Transition extends AnnotatedObject implements TransitionDefinition
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this transition is elligible for execution given the state of the provided flow execution request
|
||||
* Checks if this transition is eligible for execution given the state of the provided flow execution request
|
||||
* context.
|
||||
* @param context the flow execution request context
|
||||
* @return true if this transition should execute, false otherwise
|
||||
@@ -183,15 +182,13 @@ public class Transition extends AnnotatedObject implements TransitionDefinition
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute this state transition. Will only be called if the {@link #matches(RequestContext)} method returns true
|
||||
* for given context.
|
||||
* Execute this state transition. Should only be called if the {@link #matches(RequestContext)} method returns true
|
||||
* for the given context.
|
||||
* @param sourceState the source state to transition from, may be null if the current state is null
|
||||
* @param context the flow execution control context
|
||||
* @return a view selection containing model and view information needed to render the results of the transition
|
||||
* execution
|
||||
* @throws FlowExecutionException when transition execution fails
|
||||
*/
|
||||
public ViewSelection execute(State sourceState, RequestControlContext context) throws FlowExecutionException {
|
||||
ViewSelection selectedView;
|
||||
public void execute(State sourceState, RequestControlContext context) throws FlowExecutionException {
|
||||
if (canExecute(context)) {
|
||||
if (sourceState != null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
@@ -209,11 +206,10 @@ public class Transition extends AnnotatedObject implements TransitionDefinition
|
||||
State targetState = targetStateResolver.resolveTargetState(this, sourceState, context);
|
||||
context.setLastTransition(this);
|
||||
// enter the target state (note: any exceptions are propagated)
|
||||
selectedView = targetState.enter(context);
|
||||
targetState.enter(context);
|
||||
} else {
|
||||
if (sourceState != null && sourceState instanceof TransitionableState) {
|
||||
// 'roll back' and re-enter the transitionable source state
|
||||
selectedView = ((TransitionableState) sourceState).reenter(context);
|
||||
((TransitionableState) sourceState).reenter(context);
|
||||
} else {
|
||||
throw new IllegalStateException("Execution of '" + this + "' was blocked by '" + getExecutionCriteria()
|
||||
+ "', " + "; however, no source state is set at runtime. "
|
||||
@@ -222,13 +218,12 @@ public class Transition extends AnnotatedObject implements TransitionDefinition
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
if (context.getFlowExecutionContext().isActive()) {
|
||||
logger.debug("Completed execution of " + this + ", as a result the new state is '"
|
||||
logger.debug("Completed execution of " + this + "; as a result, the new state is '"
|
||||
+ context.getCurrentState().getId() + "' in flow '" + context.getActiveFlow().getId() + "'");
|
||||
} else {
|
||||
logger.debug("Completed execution of " + this + ", as a result the flow execution has ended");
|
||||
logger.debug("Completed execution of " + this + "; as a result, the flow execution has ended");
|
||||
}
|
||||
}
|
||||
return selectedView;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
|
||||
@@ -20,7 +20,6 @@ import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.webflow.definition.TransitionDefinition;
|
||||
import org.springframework.webflow.definition.TransitionableStateDefinition;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
import org.springframework.webflow.execution.ViewSelection;
|
||||
|
||||
/**
|
||||
* Abstract superclass for states that can execute a transition in response to an event.
|
||||
@@ -100,11 +99,10 @@ public abstract class TransitionableState extends State implements Transitionabl
|
||||
* Inform this state definition that an event was signaled in it. The signaled event is the last event available in
|
||||
* given request context ({@link RequestContext#getLastEvent()}).
|
||||
* @param context the flow execution control context
|
||||
* @return the selected view
|
||||
* @throws NoMatchingTransitionException when a matching transition cannot be found
|
||||
*/
|
||||
public ViewSelection onEvent(RequestControlContext context) throws NoMatchingTransitionException {
|
||||
return getRequiredTransition(context).execute(this, context);
|
||||
public void handleEvent(RequestControlContext context) throws NoMatchingTransitionException {
|
||||
context.execute(getRequiredTransition(context));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,11 +111,9 @@ public abstract class TransitionableState extends State implements Transitionabl
|
||||
* <p>
|
||||
* By default, this just calls <code>enter()</code>.
|
||||
* @param context the flow control context in an executing flow (a client instance of a flow)
|
||||
* @return a view selection containing model and view information needed to render the results of the state
|
||||
* processing
|
||||
*/
|
||||
public ViewSelection reenter(RequestControlContext context) {
|
||||
return enter(context);
|
||||
public void reenter(RequestControlContext context) {
|
||||
enter(context);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* 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.engine;
|
||||
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
import org.springframework.webflow.execution.ViewSelection;
|
||||
|
||||
/**
|
||||
* Factory that produces a new, configured {@link ViewSelection} object on each invocation, taking into account the
|
||||
* information in the provided flow execution request context.
|
||||
* <p>
|
||||
* Note: this class is a runtime factory. Instances are used at flow execution time by objects like the
|
||||
* {@link ViewState} to produce new {@link ViewSelection view selections}.
|
||||
* <p>
|
||||
* This class allows for easy insertion of dynamic view selection logic, for instance, letting you determine the view to
|
||||
* render or the available model data for rendering based on contextual information.
|
||||
*
|
||||
* @see org.springframework.webflow.execution.ViewSelection
|
||||
* @see org.springframework.webflow.engine.ViewState
|
||||
* @see org.springframework.webflow.engine.EndState
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Erwin Vervaet
|
||||
*/
|
||||
public interface ViewSelector {
|
||||
|
||||
/**
|
||||
* Will the primary selection returned by 'makeEntrySelection' for the given request context be renderable in this
|
||||
* request?
|
||||
* <p>
|
||||
* "Renderable" view selections typically can have 'render-actions' execute before they are created. An example
|
||||
* would be an ApplicationView that forwards to a view template like a JSP. "Non-renderable" view selections are
|
||||
* things like a flow execution redirect--no render actually occurs, but only a redirect--rendering happens on the
|
||||
* new redirect request.
|
||||
* @param context the current request context of the executing flow
|
||||
* @return true if yes, false otherwise
|
||||
*/
|
||||
public boolean isEntrySelectionRenderable(RequestContext context);
|
||||
|
||||
/**
|
||||
* Make a new "entry" view selection for the given request context. Called when a view-state, end-state, or other
|
||||
* interactive state type is entered.
|
||||
* @param context the current request context of the executing flow
|
||||
* @return the entry view selection
|
||||
*/
|
||||
public ViewSelection makeEntrySelection(RequestContext context);
|
||||
|
||||
/**
|
||||
* Reconstitute a renderable view selection for the given request context to support a ViewState 'refresh'
|
||||
* operation.
|
||||
* @param context the current request context of the executing flow
|
||||
* @return the view selection
|
||||
*/
|
||||
public ViewSelection makeRefreshSelection(RequestContext context);
|
||||
|
||||
}
|
||||
@@ -18,16 +18,14 @@ package org.springframework.webflow.engine;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.webflow.execution.FlowExecutionException;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
import org.springframework.webflow.execution.ViewSelection;
|
||||
import org.springframework.webflow.execution.View;
|
||||
import org.springframework.webflow.execution.ViewFactory;
|
||||
|
||||
/**
|
||||
* A view state is a state that issues a response to the user, for example, for soliciting form input.
|
||||
* <p>
|
||||
* To accomplish this, a <code>ViewState</code> makes a {@link ViewSelection}, which contains the necessary
|
||||
* information to issue a suitable response.
|
||||
* A view state is a state that issues a response to the user, for example, for soliciting form input. To accomplish
|
||||
* this, a <code>ViewState</code> delegates to a {@link ViewFactory}.
|
||||
*
|
||||
* @see org.springframework.webflow.engine.ViewSelector
|
||||
* @see ViewFactory
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Erwin Vervaet
|
||||
@@ -40,33 +38,34 @@ public class ViewState extends TransitionableState {
|
||||
private ActionList renderActionList = new ActionList();
|
||||
|
||||
/**
|
||||
* The factory for the view selection to return when this state is entered.
|
||||
* Whether or not a redirect should occur before the view is rendered.
|
||||
*/
|
||||
private ViewSelector viewSelector = NullViewSelector.INSTANCE;
|
||||
private boolean redirect;
|
||||
|
||||
/**
|
||||
* A factory for creating and restoring the view rendered by this view state.
|
||||
*/
|
||||
private ViewFactory viewFactory;
|
||||
|
||||
/**
|
||||
* Create a new view state.
|
||||
* @param flow the owning flow
|
||||
* @param id the state identifier (must be unique to the flow)
|
||||
* @param viewFactory the view factory
|
||||
* @throws IllegalArgumentException when this state cannot be added to given flow, e.g. because the id is not unique
|
||||
*/
|
||||
public ViewState(Flow flow, String id) throws IllegalArgumentException {
|
||||
public ViewState(Flow flow, String id, ViewFactory viewFactory) throws IllegalArgumentException {
|
||||
super(flow, id);
|
||||
Assert.notNull(viewFactory, "The view factory is required");
|
||||
this.viewFactory = viewFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the strategy used to select the view to render in this view state.
|
||||
* Sets whether this view state should send a flow execution redirect when entered.
|
||||
* @param redirect the redirect flag
|
||||
*/
|
||||
public ViewSelector getViewSelector() {
|
||||
return viewSelector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the strategy used to select the view to render in this view state.
|
||||
*/
|
||||
public void setViewSelector(ViewSelector viewSelector) {
|
||||
Assert.notNull(viewSelector, "The view selector to make view selections is required");
|
||||
this.viewSelector = viewSelector;
|
||||
public void setRedirect(boolean redirect) {
|
||||
this.redirect = redirect;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,40 +76,37 @@ public class ViewState extends TransitionableState {
|
||||
return renderActionList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specialization of State's <code>doEnter</code> template method that executes behavior specific to this state
|
||||
* type in polymorphic fashion.
|
||||
* <p>
|
||||
* Returns a view selection indicating a response to issue. The view selection typically contains all the data
|
||||
* necessary to issue the response.
|
||||
* @param context the control context for the currently executing flow, used by this state to manipulate the flow
|
||||
* execution
|
||||
* @return a view selection serving as a response instruction
|
||||
* @throws FlowExecutionException if an exception occurs in this state
|
||||
*/
|
||||
protected ViewSelection doEnter(RequestControlContext context) throws FlowExecutionException {
|
||||
if (viewSelector.isEntrySelectionRenderable(context)) {
|
||||
// the entry selection will be rendered!
|
||||
protected void doEnter(RequestControlContext context) throws FlowExecutionException {
|
||||
context.assignFlowExecutionKey();
|
||||
if (shouldRedirect(context)) {
|
||||
context.sendFlowExecutionRedirect();
|
||||
} else {
|
||||
View view = viewFactory.getView(context);
|
||||
renderActionList.execute(context);
|
||||
view.render();
|
||||
context.getMessageContext().clearMessages();
|
||||
context.getFlashScope().clear();
|
||||
}
|
||||
return viewSelector.makeEntrySelection(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Request that the current view selection be reconstituted to support reissuing the response. This is an idempotent
|
||||
* operation that may be safely called any number of times on a paused execution, used primarily to support a flow
|
||||
* execution redirect.
|
||||
* @param context the request context
|
||||
* @return the view selection
|
||||
* @throws FlowExecutionException if an exception occurs in this state
|
||||
*/
|
||||
public ViewSelection refresh(RequestContext context) throws FlowExecutionException {
|
||||
renderActionList.execute(context);
|
||||
return viewSelector.makeRefreshSelection(context);
|
||||
public void resume(RequestControlContext context) {
|
||||
View view = viewFactory.getView(context);
|
||||
if (view.eventSignaled()) {
|
||||
context.handleEvent(view.getEvent());
|
||||
} else {
|
||||
renderActionList.execute(context);
|
||||
view.render();
|
||||
context.getMessageContext().clearMessages();
|
||||
context.getFlashScope().clear();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldRedirect(RequestControlContext context) {
|
||||
return redirect || context.getAlwaysRedirectOnPause();
|
||||
}
|
||||
|
||||
protected void appendToString(ToStringCreator creator) {
|
||||
creator.append("viewSelector", viewSelector);
|
||||
super.appendToString(creator);
|
||||
creator.append("viewManager", viewFactory);
|
||||
}
|
||||
}
|
||||
@@ -1,881 +0,0 @@
|
||||
/*
|
||||
* 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.engine.builder;
|
||||
|
||||
import org.springframework.binding.expression.Expression;
|
||||
import org.springframework.binding.expression.SettableExpression;
|
||||
import org.springframework.binding.mapping.AttributeMapper;
|
||||
import org.springframework.binding.mapping.Mapping;
|
||||
import org.springframework.binding.mapping.MappingBuilder;
|
||||
import org.springframework.binding.method.MethodSignature;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.webflow.action.AbstractBeanInvokingAction;
|
||||
import org.springframework.webflow.action.ActionResultExposer;
|
||||
import org.springframework.webflow.action.BeanInvokingActionFactory;
|
||||
import org.springframework.webflow.action.EvaluateAction;
|
||||
import org.springframework.webflow.action.MultiAction;
|
||||
import org.springframework.webflow.core.collection.AttributeMap;
|
||||
import org.springframework.webflow.core.collection.CollectionUtils;
|
||||
import org.springframework.webflow.engine.AnnotatedAction;
|
||||
import org.springframework.webflow.engine.Flow;
|
||||
import org.springframework.webflow.engine.FlowAttributeMapper;
|
||||
import org.springframework.webflow.engine.FlowExecutionExceptionHandler;
|
||||
import org.springframework.webflow.engine.State;
|
||||
import org.springframework.webflow.engine.TargetStateResolver;
|
||||
import org.springframework.webflow.engine.Transition;
|
||||
import org.springframework.webflow.engine.TransitionCriteria;
|
||||
import org.springframework.webflow.engine.ViewSelector;
|
||||
import org.springframework.webflow.engine.support.ActionTransitionCriteria;
|
||||
import org.springframework.webflow.execution.Action;
|
||||
import org.springframework.webflow.execution.ScopeType;
|
||||
import org.springframework.webflow.execution.support.EventFactorySupport;
|
||||
|
||||
/**
|
||||
* Base class for flow builders that programmatically build flows in Java configuration code.
|
||||
* <p>
|
||||
* To give you an example of what a simple Java-based web flow builder definition might look like, the following example
|
||||
* defines the 'dynamic' web flow roughly equivalent to the work flow statically implemented in Spring MVC's simple form
|
||||
* controller:
|
||||
*
|
||||
* <pre class="code">
|
||||
* public class CustomerDetailFlowBuilder extends AbstractFlowBuilder {
|
||||
* public void buildStates() {
|
||||
* // get customer information
|
||||
* addActionState("getDetails", action("customerAction"), transition(on(success()), to("displayDetails")));
|
||||
*
|
||||
* // view customer information
|
||||
* addViewState("displayDetails", "customerDetails", transition(on(submit()), to("bindAndValidate")));
|
||||
*
|
||||
* // bind and validate customer information updates
|
||||
* addActionState("bindAndValidate", action("customerAction"), new Transition[] {
|
||||
* transition(on(error()), to("displayDetails")), transition(on(success()), to("finish")) });
|
||||
*
|
||||
* // finish
|
||||
* addEndState("finish");
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* What this Java-based FlowBuilder implementation does is add four states to a flow. These include a "get"
|
||||
* <code>ActionState</code> (the start state), a <code>ViewState</code> state, a "bind and validate"
|
||||
* <code>ActionState</code>, and an end marker state (<code>EndState</code>).
|
||||
* <p>
|
||||
* The first state, an action state, will be assigned the indentifier <code>getDetails</code>. This action state will
|
||||
* automatically be configured with the following defaults:
|
||||
* <ol>
|
||||
* <li>The action instance with id <code>customerAction</code>. This is the <code>Action</code> implementation
|
||||
* that will execute when this state is entered. In this example, that <code>Action</code> will go out to the DB, load
|
||||
* the Customer, and put it in the Flow's request context.
|
||||
* <li>A <code>success</code> transition to a default view state, called <code>displayDetails</code>. This means
|
||||
* when the <code>Action</code> returns a <code>success</code> result event (aka outcome), the
|
||||
* <code>displayDetails</code> state will be entered.
|
||||
* <li>It will act as the start state for this flow (by default, the first state added to a flow during the build
|
||||
* process is treated as the start state).
|
||||
* </ol>
|
||||
* <p>
|
||||
* The second state, a view state, will be identified as <code>displayDetails</code>. This view state will
|
||||
* automatically be configured with the following defaults:
|
||||
* <ol>
|
||||
* <li>A view name called <code>customerDetails</code>. This is the logical name of a view resource. This logical
|
||||
* view name gets mapped to a physical view resource (jsp, etc.) by the calling front controller (via a Spring view
|
||||
* resolver, or a Struts action forward, for example).
|
||||
* <li>A <code>submit</code> transition to a bind and validate action state, indentified by the default id
|
||||
* <code>bindAndValidate</code>. This means when a <code>submit</code> event is signaled by the view (for example,
|
||||
* on a submit button click), the bindAndValidate action state will be entered and the <code>bindAndValidate</code>
|
||||
* method of the <code>customerAction</code> <code>Action</code> implementation will be executed.
|
||||
* </ol>
|
||||
* <p>
|
||||
* The third state, an action state, will be indentified as <code>bindAndValidate</code>. This action state will
|
||||
* automatically be configured with the following defaults:
|
||||
* <ol>
|
||||
* <li>An action bean named <code>customerAction</code> -- this is the name of the <code>Action</code>
|
||||
* implementation exported in the application context that will execute when this state is entered. In this example, the
|
||||
* <code>Action</code> has a "bindAndValidate" method that will bind form input in the HTTP request to a backing
|
||||
* Customer form object, validate it, and update the DB.
|
||||
* <li>A <code>success</code> transition to a default end state, called <code>finish</code>. This means if the
|
||||
* <code>Action</code> returns a <code>success</code> result, the <code>finish</code> end state will be
|
||||
* transitioned to and the flow will terminate.
|
||||
* <li>An <code>error</code> transition back to the form view. This means if the <code>Action</code> returns an
|
||||
* <code>error</code> event, the <code>
|
||||
* displayDetails</code> view state will be transitioned back to.
|
||||
* </ol>
|
||||
* <p>
|
||||
* The fourth and last state, an end state, will be indentified with the default end state id <code>finish</code>.
|
||||
* This end state is a marker that signals the end of the flow. When entered, the flow session terminates, and if this
|
||||
* flow is acting as a root flow in the current flow execution, any flow-allocated resources will be cleaned up. An end
|
||||
* state can optionally be configured with a logical view name to forward to when entered. It will also trigger a state
|
||||
* transition in a resuming parent flow if this flow was participating as a spawned 'subflow' within a suspended parent
|
||||
* flow.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Erwin Vervaet
|
||||
*/
|
||||
public abstract class AbstractFlowBuilder extends BaseFlowBuilder {
|
||||
|
||||
/**
|
||||
* A helper for creating commonly used event identifiers that drive transitions created by this builder.
|
||||
*/
|
||||
private EventFactorySupport eventFactorySupport = new EventFactorySupport();
|
||||
|
||||
/**
|
||||
* Default constructor for subclassing.
|
||||
*/
|
||||
protected AbstractFlowBuilder() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of an abstract flow builder, using the specified locator to obtain needed flow services at
|
||||
* build time.
|
||||
* @param flowServiceLocator the locator for services needed by this builder to build its Flow
|
||||
*/
|
||||
protected AbstractFlowBuilder(FlowServiceLocator flowServiceLocator) {
|
||||
super(flowServiceLocator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configured event factory support helper for creating commonly used event identifiers that drive
|
||||
* transitions created by this builder.
|
||||
*/
|
||||
public EventFactorySupport getEventFactorySupport() {
|
||||
return eventFactorySupport;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the event factory support helper to use to create commonly used event identifiers that drive transitions
|
||||
* created by this builder.
|
||||
*/
|
||||
public void setEventFactorySupport(EventFactorySupport eventFactorySupport) {
|
||||
this.eventFactorySupport = eventFactorySupport;
|
||||
}
|
||||
|
||||
public void init(String flowId, AttributeMap attributes) throws FlowBuilderException {
|
||||
setFlow(getFlowArtifactFactory().createFlow(flowId, flowAttributes().union(attributes)));
|
||||
initBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook subclasses may override to provide additional properties for the flow built by this builder. Returns a empty
|
||||
* collection by default.
|
||||
* @return additional properties describing the flow being built, should not return null
|
||||
*/
|
||||
protected AttributeMap flowAttributes() {
|
||||
return CollectionUtils.EMPTY_ATTRIBUTE_MAP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook method subclasses can override to initialize the flow builder. Will be called by
|
||||
* {@link #init(String, AttributeMap)} after creating the initial Flow object. As a consequence, {@link #getFlow()}
|
||||
* can be called to retrieve the Flow object under construction.
|
||||
*/
|
||||
protected void initBuilder() {
|
||||
}
|
||||
|
||||
// view state
|
||||
|
||||
/**
|
||||
* Adds a view state to the flow built by this builder.
|
||||
* @param stateId the state identifier
|
||||
* @param viewName the string-encoded view selector
|
||||
* @param transition the sole transition (path) out of this state
|
||||
* @return the fully constructed view state instance
|
||||
*/
|
||||
protected State addViewState(String stateId, String viewName, Transition transition) {
|
||||
return getFlowArtifactFactory().createViewState(stateId, getFlow(), null, viewSelector(viewName), null,
|
||||
new Transition[] { transition }, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a view state to the flow built by this builder.
|
||||
* @param stateId the state identifier
|
||||
* @param viewName the string-encoded view selector
|
||||
* @param transitions the transitions (paths) out of this state
|
||||
* @return the fully constructed view state instance
|
||||
*/
|
||||
protected State addViewState(String stateId, String viewName, Transition[] transitions) {
|
||||
return getFlowArtifactFactory().createViewState(stateId, getFlow(), null, viewSelector(viewName), null,
|
||||
transitions, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a view state to the flow built by this builder.
|
||||
* @param stateId the state identifier
|
||||
* @param viewName the string-encoded view selector
|
||||
* @param renderAction the action to execute on state entry and refresh; may be null
|
||||
* @param transition the sole transition (path) out of this state
|
||||
* @return the fully constructed view state instance
|
||||
*/
|
||||
protected State addViewState(String stateId, String viewName, Action renderAction, Transition transition) {
|
||||
return getFlowArtifactFactory().createViewState(stateId, getFlow(), null, viewSelector(viewName),
|
||||
new Action[] { renderAction }, new Transition[] { transition }, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a view state to the flow built by this builder.
|
||||
* @param stateId the state identifier
|
||||
* @param viewName the string-encoded view selector
|
||||
* @param renderAction the action to execute on state entry and refresh; may be null
|
||||
* @param transitions the transitions (paths) out of this state
|
||||
* @return the fully constructed view state instance
|
||||
*/
|
||||
protected State addViewState(String stateId, String viewName, Action renderAction, Transition[] transitions) {
|
||||
return getFlowArtifactFactory().createViewState(stateId, getFlow(), null, viewSelector(viewName),
|
||||
new Action[] { renderAction }, transitions, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a view state to the flow built by this builder.
|
||||
* @param stateId the state identifier
|
||||
* @param entryActions the actions to execute when the state is entered
|
||||
* @param viewSelector the view selector that will make the view selection when the state is entered
|
||||
* @param renderActions any 'render actions' to execute on state entry and refresh; may be null
|
||||
* @param transitions the transitions (path) out of this state
|
||||
* @param exceptionHandlers any exception handlers to attach to the state
|
||||
* @param exitActions the actions to execute when the state exits
|
||||
* @param attributes attributes to assign to the state that may be used to affect state construction and execution
|
||||
* @return the fully constructed view state instance
|
||||
*/
|
||||
protected State addViewState(String stateId, Action[] entryActions, ViewSelector viewSelector,
|
||||
Action[] renderActions, Transition[] transitions, FlowExecutionExceptionHandler[] exceptionHandlers,
|
||||
Action[] exitActions, AttributeMap attributes) {
|
||||
return getFlowArtifactFactory().createViewState(stateId, getFlow(), entryActions, viewSelector, renderActions,
|
||||
transitions, exceptionHandlers, exitActions, attributes);
|
||||
}
|
||||
|
||||
// action state
|
||||
|
||||
/**
|
||||
* Adds an action state to the flow built by this builder.
|
||||
* @param stateId the state identifier
|
||||
* @param action the single action to execute when the state is entered
|
||||
* @param transition the single transition (path) out of this state
|
||||
* @return the fully constructed action state instance
|
||||
*/
|
||||
protected State addActionState(String stateId, Action action, Transition transition) {
|
||||
return getFlowArtifactFactory().createActionState(stateId, getFlow(), null, new Action[] { action },
|
||||
new Transition[] { transition }, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an action state to the flow built by this builder.
|
||||
* @param stateId the state identifier
|
||||
* @param action the single action to execute when the state is entered
|
||||
* @param transitions the transitions (paths) out of this state
|
||||
* @return the fully constructed action state instance
|
||||
*/
|
||||
protected State addActionState(String stateId, Action action, Transition[] transitions) {
|
||||
return getFlowArtifactFactory().createActionState(stateId, getFlow(), null, new Action[] { action },
|
||||
transitions, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an action state to the flow built by this builder.
|
||||
* @param stateId the state identifier
|
||||
* @param action the single action to execute when the state is entered
|
||||
* @param transition the single transition (path) out of this state
|
||||
* @param exceptionHandler the exception handler to handle exceptions thrown by the action
|
||||
* @return the fully constructed action state instance
|
||||
*/
|
||||
protected State addActionState(String stateId, Action action, Transition transition,
|
||||
FlowExecutionExceptionHandler exceptionHandler) {
|
||||
return getFlowArtifactFactory().createActionState(stateId, getFlow(), null, new Action[] { action },
|
||||
new Transition[] { transition }, new FlowExecutionExceptionHandler[] { exceptionHandler }, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an action state to the flow built by this builder.
|
||||
* @param stateId the state identifier
|
||||
* @param entryActions any generic entry actions to add to the state
|
||||
* @param actions the actions to execute in a chain when the state is entered
|
||||
* @param transitions the transitions (paths) out of this state
|
||||
* @param exceptionHandlers the exception handlers to handle exceptions thrown by the actions
|
||||
* @param exitActions the exit actions to execute when the state exits
|
||||
* @param attributes attributes to assign to the state that may be used to affect state construction and execution
|
||||
* @return the fully constructed action state instance
|
||||
*/
|
||||
protected State addActionState(String stateId, Action[] entryActions, Action[] actions, Transition[] transitions,
|
||||
FlowExecutionExceptionHandler[] exceptionHandlers, Action[] exitActions, AttributeMap attributes) {
|
||||
return getFlowArtifactFactory().createActionState(stateId, getFlow(), entryActions, actions, transitions,
|
||||
exceptionHandlers, exitActions, attributes);
|
||||
}
|
||||
|
||||
// decision state
|
||||
|
||||
/**
|
||||
* Adds a decision state to the flow built by this builder.
|
||||
* @param stateId the state identifier
|
||||
* @param transitions the transitions (paths) out of this state
|
||||
* @return the fully constructed decision state instance
|
||||
*/
|
||||
protected State addDecisionState(String stateId, Transition[] transitions) {
|
||||
return getFlowArtifactFactory().createDecisionState(stateId, getFlow(), null, transitions, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a decision state to the flow built by this builder.
|
||||
* @param stateId the state identifier
|
||||
* @param decisionCriteria the criteria that defines the decision
|
||||
* @param trueStateId the target state on a "true" decision
|
||||
* @param falseStateId the target state on a "false" decision
|
||||
* @return the fully constructed decision state instance
|
||||
*/
|
||||
protected State addDecisionState(String stateId, TransitionCriteria decisionCriteria, String trueStateId,
|
||||
String falseStateId) {
|
||||
Transition thenTransition = getFlowArtifactFactory().createTransition(to(trueStateId), decisionCriteria, null,
|
||||
null);
|
||||
Transition elseTransition = getFlowArtifactFactory().createTransition(to(falseStateId), null, null, null);
|
||||
return getFlowArtifactFactory().createDecisionState(stateId, getFlow(), null,
|
||||
new Transition[] { thenTransition, elseTransition }, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a decision state to the flow built by this builder.
|
||||
* @param stateId the state identifier
|
||||
* @param entryActions the entry actions to execute when the state enters
|
||||
* @param transitions the transitions (paths) out of this state
|
||||
* @param exceptionHandlers the exception handlers to handle exceptions thrown by the state
|
||||
* @param exitActions the exit actions to execute when the state exits
|
||||
* @param attributes attributes to assign to the state that may be used to affect state construction and execution
|
||||
* @return the fully constructed decision state instance
|
||||
*/
|
||||
protected State addDecisionState(String stateId, Action[] entryActions, Transition[] transitions,
|
||||
FlowExecutionExceptionHandler[] exceptionHandlers, Action[] exitActions, AttributeMap attributes) {
|
||||
return getFlowArtifactFactory().createDecisionState(stateId, getFlow(), entryActions, transitions,
|
||||
exceptionHandlers, exitActions, attributes);
|
||||
}
|
||||
|
||||
// subflow state
|
||||
|
||||
/**
|
||||
* Adds a subflow state to the flow built by this builder.
|
||||
* @param stateId the state identifier
|
||||
* @param subflow the flow that will act as the subflow
|
||||
* @param attributeMapper the mapper to map subflow input and output attributes
|
||||
* @param transition the single transition (path) out of the state
|
||||
* @return the fully constructed subflow state instance
|
||||
*/
|
||||
protected State addSubflowState(String stateId, Flow subflow, FlowAttributeMapper attributeMapper,
|
||||
Transition transition) {
|
||||
return getFlowArtifactFactory().createSubflowState(stateId, getFlow(), null, subflow, attributeMapper,
|
||||
new Transition[] { transition }, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a subflow state to the flow built by this builder.
|
||||
* @param stateId the state identifier
|
||||
* @param subflow the flow that will act as the subflow
|
||||
* @param attributeMapper the mapper to map subflow input and output attributes
|
||||
* @param transitions the transitions (paths) out of the state
|
||||
* @return the fully constructed subflow state instance
|
||||
*/
|
||||
protected State addSubflowState(String stateId, Flow subflow, FlowAttributeMapper attributeMapper,
|
||||
Transition[] transitions) {
|
||||
return getFlowArtifactFactory().createSubflowState(stateId, getFlow(), null, subflow, attributeMapper,
|
||||
transitions, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a subflow state to the flow built by this builder.
|
||||
* @param stateId the state identifier
|
||||
* @param entryActions the entry actions to execute when the state enters
|
||||
* @param subflow the flow that will act as the subflow
|
||||
* @param attributeMapper the mapper to map subflow input and output attributes
|
||||
* @param transitions the transitions (paths) out of this state
|
||||
* @param exceptionHandlers the exception handlers to handle exceptions thrown by the state
|
||||
* @param exitActions the exit actions to execute when the state exits
|
||||
* @param attributes attributes to assign to the state that may be used to affect state construction and execution
|
||||
* @return the fully constructed subflow state instance
|
||||
*/
|
||||
protected State addSubflowState(String stateId, Action[] entryActions, Flow subflow,
|
||||
FlowAttributeMapper attributeMapper, Transition[] transitions,
|
||||
FlowExecutionExceptionHandler[] exceptionHandlers, Action[] exitActions, AttributeMap attributes) {
|
||||
return getFlowArtifactFactory().createSubflowState(stateId, getFlow(), entryActions, subflow, attributeMapper,
|
||||
transitions, exceptionHandlers, exitActions, attributes);
|
||||
}
|
||||
|
||||
// end state
|
||||
|
||||
/**
|
||||
* Adds an end state to the flow built by this builder.
|
||||
* @param stateId the state identifier
|
||||
* @return the fully constructed end state instance
|
||||
*/
|
||||
protected State addEndState(String stateId) {
|
||||
return getFlowArtifactFactory().createEndState(stateId, getFlow(), null, null, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an end state to the flow built by this builder.
|
||||
* @param stateId the state identifier
|
||||
* @param viewName the string-encoded view selector
|
||||
* @return the fully constructed end state instance
|
||||
*/
|
||||
protected State addEndState(String stateId, String viewName) {
|
||||
return getFlowArtifactFactory().createEndState(stateId, getFlow(), null, viewSelector(viewName), null, null,
|
||||
null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an end state to the flow built by this builder.
|
||||
* @param stateId the state identifier
|
||||
* @param viewName the string-encoded view selector
|
||||
* @param outputMapper the output mapper to map output attributes for the end state (a flow outcome)
|
||||
* @return the fully constructed end state instance
|
||||
*/
|
||||
protected State addEndState(String stateId, String viewName, AttributeMapper outputMapper) {
|
||||
return getFlowArtifactFactory().createEndState(stateId, getFlow(), null, viewSelector(viewName), outputMapper,
|
||||
null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an end state to the flow built by this builder.
|
||||
* @param stateId the state identifier
|
||||
* @param entryActions the actions to execute when the state is entered
|
||||
* @param viewSelector the view selector that will make the view selection when the state is entered
|
||||
* @param outputMapper the output mapper to map output attributes for the end state (a flow outcome)
|
||||
* @param exceptionHandlers any exception handlers to attach to the state
|
||||
* @param attributes attributes to assign to the state that may be used to affect state construction and execution
|
||||
* @return the fully constructed end state instance
|
||||
*/
|
||||
protected State addEndState(String stateId, Action[] entryActions, ViewSelector viewSelector,
|
||||
AttributeMapper outputMapper, FlowExecutionExceptionHandler[] exceptionHandlers, AttributeMap attributes) {
|
||||
return getFlowArtifactFactory().createEndState(stateId, getFlow(), entryActions, viewSelector, outputMapper,
|
||||
exceptionHandlers, attributes);
|
||||
}
|
||||
|
||||
// helpers to create misc. flow artifacts
|
||||
|
||||
/**
|
||||
* Factory method that creates a view selector from an encoded view name. See {@link TextToViewSelector} for
|
||||
* information on the conversion rules.
|
||||
* @param viewName the encoded view selector
|
||||
* @return the view selector
|
||||
*/
|
||||
public ViewSelector viewSelector(String viewName) {
|
||||
return (ViewSelector) fromStringTo(ViewSelector.class).execute(viewName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the action with the specified id. Simply looks the action up by id and returns it.
|
||||
* @param id the action id
|
||||
* @return the action
|
||||
* @throws FlowArtifactLookupException the action could not be resolved
|
||||
*/
|
||||
protected Action action(String id) throws FlowArtifactLookupException {
|
||||
return getFlowServiceLocator().getAction(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a bean invoking action that invokes the method identified by the signature on the bean associated with
|
||||
* the action identifier.
|
||||
* @param beanId the id identifying an arbitrary <code>java.lang.Object</code> to be used as an action
|
||||
* @param methodSignature the signature of the method to invoke on the POJO
|
||||
* @return the adapted bean invoking action
|
||||
* @throws FlowArtifactLookupException the action could not be resolved
|
||||
*/
|
||||
protected Action action(String beanId, MethodSignature methodSignature) throws FlowArtifactLookupException {
|
||||
return getBeanInvokingActionFactory().createBeanInvokingAction(beanId,
|
||||
getFlowServiceLocator().getBeanFactory(), methodSignature, null,
|
||||
getFlowServiceLocator().getConversionService(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a bean invoking action that invokes the method identified by the signature on the bean associated with
|
||||
* the action identifier.
|
||||
* @param beanId the id identifying an arbitrary <code>java.lang.Object</code> to be used as an action
|
||||
* @param methodSignature the signature of the method to invoke on the POJO
|
||||
* @return the adapted bean invoking action
|
||||
* @throws FlowArtifactLookupException the action could not be resolved
|
||||
*/
|
||||
protected Action action(String beanId, MethodSignature methodSignature, ActionResultExposer resultExposer)
|
||||
throws FlowArtifactLookupException {
|
||||
return getBeanInvokingActionFactory().createBeanInvokingAction(beanId,
|
||||
getFlowServiceLocator().getBeanFactory(), methodSignature, resultExposer,
|
||||
getFlowServiceLocator().getConversionService(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an evaluate action that evaluates the expression when executed.
|
||||
* @param expression the expression to evaluate
|
||||
*/
|
||||
protected Action action(Expression expression) {
|
||||
return action(expression, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an evaluate action that evaluates the expression when executed.
|
||||
* @param expression the expression to evaluate
|
||||
* @param resultExposer the evaluation result exposer
|
||||
*/
|
||||
protected Action action(Expression expression, ActionResultExposer resultExposer) {
|
||||
return new EvaluateAction(expression, resultExposer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the expression string into an evaluatable {@link Expression} object.
|
||||
* @param expressionString the expression string, e.g. flowScope.order.number
|
||||
* @return the evaluatable expression
|
||||
*/
|
||||
protected Expression expression(String expressionString) {
|
||||
return getFlowServiceLocator().getExpressionParser().parseExpression(expressionString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the expression string into a settable {@link Expression} object.
|
||||
* @param expressionString the expression string, e.g. flowScope.order.number
|
||||
* @return the evaluatable expression
|
||||
* @since 1.0.2
|
||||
*/
|
||||
protected SettableExpression settableExpression(String expressionString) {
|
||||
return getFlowServiceLocator().getExpressionParser().parseSettableExpression(expressionString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the encoded method signature string to a {@link MethodSignature} object. Method signatures are used to
|
||||
* match methods on POJO services to invoke on a {@link AbstractBeanInvokingAction bean invoking action}.
|
||||
* <p>
|
||||
* Encoded method signature format:
|
||||
*
|
||||
* Method without arguments:
|
||||
*
|
||||
* <pre>
|
||||
* ${methodName}
|
||||
* </pre>
|
||||
*
|
||||
* Method with arguments:
|
||||
*
|
||||
* <pre>
|
||||
* ${methodName}(${arg1}, ${arg2}, ${arg n})
|
||||
* </pre>
|
||||
*
|
||||
* @param method the encoded method signature
|
||||
* @return the method signature
|
||||
* @see #action(String, MethodSignature, ActionResultExposer)
|
||||
*/
|
||||
protected MethodSignature method(String method) {
|
||||
return (MethodSignature) fromStringTo(MethodSignature.class).execute(method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method for a {@link ActionResultExposer result exposer}. A result exposer is used to expose an action
|
||||
* result such as a method return value or expression evaluation result to the calling flow.
|
||||
* @param resultName the result name
|
||||
* @return the result exposer
|
||||
* @see #action(String, MethodSignature, ActionResultExposer)
|
||||
*/
|
||||
protected ActionResultExposer result(String resultName) {
|
||||
return result(resultName, ScopeType.REQUEST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method for a {@link ActionResultExposer result exposer}. A result exposer is used to expose an action
|
||||
* result such as a method return value or expression evaluation result to the calling flow.
|
||||
* @param resultName the result name
|
||||
* @param resultScope the scope of the result
|
||||
* @return the result exposer
|
||||
* @see #action(String, MethodSignature, ActionResultExposer)
|
||||
*/
|
||||
protected ActionResultExposer result(String resultName, ScopeType resultScope) {
|
||||
return new ActionResultExposer(resultName, resultScope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap given action in an {@link AnnotatedAction}} to be able to annotate it with attributes.
|
||||
* @param action the action to annotate
|
||||
* @return the wrapped action
|
||||
* @since 1.0.4
|
||||
*/
|
||||
protected AnnotatedAction annotate(Action action) {
|
||||
if (action instanceof AnnotatedAction) {
|
||||
return (AnnotatedAction) action;
|
||||
} else {
|
||||
return new AnnotatedAction(action);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an annotated action decorator that instructs the specified method be invoked on the multi action when it
|
||||
* is executed. Use this when working with MultiActions to specify the method on the MultiAction to invoke for a
|
||||
* particular usage scenario. Use the {@link #method(String)} factory method when working with
|
||||
* {@link AbstractBeanInvokingAction bean invoking actions}.
|
||||
* @param methodName the name of the method on the multi action instance
|
||||
* @param multiAction the multi action
|
||||
* @return the annotated action that when invoked sets up a context property used by the multi action to instruct it
|
||||
* with what method to invoke
|
||||
* @since 1.0.4
|
||||
*/
|
||||
protected AnnotatedAction invoke(String methodName, Action multiAction) {
|
||||
AnnotatedAction annotatedAction;
|
||||
if (multiAction instanceof AnnotatedAction) {
|
||||
// already wrapped in an AnnotatedAction
|
||||
annotatedAction = (AnnotatedAction) multiAction;
|
||||
} else {
|
||||
annotatedAction = new AnnotatedAction(multiAction);
|
||||
}
|
||||
annotatedAction.setMethod(methodName);
|
||||
return annotatedAction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an annotated action decorator that instructs the specified method be invoked on the multi action when it
|
||||
* is executed. Use this when working with MultiActions to specify the method on the MultiAction to invoke for a
|
||||
* particular usage scenario. Use the {@link #method(String)} factory method when working with
|
||||
* {@link AbstractBeanInvokingAction bean invoking actions}.
|
||||
* @param methodName the name of the method on the multi action instance
|
||||
* @param multiAction the multi action
|
||||
* @return the annotated action that when invoked sets up a context property used by the multi action to instruct it
|
||||
* with what method to invoke
|
||||
*/
|
||||
protected AnnotatedAction invoke(String methodName, MultiAction multiAction) throws FlowArtifactLookupException {
|
||||
return invoke(methodName, (Action) multiAction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an annotated action decorator that makes the given action an named action.
|
||||
* @param name the action name
|
||||
* @param action the action to name
|
||||
* @return the annotated named action
|
||||
*/
|
||||
protected AnnotatedAction name(String name, Action action) {
|
||||
AnnotatedAction annotatedAction;
|
||||
if (action instanceof AnnotatedAction) {
|
||||
// already wrapped in an AnnotatedAction
|
||||
annotatedAction = (AnnotatedAction) action;
|
||||
} else {
|
||||
annotatedAction = new AnnotatedAction(action);
|
||||
}
|
||||
annotatedAction.setName(name);
|
||||
return annotatedAction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request that the attribute mapper with the specified name be used to map attributes between a parent flow and a
|
||||
* spawning subflow when the subflow state being constructed is entered.
|
||||
* @param id the id of the attribute mapper that will map attributes between the flow built by this builder and the
|
||||
* subflow
|
||||
* @return the attribute mapper
|
||||
* @throws FlowArtifactLookupException no FlowAttributeMapper implementation was exported with the specified id
|
||||
*/
|
||||
protected FlowAttributeMapper attributeMapper(String id) throws FlowArtifactLookupException {
|
||||
return getFlowServiceLocator().getAttributeMapper(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Request that the <code>Flow</code> with the specified flowId be spawned as a subflow when the subflow state
|
||||
* being built is entered. Simply resolves the subflow definition by id and returns it; throwing a fail-fast
|
||||
* exception if it does not exist.
|
||||
* @param id the flow definition id
|
||||
* @return the flow to be used as a subflow, this should be passed to a addSubflowState call
|
||||
* @throws FlowArtifactLookupException when the flow cannot be resolved
|
||||
*/
|
||||
protected Flow flow(String id) throws FlowArtifactLookupException {
|
||||
return getFlowServiceLocator().getSubflow(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a transition criteria that is used to match a Transition. The criteria is based on the provided
|
||||
* expression string.
|
||||
* @param transitionCriteriaExpression the transition criteria expression, typically simply a static event
|
||||
* identifier (e.g. "submit")
|
||||
* @return the transition criteria
|
||||
* @see TextToTransitionCriteria
|
||||
*/
|
||||
protected TransitionCriteria on(String transitionCriteriaExpression) {
|
||||
return (TransitionCriteria) fromStringTo(TransitionCriteria.class).execute(transitionCriteriaExpression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a target state resolver for the given state id expression.
|
||||
* @param targetStateIdExpression the target state id expression
|
||||
* @return the target state resolver
|
||||
* @see TextToTargetStateResolver
|
||||
*/
|
||||
protected TargetStateResolver to(String targetStateIdExpression) {
|
||||
return (TargetStateResolver) fromStringTo(TargetStateResolver.class).execute(targetStateIdExpression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new transition.
|
||||
* @param matchingCriteria the criteria that determines when the transition matches
|
||||
* @param targetStateResolver the resolver of the transition's target state
|
||||
* @return the transition
|
||||
*/
|
||||
protected Transition transition(TransitionCriteria matchingCriteria, TargetStateResolver targetStateResolver) {
|
||||
return getFlowArtifactFactory().createTransition(targetStateResolver, matchingCriteria, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new transition.
|
||||
* @param matchingCriteria the criteria that determines when the transition matches
|
||||
* @param targetStateResolver the resolver of the transition's target state
|
||||
* @param executionCriteria the criteria that determines if a matched transition is allowed to execute
|
||||
* @return the transition
|
||||
*/
|
||||
protected Transition transition(TransitionCriteria matchingCriteria, TargetStateResolver targetStateResolver,
|
||||
TransitionCriteria executionCriteria) {
|
||||
return getFlowArtifactFactory()
|
||||
.createTransition(targetStateResolver, matchingCriteria, executionCriteria, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new transition.
|
||||
* @param matchingCriteria the criteria that determines when the transition matches
|
||||
* @param targetStateResolver the resolver of the transition's target state
|
||||
* @param executionCriteria the criteria that determines if a matched transition is allowed to execute
|
||||
* @param attributes transition attributes
|
||||
* @return the transition
|
||||
*/
|
||||
protected Transition transition(TransitionCriteria matchingCriteria, TargetStateResolver targetStateResolver,
|
||||
TransitionCriteria executionCriteria, AttributeMap attributes) {
|
||||
return getFlowArtifactFactory().createTransition(targetStateResolver, matchingCriteria, executionCriteria,
|
||||
attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <code>TransitionCriteria</code> that will execute the specified action when the Transition is
|
||||
* executed but before the transition's target state is entered.
|
||||
* <p>
|
||||
* This criteria will only allow the Transition to complete execution if the Action completes successfully.
|
||||
* @param action the action to execute after a transition is matched but before it transitions to its target state
|
||||
* @return the transition execution criteria
|
||||
*/
|
||||
protected TransitionCriteria ifReturnedSuccess(Action action) {
|
||||
return new ActionTransitionCriteria(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the <code>success</code> event id. "Success" indicates that an action completed successfuly.
|
||||
* @return the event id
|
||||
*/
|
||||
protected String success() {
|
||||
return eventFactorySupport.getSuccessEventId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the <code>error</code> event id. "Error" indicates that an action completed with an error status.
|
||||
* @return the event id
|
||||
*/
|
||||
protected String error() {
|
||||
return eventFactorySupport.getErrorEventId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the <code>submit</code> event id. "Submit" indicates the user submitted a request (form) for
|
||||
* processing.
|
||||
* @return the event id
|
||||
*/
|
||||
protected String submit() {
|
||||
return "submit";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the <code>back</code> event id. "Back" indicates the user wants to go to the previous step in the flow.
|
||||
* @return the event id
|
||||
*/
|
||||
protected String back() {
|
||||
return "back";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the <code>cancel</code> event id. "Cancel" indicates the flow was aborted because the user changed
|
||||
* their mind.
|
||||
* @return the event id
|
||||
*/
|
||||
protected String cancel() {
|
||||
return "cancel";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the <code>finish</code> event id. "Finish" indicates the flow has finished processing.
|
||||
* @return the event id
|
||||
*/
|
||||
protected String finish() {
|
||||
return "finish";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the <code>select</code> event id. "Select" indicates an object was selected for processing or display.
|
||||
* @return the event id
|
||||
*/
|
||||
protected String select() {
|
||||
return "select";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the <code>edit</code> event id. "Edit" indicates an object was selected for creation or updating.
|
||||
* @return the event id
|
||||
*/
|
||||
protected String edit() {
|
||||
return "edit";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the <code>add</code> event id. "Add" indicates a child object is being added to a parent collection.
|
||||
* @return the event id
|
||||
*/
|
||||
protected String add() {
|
||||
return "add";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the <code>delete</code> event id. "Delete" indicates a object is being removed.
|
||||
* @return the event id
|
||||
*/
|
||||
protected String delete() {
|
||||
return "delete";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the <code>yes</code> event id. "Yes" indicates a true result was returned.
|
||||
* @return the event id
|
||||
*/
|
||||
protected String yes() {
|
||||
return eventFactorySupport.getYesEventId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the <code>no</code> event id. "False" indicates a false result was returned.
|
||||
* @return the event id
|
||||
*/
|
||||
protected String no() {
|
||||
return eventFactorySupport.getNoEventId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method that returns a new, fully configured mapping builder to assist with building {@link Mapping}
|
||||
* objects used by a {@link FlowAttributeMapper} to map attributes.
|
||||
* @return the mapping builder
|
||||
*/
|
||||
protected MappingBuilder mapping() {
|
||||
MappingBuilder mapping = new MappingBuilder(getFlowServiceLocator().getExpressionParser());
|
||||
mapping.setConversionService(getFlowServiceLocator().getConversionService());
|
||||
return mapping;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).toString();
|
||||
}
|
||||
|
||||
// internal helpers
|
||||
|
||||
private FlowArtifactFactory getFlowArtifactFactory() {
|
||||
return getFlowServiceLocator().getFlowArtifactFactory();
|
||||
}
|
||||
|
||||
private BeanInvokingActionFactory getBeanInvokingActionFactory() {
|
||||
return getFlowServiceLocator().getBeanInvokingActionFactory();
|
||||
}
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
/*
|
||||
* 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.engine.builder;
|
||||
|
||||
import org.springframework.binding.convert.ConversionException;
|
||||
import org.springframework.binding.convert.ConversionExecutor;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.webflow.core.collection.AttributeMap;
|
||||
import org.springframework.webflow.engine.Flow;
|
||||
|
||||
/**
|
||||
* Abstract base implementation of a flow builder defining common functionality needed by most concrete flow builder
|
||||
* implementations. This class implements all optional parts of the FlowBuilder process as no-op methods. Subclasses are
|
||||
* only required to implement {@link #init(String, AttributeMap)} and {@link #buildStates()}.
|
||||
* <p>
|
||||
* This class also provides a {@link FlowServiceLocator} for use by subclasses in the flow construction process.
|
||||
*
|
||||
* @see org.springframework.webflow.engine.builder.FlowServiceLocator
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Erwin Vervaet
|
||||
*/
|
||||
public abstract class BaseFlowBuilder implements FlowBuilder {
|
||||
|
||||
/**
|
||||
* The <code>Flow</code> built by this builder.
|
||||
*/
|
||||
private Flow flow;
|
||||
|
||||
/**
|
||||
* Locates actions, attribute mappers, and other artifacts needed by the flow built by this builder.
|
||||
*/
|
||||
private FlowServiceLocator flowServiceLocator;
|
||||
|
||||
/**
|
||||
* Default constructor for subclassing. Sets up use of a {@link BaseFlowServiceLocator}.
|
||||
* @see #setFlowServiceLocator(FlowServiceLocator)
|
||||
*/
|
||||
protected BaseFlowBuilder() {
|
||||
setFlowServiceLocator(new BaseFlowServiceLocator());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a flow builder using the given locator to link in artifacts.
|
||||
* @param flowServiceLocator the locator for services needed by this builder to build its Flow
|
||||
*/
|
||||
protected BaseFlowBuilder(FlowServiceLocator flowServiceLocator) {
|
||||
setFlowServiceLocator(flowServiceLocator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configured flow service locator.
|
||||
*/
|
||||
public FlowServiceLocator getFlowServiceLocator() {
|
||||
return flowServiceLocator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the flow service locator to use. Defaults to {@link BaseFlowServiceLocator}.
|
||||
*/
|
||||
public void setFlowServiceLocator(FlowServiceLocator flowServiceLocator) {
|
||||
Assert.notNull(flowServiceLocator, "The flow service locator is required");
|
||||
this.flowServiceLocator = flowServiceLocator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the flow being built by this builder. Typically called during initialization to set the initial flow
|
||||
* reference returned by {@link #getFlow()} after building.
|
||||
*/
|
||||
protected void setFlow(Flow flow) {
|
||||
this.flow = flow;
|
||||
}
|
||||
|
||||
public abstract void init(String flowId, AttributeMap attributes) throws FlowBuilderException;
|
||||
|
||||
public void buildVariables() throws FlowBuilderException {
|
||||
}
|
||||
|
||||
public void buildInputMapper() throws FlowBuilderException {
|
||||
}
|
||||
|
||||
public void buildStartActions() throws FlowBuilderException {
|
||||
}
|
||||
|
||||
public void buildInlineFlows() throws FlowBuilderException {
|
||||
}
|
||||
|
||||
public abstract void buildStates() throws FlowBuilderException;
|
||||
|
||||
public void buildGlobalTransitions() throws FlowBuilderException {
|
||||
}
|
||||
|
||||
public void buildEndActions() throws FlowBuilderException {
|
||||
}
|
||||
|
||||
public void buildOutputMapper() throws FlowBuilderException {
|
||||
}
|
||||
|
||||
public void buildExceptionHandlers() throws FlowBuilderException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the flow (result) built by this builder.
|
||||
*/
|
||||
public Flow getFlow() {
|
||||
return flow;
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
setFlow(null);
|
||||
}
|
||||
|
||||
// helpers for use in subclasses
|
||||
|
||||
/**
|
||||
* Returns a conversion executor capable of converting string objects to the target class aliased by the provided
|
||||
* alias.
|
||||
* @param targetAlias the target class alias, e.g. "long" or "float"
|
||||
* @return the conversion executor, or <code>null</code> if no suitable converter exists for given alias
|
||||
*/
|
||||
protected ConversionExecutor fromStringTo(String targetAlias) {
|
||||
return getFlowServiceLocator().getConversionService().getConversionExecutorByTargetAlias(String.class,
|
||||
targetAlias);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a converter capable of converting a string value to the given type.
|
||||
* @param targetType the type you wish to convert to (from a string)
|
||||
* @return the converter
|
||||
* @throws ConversionException when the converter cannot be found
|
||||
*/
|
||||
protected ConversionExecutor fromStringTo(Class targetType) throws ConversionException {
|
||||
return getFlowServiceLocator().getConversionService().getConversionExecutor(String.class, targetType);
|
||||
}
|
||||
}
|
||||
@@ -1,246 +0,0 @@
|
||||
/*
|
||||
* 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.engine.builder;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.binding.convert.ConversionService;
|
||||
import org.springframework.binding.convert.support.CompositeConversionService;
|
||||
import org.springframework.binding.convert.support.DefaultConversionService;
|
||||
import org.springframework.binding.convert.support.GenericConversionService;
|
||||
import org.springframework.binding.convert.support.TextToExpression;
|
||||
import org.springframework.binding.expression.ExpressionParser;
|
||||
import org.springframework.binding.method.TextToMethodSignature;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.webflow.action.BeanInvokingActionFactory;
|
||||
import org.springframework.webflow.core.DefaultExpressionParserFactory;
|
||||
import org.springframework.webflow.engine.Flow;
|
||||
import org.springframework.webflow.engine.FlowAttributeMapper;
|
||||
import org.springframework.webflow.engine.FlowExecutionExceptionHandler;
|
||||
import org.springframework.webflow.engine.State;
|
||||
import org.springframework.webflow.engine.TargetStateResolver;
|
||||
import org.springframework.webflow.engine.TransitionCriteria;
|
||||
import org.springframework.webflow.engine.ViewSelector;
|
||||
import org.springframework.webflow.execution.Action;
|
||||
|
||||
/**
|
||||
* Base implementation that implements a minimal set of the <code>FlowServiceLocator</code> interface, throwing
|
||||
* unsupported operation exceptions for some operations.
|
||||
* <p>
|
||||
* May be subclassed to offer additional factory/lookup support.
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public class BaseFlowServiceLocator implements FlowServiceLocator {
|
||||
|
||||
/**
|
||||
* The factory encapsulating the creation of central Flow artifacts such as {@link Flow flows} and
|
||||
* {@link State states}.
|
||||
*/
|
||||
private FlowArtifactFactory flowArtifactFactory = new FlowArtifactFactory();
|
||||
|
||||
/**
|
||||
* The factory encapsulating the creation of bean invoking actions, actions that adapt methods on objects to the
|
||||
* {@link Action} interface.
|
||||
*/
|
||||
private BeanInvokingActionFactory beanInvokingActionFactory = new BeanInvokingActionFactory();
|
||||
|
||||
/**
|
||||
* The parser for parsing expression strings into evaluatable expression objects.
|
||||
*/
|
||||
private ExpressionParser expressionParser = DefaultExpressionParserFactory.getExpressionParser();
|
||||
|
||||
/**
|
||||
* The conversion service configured by the user (none by default).
|
||||
*/
|
||||
private ConversionService userConversionService = null;
|
||||
|
||||
/**
|
||||
* A conversion service that can convert between types.
|
||||
*/
|
||||
private ConversionService conversionService = createConversionService(userConversionService);
|
||||
|
||||
/**
|
||||
* A resource loader that can load resources.
|
||||
*/
|
||||
private ResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||
|
||||
/**
|
||||
* Sets the factory encapsulating the creation of central Flow artifacts such as {@link Flow flows} and
|
||||
* {@link State states}.
|
||||
*/
|
||||
public void setFlowArtifactFactory(FlowArtifactFactory flowArtifactFactory) {
|
||||
Assert.notNull(flowArtifactFactory, "The flow artifact factory is required");
|
||||
this.flowArtifactFactory = flowArtifactFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the factory for creating bean invoking actions, actions that adapt methods on objects to the {@link Action}
|
||||
* interface.
|
||||
*/
|
||||
public void setBeanInvokingActionFactory(BeanInvokingActionFactory beanInvokingActionFactory) {
|
||||
Assert.notNull(beanInvokingActionFactory, "The bean invoking action factory is required");
|
||||
this.beanInvokingActionFactory = beanInvokingActionFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the expression parser responsible for parsing expression strings into evaluatable expression objects.
|
||||
*/
|
||||
public void setExpressionParser(ExpressionParser expressionParser) {
|
||||
Assert.notNull(expressionParser, "The expression parser is required");
|
||||
this.expressionParser = expressionParser;
|
||||
// this has impact on the TextToExpression converter in the conversion service!
|
||||
this.conversionService = createConversionService(userConversionService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the conversion service to use to convert between types; typically from string to a rich object type.
|
||||
*/
|
||||
public void setConversionService(ConversionService userConversionService) {
|
||||
Assert.notNull(userConversionService, "The conversion service is required");
|
||||
this.userConversionService = userConversionService;
|
||||
this.conversionService = createConversionService(userConversionService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the resource loader to load file-based resources from string-encoded paths. This is optional.
|
||||
*/
|
||||
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
public Flow getSubflow(String id) throws FlowArtifactLookupException {
|
||||
throw new FlowArtifactLookupException(id, Flow.class, "Subflow lookup is not supported by this service locator");
|
||||
}
|
||||
|
||||
public Action getAction(String id) throws FlowArtifactLookupException {
|
||||
return (Action) getBean(id, Action.class);
|
||||
}
|
||||
|
||||
public FlowAttributeMapper getAttributeMapper(String id) throws FlowArtifactLookupException {
|
||||
return (FlowAttributeMapper) getBean(id, FlowAttributeMapper.class);
|
||||
}
|
||||
|
||||
public TransitionCriteria getTransitionCriteria(String id) throws FlowArtifactLookupException {
|
||||
return (TransitionCriteria) getBean(id, TransitionCriteria.class);
|
||||
}
|
||||
|
||||
public TargetStateResolver getTargetStateResolver(String id) throws FlowArtifactLookupException {
|
||||
return (TargetStateResolver) getBean(id, TargetStateResolver.class);
|
||||
}
|
||||
|
||||
public ViewSelector getViewSelector(String id) throws FlowArtifactLookupException {
|
||||
return (ViewSelector) getBean(id, ViewSelector.class);
|
||||
}
|
||||
|
||||
public FlowExecutionExceptionHandler getExceptionHandler(String id) throws FlowArtifactLookupException {
|
||||
return (FlowExecutionExceptionHandler) getBean(id, FlowExecutionExceptionHandler.class);
|
||||
}
|
||||
|
||||
public FlowArtifactFactory getFlowArtifactFactory() {
|
||||
return flowArtifactFactory;
|
||||
}
|
||||
|
||||
public BeanInvokingActionFactory getBeanInvokingActionFactory() {
|
||||
return beanInvokingActionFactory;
|
||||
}
|
||||
|
||||
public BeanFactory getBeanFactory() throws UnsupportedOperationException {
|
||||
throw new UnsupportedOperationException("Bean factory lookup is not supported by this service locator");
|
||||
}
|
||||
|
||||
public ResourceLoader getResourceLoader() {
|
||||
return resourceLoader;
|
||||
}
|
||||
|
||||
public ExpressionParser getExpressionParser() {
|
||||
return expressionParser;
|
||||
}
|
||||
|
||||
public ConversionService getConversionService() {
|
||||
return conversionService;
|
||||
}
|
||||
|
||||
// helpers for use by subclasses
|
||||
|
||||
/**
|
||||
* Helper method for determining if the configured bean factory contains the provided bean.
|
||||
* @param id the id of the bean
|
||||
* @return true if yes, false otherwise
|
||||
*/
|
||||
protected boolean containsBean(String id) {
|
||||
return getBeanFactory().containsBean(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to lookup the bean representing a flow artifact of the specified type.
|
||||
* @param id the bean id
|
||||
* @param artifactType the bean type
|
||||
* @return the bean
|
||||
* @throws FlowArtifactLookupException an exception occurred
|
||||
*/
|
||||
protected Object getBean(String id, Class artifactType) throws FlowArtifactLookupException {
|
||||
try {
|
||||
return getBeanFactory().getBean(id, artifactType);
|
||||
} catch (BeansException e) {
|
||||
throw new FlowArtifactLookupException(id, artifactType, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to lookup the type of the bean with the provided id.
|
||||
* @param id the bean id
|
||||
* @param artifactType the bean type
|
||||
* @return the bean's type
|
||||
* @throws FlowArtifactLookupException an exception occurred
|
||||
*/
|
||||
protected Class getBeanType(String id, Class artifactType) throws FlowArtifactLookupException {
|
||||
try {
|
||||
return getBeanFactory().getType(id);
|
||||
} catch (BeansException e) {
|
||||
throw new FlowArtifactLookupException(id, artifactType, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup a conversion service used by this flow service locator.
|
||||
* @param userConversionService a user supplied conversion service, optional
|
||||
* @return the newly created conversion service
|
||||
*/
|
||||
protected ConversionService createConversionService(ConversionService userConversionService) {
|
||||
DefaultConversionService defaultConversionService = new DefaultConversionService();
|
||||
addWebFlowConverters(defaultConversionService);
|
||||
if (userConversionService != null) {
|
||||
return new CompositeConversionService(new ConversionService[] { userConversionService,
|
||||
defaultConversionService });
|
||||
} else {
|
||||
return defaultConversionService;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all web flow specific converters to given conversion service.
|
||||
*/
|
||||
protected void addWebFlowConverters(GenericConversionService conversionService) {
|
||||
conversionService.addConverter(new TextToTransitionCriteria(this));
|
||||
conversionService.addConverter(new TextToTargetStateResolver(this));
|
||||
conversionService.addConverter(new TextToViewSelector(this));
|
||||
conversionService.addConverter(new TextToExpression(getExpressionParser()));
|
||||
conversionService.addConverter(new TextToMethodSignature(conversionService));
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* 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.engine.builder;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
|
||||
import org.springframework.webflow.definition.registry.NoSuchFlowDefinitionException;
|
||||
import org.springframework.webflow.engine.Flow;
|
||||
|
||||
/**
|
||||
* The default flow service locator implementation that obtains subflow definitions from a dedicated
|
||||
* {@link FlowDefinitionRegistry} and obtains the remaining services from a generic Spring {@link BeanFactory}.
|
||||
*
|
||||
* @see FlowDefinitionRegistry
|
||||
* @see FlowServiceLocator#getSubflow(String)
|
||||
* @see BeanFactory
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public class DefaultFlowServiceLocator extends BaseFlowServiceLocator {
|
||||
|
||||
/**
|
||||
* The registry for locating subflows.
|
||||
*/
|
||||
private FlowDefinitionRegistry subflowRegistry;
|
||||
|
||||
/**
|
||||
* The Spring bean factory used.
|
||||
*/
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
/**
|
||||
* Creates a flow service locator that retrieves subflows from the provided registry and additional artifacts from
|
||||
* the provided bean factory.
|
||||
* @param subflowRegistry the registry for loading subflows
|
||||
* @param beanFactory the spring bean factory
|
||||
*/
|
||||
public DefaultFlowServiceLocator(FlowDefinitionRegistry subflowRegistry, BeanFactory beanFactory) {
|
||||
Assert.notNull(subflowRegistry, "The subflow registry is required");
|
||||
Assert.notNull(beanFactory, "The bean factory is required");
|
||||
this.subflowRegistry = subflowRegistry;
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience flow service locator constructor that looks up a flow definition registry using given bean id in
|
||||
* given bean factory. The registry is used to retrieve subflows. All additional artifacts are looked up in the
|
||||
* provided bean factory.
|
||||
* @param subflowRegistryBeanId the bean id of the subflow FlowDefinitionRegistry
|
||||
* @param beanFactory the Spring bean factory
|
||||
* @since 1.0.2
|
||||
*/
|
||||
public DefaultFlowServiceLocator(String subflowRegistryBeanId, BeanFactory beanFactory) {
|
||||
Assert.notNull(subflowRegistryBeanId, "The subflow registry bean id is required");
|
||||
Assert.notNull(beanFactory, "The bean factory is required");
|
||||
this.subflowRegistry = (FlowDefinitionRegistry) beanFactory.getBean(subflowRegistryBeanId,
|
||||
FlowDefinitionRegistry.class);
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
public Flow getSubflow(String id) throws FlowArtifactLookupException {
|
||||
try {
|
||||
return (Flow) subflowRegistry.getFlowDefinition(id);
|
||||
} catch (NoSuchFlowDefinitionException e) {
|
||||
throw new FlowArtifactLookupException(id, Flow.class, "Could not locate subflow definition with id '" + id
|
||||
+ "'", e);
|
||||
}
|
||||
}
|
||||
|
||||
public BeanFactory getBeanFactory() {
|
||||
return beanFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the flow definition registry used to lookup subflows.
|
||||
* @return the flow definition registry
|
||||
*/
|
||||
protected FlowDefinitionRegistry getSubflowRegistry() {
|
||||
return subflowRegistry;
|
||||
}
|
||||
}
|
||||
@@ -29,9 +29,9 @@ import org.springframework.webflow.engine.TargetStateResolver;
|
||||
import org.springframework.webflow.engine.Transition;
|
||||
import org.springframework.webflow.engine.TransitionCriteria;
|
||||
import org.springframework.webflow.engine.TransitionableState;
|
||||
import org.springframework.webflow.engine.ViewSelector;
|
||||
import org.springframework.webflow.engine.ViewState;
|
||||
import org.springframework.webflow.execution.Action;
|
||||
import org.springframework.webflow.execution.ViewFactory;
|
||||
|
||||
/**
|
||||
* A factory for core web flow elements such as {@link Flow flows}, {@link State states}, and
|
||||
@@ -55,12 +55,9 @@ public class FlowArtifactFactory {
|
||||
* @param attributes attributes to assign to the Flow, which may also be used to affect flow construction; may be
|
||||
* null
|
||||
* @return the initial flow instance, ready for assembly by a FlowBuilder
|
||||
* @throws FlowArtifactLookupException an exception occured creating the Flow instance
|
||||
*/
|
||||
public Flow createFlow(String id, AttributeMap attributes) throws FlowArtifactLookupException {
|
||||
Flow flow = new Flow(id);
|
||||
flow.getAttributeMap().putAll(attributes);
|
||||
return flow;
|
||||
public Flow createFlow(String id, AttributeMap attributes) {
|
||||
return Flow.create(id, attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,7 +67,8 @@ public class FlowArtifactFactory {
|
||||
* @param id the identifier to assign to the state, must be unique to its owning flow (required)
|
||||
* @param flow the flow that will own (contain) this state (required)
|
||||
* @param entryActions any state entry actions; may be null
|
||||
* @param viewSelector the state view selector strategy; may be null
|
||||
* @param viewFactory the state view factory strategy
|
||||
* @param redirect whether to send a flow execution redirect before rendering
|
||||
* @param renderActions any 'render actions' to execute on entry and refresh; may be null
|
||||
* @param transitions any transitions (paths) out of this state; may be null
|
||||
* @param exceptionHandlers any exception handlers; may be null
|
||||
@@ -78,15 +76,12 @@ public class FlowArtifactFactory {
|
||||
* @param attributes attributes to assign to the State, which may also be used to affect state construction; may be
|
||||
* null
|
||||
* @return the fully initialized view state instance
|
||||
* @throws FlowArtifactLookupException an exception occured creating the state
|
||||
*/
|
||||
public State createViewState(String id, Flow flow, Action[] entryActions, ViewSelector viewSelector,
|
||||
Action[] renderActions, Transition[] transitions, FlowExecutionExceptionHandler[] exceptionHandlers,
|
||||
Action[] exitActions, AttributeMap attributes) throws FlowArtifactLookupException {
|
||||
ViewState viewState = new ViewState(flow, id);
|
||||
if (viewSelector != null) {
|
||||
viewState.setViewSelector(viewSelector);
|
||||
}
|
||||
public State createViewState(String id, Flow flow, Action[] entryActions, ViewFactory viewFactory,
|
||||
boolean redirect, Action[] renderActions, Transition[] transitions,
|
||||
FlowExecutionExceptionHandler[] exceptionHandlers, Action[] exitActions, AttributeMap attributes) {
|
||||
ViewState viewState = new ViewState(flow, id, viewFactory);
|
||||
viewState.setRedirect(redirect);
|
||||
viewState.getRenderActionList().addAll(renderActions);
|
||||
configureCommonProperties(viewState, entryActions, transitions, exceptionHandlers, exitActions, attributes);
|
||||
return viewState;
|
||||
@@ -106,11 +101,10 @@ public class FlowArtifactFactory {
|
||||
* @param attributes attributes to assign to the State, which may also be used to affect state construction; may be
|
||||
* null
|
||||
* @return the fully initialized action state instance
|
||||
* @throws FlowArtifactLookupException an exception occured creating the state
|
||||
*/
|
||||
public State createActionState(String id, Flow flow, Action[] entryActions, Action[] actions,
|
||||
Transition[] transitions, FlowExecutionExceptionHandler[] exceptionHandlers, Action[] exitActions,
|
||||
AttributeMap attributes) throws FlowArtifactLookupException {
|
||||
AttributeMap attributes) {
|
||||
ActionState actionState = new ActionState(flow, id);
|
||||
actionState.getActionList().addAll(actions);
|
||||
configureCommonProperties(actionState, entryActions, transitions, exceptionHandlers, exitActions, attributes);
|
||||
@@ -130,11 +124,9 @@ public class FlowArtifactFactory {
|
||||
* @param attributes attributes to assign to the State, which may also be used to affect state construction; may be
|
||||
* null
|
||||
* @return the fully initialized decision state instance
|
||||
* @throws FlowArtifactLookupException an exception occured creating the state
|
||||
*/
|
||||
public State createDecisionState(String id, Flow flow, Action[] entryActions, Transition[] transitions,
|
||||
FlowExecutionExceptionHandler[] exceptionHandlers, Action[] exitActions, AttributeMap attributes)
|
||||
throws FlowArtifactLookupException {
|
||||
FlowExecutionExceptionHandler[] exceptionHandlers, Action[] exitActions, AttributeMap attributes) {
|
||||
DecisionState decisionState = new DecisionState(flow, id);
|
||||
configureCommonProperties(decisionState, entryActions, transitions, exceptionHandlers, exitActions, attributes);
|
||||
return decisionState;
|
||||
@@ -155,12 +147,10 @@ public class FlowArtifactFactory {
|
||||
* @param attributes attributes to assign to the State, which may also be used to affect state construction; may be
|
||||
* null
|
||||
* @return the fully initialized subflow state instance
|
||||
* @throws FlowArtifactLookupException an exception occured creating the state
|
||||
*/
|
||||
public State createSubflowState(String id, Flow flow, Action[] entryActions, Flow subflow,
|
||||
FlowAttributeMapper attributeMapper, Transition[] transitions,
|
||||
FlowExecutionExceptionHandler[] exceptionHandlers, Action[] exitActions, AttributeMap attributes)
|
||||
throws FlowArtifactLookupException {
|
||||
FlowExecutionExceptionHandler[] exceptionHandlers, Action[] exitActions, AttributeMap attributes) {
|
||||
SubflowState subflowState = new SubflowState(flow, id, subflow);
|
||||
if (attributeMapper != null) {
|
||||
subflowState.setAttributeMapper(attributeMapper);
|
||||
@@ -176,20 +166,18 @@ public class FlowArtifactFactory {
|
||||
* @param id the identifier to assign to the state, must be unique to its owning flow (required)
|
||||
* @param flow the flow that will own (contain) this state (required)
|
||||
* @param entryActions any state entry actions; may be null
|
||||
* @param viewSelector the state confirmation view selector strategy; may be null
|
||||
* @param finalResponseAction the state response renderer; may be null
|
||||
* @param outputMapper the state output mapper; may be null
|
||||
* @param exceptionHandlers any exception handlers; may be null
|
||||
* @param attributes attributes to assign to the State, which may also be used to affect state construction; may be
|
||||
* null
|
||||
* @return the fully initialized subflow state instance
|
||||
* @throws FlowArtifactLookupException an exception occured creating the state
|
||||
*/
|
||||
public State createEndState(String id, Flow flow, Action[] entryActions, ViewSelector viewSelector,
|
||||
AttributeMapper outputMapper, FlowExecutionExceptionHandler[] exceptionHandlers, AttributeMap attributes)
|
||||
throws FlowArtifactLookupException {
|
||||
public State createEndState(String id, Flow flow, Action[] entryActions, Action finalResponseAction,
|
||||
AttributeMapper outputMapper, FlowExecutionExceptionHandler[] exceptionHandlers, AttributeMap attributes) {
|
||||
EndState endState = new EndState(flow, id);
|
||||
if (viewSelector != null) {
|
||||
endState.setViewSelector(viewSelector);
|
||||
if (finalResponseAction != null) {
|
||||
endState.setFinalResponseAction(finalResponseAction);
|
||||
}
|
||||
if (outputMapper != null) {
|
||||
endState.setOutputMapper(outputMapper);
|
||||
@@ -208,10 +196,9 @@ public class FlowArtifactFactory {
|
||||
* @param attributes attributes to assign to the transition, which may also be used to affect transition
|
||||
* construction; may be null
|
||||
* @return the fully initialized transition instance
|
||||
* @throws FlowArtifactLookupException an exception occured creating the transition
|
||||
*/
|
||||
public Transition createTransition(TargetStateResolver targetStateResolver, TransitionCriteria matchingCriteria,
|
||||
TransitionCriteria executionCriteria, AttributeMap attributes) throws FlowArtifactLookupException {
|
||||
TransitionCriteria executionCriteria, AttributeMap attributes) {
|
||||
Transition transition = new Transition(targetStateResolver);
|
||||
if (matchingCriteria != null) {
|
||||
transition.setMatchingCriteria(matchingCriteria);
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
* 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.engine.builder;
|
||||
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.webflow.core.FlowException;
|
||||
import org.springframework.webflow.execution.FlowExecutionException;
|
||||
|
||||
/**
|
||||
* A flow artifact lookup exception is thrown when an artifact (such as a flow, state, action, etc.) required by the
|
||||
* webflow system cannot be obtained.
|
||||
* <p>
|
||||
* Flow artifact lookup exceptions indicate unrecoverable problems with the flow definition, e.g. a required action of a
|
||||
* flow cannot be found. They're not used to signal problems related to execution of a client request. A
|
||||
* {@link FlowExecutionException} is used for that.
|
||||
*
|
||||
* @see org.springframework.webflow.execution.FlowExecutionException
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Erwin Vervaet
|
||||
*/
|
||||
public class FlowArtifactLookupException extends FlowException {
|
||||
|
||||
/**
|
||||
* The id of the artifact that could not be retrieved.
|
||||
*/
|
||||
private String artifactId;
|
||||
|
||||
/**
|
||||
* The type of artifact that could not be retrieved.
|
||||
*/
|
||||
private Class artifactType;
|
||||
|
||||
/**
|
||||
* Create a new flow artifact lookup exception.
|
||||
* @param artifactId the id of the artifact
|
||||
* @param artifactType the expected artifact type
|
||||
*/
|
||||
public FlowArtifactLookupException(String artifactId, Class artifactType) {
|
||||
this(artifactId, artifactType, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new flow artifact lookup exception.
|
||||
* @param artifactId the id of the artifact
|
||||
* @param artifactType the expected artifact type
|
||||
* @param cause the underlying cause of this exception
|
||||
*/
|
||||
public FlowArtifactLookupException(String artifactId, Class artifactType, Throwable cause) {
|
||||
this(artifactId, artifactType, null, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new flow artifact lookup exception.
|
||||
* @param artifactId the id of the artifact
|
||||
* @param artifactType the expected artifact type
|
||||
* @param message descriptive message
|
||||
*/
|
||||
public FlowArtifactLookupException(String artifactId, Class artifactType, String message) {
|
||||
this(artifactId, artifactType, message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new flow artifact lookup exception.
|
||||
* @param artifactId the id of the artifact
|
||||
* @param artifactType the expected artifact type
|
||||
* @param message descriptive message
|
||||
* @param cause the underlying cause of this exception
|
||||
*/
|
||||
public FlowArtifactLookupException(String artifactId, Class artifactType, String message, Throwable cause) {
|
||||
super((StringUtils.hasText(message) ? message : "Unable to obtain a " + ClassUtils.getShortName(artifactType)
|
||||
+ " flow artifact with id '" + artifactId + "': make sure there is a valid [" + artifactType
|
||||
+ "] exported with this id"), cause);
|
||||
this.artifactType = artifactType;
|
||||
this.artifactId = artifactId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the id of the artifact that cannot be found.
|
||||
*/
|
||||
public String getArtifactId() {
|
||||
return artifactId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the expected artifact type.
|
||||
*/
|
||||
public Class getArtifactType() {
|
||||
return artifactType;
|
||||
}
|
||||
}
|
||||
@@ -15,11 +15,7 @@
|
||||
*/
|
||||
package org.springframework.webflow.engine.builder;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.webflow.core.collection.AttributeMap;
|
||||
import org.springframework.webflow.core.collection.CollectionUtils;
|
||||
import org.springframework.webflow.engine.Flow;
|
||||
|
||||
/**
|
||||
@@ -31,7 +27,7 @@ import org.springframework.webflow.engine.Flow;
|
||||
*
|
||||
* <pre>
|
||||
* FlowBuilder builder = ...;
|
||||
* Flow flow = new FlowAssembler("myFlow", builder).assembleFlow();
|
||||
* Flow flow = new FlowAssembler("myFlow", builder, null).assembleFlow();
|
||||
* </pre>
|
||||
*
|
||||
* @see org.springframework.webflow.engine.builder.FlowBuilder
|
||||
@@ -41,58 +37,26 @@ import org.springframework.webflow.engine.Flow;
|
||||
*/
|
||||
public class FlowAssembler {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(FlowAssembler.class);
|
||||
|
||||
/**
|
||||
* The identifier to assign to the flow.
|
||||
*/
|
||||
private String flowId;
|
||||
|
||||
/**
|
||||
* Attributes that can be used to affect flow construction.
|
||||
*/
|
||||
private AttributeMap flowAttributes;
|
||||
|
||||
/**
|
||||
* The flow builder strategy used to construct the flow from its component parts.
|
||||
*/
|
||||
private FlowBuilder flowBuilder;
|
||||
|
||||
/**
|
||||
* Create a new flow assembler that will direct Flow assembly using the specified builder strategy.
|
||||
* @param flowId the flow id to assign
|
||||
* @param flowBuilder the builder the factory will use to build flows
|
||||
* Context needed to initialize the builder so it can perform a build operation.
|
||||
*/
|
||||
public FlowAssembler(String flowId, FlowBuilder flowBuilder) {
|
||||
this(flowId, null, flowBuilder);
|
||||
}
|
||||
private FlowBuilderContext flowBuilderContext;
|
||||
|
||||
/**
|
||||
* Create a new flow assembler that will direct Flow assembly using the specified builder strategy.
|
||||
* @param flowId the flow id to assign
|
||||
* @param flowAttributes externally assigned flow attributes that can affect flow construction
|
||||
* @param flowBuilder the builder the factory will use to build flows
|
||||
* @param flowBuilderContext context to influence the build process
|
||||
*/
|
||||
public FlowAssembler(String flowId, AttributeMap flowAttributes, FlowBuilder flowBuilder) {
|
||||
Assert.hasText(flowId, "The flow id is required");
|
||||
Assert.notNull(flowBuilder, "The flow builder is required");
|
||||
this.flowId = flowId;
|
||||
this.flowAttributes = (flowAttributes != null ? flowAttributes : CollectionUtils.EMPTY_ATTRIBUTE_MAP);
|
||||
public FlowAssembler(FlowBuilder flowBuilder, FlowBuilderContext flowBuilderContext) {
|
||||
Assert.notNull(flowBuilder, "A flow builder is required for flow assembly");
|
||||
Assert.notNull(flowBuilderContext, "A flow builder context is required for flow assembly");
|
||||
this.flowBuilder = flowBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the identifier to assign to the flow.
|
||||
*/
|
||||
public String getFlowId() {
|
||||
return flowId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns externally assigned attributes that can be used to affect flow construction.
|
||||
*/
|
||||
public AttributeMap getFlowAttributes() {
|
||||
return flowAttributes;
|
||||
this.flowBuilderContext = flowBuilderContext;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,23 +66,27 @@ public class FlowAssembler {
|
||||
return flowBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the flow builder context.
|
||||
* @return flow builder context
|
||||
*/
|
||||
public FlowBuilderContext getFlowBuilderContext() {
|
||||
return flowBuilderContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assembles the flow, directing the construction process by delegating to the configured FlowBuilder. Every call to
|
||||
* this method will assemble the Flow instance.
|
||||
* <p>
|
||||
* This will drive the flow construction process as described in the {@link FlowBuilder} JavaDoc, starting with
|
||||
* builder initialisation using {@link FlowBuilder#init(String, AttributeMap)} and finishing by cleaning up the
|
||||
* builder initialization using {@link FlowBuilder#init(FlowBuilderContext)} and finishing by cleaning up the
|
||||
* builder with a call to {@link FlowBuilder#dispose()}.
|
||||
* @return the constructed flow
|
||||
* @throws FlowBuilderException when flow assembly fails
|
||||
*/
|
||||
public Flow assembleFlow() throws FlowBuilderException {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Assembling flow definition with id '" + flowId + "' using flow builder '" + flowBuilder
|
||||
+ "'; externally assigned flow attributes are '" + flowAttributes + "'");
|
||||
}
|
||||
try {
|
||||
flowBuilder.init(flowId, flowAttributes);
|
||||
flowBuilder.init(flowBuilderContext);
|
||||
directAssembly();
|
||||
return flowBuilder.getFlow();
|
||||
} finally {
|
||||
@@ -134,7 +102,6 @@ public class FlowAssembler {
|
||||
flowBuilder.buildVariables();
|
||||
flowBuilder.buildInputMapper();
|
||||
flowBuilder.buildStartActions();
|
||||
flowBuilder.buildInlineFlows();
|
||||
flowBuilder.buildStates();
|
||||
flowBuilder.buildGlobalTransitions();
|
||||
flowBuilder.buildEndActions();
|
||||
|
||||
@@ -15,18 +15,15 @@
|
||||
*/
|
||||
package org.springframework.webflow.engine.builder;
|
||||
|
||||
import org.springframework.webflow.core.collection.AttributeMap;
|
||||
import org.springframework.webflow.engine.Flow;
|
||||
|
||||
/**
|
||||
* Builder interface used to build a flow definition. The process of building a flow consists of the following steps:
|
||||
* <ol>
|
||||
* <li> Initialize this builder, creating the initial flow definition, by calling {@link #init(String, AttributeMap)}.
|
||||
* <li> Initialize this builder, creating the initial flow definition, by calling {@link #init(FlowBuilderContext)}.
|
||||
* <li> Call {@link #buildVariables()} to create any variables of the flow and add them to the flow definition.
|
||||
* <li> Call {@link #buildInputMapper()} to create and set the input mapper for the flow.
|
||||
* <li> Call {@link #buildStartActions()} to create and add any start actions to the flow.
|
||||
* <li> Call {@link #buildInlineFlows()} to create any inline flows encapsulated by the flow and add them to the flow
|
||||
* definition.
|
||||
* <li> Call {@link #buildStates()} to create the states of the flow and add them to the flow definition.
|
||||
* <li> Call {@link #buildGlobalTransitions()} to create the any transitions shared by all states of the flow and add
|
||||
* them to the flow definition.
|
||||
@@ -43,17 +40,14 @@ import org.springframework.webflow.engine.Flow;
|
||||
* <code>OrderFlowBuilder</code> built in Java code, or a generic flow builder strategy, like the
|
||||
* <code>XmlFlowBuilder</code>, for building flows from an XML-definition.
|
||||
* <p>
|
||||
* Flow builders are used by the {@link org.springframework.webflow.engine.builder.FlowAssembler}, which acts as an
|
||||
* assembler (director). Flow Builders may be reused, however, exercise caution when doing this as these objects are not
|
||||
* thread safe. Also, for each use be sure to call init, followed by the build* methods, getFlow, and dispose completely
|
||||
* in that order.
|
||||
* Flow builders are used by the {@link FlowAssembler}, which acts as an assembler (director). Flow Builders may be
|
||||
* reused, however, exercise caution when doing this as these objects are not thread safe. Also, for each use be sure to
|
||||
* call init, followed by the build* methods, getFlow, and dispose completely in that order.
|
||||
* <p>
|
||||
* This is an example of the classic GoF builder pattern.
|
||||
*
|
||||
* @see Flow
|
||||
* @see org.springframework.webflow.engine.builder.FlowAssembler
|
||||
* @see org.springframework.webflow.engine.builder.AbstractFlowBuilder
|
||||
* @see org.springframework.webflow.engine.builder.xml.XmlFlowBuilder
|
||||
* @see FlowAssembler
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Erwin Vervaet
|
||||
@@ -63,63 +57,56 @@ public interface FlowBuilder {
|
||||
/**
|
||||
* Initialize this builder. This could cause the builder to open a stream to an externalized resource representing
|
||||
* the flow definition, for example.
|
||||
* @param flowId the identifier to assign to the flow
|
||||
* @param attributes custom attributes to assign to the flow
|
||||
* @throws FlowBuilderException an exception occured building the flow
|
||||
* @param context the flow builder context
|
||||
* @throws FlowBuilderException an exception occurred building the flow
|
||||
*/
|
||||
public void init(String flowId, AttributeMap attributes) throws FlowBuilderException;
|
||||
public void init(FlowBuilderContext context) throws FlowBuilderException;
|
||||
|
||||
/**
|
||||
* Builds any variables initialized by the flow when it starts.
|
||||
* @throws FlowBuilderException an exception occured building the flow
|
||||
* @throws FlowBuilderException an exception occurred building the flow
|
||||
*/
|
||||
public void buildVariables() throws FlowBuilderException;
|
||||
|
||||
/**
|
||||
* Builds the input mapper responsible for mapping flow input on start.
|
||||
* @throws FlowBuilderException an exception occured building the flow
|
||||
* @throws FlowBuilderException an exception occurred building the flow
|
||||
*/
|
||||
public void buildInputMapper() throws FlowBuilderException;
|
||||
|
||||
/**
|
||||
* Builds any start actions to execute when the flow starts.
|
||||
* @throws FlowBuilderException an exception occured building the flow
|
||||
* @throws FlowBuilderException an exception occurred building the flow
|
||||
*/
|
||||
public void buildStartActions() throws FlowBuilderException;
|
||||
|
||||
/**
|
||||
* Builds any "in-line" flows encapsulated by the flow.
|
||||
* @throws FlowBuilderException an exception occured building the flow
|
||||
*/
|
||||
public void buildInlineFlows() throws FlowBuilderException;
|
||||
|
||||
/**
|
||||
* Builds the states of the flow.
|
||||
* @throws FlowBuilderException an exception occured building the flow
|
||||
* @throws FlowBuilderException an exception occurred building the flow
|
||||
*/
|
||||
public void buildStates() throws FlowBuilderException;
|
||||
|
||||
/**
|
||||
* Builds any transitions shared by all states of the flow.
|
||||
* @throws FlowBuilderException an exception occured building the flow
|
||||
* @throws FlowBuilderException an exception occurred building the flow
|
||||
*/
|
||||
public void buildGlobalTransitions() throws FlowBuilderException;
|
||||
|
||||
/**
|
||||
* Builds any end actions to execute when the flow ends.
|
||||
* @throws FlowBuilderException an exception occured building the flow
|
||||
* @throws FlowBuilderException an exception occurred building the flow
|
||||
*/
|
||||
public void buildEndActions() throws FlowBuilderException;
|
||||
|
||||
/**
|
||||
* Builds the output mapper responsible for mapping flow output on end.
|
||||
* @throws FlowBuilderException an exception occured building the flow
|
||||
* @throws FlowBuilderException an exception occurred building the flow
|
||||
*/
|
||||
public void buildOutputMapper() throws FlowBuilderException;
|
||||
|
||||
/**
|
||||
* Creates and adds all exception handlers to the flow built by this builder.
|
||||
* @throws FlowBuilderException an exception occured building this flow
|
||||
* @throws FlowBuilderException an exception occurred building this flow
|
||||
*/
|
||||
public void buildExceptionHandlers() throws FlowBuilderException;
|
||||
|
||||
@@ -132,7 +119,7 @@ public interface FlowBuilder {
|
||||
|
||||
/**
|
||||
* Shutdown the builder, releasing any resources it holds. A new flow construction process should start with another
|
||||
* call to the {@link #init(String, AttributeMap)} method.
|
||||
* call to the {@link #init(FlowBuilderContext)} method.
|
||||
*/
|
||||
public void dispose();
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package org.springframework.webflow.engine.builder;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.binding.convert.ConversionService;
|
||||
import org.springframework.binding.expression.ExpressionParser;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.webflow.action.BeanInvokingActionFactory;
|
||||
import org.springframework.webflow.core.collection.AttributeMap;
|
||||
import org.springframework.webflow.definition.registry.FlowDefinitionLocator;
|
||||
|
||||
public interface FlowBuilderContext {
|
||||
|
||||
/**
|
||||
* Returns an externally configured flow definition identifier to assign to the flow being built.
|
||||
* @return the flow id
|
||||
*/
|
||||
public String getFlowId();
|
||||
|
||||
/**
|
||||
* Returns externally configured attributes to assign to the flow definition being built.
|
||||
* @return the flow attributes
|
||||
*/
|
||||
public AttributeMap getFlowAttributes();
|
||||
|
||||
/**
|
||||
* Returns the locator for locating dependent flows (subflows).
|
||||
* @return the flow definition locator
|
||||
*/
|
||||
public FlowDefinitionLocator getFlowDefinitionLocator();
|
||||
|
||||
/**
|
||||
* Returns the factory for core flow artifacts such as Flow and State.
|
||||
* @return the flow artifact factory
|
||||
*/
|
||||
public FlowArtifactFactory getFlowArtifactFactory();
|
||||
|
||||
/**
|
||||
* Returns the factory for bean invoking actions.
|
||||
* @return the bean invoking action factory
|
||||
*/
|
||||
public BeanInvokingActionFactory getBeanInvokingActionFactory();
|
||||
|
||||
/**
|
||||
* Returns the view factory creator for configuring a ViewFactory per view state
|
||||
* @return the view factory creator
|
||||
*/
|
||||
public ViewFactoryCreator getViewFactoryCreator();
|
||||
|
||||
/**
|
||||
* Returns the expression parser for parsing expression strings.
|
||||
* @return the expression parser
|
||||
*/
|
||||
public ExpressionParser getExpressionParser();
|
||||
|
||||
/**
|
||||
* Returns a generic type conversion service for converting between types, typically from string to a rich value
|
||||
* object.
|
||||
* @return the generic conversion service
|
||||
*/
|
||||
public ConversionService getConversionService();
|
||||
|
||||
/**
|
||||
* Returns a generic resource loader for accessing file-based resources.
|
||||
* @return the generic resource loader
|
||||
*/
|
||||
public ResourceLoader getResourceLoader();
|
||||
|
||||
/**
|
||||
* Returns a generic bean factory for accessing arbitrary services by their id.
|
||||
* @return the bean factory
|
||||
*/
|
||||
public BeanFactory getBeanFactory();
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
/*
|
||||
* 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.engine.builder;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.webflow.definition.registry.FlowDefinitionRegistrar;
|
||||
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
|
||||
import org.springframework.webflow.definition.registry.FlowDefinitionRegistryImpl;
|
||||
import org.springframework.webflow.definition.registry.FlowDefinitionResource;
|
||||
import org.springframework.webflow.engine.builder.xml.XmlFlowRegistrar;
|
||||
|
||||
/**
|
||||
* A factory bean that accepts an arbitrary list of {@link FlowDefinitionRegistrar}s and uses them to register flows.
|
||||
* This implementation should not be used programmatically but rather from the spring-webflow-config XML namespace.
|
||||
* <p>
|
||||
* Example Usage:
|
||||
*
|
||||
* <pre>
|
||||
* <flow:registry id="flowRegistry">
|
||||
* <flow:location path="flow1.xml"/>
|
||||
* <flow:location id="foo" path="flow2.xml"/>
|
||||
* <flow:class name="BarClass"/>
|
||||
* <flow:crud entity="Account"/>
|
||||
* <flow:namespace name="baz">
|
||||
* <flow:location path="flow3.xml"/>
|
||||
* <flow:class name="XyzClass"/>
|
||||
* </flow:namespace>
|
||||
* </flow:registry>
|
||||
* </pre>
|
||||
*
|
||||
* @author Ben Hale
|
||||
*/
|
||||
public class FlowRegistryFactoryBean implements FactoryBean, InitializingBean, BeanFactoryAware {
|
||||
|
||||
private FlowDefinitionRegistry registry;
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
private FlowServiceLocator flowServiceLocator;
|
||||
|
||||
private Map xmlNamespaceFlowMappings;
|
||||
|
||||
public void setFlowServiceLocator(FlowServiceLocator flowServiceLocator) {
|
||||
this.flowServiceLocator = flowServiceLocator;
|
||||
}
|
||||
|
||||
public void setXmlNamespaceFlowMappings(Map xmlNamespaceFlowMappings) {
|
||||
this.xmlNamespaceFlowMappings = xmlNamespaceFlowMappings;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
this.registry = new FlowDefinitionRegistryImpl();
|
||||
initXml(registry);
|
||||
}
|
||||
|
||||
public Object getObject() throws Exception {
|
||||
return registry;
|
||||
}
|
||||
|
||||
public Class getObjectType() {
|
||||
return FlowDefinitionRegistry.class;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
private void initXml(FlowDefinitionRegistry registry) {
|
||||
XmlFlowRegistrar registrar = new XmlFlowRegistrar(getFlowServiceLocator(registry));
|
||||
for (Iterator mappings = xmlNamespaceFlowMappings.entrySet().iterator(); mappings.hasNext();) {
|
||||
Map.Entry mapping = (Map.Entry) mappings.next();
|
||||
String namespace = (String) mapping.getKey();
|
||||
for (Iterator resources = ((Set) mapping.getValue()).iterator(); resources.hasNext();) {
|
||||
FlowDefinitionResource resource = (FlowDefinitionResource) resources.next();
|
||||
registrar.addResource(resource, namespace);
|
||||
}
|
||||
}
|
||||
registrar.registerFlowDefinitions(registry);
|
||||
}
|
||||
|
||||
private FlowServiceLocator getFlowServiceLocator(FlowDefinitionRegistry registry) {
|
||||
if (flowServiceLocator == null) {
|
||||
this.flowServiceLocator = new DefaultFlowServiceLocator(registry, beanFactory);
|
||||
}
|
||||
return flowServiceLocator;
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* 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.engine.builder;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
|
||||
import org.springframework.webflow.definition.registry.FlowDefinitionResource;
|
||||
import org.springframework.webflow.test.MockFlowServiceLocator;
|
||||
|
||||
/**
|
||||
* Tests that the factory bean properly creates a {@link FlowDefinitionRegistry} with the proper definitions in it.
|
||||
*/
|
||||
public class FlowRegistryFactoryBeanTests extends TestCase {
|
||||
|
||||
public void testXmlRegistrar() throws Exception {
|
||||
Set emptyNamespace = new HashSet();
|
||||
emptyNamespace.add(new FlowDefinitionResource(fromClassPath("flow1.xml")));
|
||||
Set bookingNamespace = new HashSet();
|
||||
bookingNamespace.add(new FlowDefinitionResource(fromClassPath("flow2.xml")));
|
||||
Map xmlNamespaceFlowMappings = new HashMap();
|
||||
xmlNamespaceFlowMappings.put("", emptyNamespace);
|
||||
xmlNamespaceFlowMappings.put("booking", bookingNamespace);
|
||||
FlowRegistryFactoryBean factoryBean = new FlowRegistryFactoryBean();
|
||||
factoryBean.setFlowServiceLocator(new MockFlowServiceLocator());
|
||||
factoryBean.setXmlNamespaceFlowMappings(xmlNamespaceFlowMappings);
|
||||
factoryBean.afterPropertiesSet();
|
||||
FlowDefinitionRegistry registry = (FlowDefinitionRegistry) factoryBean.getObject();
|
||||
assertEquals("Incorrect number of flows", 2, registry.getFlowDefinitionCount());
|
||||
assertTrue("Missing flow", registry.containsFlowDefinition("flow1"));
|
||||
assertTrue("Missing flow", registry.containsFlowDefinition("booking/flow2"));
|
||||
}
|
||||
|
||||
private Resource fromClassPath(String resourceName) {
|
||||
return new ClassPathResource(resourceName, getClass());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
/*
|
||||
* 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.engine.builder;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.binding.convert.ConversionService;
|
||||
import org.springframework.binding.expression.ExpressionParser;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.webflow.action.AbstractBeanInvokingAction;
|
||||
import org.springframework.webflow.action.BeanInvokingActionFactory;
|
||||
import org.springframework.webflow.engine.Flow;
|
||||
import org.springframework.webflow.engine.FlowAttributeMapper;
|
||||
import org.springframework.webflow.engine.FlowExecutionExceptionHandler;
|
||||
import org.springframework.webflow.engine.State;
|
||||
import org.springframework.webflow.engine.TargetStateResolver;
|
||||
import org.springframework.webflow.engine.Transition;
|
||||
import org.springframework.webflow.engine.TransitionCriteria;
|
||||
import org.springframework.webflow.engine.ViewSelector;
|
||||
import org.springframework.webflow.execution.Action;
|
||||
|
||||
/**
|
||||
* A support interface used by flow builders at configuration time. Acts as a "service locator" responsible for:
|
||||
* <ol>
|
||||
* <li> Retrieving dependent (but externally managed) flow services needed to configure flow and state definitions. Such
|
||||
* services are usually hosted in a backing registry and may be shared by multiple flows.
|
||||
* <li> Providing access to abstract factories to create core flow definitional artifacts such as {@link Flow},
|
||||
* {@link State}, {@link Transition}, and {@link AbstractBeanInvokingAction bean invoking actions}. These artifacts
|
||||
* are unique to each flow and are typically not shared.
|
||||
* </ol>
|
||||
* <p>
|
||||
* In general, implementations of this interface act as facades to accessing and creating flow artifacts during
|
||||
* {@link FlowAssembler flow assembly}.
|
||||
* <p>
|
||||
* Finally, this interface also exposes access to generic infrastructure services also needed by flow assemblers such as
|
||||
* a {@link ConversionService} and {@link ExpressionParser}.
|
||||
*
|
||||
* @see org.springframework.webflow.engine.builder.FlowBuilder
|
||||
* @see org.springframework.webflow.engine.builder.BaseFlowBuilder
|
||||
* @see org.springframework.webflow.engine.builder.FlowAssembler
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Erwin Vervaet
|
||||
*/
|
||||
public interface FlowServiceLocator {
|
||||
|
||||
/**
|
||||
* Returns the Flow to be used as a subflow with the provided id.
|
||||
* @param id the flow id
|
||||
* @return the flow to be used as a subflow
|
||||
* @throws FlowArtifactLookupException when no such flow is found
|
||||
*/
|
||||
public Flow getSubflow(String id) throws FlowArtifactLookupException;
|
||||
|
||||
/**
|
||||
* Retrieve an action to be executed within a flow with the assigned id.
|
||||
* @param id the id of the action
|
||||
* @throws FlowArtifactLookupException when no such action is found
|
||||
*/
|
||||
public Action getAction(String id) throws FlowArtifactLookupException;
|
||||
|
||||
/**
|
||||
* Returns the flow attribute mapper with the provided id. Flow attribute mappers are used from subflow states to
|
||||
* map input and output attributes.
|
||||
* @param id the attribute mapper id
|
||||
* @return the attribute mapper
|
||||
* @throws FlowArtifactLookupException when no such mapper is found
|
||||
*/
|
||||
public FlowAttributeMapper getAttributeMapper(String id) throws FlowArtifactLookupException;
|
||||
|
||||
/**
|
||||
* Returns the transition criteria to drive state transitions with the provided id.
|
||||
* @param id the transition criteria id
|
||||
* @return the transition criteria
|
||||
* @throws FlowArtifactLookupException when no such criteria is found
|
||||
*/
|
||||
public TransitionCriteria getTransitionCriteria(String id) throws FlowArtifactLookupException;
|
||||
|
||||
/**
|
||||
* Returns the transition target state resolver with the specified id.
|
||||
* @param id the target state resolver id
|
||||
* @return the target state resolver
|
||||
* @throws FlowArtifactLookupException when no such resolver is found
|
||||
*/
|
||||
public TargetStateResolver getTargetStateResolver(String id) throws FlowArtifactLookupException;
|
||||
|
||||
/**
|
||||
* Returns the view selector to make view selections in view states with the provided id.
|
||||
* @param id the view selector id
|
||||
* @return the view selector
|
||||
* @throws FlowArtifactLookupException when no such selector is found
|
||||
*/
|
||||
public ViewSelector getViewSelector(String id) throws FlowArtifactLookupException;
|
||||
|
||||
/**
|
||||
* Returns the exception handler to handle flow execution exceptions with the provided id.
|
||||
* @param id the exception handler id
|
||||
* @return the exception handler
|
||||
* @throws FlowArtifactLookupException when no such handler is found
|
||||
*/
|
||||
public FlowExecutionExceptionHandler getExceptionHandler(String id) throws FlowArtifactLookupException;
|
||||
|
||||
/**
|
||||
* Returns the factory for core flow artifacts such as Flow and State.
|
||||
* @return the flow artifact factory
|
||||
*/
|
||||
public FlowArtifactFactory getFlowArtifactFactory();
|
||||
|
||||
/**
|
||||
* Returns the factory for bean invoking actions.
|
||||
* @return the bean invoking action factory
|
||||
*/
|
||||
public BeanInvokingActionFactory getBeanInvokingActionFactory();
|
||||
|
||||
/**
|
||||
* Returns a generic bean (service) registry for accessing arbitrary beans.
|
||||
* @return the generic service registry
|
||||
* @throws UnsupportedOperationException when not supported by this locator
|
||||
*/
|
||||
public BeanFactory getBeanFactory() throws UnsupportedOperationException;
|
||||
|
||||
/**
|
||||
* Returns a generic resource loader for accessing file-based resources.
|
||||
* @return the generic resource loader
|
||||
*/
|
||||
public ResourceLoader getResourceLoader();
|
||||
|
||||
/**
|
||||
* Returns the expression parser for parsing expression strings.
|
||||
* @return the expression parser
|
||||
*/
|
||||
public ExpressionParser getExpressionParser();
|
||||
|
||||
/**
|
||||
* Returns a generic type conversion service for converting between types, typically from string to a rich value
|
||||
* object.
|
||||
* @return the generic conversion service
|
||||
*/
|
||||
public ConversionService getConversionService();
|
||||
}
|
||||
@@ -23,7 +23,6 @@ import org.springframework.core.io.Resource;
|
||||
import org.springframework.webflow.definition.FlowDefinition;
|
||||
import org.springframework.webflow.definition.registry.FlowDefinitionConstructionException;
|
||||
import org.springframework.webflow.definition.registry.FlowDefinitionHolder;
|
||||
import org.springframework.webflow.engine.Flow;
|
||||
import org.springframework.webflow.util.ResourceHolder;
|
||||
|
||||
/**
|
||||
@@ -32,13 +31,11 @@ import org.springframework.webflow.util.ResourceHolder;
|
||||
* <p>
|
||||
* This class is thread-safe.
|
||||
* <p>
|
||||
* Note that this {@link FlowDefinition} holder uses a {@link Flow} assembler. This is normal since a {@link Flow} is a
|
||||
* {@link FlowDefinition}! This class bridges the <i>abstract</i> world of {@link FlowDefinition flow definitions}
|
||||
* with the <i>concrete</i> world of {@link Flow flow implementations}.
|
||||
* Note that this {@link FlowDefinition} holder uses a {@link FlowAssembler}. This class bridges the <i>abstract</i>
|
||||
* world of {@link FlowDefinition flow definitions} with the <i>concrete</i> world of flow implementations.
|
||||
*
|
||||
* @see FlowDefinition
|
||||
* @see Flow
|
||||
* @see FlowAssembler
|
||||
* @see FlowDefinition
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
@@ -77,7 +74,7 @@ public class RefreshableFlowDefinitionHolder implements FlowDefinitionHolder {
|
||||
}
|
||||
|
||||
public String getFlowDefinitionId() {
|
||||
return assembler.getFlowId();
|
||||
return assembler.getFlowBuilderContext().getFlowId();
|
||||
}
|
||||
|
||||
public synchronized FlowDefinition getFlowDefinition() throws FlowDefinitionConstructionException {
|
||||
@@ -85,8 +82,9 @@ public class RefreshableFlowDefinitionHolder implements FlowDefinitionHolder {
|
||||
// must return early assembly result
|
||||
return getFlowBuilder().getFlow();
|
||||
}
|
||||
if (!isAssembled()) {
|
||||
if (flowDefinition == null) {
|
||||
lastModified = calculateLastModified();
|
||||
logger.debug("Assembling the flow definition for the first time");
|
||||
assembleFlow();
|
||||
} else {
|
||||
refreshIfChanged();
|
||||
@@ -94,91 +92,66 @@ public class RefreshableFlowDefinitionHolder implements FlowDefinitionHolder {
|
||||
return flowDefinition;
|
||||
}
|
||||
|
||||
public synchronized void refresh() throws FlowBuilderException {
|
||||
public synchronized void refresh() throws FlowDefinitionConstructionException {
|
||||
assembleFlow();
|
||||
}
|
||||
|
||||
// internal helpers
|
||||
|
||||
/**
|
||||
* Returns the flow builder that actually builds the Flow definition.
|
||||
*/
|
||||
protected FlowBuilder getFlowBuilder() {
|
||||
return assembler.getFlowBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reassemble the flow if its underlying resource has changed.
|
||||
*/
|
||||
protected void refreshIfChanged() {
|
||||
if (this.lastModified == -1) {
|
||||
// just ignore, tracking last modified date not supported
|
||||
return;
|
||||
}
|
||||
long calculatedLastModified = calculateLastModified();
|
||||
if (this.lastModified < calculatedLastModified) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Resource modification detected, reloading flow definition with id '"
|
||||
+ assembler.getFlowId() + "'");
|
||||
}
|
||||
assembleFlow();
|
||||
this.lastModified = calculatedLastModified;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper that retrieves the last modified date by querying the backing flow resource.
|
||||
* @return the last modified date, or -1 if it could not be retrieved
|
||||
* @return the last modified date, or 0L if it could not be retrieved
|
||||
*/
|
||||
protected long calculateLastModified() {
|
||||
private long calculateLastModified() {
|
||||
if (getFlowBuilder() instanceof ResourceHolder) {
|
||||
Resource resource = ((ResourceHolder) getFlowBuilder()).getResource();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Calculating last modified timestamp for flow definition resource '" + resource + "'");
|
||||
}
|
||||
try {
|
||||
return resource.getFile().lastModified();
|
||||
long lastModified = resource.getFile().lastModified();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Flow definition [" + resource + "] was last modified on " + lastModified);
|
||||
}
|
||||
return lastModified;
|
||||
} catch (IOException e) {
|
||||
// ignore, last modified checks not supported
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last modified date of the backed flow definition resource.
|
||||
* @return the last modified date
|
||||
*/
|
||||
protected long getLastModified() {
|
||||
return lastModified;
|
||||
return 0L;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assemble the held flow definition, delegating to the configured FlowAssembler (director).
|
||||
*/
|
||||
protected void assembleFlow() throws FlowBuilderException {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Assembling flow definition with id '" + assembler.getFlowId() + "'");
|
||||
}
|
||||
private void assembleFlow() throws FlowDefinitionConstructionException {
|
||||
try {
|
||||
assembling = true;
|
||||
flowDefinition = assembler.assembleFlow();
|
||||
} catch (FlowBuilderException e) {
|
||||
throw new FlowDefinitionConstructionException(assembler.getFlowBuilderContext().getFlowId(), e);
|
||||
} finally {
|
||||
assembling = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a flag indicating if this holder has performed and completed flow definition assembly.
|
||||
* Reassemble the flow if its underlying resource has changed.
|
||||
*/
|
||||
protected boolean isAssembled() {
|
||||
return flowDefinition != null;
|
||||
private void refreshIfChanged() {
|
||||
long calculatedLastModified = calculateLastModified();
|
||||
if (calculatedLastModified > lastModified) {
|
||||
assembleFlow();
|
||||
lastModified = calculatedLastModified;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a flag indicating if this holder is performing assembly.
|
||||
* Returns the flow builder that actually builds the Flow definition.
|
||||
*/
|
||||
protected boolean isAssembling() {
|
||||
return assembling;
|
||||
private FlowBuilder getFlowBuilder() {
|
||||
return assembler.getFlowBuilder();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "'" + getFlowDefinitionId() + "'";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
/*
|
||||
* 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.engine.builder;
|
||||
|
||||
import org.springframework.binding.convert.ConversionContext;
|
||||
import org.springframework.binding.convert.support.ConversionServiceAwareConverter;
|
||||
import org.springframework.binding.expression.Expression;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.webflow.engine.NullViewSelector;
|
||||
import org.springframework.webflow.engine.ViewSelector;
|
||||
import org.springframework.webflow.engine.support.ApplicationViewSelector;
|
||||
import org.springframework.webflow.engine.support.ExternalRedirectSelector;
|
||||
import org.springframework.webflow.engine.support.FlowDefinitionRedirectSelector;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Converter that converts an encoded string representation of a view selector into a {@link ViewSelector} object that
|
||||
* will make selections at runtime.
|
||||
* <p>
|
||||
* This converter supports the following encoded forms:
|
||||
* <ul>
|
||||
* <li>empty - will result in a {@link NullViewSelector}.</li>
|
||||
* <li>"viewName" - will result in an {@link ApplicationViewSelector} that returns an {@link ApplicationView}
|
||||
* ViewSelection with the provided view name expression.</li>
|
||||
* <li>"redirect:<viewName>" - will result in an {@link ApplicationViewSelector} that returns an
|
||||
* {@link FlowExecutionRedirect} to a flow execution URL.</li>
|
||||
* <li>"externalRedirect:<url>" - will result in an {@link ExternalRedirectSelector} that returns an
|
||||
* {@link ExternalRedirect} to a URL.</li>
|
||||
* <li>"flowRedirect:<flowId>" - will result in a {@link FlowDefinitionRedirectSelector} that returns a
|
||||
* {@link FlowDefinitionRedirect} to a flow.</li>
|
||||
* <li>"bean:<id>" - will result in usage of a custom <code>ViewSelector</code> bean implementation.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @see org.springframework.webflow.execution.ViewSelection
|
||||
* @see org.springframework.webflow.engine.ViewSelector
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Erwin Vervaet
|
||||
*/
|
||||
public class TextToViewSelector extends ConversionServiceAwareConverter {
|
||||
|
||||
/**
|
||||
* Prefix used when the encoded view name wants to specify that a redirect is required. ("redirect:")
|
||||
*/
|
||||
public static final String REDIRECT_PREFIX = "redirect:";
|
||||
|
||||
/**
|
||||
* Prefix used when the encoded view name wants to specify that a redirect to an external URL is required.
|
||||
* ("externalRedirect:")
|
||||
*/
|
||||
public static final String EXTERNAL_REDIRECT_PREFIX = "externalRedirect:";
|
||||
|
||||
/**
|
||||
* Prefix used when the encoded view name wants to specify that a redirect to a flow definition is requred.
|
||||
* ("flowRedirect:")
|
||||
*/
|
||||
public static final String FLOW_DEFINITION_REDIRECT_PREFIX = "flowRedirect:";
|
||||
|
||||
/**
|
||||
* Prefix used when the user wants to use a ViewSelector implementation managed by a bean factory. ("bean:")
|
||||
*/
|
||||
private static final String BEAN_PREFIX = "bean:";
|
||||
|
||||
/**
|
||||
* Locator to use for loading custom ViewSelector beans.
|
||||
*/
|
||||
private FlowServiceLocator flowServiceLocator;
|
||||
|
||||
/**
|
||||
* Create a new text to ViewSelector converter. Custom ViewSelector implemenations will be looked up using given
|
||||
* service locator.
|
||||
*/
|
||||
public TextToViewSelector(FlowServiceLocator flowServiceLocator) {
|
||||
this.flowServiceLocator = flowServiceLocator;
|
||||
setConversionService(flowServiceLocator.getConversionService());
|
||||
}
|
||||
|
||||
public Class[] getSourceClasses() {
|
||||
return new Class[] { String.class };
|
||||
}
|
||||
|
||||
public Class[] getTargetClasses() {
|
||||
return new Class[] { ViewSelector.class };
|
||||
}
|
||||
|
||||
protected Object doConvert(Object source, Class targetClass, ConversionContext context) throws Exception {
|
||||
String encodedView = (String) source;
|
||||
if (!StringUtils.hasText(encodedView)) {
|
||||
return NullViewSelector.INSTANCE;
|
||||
} else {
|
||||
return convertEncodedViewSelector(encodedView);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert given encoded view into an appropriate view selector.
|
||||
* @param encodedView the encoded view selector
|
||||
* @return the view selector
|
||||
*/
|
||||
protected ViewSelector convertEncodedViewSelector(String encodedView) {
|
||||
if (encodedView.startsWith(REDIRECT_PREFIX)) {
|
||||
String viewName = encodedView.substring(REDIRECT_PREFIX.length());
|
||||
Expression viewNameExpr = (Expression) fromStringTo(Expression.class).execute(viewName);
|
||||
// just show the application view using a redirect
|
||||
return new ApplicationViewSelector(viewNameExpr, true);
|
||||
} else if (encodedView.startsWith(EXTERNAL_REDIRECT_PREFIX)) {
|
||||
String externalUrl = encodedView.substring(EXTERNAL_REDIRECT_PREFIX.length());
|
||||
Expression urlExpr = (Expression) fromStringTo(Expression.class).execute(externalUrl);
|
||||
return new ExternalRedirectSelector(urlExpr);
|
||||
} else if (encodedView.startsWith(FLOW_DEFINITION_REDIRECT_PREFIX)) {
|
||||
String flowRedirect = encodedView.substring(FLOW_DEFINITION_REDIRECT_PREFIX.length());
|
||||
Expression redirectExpr = (Expression) fromStringTo(Expression.class).execute(flowRedirect);
|
||||
return new FlowDefinitionRedirectSelector(redirectExpr);
|
||||
} else if (encodedView.startsWith(BEAN_PREFIX)) {
|
||||
String id = encodedView.substring(BEAN_PREFIX.length());
|
||||
return flowServiceLocator.getViewSelector(id);
|
||||
} else {
|
||||
Expression viewNameExpr = (Expression) fromStringTo(Expression.class).execute(encodedView);
|
||||
return new ApplicationViewSelector(viewNameExpr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package org.springframework.webflow.engine.builder;
|
||||
|
||||
import org.springframework.binding.expression.Expression;
|
||||
import org.springframework.webflow.execution.Action;
|
||||
import org.springframework.webflow.execution.ViewFactory;
|
||||
|
||||
public interface ViewFactoryCreator {
|
||||
|
||||
public ViewFactory createViewFactory(Expression viewId);
|
||||
|
||||
public Action createFinalResponseAction(Expression viewId);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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.engine.builder.support;
|
||||
|
||||
import org.springframework.webflow.core.collection.AttributeMap;
|
||||
import org.springframework.webflow.engine.Flow;
|
||||
import org.springframework.webflow.engine.builder.FlowBuilder;
|
||||
import org.springframework.webflow.engine.builder.FlowBuilderContext;
|
||||
import org.springframework.webflow.engine.builder.FlowBuilderException;
|
||||
|
||||
/**
|
||||
* Abstract base implementation of a flow builder defining common functionality needed by most concrete flow builder
|
||||
* implementations. This class implements all optional parts of the FlowBuilder process as no-op methods. Subclasses are
|
||||
* only required to implement {@link #buildStates()}.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Erwin Vervaet
|
||||
*/
|
||||
public abstract class AbstractFlowBuilder implements FlowBuilder {
|
||||
|
||||
/**
|
||||
* The <code>Flow</code> built by this builder.
|
||||
*/
|
||||
private Flow flow;
|
||||
|
||||
private FlowBuilderContext context;
|
||||
|
||||
protected FlowBuilderContext getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
public void init(FlowBuilderContext context) throws FlowBuilderException {
|
||||
this.context = context;
|
||||
doInit();
|
||||
this.flow = createFlow();
|
||||
}
|
||||
|
||||
protected void doInit() {
|
||||
|
||||
}
|
||||
|
||||
protected Flow createFlow() {
|
||||
String id = getContext().getFlowId();
|
||||
AttributeMap attributes = getContext().getFlowAttributes();
|
||||
return getContext().getFlowArtifactFactory().createFlow(id, attributes);
|
||||
}
|
||||
|
||||
public void buildVariables() throws FlowBuilderException {
|
||||
}
|
||||
|
||||
public void buildInputMapper() throws FlowBuilderException {
|
||||
}
|
||||
|
||||
public void buildStartActions() throws FlowBuilderException {
|
||||
}
|
||||
|
||||
public abstract void buildStates() throws FlowBuilderException;
|
||||
|
||||
public void buildGlobalTransitions() throws FlowBuilderException {
|
||||
}
|
||||
|
||||
public void buildEndActions() throws FlowBuilderException {
|
||||
}
|
||||
|
||||
public void buildOutputMapper() throws FlowBuilderException {
|
||||
}
|
||||
|
||||
public void buildExceptionHandlers() throws FlowBuilderException {
|
||||
}
|
||||
|
||||
public Flow getFlow() {
|
||||
return flow;
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
flow = null;
|
||||
doDispose();
|
||||
}
|
||||
|
||||
protected void doDispose() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package org.springframework.webflow.engine.builder.support;
|
||||
|
||||
import org.springframework.webflow.execution.Action;
|
||||
import org.springframework.webflow.execution.Event;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
import org.springframework.webflow.execution.View;
|
||||
import org.springframework.webflow.execution.ViewFactory;
|
||||
|
||||
public class ActionInvokingViewFactory implements ViewFactory {
|
||||
|
||||
private Action action;
|
||||
|
||||
public ActionInvokingViewFactory(Action action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public View getView(RequestContext context) {
|
||||
return new ActionExecutingView(action, context);
|
||||
}
|
||||
|
||||
private static class ActionExecutingView implements View {
|
||||
|
||||
private Action action;
|
||||
|
||||
private RequestContext context;
|
||||
|
||||
private ActionExecutingView(Action action, RequestContext context) {
|
||||
this.action = action;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public boolean eventSignaled() {
|
||||
return context.getExternalContext().getRequestParameterMap().contains("_eventId");
|
||||
}
|
||||
|
||||
public Event getEvent() {
|
||||
return new Event(this, context.getExternalContext().getRequestParameterMap().get("_eventId"));
|
||||
}
|
||||
|
||||
public void render() {
|
||||
try {
|
||||
action.execute(context);
|
||||
} catch (Exception e) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package org.springframework.webflow.engine.builder.support;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.binding.convert.ConversionService;
|
||||
import org.springframework.binding.convert.support.GenericConversionService;
|
||||
import org.springframework.binding.expression.ExpressionParser;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.webflow.action.BeanInvokingActionFactory;
|
||||
import org.springframework.webflow.core.collection.AttributeMap;
|
||||
import org.springframework.webflow.definition.registry.FlowDefinitionLocator;
|
||||
import org.springframework.webflow.engine.builder.FlowArtifactFactory;
|
||||
import org.springframework.webflow.engine.builder.FlowBuilderContext;
|
||||
import org.springframework.webflow.engine.builder.ViewFactoryCreator;
|
||||
|
||||
public class FlowBuilderContextImpl implements FlowBuilderContext {
|
||||
|
||||
private String flowId;
|
||||
|
||||
private AttributeMap flowAttributes;
|
||||
|
||||
private FlowDefinitionLocator flowDefinitionLocator;
|
||||
|
||||
private FlowBuilderServices flowBuilderServices;
|
||||
|
||||
private GenericConversionService flowConversionService;
|
||||
|
||||
public FlowBuilderContextImpl(String flowId, AttributeMap flowAttributes,
|
||||
FlowDefinitionLocator flowDefinitionLocator, FlowBuilderServices flowBuilderServices) {
|
||||
this.flowId = flowId;
|
||||
this.flowAttributes = flowAttributes;
|
||||
this.flowDefinitionLocator = flowDefinitionLocator;
|
||||
this.flowBuilderServices = flowBuilderServices;
|
||||
flowConversionService = new GenericConversionService();
|
||||
flowConversionService.addConverter(new TextToTransitionCriteria(this));
|
||||
flowConversionService.addConverter(new TextToTargetStateResolver(this));
|
||||
flowConversionService.setParent(this.flowBuilderServices.getConversionService());
|
||||
}
|
||||
|
||||
public String getFlowId() {
|
||||
return flowId;
|
||||
}
|
||||
|
||||
public AttributeMap getFlowAttributes() {
|
||||
return flowAttributes;
|
||||
}
|
||||
|
||||
public FlowArtifactFactory getFlowArtifactFactory() {
|
||||
return flowBuilderServices.getFlowArtifactFactory();
|
||||
}
|
||||
|
||||
public BeanInvokingActionFactory getBeanInvokingActionFactory() {
|
||||
return flowBuilderServices.getBeanInvokingActionFactory();
|
||||
}
|
||||
|
||||
public ViewFactoryCreator getViewFactoryCreator() {
|
||||
return flowBuilderServices.getViewFactoryCreator();
|
||||
}
|
||||
|
||||
public ExpressionParser getExpressionParser() {
|
||||
return flowBuilderServices.getExpressionParser();
|
||||
}
|
||||
|
||||
public ConversionService getConversionService() {
|
||||
return flowConversionService;
|
||||
}
|
||||
|
||||
public ResourceLoader getResourceLoader() {
|
||||
return flowBuilderServices.getResourceLoader();
|
||||
}
|
||||
|
||||
public BeanFactory getBeanFactory() {
|
||||
return flowBuilderServices.getBeanFactory();
|
||||
}
|
||||
|
||||
public FlowDefinitionLocator getFlowDefinitionLocator() {
|
||||
return flowDefinitionLocator;
|
||||
}
|
||||
|
||||
public FlowBuilderServices getFlowBuilderServices() {
|
||||
return flowBuilderServices;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package org.springframework.webflow.engine.builder.support;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.binding.convert.ConversionService;
|
||||
import org.springframework.binding.convert.support.DefaultConversionService;
|
||||
import org.springframework.binding.expression.ExpressionParser;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.webflow.action.BeanInvokingActionFactory;
|
||||
import org.springframework.webflow.core.expression.DefaultExpressionParserFactory;
|
||||
import org.springframework.webflow.engine.Flow;
|
||||
import org.springframework.webflow.engine.State;
|
||||
import org.springframework.webflow.engine.builder.FlowArtifactFactory;
|
||||
import org.springframework.webflow.engine.builder.ViewFactoryCreator;
|
||||
import org.springframework.webflow.execution.Action;
|
||||
|
||||
public class FlowBuilderServices implements ResourceLoaderAware, BeanFactoryAware {
|
||||
|
||||
/**
|
||||
* The factory encapsulating the creation of central Flow artifacts such as {@link Flow flows} and
|
||||
* {@link State states}.
|
||||
*/
|
||||
private FlowArtifactFactory flowArtifactFactory = new FlowArtifactFactory();
|
||||
|
||||
/**
|
||||
* The factory encapsulating the creation of bean invoking actions, actions that adapt methods on objects to the
|
||||
* {@link Action} interface.
|
||||
*/
|
||||
private BeanInvokingActionFactory beanInvokingActionFactory = new BeanInvokingActionFactory();
|
||||
|
||||
/**
|
||||
* The view factory creator.
|
||||
*/
|
||||
private ViewFactoryCreator viewFactoryCreator;
|
||||
|
||||
/**
|
||||
* The conversion service.
|
||||
*/
|
||||
private ConversionService conversionService = new DefaultConversionService();
|
||||
|
||||
/**
|
||||
* The parser for parsing expression strings into expression objects.
|
||||
*/
|
||||
private ExpressionParser expressionParser = DefaultExpressionParserFactory.getExpressionParser();
|
||||
|
||||
/**
|
||||
* A resource loader that can load resources.
|
||||
*/
|
||||
private ResourceLoader resourceLoader;
|
||||
|
||||
/**
|
||||
* The Spring bean factory used.
|
||||
*/
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
public FlowArtifactFactory getFlowArtifactFactory() {
|
||||
return flowArtifactFactory;
|
||||
}
|
||||
|
||||
public void setFlowArtifactFactory(FlowArtifactFactory flowArtifactFactory) {
|
||||
Assert.notNull(flowArtifactFactory, "The flow artifact factory is required");
|
||||
this.flowArtifactFactory = flowArtifactFactory;
|
||||
}
|
||||
|
||||
public BeanInvokingActionFactory getBeanInvokingActionFactory() {
|
||||
return beanInvokingActionFactory;
|
||||
}
|
||||
|
||||
public void setBeanInvokingActionFactory(BeanInvokingActionFactory beanInvokingActionFactory) {
|
||||
Assert.notNull(beanInvokingActionFactory, "The bean invoking action factory is required");
|
||||
this.beanInvokingActionFactory = beanInvokingActionFactory;
|
||||
}
|
||||
|
||||
public ViewFactoryCreator getViewFactoryCreator() {
|
||||
return viewFactoryCreator;
|
||||
}
|
||||
|
||||
public void setViewFactoryCreator(ViewFactoryCreator viewFactoryCreator) {
|
||||
Assert.notNull("The view factory creator cannot be null");
|
||||
this.viewFactoryCreator = viewFactoryCreator;
|
||||
}
|
||||
|
||||
public ConversionService getConversionService() {
|
||||
return conversionService;
|
||||
}
|
||||
|
||||
public void setConversionService(ConversionService conversionService) {
|
||||
Assert.notNull(conversionService, "The type conversion service cannot be null");
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
public ExpressionParser getExpressionParser() {
|
||||
return expressionParser;
|
||||
}
|
||||
|
||||
public void setExpressionParser(ExpressionParser expressionParser) {
|
||||
Assert.notNull(expressionParser, "The expression parser cannot be null");
|
||||
this.expressionParser = expressionParser;
|
||||
}
|
||||
|
||||
public ResourceLoader getResourceLoader() {
|
||||
return resourceLoader;
|
||||
}
|
||||
|
||||
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||
Assert.notNull("The resource loader cannot be null");
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
public BeanFactory getBeanFactory() {
|
||||
return beanFactory;
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
Assert.notNull("The bean factory cannot be null");
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
}
|
||||
@@ -13,13 +13,16 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.webflow.engine.builder;
|
||||
package org.springframework.webflow.engine.builder.support;
|
||||
|
||||
import org.springframework.binding.convert.ConversionContext;
|
||||
import org.springframework.binding.convert.support.AbstractConverter;
|
||||
import org.springframework.binding.expression.Expression;
|
||||
import org.springframework.binding.expression.ExpressionParser;
|
||||
import org.springframework.webflow.engine.TargetStateResolver;
|
||||
import org.springframework.webflow.engine.builder.FlowBuilderContext;
|
||||
import org.springframework.webflow.engine.support.DefaultTargetStateResolver;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
|
||||
/**
|
||||
* Converter that takes an encoded string representation and produces a corresponding {@link TargetStateResolver}
|
||||
@@ -37,7 +40,7 @@ import org.springframework.webflow.engine.support.DefaultTargetStateResolver;
|
||||
* @author Keith Donald
|
||||
* @author Erwin Vervaet
|
||||
*/
|
||||
public class TextToTargetStateResolver extends AbstractConverter {
|
||||
class TextToTargetStateResolver extends AbstractConverter {
|
||||
|
||||
/**
|
||||
* Prefix used when the user wants to use a custom TargetStateResolver implementation managed by a factory.
|
||||
@@ -45,16 +48,16 @@ public class TextToTargetStateResolver extends AbstractConverter {
|
||||
private static final String BEAN_PREFIX = "bean:";
|
||||
|
||||
/**
|
||||
* Locator to use for loading custom TargetStateResolver beans.
|
||||
* Context for flow builder services.
|
||||
*/
|
||||
private FlowServiceLocator flowServiceLocator;
|
||||
private FlowBuilderContext flowBuilderContext;
|
||||
|
||||
/**
|
||||
* Create a new converter that converts strings to transition target state resolver objects. The given conversion
|
||||
* service will be used to do all necessary internal conversion (e.g. parsing expression strings).
|
||||
*/
|
||||
public TextToTargetStateResolver(FlowServiceLocator flowServiceLocator) {
|
||||
this.flowServiceLocator = flowServiceLocator;
|
||||
public TextToTargetStateResolver(FlowBuilderContext flowBuilderContext) {
|
||||
this.flowBuilderContext = flowBuilderContext;
|
||||
}
|
||||
|
||||
public Class[] getSourceClasses() {
|
||||
@@ -67,11 +70,12 @@ public class TextToTargetStateResolver extends AbstractConverter {
|
||||
|
||||
protected Object doConvert(Object source, Class targetClass, ConversionContext context) throws Exception {
|
||||
String targetStateId = (String) source;
|
||||
if (flowServiceLocator.getExpressionParser().isDelimitedExpression(targetStateId)) {
|
||||
Expression expression = flowServiceLocator.getExpressionParser().parseExpression(targetStateId);
|
||||
ExpressionParser parser = flowBuilderContext.getExpressionParser();
|
||||
if (parser.isEvalExpressionString(targetStateId)) {
|
||||
Expression expression = parser.parseExpression(targetStateId, RequestContext.class, String.class, null);
|
||||
return new DefaultTargetStateResolver(expression);
|
||||
} else if (targetStateId.startsWith(BEAN_PREFIX)) {
|
||||
return flowServiceLocator.getTargetStateResolver(targetStateId.substring(BEAN_PREFIX.length()));
|
||||
return flowBuilderContext.getBeanFactory().getBean(targetStateId.substring(BEAN_PREFIX.length()));
|
||||
} else {
|
||||
return new DefaultTargetStateResolver(targetStateId);
|
||||
}
|
||||
@@ -13,17 +13,21 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.webflow.engine.builder;
|
||||
package org.springframework.webflow.engine.builder.support;
|
||||
|
||||
import org.springframework.binding.convert.ConversionContext;
|
||||
import org.springframework.binding.convert.ConversionException;
|
||||
import org.springframework.binding.convert.support.AbstractConverter;
|
||||
import org.springframework.binding.expression.Expression;
|
||||
import org.springframework.binding.expression.ExpressionParser;
|
||||
import org.springframework.binding.expression.ExpressionVariable;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.webflow.engine.TransitionCriteria;
|
||||
import org.springframework.webflow.engine.WildcardTransitionCriteria;
|
||||
import org.springframework.webflow.engine.builder.FlowBuilderContext;
|
||||
import org.springframework.webflow.engine.support.BooleanExpressionTransitionCriteria;
|
||||
import org.springframework.webflow.engine.support.EventIdTransitionCriteria;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
|
||||
/**
|
||||
* Converter that takes an encoded string representation and produces a corresponding <code>TransitionCriteria</code>
|
||||
@@ -45,7 +49,7 @@ import org.springframework.webflow.engine.support.EventIdTransitionCriteria;
|
||||
* @author Keith Donald
|
||||
* @author Erwin Vervaet
|
||||
*/
|
||||
public class TextToTransitionCriteria extends AbstractConverter {
|
||||
class TextToTransitionCriteria extends AbstractConverter {
|
||||
|
||||
/**
|
||||
* Prefix used when the user wants to use a custom TransitionCriteria implementation managed by a bean factory.
|
||||
@@ -53,16 +57,16 @@ public class TextToTransitionCriteria extends AbstractConverter {
|
||||
private static final String BEAN_PREFIX = "bean:";
|
||||
|
||||
/**
|
||||
* Locator to use for loading custom TransitionCriteria beans.
|
||||
* Context for flow builder services.
|
||||
*/
|
||||
private FlowServiceLocator flowServiceLocator;
|
||||
private FlowBuilderContext flowBuilderContext;
|
||||
|
||||
/**
|
||||
* Create a new converter that converts strings to transition criteria objects. Custom transition criteria will be
|
||||
* looked up using given service locator.
|
||||
*/
|
||||
public TextToTransitionCriteria(FlowServiceLocator flowServiceLocator) {
|
||||
this.flowServiceLocator = flowServiceLocator;
|
||||
public TextToTransitionCriteria(FlowBuilderContext flowBuilderContext) {
|
||||
this.flowBuilderContext = flowBuilderContext;
|
||||
}
|
||||
|
||||
public Class[] getSourceClasses() {
|
||||
@@ -75,14 +79,18 @@ public class TextToTransitionCriteria extends AbstractConverter {
|
||||
|
||||
protected Object doConvert(Object source, Class targetClass, ConversionContext context) throws Exception {
|
||||
String encodedCriteria = (String) source;
|
||||
ExpressionParser parser = flowBuilderContext.getExpressionParser();
|
||||
if (!StringUtils.hasText(encodedCriteria)
|
||||
|| WildcardTransitionCriteria.WILDCARD_EVENT_ID.equals(encodedCriteria)) {
|
||||
return WildcardTransitionCriteria.INSTANCE;
|
||||
} else if (flowServiceLocator.getExpressionParser().isDelimitedExpression(encodedCriteria)) {
|
||||
Expression expression = flowServiceLocator.getExpressionParser().parseExpression(encodedCriteria);
|
||||
} else if (parser.isEvalExpressionString(encodedCriteria)) {
|
||||
ExpressionVariable[] variables = new ExpressionVariable[] { new ExpressionVariable("result", "lastEvent.id") };
|
||||
Expression expression = parser.parseExpression(encodedCriteria, RequestContext.class, Boolean.class,
|
||||
variables);
|
||||
return createBooleanExpressionTransitionCriteria(expression);
|
||||
} else if (encodedCriteria.startsWith(BEAN_PREFIX)) {
|
||||
return flowServiceLocator.getTransitionCriteria(encodedCriteria.substring(BEAN_PREFIX.length()));
|
||||
return flowBuilderContext.getBeanFactory().getBean(encodedCriteria.substring(BEAN_PREFIX.length()),
|
||||
TransitionCriteria.class);
|
||||
} else {
|
||||
return createEventIdTransitionCriteria(encodedCriteria);
|
||||
}
|
||||
@@ -87,7 +87,6 @@ public class DefaultDocumentLoader implements DocumentLoader {
|
||||
/**
|
||||
* Set a SAX entity resolver to be used for parsing. Can be overridden for custom entity resolution, for example
|
||||
* relative to some specific base path.
|
||||
* @see org.springframework.webflow.engine.builder.xml.WebFlowEntityResolver
|
||||
*/
|
||||
public void setEntityResolver(EntityResolver entityResolver) {
|
||||
this.entityResolver = entityResolver;
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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.engine.builder.xml;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.binding.convert.ConversionService;
|
||||
import org.springframework.binding.expression.ExpressionParser;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.webflow.action.BeanInvokingActionFactory;
|
||||
import org.springframework.webflow.core.collection.AttributeMap;
|
||||
import org.springframework.webflow.definition.registry.FlowDefinitionLocator;
|
||||
import org.springframework.webflow.engine.builder.FlowArtifactFactory;
|
||||
import org.springframework.webflow.engine.builder.FlowBuilderContext;
|
||||
import org.springframework.webflow.engine.builder.ViewFactoryCreator;
|
||||
|
||||
class LocalFlowBuilderContext implements FlowBuilderContext {
|
||||
|
||||
private FlowBuilderContext parent;
|
||||
|
||||
private BeanFactory localFlowBeanFactory;
|
||||
|
||||
public LocalFlowBuilderContext(FlowBuilderContext parent, BeanFactory localFlowBeanFactory) {
|
||||
this.parent = parent;
|
||||
this.localFlowBeanFactory = localFlowBeanFactory;
|
||||
}
|
||||
|
||||
public String getFlowId() {
|
||||
return parent.getFlowId();
|
||||
}
|
||||
|
||||
public AttributeMap getFlowAttributes() {
|
||||
return parent.getFlowAttributes();
|
||||
}
|
||||
|
||||
public FlowDefinitionLocator getFlowDefinitionLocator() {
|
||||
return (FlowDefinitionLocator) localFlowBeanFactory.getBean("flowRegistry", FlowDefinitionLocator.class);
|
||||
}
|
||||
|
||||
public FlowArtifactFactory getFlowArtifactFactory() {
|
||||
if (localFlowBeanFactory.containsBean("flowArtifactFactory")) {
|
||||
return (FlowArtifactFactory) localFlowBeanFactory.getBean("flowArtifactFactory", FlowArtifactFactory.class);
|
||||
} else {
|
||||
return parent.getFlowArtifactFactory();
|
||||
}
|
||||
}
|
||||
|
||||
public BeanInvokingActionFactory getBeanInvokingActionFactory() {
|
||||
if (localFlowBeanFactory.containsBean("beanInvokingActionFactory")) {
|
||||
return (BeanInvokingActionFactory) localFlowBeanFactory.getBean("beanInvokingActionFactory",
|
||||
BeanInvokingActionFactory.class);
|
||||
} else {
|
||||
return parent.getBeanInvokingActionFactory();
|
||||
}
|
||||
}
|
||||
|
||||
public ViewFactoryCreator getViewFactoryCreator() {
|
||||
if (localFlowBeanFactory.containsBean("viewFactoryCreator")) {
|
||||
return (ViewFactoryCreator) localFlowBeanFactory.getBean("viewFactoryCreator", ViewFactoryCreator.class);
|
||||
} else {
|
||||
return parent.getViewFactoryCreator();
|
||||
}
|
||||
}
|
||||
|
||||
public ConversionService getConversionService() {
|
||||
if (localFlowBeanFactory.containsBean("conversionService")) {
|
||||
return (ConversionService) localFlowBeanFactory.getBean("conversionService", ConversionService.class);
|
||||
} else {
|
||||
return parent.getConversionService();
|
||||
}
|
||||
}
|
||||
|
||||
public ExpressionParser getExpressionParser() {
|
||||
if (localFlowBeanFactory.containsBean("expressionParser")) {
|
||||
return (ExpressionParser) localFlowBeanFactory.getBean("expressionParser", ExpressionParser.class);
|
||||
} else {
|
||||
return parent.getExpressionParser();
|
||||
}
|
||||
}
|
||||
|
||||
public ResourceLoader getResourceLoader() {
|
||||
return (ResourceLoader) getBeanFactory();
|
||||
}
|
||||
|
||||
public BeanFactory getBeanFactory() {
|
||||
return localFlowBeanFactory;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user