flow-execution-repository element to tune flow execution persistence settings:
max-conversations attribute to place a cap on the number of conversations that can be created per user session.
+ Tune the max-executions attribute to place a cap on the number of flow executions that can be created per user session.
max-snapshots attribute to place a cap on the number of flow execution snapshots that can be taken per conversation.
- ConversationManager to completely customize where conversational flow state is persisted.
+ Tune the max-execution-snapshots attribute to place a cap on the number of history snapshots that can be taken per flow execution.
<execution-listeners> tag.
+ * {@link BeanDefinitionParser} for the <flow-execution-listeners> 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;
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/config/FlowExecutorBeanDefinitionParser.java b/spring-webflow/src/main/java/org/springframework/webflow/config/FlowExecutorBeanDefinitionParser.java
index 6edcc3b6..53c5bee0 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/config/FlowExecutorBeanDefinitionParser.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/config/FlowExecutorBeanDefinitionParser.java
@@ -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 repository-type attribute or a repository 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()));
+ }
+ }
}
\ No newline at end of file
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/config/FlowExecutorFactoryBean.java b/spring-webflow/src/main/java/org/springframework/webflow/config/FlowExecutorFactoryBean.java
index 416c2ca3..18ed81c7 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/config/FlowExecutorFactoryBean.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/config/FlowExecutorFactoryBean.java
@@ -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 FactoryBean, this class has been designed for
- * use as a Spring managed bean.
- *
* This factory encapsulates the construction and assembly of a {@link FlowExecutor}, including the provision of its
- * {@link FlowExecutionRepository} strategy.
+ * {@link FlowExecutionRepository} strategy. As a FactoryBean, this class has been designed for use as a
+ * Spring managed bean.
*
* The definition locator property is required, all other properties are optional. - *
- * 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. - *
- * The conversation manager is used by the flow execution repository subsystem to begin and end new conversations - * that store execution state. - *
- * 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());
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/config/FlowExecutorSystemDefaults.java b/spring-webflow/src/main/java/org/springframework/webflow/config/FlowExecutorSystemDefaults.java
deleted file mode 100644
index 790a9a8c..00000000
--- a/spring-webflow/src/main/java/org/springframework/webflow/config/FlowExecutorSystemDefaults.java
+++ /dev/null
@@ -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();
- }
-}
\ No newline at end of file
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/config/FlowRegistryBeanDefinitionParser.java b/spring-webflow/src/main/java/org/springframework/webflow/config/FlowRegistryBeanDefinitionParser.java
index 081c1551..c4d64c15 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/config/FlowRegistryBeanDefinitionParser.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/config/FlowRegistryBeanDefinitionParser.java
@@ -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();
}
}
\ No newline at end of file
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/config/spring-webflow-config-2.0.xsd b/spring-webflow/src/main/java/org/springframework/webflow/config/spring-webflow-config-2.0.xsd
index b5e3acd2..93b5b8c5 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/config/spring-webflow-config-2.0.xsd
+++ b/spring-webflow/src/main/java/org/springframework/webflow/config/spring-webflow-config-2.0.xsd
@@ -169,66 +169,7 @@ The fully qualified class name of the FlowBuilder implementation.
-
-