made documentation related to webflow-config consistent with implementation

This commit is contained in:
Keith Donald
2008-04-26 04:56:54 +00:00
parent 6612b8982d
commit e4d595b544
20 changed files with 284 additions and 848 deletions

View File

@@ -22,7 +22,7 @@ import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
*
* @author Jeremy Grelle
*/
public class FacesConfigNamespaceHandler extends NamespaceHandlerSupport {
public class FacesNamespaceHandler extends NamespaceHandlerSupport {
public void init() {
registerBeanDefinitionParser("flow-builder-services", new FacesFlowBuilderServicesBeanDefinitionParser());
}

View File

@@ -1 +1 @@
http\://www.springframework.org/schema/faces=org.springframework.faces.config.FacesConfigNamespaceHandler
http\://www.springframework.org/schema/faces=org.springframework.faces.config.FacesNamespaceHandler

View File

@@ -1,6 +1,6 @@
<?xml version='1.0' encoding="iso-8859-1"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd"
[
<!ENTITY overview SYSTEM "overview.xml">
<!ENTITY defining-flows SYSTEM "defining-flows.xml">

View File

@@ -207,24 +207,18 @@
Use the <code>flow-execution-repository</code> element to tune flow execution persistence settings:
</para>
<programlisting type="xml"><![CDATA[
<flow-execution-repository max-conversations="5" max-snapshots="30" />]]>
<flow-execution-repository max-executions="5" max-execution-snapshots="30" />]]>
</programlisting>
<sect3 id="repository-max-conversations">
<title>max-conversations</title>
<para>
Tune the <code>max-conversations</code> attribute to place a cap on the number of conversations that can be created per user session.
Tune the <code>max-executions</code> attribute to place a cap on the number of flow executions that can be created per user session.
</para>
</sect3>
<sect3 id="repository-max-snapshots">
<title>max-snapshots</title>
<para>
Tune the <code>max-snapshots</code> attribute to place a cap on the number of flow execution snapshots that can be taken per conversation.
</para>
</sect3>
<sect3 id="repository-conversation-manager">
<title>conversation-manager</title>
<para>
Set a custom <code>ConversationManager</code> to completely customize where conversational flow state is persisted.
Tune the <code>max-execution-snapshots</code> attribute to place a cap on the number of history snapshots that can be taken per flow execution.
</para>
</sect3>
</sect2>

View File

@@ -32,9 +32,6 @@
<!-- Executes flows: the central entry point into the Spring Web Flow system -->
<webflow:flow-executor id="flowExecutor">
<webflow:flow-execution-attributes>
<webflow:always-redirect-on-pause value="false"/>
</webflow:flow-execution-attributes>
<webflow:flow-execution-listeners>
<webflow:listener ref="jpaFlowExecutionListener" />
</webflow:flow-execution-listeners>

View File

@@ -15,9 +15,7 @@
*/
package org.springframework.webflow.config;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
@@ -36,22 +34,6 @@ import org.w3c.dom.Element;
*/
class FlowBuilderServicesBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
private static final String FORMATTER_REGISTRY_ATTRIBUTE = "formatter-registry";
private static final String CONVERSION_SERVICE_ATTRIBUTE = "conversion-service";
private static final String EXPRESSION_PARSER_ATTRIBUTE = "expression-parser";
private static final String VIEW_FACTORY_CREATOR_ATTRIBUTE = "view-factory-creator";
private static final String FORMATTER_REGISTRY_PROPERTY = "formatterRegistry";
private static final String CONVERSION_SERVICE_PROPERTY = "conversionService";
private static final String EXPRESSION_PARSER_PROPERTY = "expressionParser";
private static final String VIEW_FACTORY_CREATOR_PROPERTY = "viewFactoryCreator";
protected Class getBeanClass(Element element) {
return FlowBuilderServices.class;
}
@@ -64,66 +46,41 @@ class FlowBuilderServicesBeanDefinitionParser extends AbstractSingleBeanDefiniti
}
private void parseFormatterRegistry(Element element, BeanDefinitionBuilder definitionBuilder, ParserContext context) {
String formatterRegistry = element.getAttribute(FORMATTER_REGISTRY_ATTRIBUTE);
String formatterRegistry = element.getAttribute("formatter-registry");
if (StringUtils.hasText(formatterRegistry)) {
definitionBuilder.addPropertyReference(FORMATTER_REGISTRY_PROPERTY, formatterRegistry);
definitionBuilder.addPropertyReference("formatterRegistry", formatterRegistry);
} else {
definitionBuilder.addPropertyValue(FORMATTER_REGISTRY_PROPERTY, DefaultFormatterRegistry
.getSharedInstance());
definitionBuilder.addPropertyValue("formatterRegistry", DefaultFormatterRegistry.getSharedInstance());
}
}
private void parseConversionService(Element element, BeanDefinitionBuilder definitionBuilder, ParserContext context) {
String conversionService = element.getAttribute(CONVERSION_SERVICE_ATTRIBUTE);
String conversionService = element.getAttribute("conversion-service");
if (StringUtils.hasText(conversionService)) {
definitionBuilder.addPropertyReference(CONVERSION_SERVICE_PROPERTY, conversionService);
definitionBuilder.addPropertyReference("conversionService", conversionService);
} else {
definitionBuilder.addPropertyValue(CONVERSION_SERVICE_PROPERTY, DefaultConversionService
.getSharedInstance());
definitionBuilder.addPropertyValue("conversionService", DefaultConversionService.getSharedInstance());
}
}
private void parseExpressionParser(Element element, BeanDefinitionBuilder definitionBuilder, ParserContext context) {
String expressionParser = element.getAttribute(EXPRESSION_PARSER_ATTRIBUTE);
String expressionParser = element.getAttribute("expression-parser");
if (StringUtils.hasText(expressionParser)) {
definitionBuilder.addPropertyReference(EXPRESSION_PARSER_PROPERTY, expressionParser);
definitionBuilder.addPropertyReference("expressionParser", expressionParser);
} else {
definitionBuilder.addPropertyValue(EXPRESSION_PARSER_PROPERTY, DefaultExpressionParserFactory
.getExpressionParser());
definitionBuilder
.addPropertyValue("expressionParser", DefaultExpressionParserFactory.getExpressionParser());
}
}
private void parseViewFactoryCreator(Element element, BeanDefinitionBuilder definitionBuilder, ParserContext context) {
String viewFactoryCreator = element.getAttribute(VIEW_FACTORY_CREATOR_ATTRIBUTE);
String viewFactoryCreator = element.getAttribute("view-factory-creator");
if (StringUtils.hasText(viewFactoryCreator)) {
definitionBuilder.addPropertyReference(VIEW_FACTORY_CREATOR_PROPERTY, viewFactoryCreator);
definitionBuilder.addPropertyReference("viewFactoryCreator", viewFactoryCreator);
} else {
definitionBuilder.addPropertyReference(VIEW_FACTORY_CREATOR_PROPERTY, createBeanDefinitionForClass(
MvcViewFactoryCreator.class, context).getBeanName());
definitionBuilder.addPropertyValue("viewFactoryCreator", BeanDefinitionBuilder.genericBeanDefinition(
MvcViewFactoryCreator.class).getBeanDefinition());
}
}
private BeanDefinitionHolder createBeanDefinitionForClass(Class clazz, ParserContext context) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
BeanDefinitionHolder holder = new BeanDefinitionHolder(builder.getBeanDefinition(), BeanDefinitionReaderUtils
.generateBeanName(builder.getBeanDefinition(), context.getRegistry()));
registerBeanDefinition(holder, context.getRegistry());
return holder;
}
public static BeanDefinitionHolder registerDefaultFlowBuilderServicesBeanDefinition(ParserContext context) {
FlowBuilderServicesBeanDefinitionParser parser = new FlowBuilderServicesBeanDefinitionParser();
BeanDefinitionBuilder defaultBuilder = BeanDefinitionBuilder.genericBeanDefinition(FlowBuilderServices.class);
defaultBuilder.addPropertyValue(FORMATTER_REGISTRY_PROPERTY, DefaultFormatterRegistry.getSharedInstance());
defaultBuilder.addPropertyValue(CONVERSION_SERVICE_PROPERTY, DefaultConversionService.getSharedInstance());
defaultBuilder.addPropertyValue(EXPRESSION_PARSER_PROPERTY, DefaultExpressionParserFactory
.getExpressionParser());
defaultBuilder.addPropertyReference(VIEW_FACTORY_CREATOR_PROPERTY, parser.createBeanDefinitionForClass(
MvcViewFactoryCreator.class, context).getBeanName());
BeanDefinitionHolder holder = new BeanDefinitionHolder(defaultBuilder.getBeanDefinition(),
BeanDefinitionReaderUtils.generateBeanName(defaultBuilder.getBeanDefinition(), context.getRegistry()));
parser.registerBeanDefinition(holder, context.getRegistry());
return holder;
}
}
}

View File

@@ -28,31 +28,19 @@ import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
/**
* {@link BeanDefinitionParser} for the <code>&lt;execution-listeners&gt;</code> tag.
* {@link BeanDefinitionParser} for the <code>&lt;flow-execution-listeners&gt;</code> tag.
*
* @author Ben Hale
*/
class FlowExecutionListenerLoaderBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
// elements and attributes
private static final String LISTENER_ELEMENT = "listener";
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 FlowExecutionListenerLoaderFactoryBean.class;
}
protected void doParse(Element element, BeanDefinitionBuilder definitionBuilder) {
List listenerElements = DomUtils.getChildElementsByTagName(element, LISTENER_ELEMENT);
definitionBuilder.addPropertyValue(LISTENERS_PROPERTY, getListenersWithCriteria(listenerElements));
List listenerElements = DomUtils.getChildElementsByTagName(element, "listener");
definitionBuilder.addPropertyValue("listeners", parseListenersWithCriteria(listenerElements));
}
/**
@@ -61,12 +49,12 @@ class FlowExecutionListenerLoaderBeanDefinitionParser extends AbstractSingleBean
* @return a map containing keys that are references to given listeners and values of string that represent the
* criteria
*/
private Map getListenersWithCriteria(List listeners) {
private Map parseListenersWithCriteria(List listeners) {
Map listenersWithCriteria = new ManagedMap(listeners.size());
for (Iterator i = listeners.iterator(); i.hasNext();) {
Element listenerElement = (Element) i.next();
RuntimeBeanReference ref = new RuntimeBeanReference(listenerElement.getAttribute(REF_ATTRIBUTE));
String criteria = listenerElement.getAttribute(CRITERIA_ATTRIBUTE);
RuntimeBeanReference ref = new RuntimeBeanReference(listenerElement.getAttribute("ref"));
String criteria = listenerElement.getAttribute("criteria");
listenersWithCriteria.put(ref, criteria);
}
return listenersWithCriteria;

View File

@@ -35,129 +35,19 @@ import org.w3c.dom.Element;
*/
class FlowExecutorBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
// elements and attributes
private static final String CONVERSATION_MANAGER_REF_ATTRIBUTE = "conversation-manager";
private static final String EXECUTION_ATTRIBUTES_ELEMENT = "flow-execution-attributes";
private static final String ALWAYS_REDIRECT_ON_PAUSE_ELEMENT = "always-redirect-on-pause";
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_ATTRIBUTE = "flow-registry";
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 = "flowDefinitionLocator";
private static final String REPOSITORY_TYPE_PROPERTY = "flowExecutionRepositoryType";
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";
protected Class getBeanClass(Element element) {
return FlowExecutorFactoryBean.class;
}
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder definitionBuilder) {
definitionBuilder.addPropertyReference(DEFINITION_LOCATOR_PROPERTY, getRegistry(element, parserContext));
definitionBuilder.addPropertyValue(EXECUTION_ATTRIBUTES_PROPERTY, parseAttributes(element));
addExecutionListenerLoader(element, parserContext, definitionBuilder);
configureRepository(element, definitionBuilder, parserContext);
definitionBuilder.addPropertyReference("flowDefinitionLocator", getFlowRegistry(element, parserContext));
addFlowExecutionRepositoryProperties(element, definitionBuilder, parserContext);
definitionBuilder.addPropertyValue("flowExecutionAttributes", parseFlowExecutionAttributes(element));
addFlowExecutionListenerLoader(element, parserContext, definitionBuilder);
}
/**
* Configures a repository based on the <code>repository-type</code> attribute or a <code>repository</code> tag.
* @param element the root element to extract repository configuration from
* @param definitionBuilder the builder
* @param parserContext the parserContext
*/
private void configureRepository(Element element, BeanDefinitionBuilder definitionBuilder,
ParserContext parserContext) {
Element repositoryElement = DomUtils.getChildElementByTagName(element, REPOSITORY_ELEMENT);
if (repositoryElement != null) {
String type = getType(element);
if (StringUtils.hasText(type)) {
definitionBuilder.addPropertyValue(REPOSITORY_TYPE_PROPERTY, type);
}
configureContinuations(repositoryElement, definitionBuilder, parserContext);
configureConversationManager(repositoryElement, definitionBuilder, parserContext);
}
}
/**
* Configure the max continuations setting.
* @param repositoryElement the repository element
* @param definitionBuilder the builder
* @param parserContext the parserContext
*/
private void configureContinuations(Element repositoryElement, BeanDefinitionBuilder definitionBuilder,
ParserContext parserContext) {
String maxContinuations = getMaxContinuations(repositoryElement);
if (StringUtils.hasText(maxContinuations)) {
String type = getType(repositoryElement);
if (StringUtils.hasText(type) && !type.toLowerCase().equals("CONTINUATION")) {
parserContext.getReaderContext().error(
"The 'max-continuations' attribute of the 'flow-execution-repository' element must not "
+ "have a value if the 'type' attribute is not 'continuation'", repositoryElement);
}
definitionBuilder.addPropertyValue(MAX_CONTINUATIONS_PROPERTY, maxContinuations);
}
}
/**
* Configure the conversation manager
* @param repositoryElement the repository element
* @param definitionBuilder the builder
* @param parserContext the parserContext
*/
private void configureConversationManager(Element repositoryElement, BeanDefinitionBuilder definitionBuilder,
ParserContext parserContext) {
String conversationManagerRef = getConversationManagerRef(repositoryElement);
String maxConversations = getMaxConversations(repositoryElement);
if (StringUtils.hasText(conversationManagerRef)) {
if (StringUtils.hasText(maxConversations)) {
parserContext.getReaderContext().error(
"The 'max-conversations' attribute of the 'flow-execution-repository' element must not "
+ "have a value if there is a value for the 'conversation-manager' attribute",
repositoryElement);
}
definitionBuilder.addPropertyReference(CONVERSATION_MANAGER_PROPERTY, conversationManagerRef);
} else if (StringUtils.hasText(maxConversations)) {
definitionBuilder.addPropertyValue(MAX_CONVERSATIONS_PROPERTY, maxConversations);
}
}
/**
* Returns the name of the registry detailed in the bean definition.
* @param element the element to extract the registry name from
* @return the name of the registry
* @param parserContext the parserContext
*/
private String getRegistry(Element element, ParserContext parserContext) {
String registry = element.getAttribute(REGISTRY_ATTRIBUTE);
private String getFlowRegistry(Element element, ParserContext parserContext) {
String registry = element.getAttribute("flow-registry");
if (!StringUtils.hasText(registry)) {
return "flowRegistry";
} else {
@@ -165,70 +55,45 @@ class FlowExecutorBeanDefinitionParser extends AbstractSingleBeanDefinitionParse
}
}
/**
* 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 getType(Element element) {
return element.getAttribute(TYPE_ATTRIBUTE);
}
/**
* Returns the maximum number of continuations detailed in the bean definition.
* @param element the element to extract the max continuations from
* @return the max continuations
*/
private String getMaxContinuations(Element element) {
return element.getAttribute(MAX_CONTINUATIONS_ATTRIBUTE);
}
/**
* Returns the maximum number of conversations detailed in the bean definition.
* @param element the element to extract the max conversations from
* @return the max conversations
*/
private String getMaxConversations(Element element) {
return element.getAttribute(MAX_CONVERSATIONS_ATTRIBUTE);
}
/**
* Returns the name of the conversation manager detailed in the bean definition.
* @param element the element to extract the conversation manager name from
* @return the name of the conversation manager
*/
private String getConversationManagerRef(Element element) {
return element.getAttribute(CONVERSATION_MANAGER_REF_ATTRIBUTE);
}
/**
* Parse execution listener definitions contained in given element.
*/
private void addExecutionListenerLoader(Element element, ParserContext parserContext,
BeanDefinitionBuilder definitionBuilder) {
Element listenersElement = DomUtils.getChildElementByTagName(element, EXECUTION_LISTENERS_ELEMENT);
if (listenersElement != null) {
definitionBuilder.addPropertyValue(EXECUTION_LISTENER_LOADER_PROPERTY, parserContext.getDelegate()
.parseCustomElement(listenersElement, definitionBuilder.getBeanDefinition()));
private void addFlowExecutionRepositoryProperties(Element element, BeanDefinitionBuilder definitionBuilder,
ParserContext parserContext) {
Element repositoryElement = DomUtils.getChildElementByTagName(element, "flow-execution-repository");
if (repositoryElement != null) {
addMaxExecutions(repositoryElement, definitionBuilder, parserContext);
addMaxSnapshots(repositoryElement, definitionBuilder, parserContext);
}
}
private Set parseAttributes(Element element) {
Element executionAttributesElement = DomUtils.getChildElementByTagName(element, EXECUTION_ATTRIBUTES_ELEMENT);
private void addMaxExecutions(Element element, BeanDefinitionBuilder definitionBuilder, ParserContext parserContext) {
String maxConversations = element.getAttribute("max-executions");
if (StringUtils.hasText(maxConversations)) {
definitionBuilder.addPropertyValue("maxFlowExecutions", maxConversations);
}
}
private void addMaxSnapshots(Element element, BeanDefinitionBuilder definitionBuilder, ParserContext parserContext) {
String maxSnapshots = element.getAttribute("max-execution-snapshots");
if (StringUtils.hasText(maxSnapshots)) {
definitionBuilder.addPropertyValue("maxFlowExecutionSnapshots", maxSnapshots);
}
}
private Set parseFlowExecutionAttributes(Element element) {
Element executionAttributesElement = DomUtils.getChildElementByTagName(element, "flow-execution-attributes");
if (executionAttributesElement != null) {
HashSet attributes = new HashSet();
Element redirectElement = DomUtils.getChildElementByTagName(executionAttributesElement,
ALWAYS_REDIRECT_ON_PAUSE_ELEMENT);
"always-redirect-on-pause");
if (redirectElement != null) {
String value = redirectElement.getAttribute(VALUE_ATTRIBUTE);
String value = redirectElement.getAttribute("value");
attributes.add(new FlowElementAttribute("alwaysRedirectOnPause", value, "boolean"));
}
List attributeElements = DomUtils.getChildElementsByTagName(executionAttributesElement, ATTRIBUTE_ELEMENT);
List attributeElements = DomUtils.getChildElementsByTagName(executionAttributesElement, "attribute");
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);
String name = attributeElement.getAttribute("name");
String value = attributeElement.getAttribute("value");
String type = attributeElement.getAttribute("type");
attributes.add(new FlowElementAttribute(name, value, type));
}
return attributes;
@@ -236,4 +101,13 @@ class FlowExecutorBeanDefinitionParser extends AbstractSingleBeanDefinitionParse
return null;
}
}
private void addFlowExecutionListenerLoader(Element element, ParserContext parserContext,
BeanDefinitionBuilder definitionBuilder) {
Element listenersElement = DomUtils.getChildElementByTagName(element, "flow-execution-listeners");
if (listenersElement != null) {
definitionBuilder.addPropertyValue("flowExecutionListenerLoader", parserContext.getDelegate()
.parseCustomElement(listenersElement, definitionBuilder.getBeanDefinition()));
}
}
}

View File

@@ -18,11 +18,14 @@ package org.springframework.webflow.config;
import java.util.Iterator;
import java.util.Set;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.binding.convert.ConversionExecutor;
import org.springframework.binding.convert.ConversionService;
import org.springframework.binding.convert.service.DefaultConversionService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.Assert;
import org.springframework.webflow.conversation.ConversationManager;
import org.springframework.webflow.conversation.impl.SessionBindingConversationManager;
@@ -41,73 +44,38 @@ import org.springframework.webflow.execution.repository.impl.DefaultFlowExecutio
import org.springframework.webflow.execution.repository.support.FlowExecutionStateRestorer;
import org.springframework.webflow.executor.FlowExecutor;
import org.springframework.webflow.executor.FlowExecutorImpl;
import org.springframework.webflow.mvc.builder.MvcEnvironment;
/**
* The default flow executor factory implementation. As a <code>FactoryBean</code>, this class has been designed for
* use as a Spring managed bean.
* <p>
* This factory encapsulates the construction and assembly of a {@link FlowExecutor}, including the provision of its
* {@link FlowExecutionRepository} strategy.
* {@link FlowExecutionRepository} strategy. As a <code>FactoryBean</code>, this class has been designed for use as a
* Spring managed bean.
* <p>
* 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
* class and overriding one or more of the provided hook methods.
*
* @author Keith Donald
* @author Erwin Vervaet
*/
class FlowExecutorFactoryBean implements FactoryBean, InitializingBean {
class FlowExecutorFactoryBean implements FactoryBean, ApplicationContextAware, InitializingBean {
private static final String ALWAYS_REDIRECT_ON_PAUSE = "alwaysRedirectOnPause";
/**
* The locator the executor will use to access flow definitions registered in a central registry. Required.
*/
private FlowDefinitionLocator flowDefinitionLocator;
/**
* Execution attributes to apply.
*/
private Integer maxFlowExecutions;
private Integer maxFlowExecutionSnapshots;
private Set flowExecutionAttributes;
/**
* The loader that will determine which listeners to attach to flow definition executions.
*/
private FlowExecutionListenerLoader flowExecutionListenerLoader;
/**
* The conversation manager to be used by the flow execution repository to store state associated with conversations
* driven by Spring Web Flow.
*/
private ConversationManager conversationManager;
/**
* The maximum number of allowed concurrent conversations in the session.
*/
private Integer maxConversations;
/**
* The type of execution repository to configure with executors created by this factory. Optional. Will fallback to
* default value if not set.
*/
private FlowExecutionRepositoryType flowExecutionRepositoryType;
/**
* The maximum number of allowed continuations for a single conversation. Only used when the repository type is
* {@link FlowExecutionRepositoryType#CONTINUATION}.
*/
private Integer maxContinuations;
/**
* The conversion service to use for type conversion of flow execution attribute values.
*/
private ConversionService conversionService = DefaultConversionService.getSharedInstance();
/**
* The flow executor this factory bean creates.
*/
private FlowExecutor flowExecutor;
private MvcEnvironment environment;
/**
* Sets the flow definition locator that will locate flow definitions needed for execution. Typically also a
* {@link FlowDefinitionRegistry}. Required.
@@ -117,6 +85,20 @@ class FlowExecutorFactoryBean implements FactoryBean, InitializingBean {
this.flowDefinitionLocator = flowDefinitionLocator;
}
/**
* Set the maximum number of allowed flow executions allowed per user.
*/
public void setMaxFlowExecutions(int maxFlowExecutions) {
this.maxFlowExecutions = new Integer(maxFlowExecutions);
}
/**
* Set the maximum number of history snapshots allowed per flow execution.
*/
public void setMaxFlowExecutionSnapshots(int maxFlowExecutionSnapshots) {
this.maxFlowExecutionSnapshots = new Integer(maxFlowExecutionSnapshots);
}
/**
* Sets the system attributes that apply to flow executions launched by the executor created by this factory.
* Execution attributes may affect flow execution behavior.
@@ -135,84 +117,20 @@ class FlowExecutorFactoryBean implements FactoryBean, InitializingBean {
this.flowExecutionListenerLoader = flowExecutionListenerLoader;
}
/**
* Sets the type of flow execution repository that should be configured for the flow executors created by this
* factory. This factory encapsulates the construction of the repository implementation corresponding to the
* provided type.
* @param repositoryType the flow execution repository type
*/
public void setFlowExecutionRepositoryType(FlowExecutionRepositoryType repositoryType) {
this.flowExecutionRepositoryType = repositoryType;
}
// implementing ApplicationContextAware
/**
* Set the maximum number of continuation snapshots allowed for a single conversation when using the
*/
public void setMaxContinuations(int maxContinuations) {
this.maxContinuations = new Integer(maxContinuations);
}
/**
* 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)
*/
public void setMaxConversations(int maxConversations) {
this.maxConversations = new Integer(maxConversations);
}
/**
* Sets the strategy for managing conversations that should be configured for flow executors created by this
* factory.
* <p>
* 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 setMaxConversations when using this
* method.
*/
public void setConversationManager(ConversationManager conversationManager) {
this.conversationManager = conversationManager;
}
/**
* TODO never called. Sets the conversion service for converting string-encoded flow execution attributes to typed
* values.
* @param conversionService the conversion service
*/
public void setConversionService(ConversionService conversionService) {
this.conversionService = conversionService;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
environment = MvcEnvironment.environmentFor(applicationContext);
}
// implementing InitializingBean
public void afterPropertiesSet() throws Exception {
Assert.notNull(flowDefinitionLocator, "The flow definition locator property is required");
// apply defaults
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 strategy to restore deserialized flow executions
FlowExecutionStateRestorer executionStateRestorer = createFlowExecutionStateRestorer(flowDefinitionLocator,
executionAttributes, flowExecutionListenerLoader);
// a repository to store flow executions
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(flowDefinitionLocator, executionFactory, executionRepository);
MutableAttributeMap executionAttributes = createFlowExecutionAttributes();
DefaultFlowExecutionRepository executionRepository = createFlowExecutionRepository(executionAttributes);
FlowExecutionFactory executionFactory = createFlowExecutionFactory(executionAttributes, executionRepository);
flowExecutor = new FlowExecutorImpl(flowDefinitionLocator, executionFactory, executionRepository);
}
// implementing FactoryBean
@@ -229,110 +147,7 @@ class FlowExecutorFactoryBean implements FactoryBean, InitializingBean {
return flowExecutor;
}
// subclassing hook methods
/**
* 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
* implementation.
* @param definitionLocator the definition locator to use
* @param executionAttributes execution attributes to apply to restored executions
* @param executionListenerLoader decides which listeners should apply to restored flow executions
* @return a new state restorer instance
*/
protected FlowExecutionStateRestorer createFlowExecutionStateRestorer(FlowDefinitionLocator definitionLocator,
AttributeMap executionAttributes, FlowExecutionListenerLoader executionListenerLoader) {
FlowExecutionImplStateRestorer executionStateRestorer = new FlowExecutionImplStateRestorer(definitionLocator);
executionStateRestorer.setExecutionAttributes(executionAttributes);
if (executionListenerLoader != null) {
executionStateRestorer.setExecutionListenerLoader(executionListenerLoader);
}
return executionStateRestorer;
}
/**
* Factory method for creating the flow execution repository for saving and loading executing flows. Subclasses may
* override to customize the repository implementation used.
* @param repositoryType a hint indicating what type of repository to create
* @param executionStateRestorer the execution state restorer strategy to be used by the repository
* @param conversationManager the conversation manager specified by the user, could be null in which case the
* default conversation manager should be used
* @return a new flow execution repository instance
*/
protected FlowExecutionRepository createFlowExecutionRepository(FlowExecutionRepositoryType repositoryType,
FlowExecutionStateRestorer executionStateRestorer, ConversationManager conversationManager) {
if (conversationManager == null) {
conversationManager = createDefaultConversationManager();
}
if (repositoryType == null || repositoryType == FlowExecutionRepositoryType.CONTINUATION) {
DefaultFlowExecutionRepository repository = new DefaultFlowExecutionRepository(conversationManager,
executionStateRestorer);
if (maxContinuations != null) {
repository.setMaxContinuations(maxContinuations.intValue());
}
return repository;
} else if (repositoryType == FlowExecutionRepositoryType.SIMPLE) {
DefaultFlowExecutionRepository repository = new DefaultFlowExecutionRepository(conversationManager,
executionStateRestorer);
repository.setMaxContinuations(1);
return repository;
} else if (repositoryType == FlowExecutionRepositoryType.SINGLEKEY) {
DefaultFlowExecutionRepository repository = new DefaultFlowExecutionRepository(conversationManager,
executionStateRestorer);
repository.setAlwaysGenerateNewNextKey(false);
return repository;
} else {
throw new IllegalStateException("Cannot create execution repository - unsupported repository type "
+ repositoryType);
}
}
/**
* 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.
* @param definitionLocator the definition locator to use
* @param executionFactory the execution factory to use
* @param executionRepository the execution repository to use
* @return a new flow executor instance
*/
protected FlowExecutor createFlowExecutor(FlowDefinitionLocator definitionLocator,
FlowExecutionFactory executionFactory, FlowExecutionRepository executionRepository) {
return new FlowExecutorImpl(definitionLocator, executionFactory, executionRepository);
}
private MutableAttributeMap createExecutionAttributeMap() {
private MutableAttributeMap createFlowExecutionAttributes() {
LocalAttributeMap executionAttributes = new LocalAttributeMap();
if (flowExecutionAttributes != null) {
for (Iterator it = flowExecutionAttributes.iterator(); it.hasNext();) {
@@ -340,9 +155,62 @@ class FlowExecutorFactoryBean implements FactoryBean, InitializingBean {
executionAttributes.put(attribute.getName(), getConvertedValue(attribute));
}
}
putDefaultFlowExecutionAttributes(executionAttributes);
return executionAttributes;
}
private void putDefaultFlowExecutionAttributes(LocalAttributeMap executionAttributes) {
if (!executionAttributes.contains(ALWAYS_REDIRECT_ON_PAUSE)) {
if (environment == MvcEnvironment.PORTLET) {
executionAttributes.put(ALWAYS_REDIRECT_ON_PAUSE, Boolean.FALSE);
} else {
executionAttributes.put(ALWAYS_REDIRECT_ON_PAUSE, Boolean.TRUE);
}
}
}
private DefaultFlowExecutionRepository createFlowExecutionRepository(AttributeMap executionAttributes) {
ConversationManager conversationManager = createConversationManager();
FlowExecutionStateRestorer executionStateRestorer = createFlowExecutionStateRestorer(executionAttributes);
DefaultFlowExecutionRepository repository = new DefaultFlowExecutionRepository(conversationManager,
executionStateRestorer);
if (maxFlowExecutionSnapshots != null) {
repository.setMaxContinuations(maxFlowExecutionSnapshots.intValue());
}
return repository;
}
private ConversationManager createConversationManager() {
SessionBindingConversationManager conversationManager = new SessionBindingConversationManager();
if (maxFlowExecutions != null) {
conversationManager.setMaxConversations(maxFlowExecutions.intValue());
}
return conversationManager;
}
private FlowExecutionStateRestorer createFlowExecutionStateRestorer(AttributeMap executionAttributes) {
FlowExecutionImplStateRestorer executionStateRestorer = new FlowExecutionImplStateRestorer(
flowDefinitionLocator);
executionStateRestorer.setExecutionAttributes(executionAttributes);
if (flowExecutionListenerLoader != null) {
executionStateRestorer.setExecutionListenerLoader(flowExecutionListenerLoader);
}
return executionStateRestorer;
}
private FlowExecutionFactory createFlowExecutionFactory(AttributeMap executionAttributes,
FlowExecutionKeyFactory keyFactory) {
FlowExecutionImplFactory executionFactory = new FlowExecutionImplFactory();
executionFactory.setExecutionAttributes(executionAttributes);
if (flowExecutionListenerLoader != null) {
executionFactory.setExecutionListenerLoader(flowExecutionListenerLoader);
}
executionFactory.setExecutionKeyFactory(keyFactory);
return executionFactory;
}
// utility methods
private Object getConvertedValue(FlowElementAttribute attribute) {
if (attribute.needsTypeConversion()) {
Class targetType = fromStringToClass(attribute.getType());

View File

@@ -1,91 +0,0 @@
/*
* Copyright 2004-2008 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.Serializable;
import org.springframework.core.style.ToStringCreator;
import org.springframework.webflow.core.collection.LocalAttributeMap;
import org.springframework.webflow.core.collection.MutableAttributeMap;
/**
* Encapsulates overall flow system configuration defaults. Allows for centralized application of, and if necessary,
* overridding of system-wide default values.
*
* @author Keith Donald
*/
class FlowExecutorSystemDefaults implements Serializable {
/**
* The default 'alwaysRedirectOnPause' execution attribute value.
*/
private boolean alwaysRedirectOnPause = true;
/**
* The default flow execution repository type.
*/
private FlowExecutionRepositoryType repositoryType = FlowExecutionRepositoryType.CONTINUATION;
/**
* Overrides the alwaysRedirectOnPause execution attribute default. Defaults to "true".
* @param alwaysRedirectOnPause the new default value
*/
public void setAlwaysRedirectOnPause(boolean alwaysRedirectOnPause) {
this.alwaysRedirectOnPause = alwaysRedirectOnPause;
}
/**
* Overrides the default repository type.
* @param repositoryType the new default value
*/
public void setRepositoryType(FlowExecutionRepositoryType repositoryType) {
this.repositoryType = repositoryType;
}
/**
* Applies default execution attributes if necessary. Defaults will only apply in the case where the user did not
* configure a value, or explicitly requested the 'default' value.
* @param executionAttributes the user-configured execution attribute map
* @return the map with defaults applied as appropriate
*/
public MutableAttributeMap applyExecutionAttributes(MutableAttributeMap executionAttributes) {
if (executionAttributes == null) {
executionAttributes = new LocalAttributeMap(1, 1);
}
if (!executionAttributes.contains("alwaysRedirectOnPause")) {
executionAttributes.put("alwaysRedirectOnPause", new Boolean(alwaysRedirectOnPause));
}
return executionAttributes;
}
/**
* Applies the default repository type if requested by the user.
* @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 FlowExecutionRepositoryType applyIfNecessary(FlowExecutionRepositoryType selectedType) {
if (selectedType == null) {
return repositoryType;
} else {
return selectedType;
}
}
public String toString() {
return new ToStringCreator(this).append("alwaysRedirectOnPause", alwaysRedirectOnPause).append(
"repositoryType", repositoryType).toString();
}
}

View File

@@ -22,12 +22,18 @@ import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.binding.convert.service.DefaultConversionService;
import org.springframework.binding.format.registry.DefaultFormatterRegistry;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.springframework.webflow.engine.builder.support.FlowBuilderServices;
import org.springframework.webflow.expression.DefaultExpressionParserFactory;
import org.springframework.webflow.mvc.builder.MvcViewFactoryCreator;
import org.w3c.dom.Element;
/**
@@ -37,103 +43,65 @@ import org.w3c.dom.Element;
*/
class FlowRegistryBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
private static final String FLOW_BUILDER_SERVICES_ATTRIBUTE = "flow-builder-services";
private static final String PARENT_ATTRIBUTE = "parent";
private static final String FLOW_LOCATION_ELEMENT = "flow-location";
private static final String FLOW_LOCATION_PATTERN_ELEMENT = "flow-location-pattern";
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_LOCATION_PATTERNS_PROPERTY = "flowLocationPatterns";
private static final String FLOW_BUILDERS_PROPERTY = "flowBuilders";
private static final String FLOW_BUILDER_SERVICES_PROPERTY = "flowBuilderServices";
private static final String PARENT_PROPERTY = "parent";
protected Class getBeanClass(Element element) {
return FlowRegistryFactoryBean.class;
}
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder definitionBuilder) {
String flowBuilderServices = getFlowBuilderServicesAttribute(element);
String flowBuilderServices = element.getAttribute("flow-builder-services");
if (StringUtils.hasText(flowBuilderServices)) {
definitionBuilder.addPropertyReference(FLOW_BUILDER_SERVICES_PROPERTY, flowBuilderServices);
definitionBuilder.addPropertyReference("flowBuilderServices", flowBuilderServices);
} else {
definitionBuilder.addPropertyReference(FLOW_BUILDER_SERVICES_PROPERTY,
FlowBuilderServicesBeanDefinitionParser.registerDefaultFlowBuilderServicesBeanDefinition(
parserContext).getBeanName());
definitionBuilder.addPropertyValue("flowBuilderServices", createDefaultFlowBuilderServices(parserContext));
}
String parent = getParentAttribute(element);
String parent = element.getAttribute("parent");
if (StringUtils.hasText(parent)) {
definitionBuilder.addPropertyReference(PARENT_PROPERTY, parent);
definitionBuilder.addPropertyReference("parent", parent);
}
definitionBuilder.addPropertyValue(FLOW_LOCATIONS_PROPERTY, parseLocations(element));
definitionBuilder.addPropertyValue(FLOW_LOCATION_PATTERNS_PROPERTY, parseLocationPatterns(element));
definitionBuilder.addPropertyValue(FLOW_BUILDERS_PROPERTY, parseFlowBuilders(element));
definitionBuilder.addPropertyValue("flowLocations", parseLocations(element));
definitionBuilder.addPropertyValue("flowLocationPatterns", parseLocationPatterns(element));
definitionBuilder.addPropertyValue("flowBuilders", parseFlowBuilders(element));
}
private List parseLocations(Element element) {
List locationElements = DomUtils.getChildElementsByTagName(element, FLOW_LOCATION_ELEMENT);
List locationElements = DomUtils.getChildElementsByTagName(element, "flow-location");
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);
String id = locationElement.getAttribute("id");
String path = locationElement.getAttribute("path");
locations.add(new FlowLocation(id, path, parseAttributes(locationElement)));
}
return locations;
}
private List parseLocationPatterns(Element element) {
List locationPatternElements = DomUtils.getChildElementsByTagName(element, FLOW_LOCATION_PATTERN_ELEMENT);
List locationPatternElements = DomUtils.getChildElementsByTagName(element, "flow-location-pattern");
if (locationPatternElements.isEmpty()) {
return Collections.EMPTY_LIST;
}
List locationPatterns = new ArrayList(locationPatternElements.size());
for (Iterator it = locationPatternElements.iterator(); it.hasNext();) {
Element locationPatternElement = (Element) it.next();
String value = locationPatternElement.getAttribute(VALUE_ATTRIBUTE);
String value = locationPatternElement.getAttribute("value");
locationPatterns.add(value);
}
return locationPatterns;
}
private Set parseAttributes(Element element) {
Element definitionAttributesElement = DomUtils.getChildElementByTagName(element, DEFINITION_ATTRIBUTES_ELEMENT);
Element definitionAttributesElement = DomUtils.getChildElementByTagName(element, "flow-definition-attributes");
if (definitionAttributesElement != null) {
List attributeElements = DomUtils.getChildElementsByTagName(definitionAttributesElement, ATTRIBUTE_ELEMENT);
List attributeElements = DomUtils.getChildElementsByTagName(definitionAttributesElement, "attribute");
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);
String name = attributeElement.getAttribute("name");
String value = attributeElement.getAttribute("value");
String type = attributeElement.getAttribute("type");
attributes.add(new FlowElementAttribute(name, value, type));
}
return attributes;
@@ -143,26 +111,28 @@ class FlowRegistryBeanDefinitionParser extends AbstractSingleBeanDefinitionParse
}
private List parseFlowBuilders(Element element) {
List builderElements = DomUtils.getChildElementsByTagName(element, FLOW_BUILDER_ELEMENT);
List builderElements = DomUtils.getChildElementsByTagName(element, "flow-builder");
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);
String id = builderElement.getAttribute("id");
String className = builderElement.getAttribute("class");
builders.add(new FlowBuilderInfo(id, className, parseAttributes(builderElement)));
}
return builders;
}
private String getFlowBuilderServicesAttribute(Element element) {
return element.getAttribute(FLOW_BUILDER_SERVICES_ATTRIBUTE);
}
private String getParentAttribute(Element element) {
return element.getAttribute(PARENT_ATTRIBUTE);
private BeanDefinition createDefaultFlowBuilderServices(ParserContext context) {
BeanDefinitionBuilder defaultBuilder = BeanDefinitionBuilder.genericBeanDefinition(FlowBuilderServices.class);
defaultBuilder.addPropertyValue("formatterRegistry", DefaultFormatterRegistry.getSharedInstance());
defaultBuilder.addPropertyValue("conversionService", DefaultConversionService.getSharedInstance());
defaultBuilder.addPropertyValue("expressionParser", DefaultExpressionParserFactory.getExpressionParser());
defaultBuilder.addPropertyValue("viewFactoryCreator", BeanDefinitionBuilder.genericBeanDefinition(
MvcViewFactoryCreator.class).getBeanDefinition());
return defaultBuilder.getBeanDefinition();
}
}

View File

@@ -169,66 +169,7 @@ The fully qualified class name of the FlowBuilder implementation.
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:element name="flow-executor">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
Deploys a flow executor. A FlowExecutor executes flow definitions and acts as the entry-point into the Web Flow system.
A flow executor launches new flow executions and resumes paused flow executions.
Paused flow executions are stored between requests in a FlowExecutionRepository.
]]>
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="beans:identifiedType">
<xsd:sequence>
<xsd:element name="flow-execution-repository" type="flowExecutionRepositoryType" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
Configures the type of FlowExecutionRepository that should be used by this executor. Optional.
Specify when you want to override the default repository implementation.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="flow-execution-attributes" type="flowExecutionAttributesType" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
Meta attributes to assign new flow executions launched by this executor. These attributes will be associated with all flow executions,
regardless of their underlying flow definition. Such attributes may influence flow execution behavior at runtime.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="flow-execution-listeners" type="flowExecutionListenersType" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
Registers listeners that observe the lifecycle of flow executions launched by this executor.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="flow-registry" type="xsd:string" default="flowRegistry">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
A reference to the FlowRegistry bean containing the flows eligible for execution by this executor.
The default value 'flowRegistry'.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="flow-builder-services">
<xsd:annotation>
<xsd:documentation>
@@ -274,6 +215,65 @@ The custom ExpressionParser implementation to use to compile expression strings
<xsd:documentation>
<![CDATA[
The custom ViewFactoryCreator implementation to use produce ViewFactories capable of rendering Views.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="flow-executor">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
Deploys a flow executor. A FlowExecutor executes flow definitions and acts as the entry-point into the Web Flow system.
A flow executor launches new flow executions and resumes paused flow executions.
Paused flow executions are stored between requests in a FlowExecutionRepository.
]]>
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="beans:identifiedType">
<xsd:choice maxOccurs="unbounded">
<xsd:element name="flow-execution-repository" type="flowExecutionRepositoryType" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
Configures the default FlowExecutionRepository by this executor. Optional.
Specify when you want to customize attributes of the default repository implementation.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="flow-execution-attributes" type="flowExecutionAttributesType" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
Meta attributes to assign new flow executions launched by this executor. These attributes will be associated with all flow executions,
regardless of their underlying flow definition. Such attributes may influence flow execution behavior at runtime.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="flow-execution-listeners" type="flowExecutionListenersType" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
Registers listeners that observe the lifecycle of flow executions launched by this executor.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:choice>
<xsd:attribute name="flow-registry" type="xsd:string" default="flowRegistry">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
A reference to the FlowRegistry bean containing the flows eligible for execution by this executor.
The default value 'flowRegistry'.
]]>
</xsd:documentation>
</xsd:annotation>
@@ -284,82 +284,26 @@ The custom ViewFactoryCreator implementation to use produce ViewFactories capabl
</xsd:element>
<xsd:complexType name="flowExecutionRepositoryType">
<xsd:attribute name="type" type="flowExecutionRepositoryTypeAttribute">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
The type of flow execution repository to use.
A FlowExecutionRepository manages flow execution persistence between requests.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="max-conversations" type="xsd:integer">
<xsd:attribute name="max-executions" type="xsd:integer">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
The maximum number of concurrent conversations allowed by this repository. The default is 5.
Do not set this attribute if you are also setting the 'conversation-manager' attribute.
The maximum number of persistent flow executions allowed per user session. The default is 5.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="max-continuations" type="xsd:integer">
<xsd:attribute name="max-execution-snapshots" type="xsd:integer">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
The maximum number of flow execution continuations (snapshots) allowed by this repository per conversation. The default is 30.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="conversation-manager" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
The custom ConversationManager implementation to use with the FlowExecutionRepository.
Setting this attribute allows full control over the conversation manager implementation and configuration.
When used, any value for the 'max-conversations' attribute is ignored.
The maximum number of history snapshots allowed per flow execution. The default is 30.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<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. This is the default if not specified.
]]>
</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="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="flowExecutionListenerType" maxOccurs="unbounded">

View File

@@ -15,7 +15,11 @@
*/
package org.springframework.webflow.mvc.builder;
import org.springframework.context.ApplicationContext;
import org.springframework.core.enums.StaticLabeledEnum;
import org.springframework.util.ClassUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.portlet.context.ConfigurablePortletApplicationContext;
/**
* Supported Spring Web MVC environments.
@@ -38,4 +42,20 @@ public class MvcEnvironment extends StaticLabeledEnum {
super(code, label);
}
/**
* Calculates the web environment from the state of the provided application context.
* @param applicationContext the application context
* @return the web environment the context is running in, or null if not running in a web environment
*/
public static MvcEnvironment environmentFor(ApplicationContext applicationContext) {
if (ClassUtils.isPresent("javax.portlet.PortletContext")
&& applicationContext instanceof ConfigurablePortletApplicationContext) {
return MvcEnvironment.PORTLET;
} else if (applicationContext instanceof WebApplicationContext) {
return MvcEnvironment.SERVLET;
} else {
return null;
}
}
}

View File

@@ -22,8 +22,6 @@ import org.springframework.binding.expression.ExpressionParser;
import org.springframework.binding.format.FormatterRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.ClassUtils;
import org.springframework.web.portlet.context.ConfigurablePortletApplicationContext;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.webflow.engine.builder.ViewFactoryCreator;
@@ -113,12 +111,7 @@ public class MvcViewFactoryCreator implements ViewFactoryCreator, ApplicationCon
}
public void setApplicationContext(ApplicationContext applicationContext) {
if (ClassUtils.isPresent("javax.portlet.PortletContext")
&& applicationContext instanceof ConfigurablePortletApplicationContext) {
environment = MvcEnvironment.PORTLET;
} else {
environment = MvcEnvironment.SERVLET;
}
environment = MvcEnvironment.environmentFor(applicationContext);
}
public ViewFactory createViewFactory(Expression viewId, ExpressionParser expressionParser,
@@ -128,7 +121,7 @@ public class MvcViewFactoryCreator implements ViewFactoryCreator, ApplicationCon
} else if (environment == MvcEnvironment.PORTLET) {
return new PortletMvcViewFactory(viewId, flowViewResolver, expressionParser, formatterRegistry);
} else {
throw new IllegalStateException("Environment not supported " + environment);
throw new IllegalStateException("Web Environment " + environment + " not supported ");
}
}

View File

@@ -1,15 +0,0 @@
package org.springframework.webflow.config;
import org.springframework.util.Assert;
public class EnableScopesService {
private EnableScopesUser user;
public void setUser(EnableScopesUser user) {
this.user = user;
}
public void execute() {
Assert.isTrue("foo".equals(user.getName()));
}
}

View File

@@ -1,9 +0,0 @@
package org.springframework.webflow.config;
public class EnableScopesUser {
private String name = "foo";
public String getName() {
return name;
}
}

View File

@@ -66,13 +66,12 @@ public class FlowExecutorFactoryBeanTests extends TestCase {
Set attributes = new HashSet();
attributes.add(new FlowElementAttribute("foo", "bar", null));
factoryBean.setFlowExecutionAttributes(attributes);
factoryBean.setFlowExecutionRepositoryType(FlowExecutionRepositoryType.CONTINUATION);
FlowExecutionListener listener = new FlowExecutionListenerAdapter() {
};
factoryBean.setFlowExecutionListenerLoader(new StaticFlowExecutionListenerLoader(listener));
factoryBean.setMaxContinuations(2);
factoryBean.setMaxConversations(1);
factoryBean.setMaxFlowExecutionSnapshots(2);
factoryBean.setMaxFlowExecutions(1);
factoryBean.afterPropertiesSet();
FlowExecutor executor = (FlowExecutor) factoryBean.getObject();
MockExternalContext context = new MockExternalContext();

View File

@@ -1,11 +0,0 @@
<flow xmlns="http://www.springframework.org/schema/webflow"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
<end-state id="end">
<entry-actions>
<bean-action bean="service" method="execute"/>
</entry-actions>
</end-state>
</flow>

View File

@@ -1,42 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:webflow="http://www.springframework.org/schema/webflow-config"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/webflow-config
http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd">
<webflow:enable-flow-scopes/>
<webflow:flow-executor id="flowExecutor" flow-registry="flowRegistry">
<webflow:flow-execution-repository type="continuation" max-conversations="1" max-continuations="2"/>
<webflow:flow-execution-attributes>
<webflow:alwaysRedirectOnPause value="false"/>
<webflow:attribute name="foo" value="bar"/>
<webflow:attribute name="bar" value="2" type="integer"/>
</webflow:flow-execution-attributes>
<webflow:flow-execution-listeners>
<webflow:listener ref="listener" criteria="*"/>
</webflow:flow-execution-listeners>
</webflow:flow-executor>
<bean id="listener" class="org.springframework.webflow.config.EnableScopesBeanDefinitionParserTests$ConfigurationListener" />
<webflow:flow-registry id="flowRegistry">
<webflow:flow-location path="org/springframework/webflow/config/enable-flow-scopes-flowdef.xml" />
</webflow:flow-registry>
<bean id="user" class="org.springframework.webflow.config.EnableScopesUser" scope="flow">
<aop:scoped-proxy/>
</bean>
<bean id="service" class="org.springframework.webflow.config.EnableScopesService">
<property name="user" ref="user"/>
</bean>
</beans>

View File

@@ -9,7 +9,7 @@
http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd">
<webflow:flow-executor id="flowExecutor" flow-registry="flowRegistry">
<webflow:flow-execution-repository max-conversations="1" max-continuations="2"/>
<webflow:flow-execution-repository max-executions="1" max-execution-snapshots="2"/>
<webflow:flow-execution-attributes>
<webflow:always-redirect-on-pause value="false"/>
<webflow:attribute name="foo" value="bar"/>