RESOLVED - issue SWF-380: Allow users to specify the namespace to register a flow in

http://opensource.atlassian.com/projects/spring/browse/SWF-380
This commit is contained in:
Ben Hale
2007-08-28 08:11:33 +00:00
parent 3849855888
commit a0f8c4e1e0
27 changed files with 800 additions and 1220 deletions

View File

@@ -15,16 +15,25 @@
*/
package org.springframework.webflow.config;
import java.util.ArrayList;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.util.StringUtils;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.util.xml.DomUtils;
import org.springframework.webflow.engine.builder.xml.XmlFlowRegistryFactoryBean;
import org.springframework.webflow.definition.registry.FlowDefinitionResource;
import org.springframework.webflow.engine.builder.FlowRegistryFactoryBean;
import org.w3c.dom.Element;
/**
@@ -32,40 +41,73 @@ import org.w3c.dom.Element;
*
* @author Ben Hale
*/
class RegistryBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
// elements and attributes
class RegistryBeanDefinitionParser extends AbstractBeanDefinitionParser {
// elements
private static final String LOCATION_ELEMENT = "location";
// properties
private static final String NAMESPACE_ELEMENT = "namespace";
private static final String FLOW_LOCATIONS_PROPERTY = "flowLocations";
// attributes
private static final String ID_ATTRIBUTE = "id";
private static final String NAME_ATTRIBUTE = "name";
private static final String PATH_ATTRIBUTE = "path";
protected Class getBeanClass(Element element) {
return XmlFlowRegistryFactoryBean.class;
// Properties
private static final String XML_NAMESPACE_FLOW_MAPPINGS_PROPERTY = "xmlNamespaceFlowMappings";
protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder
.rootBeanDefinition(FlowRegistryFactoryBean.class);
definitionBuilder.setSource(parserContext.extractSource(element));
parseXml(element, definitionBuilder, parserContext);
return definitionBuilder.getBeanDefinition();
}
protected void doParse(Element element, BeanDefinitionBuilder definitionBuilder) {
List locationElements = DomUtils.getChildElementsByTagName(element, LOCATION_ELEMENT);
List locations = getLocations(locationElements);
definitionBuilder.addPropertyValue(FLOW_LOCATIONS_PROPERTY, locations.toArray(new String[locations.size()]));
public void parseXml(Element element, BeanDefinitionBuilder definitionBuilder, ParserContext parserContext) {
Map xmlNamespaceFlowMappings = new HashMap();
xmlNamespaceFlowMappings.put("", parseXmlElements(
DomUtils.getChildElementsByTagName(element, LOCATION_ELEMENT), parserContext));
List namespaceElements = DomUtils.getChildElementsByTagName(element, NAMESPACE_ELEMENT);
for (Iterator it = namespaceElements.iterator(); it.hasNext();) {
Element namespaceElement = (Element) it.next();
String namespace = namespaceElement.getAttribute(NAME_ATTRIBUTE);
xmlNamespaceFlowMappings.put(namespace, parseXmlElements(DomUtils.getChildElementsByTagName(
namespaceElement, LOCATION_ELEMENT), parserContext));
}
definitionBuilder.addPropertyValue(XML_NAMESPACE_FLOW_MAPPINGS_PROPERTY, xmlNamespaceFlowMappings);
}
/**
* Parse location definitions from given list of location elements.
*/
private List getLocations(List locationElements) {
List locations = new ArrayList(locationElements.size());
for (Iterator i = locationElements.iterator(); i.hasNext();) {
Element locationElement = (Element) i.next();
String path = locationElement.getAttribute(PATH_ATTRIBUTE);
if (StringUtils.hasText(path)) {
locations.add(path);
private Set parseXmlElements(List locationElements, ParserContext parserContext) {
ResourcePatternResolver resolver = ResourcePatternUtils.getResourcePatternResolver(parserContext
.getReaderContext().getResourceLoader());
Set resources = new HashSet();
for (Iterator it = locationElements.iterator(); it.hasNext();) {
Element locationElement = (Element) it.next();
try {
Resource[] locations = resolver.getResources(locationElement.getAttribute(PATH_ATTRIBUTE));
if (locationElement.hasAttribute(ID_ATTRIBUTE)) {
if (locations.length != 1) {
parserContext.getReaderContext().error(
"The 'path' attribute of the 'location' element must point to a single value "
+ "flow definition if an id has been specified", locationElement);
} else {
resources.add(new FlowDefinitionResource(locationElement.getAttribute(ID_ATTRIBUTE),
locations[0]));
}
} else {
for (int i = 0; i < locations.length; i++) {
resources.add(new FlowDefinitionResource(locations[i]));
}
}
} catch (IOException e) {
parserContext.getReaderContext().error(
"The 'path' attribute of the 'location' element must point to a valid flow definition "
+ "or definitions", locationElement);
}
}
return locations;
return resources;
}
}

View File

@@ -20,17 +20,17 @@ Provides an easy way to configure a flow executor and an XML flow definition reg
<xsd:import namespace="http://www.springframework.org/schema/beans"
schemaLocation="http://www.springframework.org/schema/beans/spring-beans-2.0.xsd" />
<xsd:element name="enable-scopes">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
Allows access to the Spring Web Flow scopes (conversation, flow, flash) from a Spring ApplicationContext.
Only one of these elements is required per ApplicationContext.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="enable-scopes">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
Allows access to the Spring Web Flow scopes (conversation, flow, flash) from a Spring ApplicationContext.
Only one of these elements is required per ApplicationContext.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="registry">
<xsd:annotation>
@@ -52,7 +52,7 @@ A flow registry is used by a flow executor at runtime to launch new executions o
<xsd:complexContent>
<xsd:extension base="beans:identifiedType">
<xsd:sequence>
<xsd:element name="location" type="locationType" maxOccurs="unbounded">
<xsd:element name="location" type="locationType" minOccurs="0" maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
@@ -67,6 +67,47 @@ Individual paths such as:
<pre>
/WEB-INF/flows/**/*-flow.xml
</pre>
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="class" type="classType" minOccurs="0" maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
Specifies a class containing a flow definition. The flow definition built from this
class will be registered in this registry.
<br>
Classes must be declared using a fully qualified path name such as
<pre>
com.interface21.SellItemFlow
</pre>
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="crud" type="crudType" minOccurs="0" maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
Specifies a domain entity class to have crud flows built for. This will register flows for Creating, Reading, Updating,
and Deleting the domain entity in a namespace that is equal to the short name of the class.
<br>
Entities must be declared using a fully qualified path name such as
<pre>
com.interface21.Account
</pre>
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="namespace" type="namespaceType" minOccurs="0" maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
Specifies a namespace to register nested flow definitions in. This namespace is a course-grained grouping for uniquely
identifying a flow. Since some flows define a namespace implicitly (CRUD flows for example), not all flow types are
allowed to be nested in the namespace tag.
]]>
</xsd:documentation>
</xsd:annotation>
@@ -78,12 +119,96 @@ Individual paths such as:
</xsd:element>
<xsd:complexType name="locationType">
<xsd:attribute name="id" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
The id to the externalized flow definition resource. This only needs to be populated for explict declaration of flow
identifiers. If not populated this id of the flow is equal to the filename of the underlying definition resource, minus
the filename extension. This value cannot be set if an ANT-style path expression that matches multiple resources is
used in path attribute.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="path" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
The path to the externalized flow definition resource. May be a path to a single resource or
a ANT-style path expression that matches multiple resources.
The path to the externalized flow definition resource. May be a path to a single resource or a ANT-style path expression
that matches multiple resources.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="classType">
<xsd:attribute name="name" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
The name of the java based flow definition.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="crudType">
<xsd:attribute name="entity" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
The name of a domain entity class to have crud flows created for.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="namespaceType">
<xsd:sequence>
<xsd:element name="location" type="locationType" minOccurs="0" maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
Specifies a path to a externalized flow definition resource. The flow definition built from this
resource will be registered in this registry.
<br>
Individual paths such as:
<pre>
/WEB-INF/flows/orderitem-flow.xml
</pre>
... are supported as well as wildcard paths such as:
<pre>
/WEB-INF/flows/**/*-flow.xml
</pre>
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="class" type="classType" minOccurs="0" maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
Specifies a class containing a flow definition. The flow definition built from this
class will be registered in this registry.
<br>
Classes must be declared using a fully qualified path name such as
<pre>
com.interface21.SellItemFlow
</pre>
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
The name of the namespace to group all child flow definitions in.
]]>
</xsd:documentation>
</xsd:annotation>

View File

@@ -38,7 +38,7 @@ public interface AttributeMap extends MapAdaptable {
/**
* Returns the size of this map.
* @return the nubmer of entries in the map
* @return the number of entries in the map
*/
public int size();

View File

@@ -1,97 +0,0 @@
/*
* Copyright 2004-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.webflow.definition.registry;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
/**
* A base class for factory beans that create populated flow definition registries. Subclasses should override the
* {@link #doPopulate(FlowDefinitionRegistry)} method to perform the registry population logic, typically delegating to
* a {@link FlowDefinitionRegistrar} strategy to perform the population.
*
* @author Keith Donald
*/
public abstract class AbstractFlowDefinitionRegistryFactoryBean implements FactoryBean, InitializingBean {
/**
* The registry to register flow definitions in.
*/
private FlowDefinitionRegistry registry = createFlowDefinitionRegistry();
/**
* Sets the parent registry of the registry constructed by this factory bean.
* <p>
* A child registry will delegate to its parent if it cannot fulfill a request to locate a flow definition itself.
* @param parent the parent flow definition registry
*/
public void setParent(FlowDefinitionRegistry parent) {
registry.setParent(parent);
}
// implementing InitializingBean
public final void afterPropertiesSet() throws Exception {
init();
doPopulate(registry);
}
// implementing FactoryBean
public Class getObjectType() {
return FlowDefinitionRegistry.class;
}
public boolean isSingleton() {
return true;
}
public Object getObject() throws Exception {
// the registry is populated by the time this is called
return getRegistry();
}
/**
* Returns the flow definition registry constructed by the factory bean.
*/
public FlowDefinitionRegistry getRegistry() {
return registry;
}
// subclassing hooks
/**
* Create the flow definition registry to be populated in {@link #doPopulate(FlowDefinitionRegistry)}. Subclasses
* can override this method if they want to use a custom flow definition registry implementation.
*/
protected FlowDefinitionRegistry createFlowDefinitionRegistry() {
return new FlowDefinitionRegistryImpl();
}
/**
* Template method subclasses may override to perform factory bean initialization logic before registry population.
* Will be called before {@link #doPopulate(FlowDefinitionRegistry)}. The default implementation is empty.
*/
protected void init() {
}
/**
* Template method subclasses must override to perform registry population.
* @param registry the flow definition registry to populate
*/
protected abstract void doPopulate(FlowDefinitionRegistry registry);
}

View File

@@ -15,13 +15,15 @@
*/
package org.springframework.webflow.definition.registry;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.springframework.core.io.Resource;
import org.springframework.core.style.ToStringCreator;
import org.springframework.webflow.engine.builder.FlowServiceLocator;
/**
* A flow definition registrar that populates a flow definition registry from flow definitions defined within
@@ -30,167 +32,139 @@ import org.springframework.core.style.ToStringCreator;
* <p>
* Concrete subclasses are expected to derive from this class to provide knowledge about a particular kind of definition
* format by implementing the abstract template methods in this class.
* <p>
* By default, when configuring the {@link #setLocations(Resource[]) locations} property, flow definitions at those
* locations will be assigned a registry identifier equal to the filename of the underlying definition resource, minus
* the filename extension. For example, a XML-based flow definition defined in the file "flow1.xml" will be identified
* as "flow1" when registered in a registry.
* <p>
* For full control over the assignment of flow identifiers and flow properties, configure formal
* {@link org.springframework.webflow.definition.registry.FlowDefinitionResource} instances using the
* {@link #setResources(FlowDefinitionResource[] resources)} property.
*
* @see org.springframework.webflow.definition.registry.FlowDefinitionResource
* @see org.springframework.webflow.definition.registry.FlowDefinitionRegistry
*
* @author Keith Donald
* @author Ben Hale
*/
public abstract class ExternalizedFlowDefinitionRegistrar implements FlowDefinitionRegistrar {
/**
* File locations of externalized flow definition resources to load. A set of {@link Resource}} objects.
* The locator of services needed by flow definitions.
*/
private Set locations = new HashSet();
private FlowServiceLocator flowServiceLocator;
/**
* A set of formal externalized flow definitions to load. A set of {@link FlowDefinitionResource} objects.
* A set of mappings between a namespace and a set of externalized flow definitions. A map of Strings to Sets
* containing {@link FlowDefinitionResource}s
*/
private Set resources = new HashSet();
private Map namespaceFlowMappings;
/**
* Sets the locations (file paths) pointing to externalized flow definitions.
* <p>
* Flows registered from this set will be automatically assigned an id based on the filename of the flow resource.
* @param locations the resource locations
* The default namespace for flows registered without an explicit namespace.
*/
public void setLocations(Resource[] locations) {
this.locations = new HashSet(Arrays.asList(locations));
private String defaultNamespace = "";
/**
* Creates a new registrar with an empty initial set of namespace to flow mappings.
*/
public ExternalizedFlowDefinitionRegistrar() {
this(new HashMap());
}
/**
* Sets the formal set of externalized flow definitions this registrar will register.
* <p>
* Use this method when you want full control over the assigned flow id and the set of properties applied to the
* externalized flow resources.
* @param resources the externalized flow definition specifications
* Creates a new registrar with an initial set of namespace to flow mappings.
* @param namespaceFlowMappings the initial set of namespace to flow mappings
*/
public void setResources(FlowDefinitionResource[] resources) {
this.resources = new HashSet(Arrays.asList(resources));
public ExternalizedFlowDefinitionRegistrar(Map namespaceFlowMappings) {
this.namespaceFlowMappings = namespaceFlowMappings;
}
/**
* Adds a flow location pointing to an externalized flow resource.
* <p>
* The flow registered from this location will automatically assigned an id based on the filename of the flow
* resource.
* @param location the definition location
* Sets the default namespace to register flows in. If not set the default namespace is "" (an empty string).
* @param defaultNamespace the default namespace
*/
public void setDefaultNamespace(String defaultNamespace) {
this.defaultNamespace = defaultNamespace;
}
public void setFlowServiceLocator(FlowServiceLocator flowServiceLocator) {
this.flowServiceLocator = flowServiceLocator;
}
/**
* Returns the flow service locator for use by subclasses.
*/
protected FlowServiceLocator getFlowServiceLocator() {
return flowServiceLocator;
}
/**
* Adds an externalized XML flow definition to be registered in the default namespace.
* @param location the resource to register
* @see #addLocation(Resource, String)
* @see #setDefaultNamespace(String)
*/
public boolean addLocation(Resource location) {
return locations.add(location);
return addLocation(location, defaultNamespace);
}
/**
* Adds the flow locations pointing to externalized flow resources.
* <p>
* The flow registered from this location will automatically assigned an id based on the filename of the flow
* resource.
* @param locations the definition locations
* Adds an externalized XML flow definition to be registered. The resource will be assigned a registry identifier
* equal to the filename of the resource, minus the filename extension. For example, an XML-based flow definition
* defined in the file <code>flow1.xml</code> will be identified as <code>flow1</code> in the registry created
* by this factory bean.
* @param location the resource to register
* @param namespace the namespace to register the flow definition in
*/
public boolean addLocations(Resource[] locations) {
if (locations == null) {
return false;
}
return this.locations.addAll(Arrays.asList(locations));
public boolean addLocation(Resource location, String namespace) {
return getFlows(namespace).add(new FlowDefinitionResource(location));
}
/**
* Adds an externalized flow definition specification pointing to an externalized flow resource.
* <p>
* Use this method when you want full control over the assigned flow id and the set of properties applied to the
* externalized flow resource.
* @param resource the definition the definition resource
* Adds an externalized XML flow definition resource to be registered in the default namespace.
* @param resource the flow definition resource to be registered
* @see #addResource(FlowDefinitionResource, String)
* @see #setDefaultNamespace(String)
*/
public boolean addResource(FlowDefinitionResource resource) {
return resources.add(resource);
return addResource(resource, defaultNamespace);
}
/**
* Adds the externalized flow definitions pointing to externalized flow resources.
* <p>
* Use this method when you want full control over the assigned flow id and the set of properties applied to the
* externalized flow resources.
* @param resources the definitions
* Adds an externalized XML flow definition resource to be registered.
* @param resource the flow definition resource to be registered
* @param namespace the namespace to register the flow definition in
*/
public boolean addResources(FlowDefinitionResource[] resources) {
if (resources == null) {
return false;
}
return this.resources.addAll(Arrays.asList(resources));
public boolean addResource(FlowDefinitionResource resource, String namespace) {
return getFlows(namespace).add(resource);
}
public void registerFlowDefinitions(FlowDefinitionRegistry registry) {
processLocations(registry);
processResources(registry);
}
// internal helpers
/**
* Register the flow definitions at the configured file locations.
* @param registry the registry
*/
private void processLocations(FlowDefinitionRegistry registry) {
Iterator it = locations.iterator();
while (it.hasNext()) {
Resource location = (Resource) it.next();
if (isFlowDefinitionResource(location)) {
FlowDefinitionResource resource = createFlowDefinitionResource(location);
register(resource, registry);
for (Iterator mappings = namespaceFlowMappings.entrySet().iterator(); mappings.hasNext();) {
Map.Entry mapping = (Map.Entry) mappings.next();
String namespace = (String) mapping.getKey();
for (Iterator resources = ((Set) mapping.getValue()).iterator(); resources.hasNext();) {
FlowDefinitionResource resource = (FlowDefinitionResource) resources.next();
register(resource, namespace, registry);
}
}
}
/**
* Register the flow definitions at the configured file locations.
* Registers a flow definition resource in a given namespace.
* @param resource the resource to register
* @param namespace the namespace to register in
* @param registry the registry
*/
private void processResources(FlowDefinitionRegistry registry) {
Iterator it = resources.iterator();
while (it.hasNext()) {
FlowDefinitionResource resource = (FlowDefinitionResource) it.next();
register(resource, registry);
private void register(FlowDefinitionResource resource, String namespace, FlowDefinitionRegistry registry) {
registry.registerFlowDefinition(createFlowDefinitionHolder(resource), namespace);
}
/**
* Returns the set of flows to be registered in a namespace.
* @param namespace The namespace for the collection to be returned
*/
private Set getFlows(String namespace) {
if (!namespaceFlowMappings.containsKey(namespace)) {
namespaceFlowMappings.put(namespace, new HashSet());
}
return (Set) namespaceFlowMappings.get(namespace);
}
/**
* Helper method to register the flow built from an externalized resource in the registry.
* @param resource representation of the externalized flow definition resource
* @param registry the flow registry to register the flow in
*/
protected final void register(FlowDefinitionResource resource, FlowDefinitionRegistry registry) {
registry.registerFlowDefinition(createFlowDefinitionHolder(resource));
}
// subclassing hooks
/**
* Template method that calculates if the given file resource is actually a flow definition resource. Resources that
* aren't flow definitions will be ignored. Subclasses may override; this implementation simply returns true.
* @param resource the underlying resource
* @return true if yes, false otherwise
*/
protected boolean isFlowDefinitionResource(Resource resource) {
return true;
}
/**
* Factory method that creates a flow definition from an externalized resource location.
* @param location the location of the resource
* @return the externalized flow definition pointer
*/
protected FlowDefinitionResource createFlowDefinitionResource(Resource location) {
return new FlowDefinitionResource(location);
}
// sub-classing hooks
/**
* Template factory method subclasses must override to return the holder for the flow definition to be registered
@@ -201,6 +175,6 @@ public abstract class ExternalizedFlowDefinitionRegistrar implements FlowDefinit
protected abstract FlowDefinitionHolder createFlowDefinitionHolder(FlowDefinitionResource resource);
public String toString() {
return new ToStringCreator(this).append("locations", locations).append("resources", resources).toString();
return new ToStringCreator(this).append("namespaceFlowMappings", namespaceFlowMappings).toString();
}
}

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.webflow.definition.registry;
/**
* A strategy to use to populate a flow definition registry with one or more flow definitions.
* <p>
@@ -42,4 +43,5 @@ public interface FlowDefinitionRegistrar {
* @param registry the registry to register flow definitions in
*/
public void registerFlowDefinitions(FlowDefinitionRegistry registry);
}

View File

@@ -1,83 +0,0 @@
/*
* Copyright 2004-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.webflow.engine.builder;
import org.springframework.webflow.core.collection.AttributeMap;
import org.springframework.webflow.definition.registry.FlowDefinitionHolder;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
import org.springframework.webflow.definition.registry.StaticFlowDefinitionHolder;
import org.springframework.webflow.engine.Flow;
import org.springframework.webflow.engine.builder.AbstractFlowBuilder;
import org.springframework.webflow.engine.builder.AbstractFlowBuildingFlowRegistryFactoryBean;
import org.springframework.webflow.engine.builder.FlowAssembler;
/**
* Base class for factory beans that create flow definition registries containing flows built using Java based
* {@link AbstractFlowBuilder flow builders}.
* <p>
* Subclasses only need to define the {@link #doPopulate(FlowDefinitionRegistry)} method and use the
* {@link #registerFlowDefinition(FlowDefinitionRegistry, String, AbstractFlowBuilder)} convenience methods provided by
* this class to register all relevant flows:
*
* <pre class="code">
* public class MyFlowRegistryFactoryBean extends AbstractFlowBuilderFlowRegistryFactoryBean {
* protected void doPopulate(FlowDefinitionRegistry registry) {
* registerFlowDefinition(registry, &quot;my-flow&quot;, new MyFlowBuilder());
* registerFlowDefinition(registry, &quot;my-other-flow&quot;, new MyOtherFlowBuilder());
* }
* }
* </pre>
*
* @see AbstractFlowBuilder
*
* @since 1.0.2
*
* @author Erwin Vervaet
*/
public abstract class AbstractFlowBuilderFlowRegistryFactoryBean extends AbstractFlowBuildingFlowRegistryFactoryBean {
/**
* Register the flow built by given flow builder in specified flow definition registry.
* <p>
* Note that this method will set the {@link #getFlowServiceLocator() flow service locator} of this class on given
* flow builder.
* @param registry the registry to register the flow in
* @param flowId the flow id to assign
* @param flowBuilder the builder used to build the flow
*/
protected void registerFlowDefinition(FlowDefinitionRegistry registry, String flowId,
AbstractFlowBuilder flowBuilder) {
registerFlowDefinition(registry, flowId, null, flowBuilder);
}
/**
* Register the flow built by given flow builder in specified flow definition registry.
* <p>
* Note that this method will set the {@link #getFlowServiceLocator() flow service locator} of this class on given
* flow builder.
* @param registry the registry to register the flow in
* @param flowId the flow id to assign
* @param flowAttributes externally assigned flow attributes that can affect flow construction
* @param flowBuilder the builder used to build the flow
*/
protected void registerFlowDefinition(FlowDefinitionRegistry registry, String flowId, AttributeMap flowAttributes,
AbstractFlowBuilder flowBuilder) {
flowBuilder.setFlowServiceLocator(getFlowServiceLocator());
Flow flow = new FlowAssembler(flowId, flowAttributes, flowBuilder).assembleFlow();
FlowDefinitionHolder flowHolder = new StaticFlowDefinitionHolder(flow);
registry.registerFlowDefinition(flowHolder);
}
}

View File

@@ -1,223 +0,0 @@
/*
* Copyright 2004-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.webflow.engine.builder;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.binding.convert.ConversionService;
import org.springframework.binding.expression.ExpressionParser;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.ResourceLoader;
import org.springframework.webflow.action.BeanInvokingActionFactory;
import org.springframework.webflow.definition.registry.AbstractFlowDefinitionRegistryFactoryBean;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
import org.springframework.webflow.engine.Flow;
import org.springframework.webflow.engine.State;
import org.springframework.webflow.execution.Action;
/**
* A base class for factory beans that create populated registries of flow definitions built using a {@link FlowBuilder},
* typically a {@link BaseFlowBuilder} subclass. This base class will setup a {@link FlowServiceLocator} for use by the
* flow builder.
* <p>
* Subclasses should override the {@link #doPopulate(FlowDefinitionRegistry)} template method to perform the registry
* population logic, typically delegating to a
* {@link org.springframework.webflow.definition.registry.FlowDefinitionRegistrar} strategy.
*
* @see org.springframework.webflow.definition.registry.FlowDefinitionRegistry
* @see org.springframework.webflow.definition.registry.FlowDefinitionRegistrar
*
* @author Keith Donald
*/
public abstract class AbstractFlowBuildingFlowRegistryFactoryBean extends AbstractFlowDefinitionRegistryFactoryBean
implements BeanFactoryAware, ResourceLoaderAware {
/**
* The locator of services needed by the flows built for inclusion in the registry.
*/
private FlowServiceLocator flowServiceLocator;
/**
* The factory encapsulating the creation of central Flow artifacts such as {@link Flow flows} and
* {@link State states}.
*/
private FlowArtifactFactory flowArtifactFactory;
/**
* The factory encapsulating the creation of bean invoking actions, actions that adapt methods on objects to the
* {@link Action} interface.
*/
private BeanInvokingActionFactory beanInvokingActionFactory;
/**
* The parser for parsing expression strings into evaluatable expression objects.
*/
private ExpressionParser expressionParser;
/**
* A conversion service that can convert between types.
*/
private ConversionService conversionService;
/**
* A resource loader that can load resources.
*/
private ResourceLoader resourceLoader;
/**
* The Spring bean factory that manages configured flow artifacts.
*/
private BeanFactory beanFactory;
/**
* Returns the factory encapsulating the creation of central Flow artifacts such as {@link Flow flows} and
* {@link State states}.
*/
protected FlowArtifactFactory getFlowArtifactFactory() {
return flowArtifactFactory;
}
/**
* Sets the factory encapsulating the creation of central Flow artifacts such as {@link Flow flows} and
* {@link State states}.
*/
public void setFlowArtifactFactory(FlowArtifactFactory flowArtifactFactory) {
this.flowArtifactFactory = flowArtifactFactory;
}
/**
* Returns the factory for creating bean invoking actions, actions that adapt methods on objects to the
* {@link Action} interface.
*/
protected BeanInvokingActionFactory getBeanInvokingActionFactory() {
return beanInvokingActionFactory;
}
/**
* Sets the factory for creating bean invoking actions, actions that adapt methods on objects to the {@link Action}
* interface.
*/
public void setBeanInvokingActionFactory(BeanInvokingActionFactory beanInvokingActionFactory) {
this.beanInvokingActionFactory = beanInvokingActionFactory;
}
/**
* Returns the expression parser responsible for parsing expression strings into evaluatable expression objects.
*/
protected ExpressionParser getExpressionParser() {
return expressionParser;
}
/**
* Set the expression parser responsible for parsing expression strings into evaluatable expression objects.
*/
public void setExpressionParser(ExpressionParser expressionParser) {
this.expressionParser = expressionParser;
}
/**
* Returns the conversion service to use to convert between types; typically from string to a rich object type.
*/
protected ConversionService getConversionService() {
return conversionService;
}
/**
* Set the conversion service to use to convert between types; typically from string to a rich object type.
*/
public void setConversionService(ConversionService conversionService) {
this.conversionService = conversionService;
}
// implementing ResourceLoaderAware
/**
* Returns the injected resource loader.
*/
protected ResourceLoader getResourceLoader() {
return resourceLoader;
}
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
// implementing BeanFactoryAware
/**
* Returns the bean factory managing this bean.
*/
protected BeanFactory getBeanFactory() {
return beanFactory;
}
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
protected final void init() {
flowServiceLocator = createFlowServiceLocator();
init(flowServiceLocator);
}
// subclassing hooks
/**
* Factory method for creating the service locator used to locate webflow services during flow assembly. Subclasses
* may override to customize the instantiation and configuration of the locator returned.
* @return the service locator
*/
protected FlowServiceLocator createFlowServiceLocator() {
DefaultFlowServiceLocator serviceLocator = new DefaultFlowServiceLocator(getRegistry(), beanFactory);
if (flowArtifactFactory != null) {
serviceLocator.setFlowArtifactFactory(flowArtifactFactory);
}
if (beanInvokingActionFactory != null) {
serviceLocator.setBeanInvokingActionFactory(beanInvokingActionFactory);
}
if (expressionParser != null) {
serviceLocator.setExpressionParser(expressionParser);
}
if (conversionService != null) {
serviceLocator.setConversionService(conversionService);
}
if (resourceLoader != null) {
serviceLocator.setResourceLoader(resourceLoader);
}
return serviceLocator;
}
/**
* Called after properties have been set on the service locator, but before registry population. Subclasses may
* override to perform custom initialization of the flow service locator.
* @param flowServiceLocator the flow service locator to use to locate externally managed services needed during
* flow building and assembly, typically used by a
* {@link org.springframework.webflow.definition.registry.FlowDefinitionRegistrar}
*/
protected void init(FlowServiceLocator flowServiceLocator) {
}
/**
* Returns the strategy for locating dependent artifacts when a flow is being built. May be called by subclasses
* during {@link #doPopulate(FlowDefinitionRegistry) registry population} to wire in the service locator needed for
* flow assembly.
*/
protected FlowServiceLocator getFlowServiceLocator() {
return flowServiceLocator;
}
protected abstract void doPopulate(FlowDefinitionRegistry registry);
}

View File

@@ -0,0 +1,112 @@
/*
* Copyright 2004-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.webflow.engine.builder;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistrar;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistryImpl;
import org.springframework.webflow.definition.registry.FlowDefinitionResource;
import org.springframework.webflow.engine.builder.xml.XmlFlowRegistrar;
/**
* A factory bean that accepts an arbitrary list of {@link FlowDefinitionRegistrar}s and uses them to register flows.
* This implementation should not be used programmatically but rather from the spring-webflow-config XML namespace.
* <p>
* Example Usage:
*
* <pre>
* &lt;flow:registry id=&quot;flowRegistry&quot;&gt;
* &lt;flow:location path=&quot;flow1.xml&quot;/&gt;
* &lt;flow:location id=&quot;foo&quot; path=&quot;flow2.xml&quot;/&gt;
* &lt;flow:class name=&quot;BarClass&quot;/&gt;
* &lt;flow:crud entity=&quot;Account&quot;/&gt;
* &lt;flow:namespace name=&quot;baz&quot;&gt;
* &lt;flow:location path=&quot;flow3.xml&quot;/&gt;
* &lt;flow:class name=&quot;XyzClass&quot;/&gt;
* &lt;/flow:namespace&gt;
* &lt;/flow:registry&gt;
* </pre>
*
* @author Ben Hale
*/
public class FlowRegistryFactoryBean implements FactoryBean, InitializingBean, BeanFactoryAware {
private FlowDefinitionRegistry registry;
private BeanFactory beanFactory;
private FlowServiceLocator flowServiceLocator;
private Map xmlNamespaceFlowMappings;
public void setFlowServiceLocator(FlowServiceLocator flowServiceLocator) {
this.flowServiceLocator = flowServiceLocator;
}
public void setXmlNamespaceFlowMappings(Map xmlNamespaceFlowMappings) {
this.xmlNamespaceFlowMappings = xmlNamespaceFlowMappings;
}
public void afterPropertiesSet() throws Exception {
this.registry = new FlowDefinitionRegistryImpl();
initXml(registry);
}
public Object getObject() throws Exception {
return registry;
}
public Class getObjectType() {
return FlowDefinitionRegistry.class;
}
public boolean isSingleton() {
return true;
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
private void initXml(FlowDefinitionRegistry registry) {
XmlFlowRegistrar registrar = new XmlFlowRegistrar(getFlowServiceLocator(registry));
for (Iterator mappings = xmlNamespaceFlowMappings.entrySet().iterator(); mappings.hasNext();) {
Map.Entry mapping = (Map.Entry) mappings.next();
String namespace = (String) mapping.getKey();
for (Iterator resources = ((Set) mapping.getValue()).iterator(); resources.hasNext();) {
FlowDefinitionResource resource = (FlowDefinitionResource) resources.next();
registrar.addResource(resource, namespace);
}
}
registrar.registerFlowDefinitions(registry);
}
private FlowServiceLocator getFlowServiceLocator(FlowDefinitionRegistry registry) {
if (flowServiceLocator == null) {
this.flowServiceLocator = new DefaultFlowServiceLocator(registry, beanFactory);
}
return flowServiceLocator;
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2004-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.webflow.engine.builder;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import junit.framework.TestCase;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
import org.springframework.webflow.definition.registry.FlowDefinitionResource;
import org.springframework.webflow.test.MockFlowServiceLocator;
/**
* Tests that the factory bean properly creates a {@link FlowDefinitionRegistry} with the proper definitions in it.
*/
public class FlowRegistryFactoryBeanTests extends TestCase {
public void testXmlRegistrar() throws Exception {
Set emptyNamespace = new HashSet();
emptyNamespace.add(new FlowDefinitionResource(fromClassPath("flow1.xml")));
Set bookingNamespace = new HashSet();
bookingNamespace.add(new FlowDefinitionResource(fromClassPath("flow2.xml")));
Map xmlNamespaceFlowMappings = new HashMap();
xmlNamespaceFlowMappings.put("", emptyNamespace);
xmlNamespaceFlowMappings.put("booking", bookingNamespace);
FlowRegistryFactoryBean factoryBean = new FlowRegistryFactoryBean();
factoryBean.setFlowServiceLocator(new MockFlowServiceLocator());
factoryBean.setXmlNamespaceFlowMappings(xmlNamespaceFlowMappings);
factoryBean.afterPropertiesSet();
FlowDefinitionRegistry registry = (FlowDefinitionRegistry) factoryBean.getObject();
assertEquals("Incorrect number of flows", 2, registry.getFlowDefinitionCount());
assertTrue("Missing flow", registry.containsFlowDefinition("flow1"));
assertTrue("Missing flow", registry.containsFlowDefinition("booking/flow2"));
}
private Resource fromClassPath(String resourceName) {
return new ClassPathResource(resourceName, getClass());
}
}

View File

@@ -22,17 +22,19 @@ import org.springframework.webflow.definition.registry.FlowDefinitionHolder;
import org.springframework.webflow.definition.registry.FlowDefinitionResource;
import org.springframework.webflow.engine.builder.FlowAssembler;
import org.springframework.webflow.engine.builder.FlowBuilder;
import org.springframework.webflow.engine.builder.FlowRegistryFactoryBean;
import org.springframework.webflow.engine.builder.FlowServiceLocator;
import org.springframework.webflow.engine.builder.RefreshableFlowDefinitionHolder;
/**
* A flow definition registrar that populates a flow definition registry with flow definitions defined in externalized
* XML resources. Typically used in conjunction with a {@link XmlFlowRegistryFactoryBean} but may also be used
* standalone in programmatic fashion.
* XML resources. Typically used in conjunction with a {@link FlowRegistryFactoryBean} but may also be used stand-alone
* in programmatic fashion.
* <p>
* By default, a flow definition registered by this registrar will be assigned a registry identifier equal to the
* filename of the underlying definition resource, minus the filename extension. For example, a XML-based flow
* definition defined in the file "flow1.xml" will be identified as "flow1" when registered in a registry.
* By default, a flow definition added to this registrar with the {@link #addLocation(Resource, String)} method will be
* will be assigned a registry identifier equal to the filename of the underlying definition resource, minus the
* filename extension. For example, a XML-based flow definition defined in the file "flow1.xml" will be identified as
* "flow1" when registered in a registry.
* <p>
* Programmatic usage example:
*
@@ -43,59 +45,28 @@ import org.springframework.webflow.engine.builder.RefreshableFlowDefinitionHolde
* new DefaultFlowServiceLocator(registry, beanFactory);
* XmlFlowRegistrar registrar = new XmlFlowRegistrar(flowServiceLocator);
* File parent = new File(&quot;src/webapp/WEB-INF&quot;);
* registrar.addLocation(new FileSystemResource(new File(parent, &quot;flow1.xml&quot;));
* registrar.addLocation(new FileSystemResource(new File(parent, &quot;flow2.xml&quot;));
* registrar.add(new FileSystemResource(new File(parent, &quot;flow1.xml&quot;));
* registrar.add(new FileSystemResource(new File(parent, &quot;flow2.xml&quot;));
* registrar.registerFlowDefinitions(registry);
* </pre>
*
* @author Keith Donald
* @author Ben Hale
*/
public class XmlFlowRegistrar extends ExternalizedFlowDefinitionRegistrar {
/**
* The xml file suffix constant.
*/
private static final String XML_SUFFIX = ".xml";
/**
* The locator of services needed by flow definitions.
*/
private FlowServiceLocator flowServiceLocator;
/**
* The loader of XML-based flow definition documents.
*/
private DocumentLoader documentLoader;
/**
* Creates a new XML flow registrar. Protected constructor - if used, make sure the required
* {@link #flowServiceLocator} reference is set.
*/
protected XmlFlowRegistrar() {
}
/**
* Creates a new XML flow registrar.
* @param flowServiceLocator the locator needed to support flow definition assembly
*/
public XmlFlowRegistrar(FlowServiceLocator flowServiceLocator) {
setFlowServiceLocator(flowServiceLocator);
}
/**
* Sets the flow service locator.
* @param flowServiceLocator the flow service locator (may not be null)
*/
protected void setFlowServiceLocator(FlowServiceLocator flowServiceLocator) {
Assert.notNull(flowServiceLocator, "The flow service locator is required");
this.flowServiceLocator = flowServiceLocator;
}
/**
* Returns the flow service locator.
*/
protected FlowServiceLocator getFlowServiceLocator() {
return flowServiceLocator;
setFlowServiceLocator(flowServiceLocator);
}
/**
@@ -107,16 +78,7 @@ public class XmlFlowRegistrar extends ExternalizedFlowDefinitionRegistrar {
this.documentLoader = documentLoader;
}
/**
* Returns the loader of XML-based flow definition documents.
*/
public DocumentLoader getDocumentLoader() {
return documentLoader;
}
protected boolean isFlowDefinitionResource(Resource resource) {
return resource.getFilename().endsWith(XML_SUFFIX);
}
// hook methods
protected FlowDefinitionHolder createFlowDefinitionHolder(FlowDefinitionResource resource) {
FlowBuilder builder = createFlowBuilder(resource.getLocation());
@@ -124,8 +86,6 @@ public class XmlFlowRegistrar extends ExternalizedFlowDefinitionRegistrar {
return new RefreshableFlowDefinitionHolder(assembler);
}
// hook methods
/**
* Factory method that creates and fully initializes the XML-based flow definition builder.
* @param location the xml-based resource

View File

@@ -1,238 +0,0 @@
/*
* Copyright 2004-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.webflow.engine.builder.xml;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.webflow.core.collection.AttributeMap;
import org.springframework.webflow.core.collection.LocalAttributeMap;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
import org.springframework.webflow.definition.registry.FlowDefinitionResource;
import org.springframework.webflow.engine.builder.AbstractFlowBuildingFlowRegistryFactoryBean;
import org.springframework.webflow.engine.builder.DefaultFlowServiceLocator;
import org.springframework.webflow.engine.builder.FlowServiceLocator;
/**
* A factory bean that produces a populated flow registry using an {@link XmlFlowRegistrar}. This is the simplest
* implementation to use when using a Spring BeanFactory to deploy an explicit registry of XML-based Flow definitions
* for execution.
* <p>
* By default, a configured flow definition will be assigned a registry identifier equal to the filename of the
* underlying definition resource, minus the filename extension. For example, an XML-based flow definition defined in
* the file <code>flow1.xml</code> will be identified as <code>flow1</code> in the registry created by this factory
* bean.
* <p>
* This class is also <code>BeanFactoryAware</code> and when used with Spring will automatically create a configured
* {@link DefaultFlowServiceLocator} for loading Flow artifacts like Actions from the Spring bean factory during the
* Flow registration process.
* <p>
* This class is also <code>ResourceLoaderAware</code>; when an instance is created by a Spring BeanFactory the
* factory will automatically configure the XmlFlowRegistrar with a context-relative resource loader for accessing other
* resources during Flow assembly.
*
* Usage example:
*
* <pre>
* &lt;bean id=&quot;flowRegistry&quot; class=&quot;org.springframework.webflow.engine.builder.registry.XmlFlowRegistryFactoryBean&quot;&gt;
* &lt;property name=&quot;flowLocations&quot; value=&quot;/WEB-INF/flows/*-flow.xml&quot;/&gt;
* &lt;/bean&gt;
* </pre>
*
* @author Keith Donald
*/
public class XmlFlowRegistryFactoryBean extends AbstractFlowBuildingFlowRegistryFactoryBean {
/**
* The flow registrar that will perform the definition registrations.
*/
private XmlFlowRegistrar flowRegistrar = new XmlFlowRegistrar();
/**
* Temporary holder for flow definition locations.
*/
private Resource[] locations;
/**
* Temporary holder for flow definitions configured using a property map.
*/
private Properties flowDefinitions;
/**
* A map that contains a map (java.util.Map) of flow attributes keyed by flow id (String).
*/
private Map flowAttributes;
/**
* Returns the configured externalized XML flow registrar.
*/
public XmlFlowRegistrar getXmlFlowRegistrar() {
return flowRegistrar;
}
/**
* Set the configured externalized XML flow registrar.
* @since 1.0.1
*/
public void setXmlFlowRegistrar(XmlFlowRegistrar flowRegistrar) {
Assert.notNull(flowRegistrar, "The flowRegistrar is required");
this.flowRegistrar = flowRegistrar;
}
/**
* Sets the locations (resource file paths) pointing to XML-based flow definitions.
* <p>
* When configuring as a Spring bean definition, ANT-style resource patterns/wildcards are also supported, taking
* advantage of Spring's built in ResourceArrayPropertyEditor machinery.
* <p>
* For example:
*
* <pre>
* &lt;bean id=&quot;flowRegistry&quot; class=&quot;org.springframework.webflow.engine.builder.xml.XmlFlowRegistryFactoryBean&quot;&gt;
* &lt;property name=&quot;flowLocations&quot; value=&quot;/WEB-INF/flows/*-flow.xml&quot;/&gt;
* &lt;/bean&gt;
* </pre>
*
* Another example:
*
* <pre>
* &lt;bean id=&quot;flowRegistry&quot; class=&quot;org.springframework.webflow.engine.builder.xml.XmlFlowRegistryFactoryBean&quot;&gt;
* &lt;property name=&quot;flowLocations&quot; value=&quot;classpath*:/example/flows/*-flow.xml&quot;/&gt;
* &lt;/bean&gt;
* </pre>
*
* Flows registered from this set will be automatically assigned an id based on the filename of the matched XML
* resource.
* @param locations the resource locations
*/
public void setFlowLocations(Resource[] locations) {
this.locations = locations;
}
/**
* Convenience method for setting externalized flow definitions from a <code>java.util.Properties</code> map.
* Allows for more control over the definition, including which <code>flowId</code> is assigned.
* <p>
* Each property key is the <code>flowId</code> and each property value is the string encoded location of the
* externalized flow definition resource.
* <p>
* Here is the exact format:
*
* <pre>
* flowId = resource
* </pre>
*
* For example:
*
* <pre>
* &lt;bean id=&quot;flowRegistry&quot; class=&quot;org.springframework.webflow.engine.builder.xml.XmlFlowRegistryFactoryBean&quot;&gt;
* &lt;property name=&quot;flowDefinitions&quot;&gt;
* &lt;value&gt;
* searchFlow=/WEB-INF/flows/search-flow.xml
* detailFlow=/WEB-INF/flows/detail-flow.xml
* &lt;/value&gt;
* &lt;/property&gt;
* &lt;/bean&gt;
* </pre>
*
* @param flowDefinitions the flow definitions, defined within a properties map
*/
public void setFlowDefinitions(Properties flowDefinitions) {
this.flowDefinitions = flowDefinitions;
}
/**
* Sets flow attributes from an externalized <code>java.util.Map</code>. The keys in the map are String flow ids.
* The corresponding values should be <code>java.util.Map</code> maps containing flow attributes to be assigned to
* the flow. A flow with an id not contained in the provided map will get not externally defined flow attributes
* assigned.
* <p>
* Can be used in conjunction with both {@link #setFlowLocations(Resource[])} and
* {@link #setFlowDefinitions(Properties)}.
* @param flowAttributes the flow attributes, keyed by flow id
* @since 1.0.1
*/
public void setFlowAttributes(Map flowAttributes) {
this.flowAttributes = flowAttributes;
}
/**
* Sets the loader to load XML-based flow definition documents during flow definition assembly. Allows for
* customization over how flow definition documents are loaded. Optional.
* @param documentLoader the document loader
*/
public void setDocumentLoader(DocumentLoader documentLoader) {
getXmlFlowRegistrar().setDocumentLoader(documentLoader);
}
protected void init(FlowServiceLocator flowServiceLocator) {
// simply wire in the locator to the registrar
getXmlFlowRegistrar().setFlowServiceLocator(flowServiceLocator);
}
protected void doPopulate(FlowDefinitionRegistry registry) {
addFlowDefinitionLocations();
addFlowDefinitionsFromProperties();
getXmlFlowRegistrar().registerFlowDefinitions(registry);
}
/**
* Add configured flow definition locations to the flow definition registrar.
*/
private void addFlowDefinitionLocations() {
if (locations != null) {
for (int i = 0; i < locations.length; i++) {
String flowId = FlowDefinitionResource.conventionalFlowId(locations[i]);
getXmlFlowRegistrar().addResource(
new FlowDefinitionResource(flowId, locations[i], getFlowAttributes(flowId)));
}
}
}
/**
* Add flow definitions configured using a property map to the flow definition registrar.
*/
private void addFlowDefinitionsFromProperties() {
if (flowDefinitions != null) {
Iterator it = flowDefinitions.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String flowId = (String) entry.getKey();
String location = (String) entry.getValue();
Resource resource = getFlowServiceLocator().getResourceLoader().getResource(location);
getXmlFlowRegistrar().addResource(
new FlowDefinitionResource(flowId, resource, getFlowAttributes(flowId)));
}
}
}
/**
* Returns the flow attributes to be assigned to the flow with given id. Returns null if no attributes should be
* assigned.
*/
private AttributeMap getFlowAttributes(String flowId) {
if (flowAttributes != null) {
Map attributes = (Map) flowAttributes.get(flowId);
if (attributes != null) {
return new LocalAttributeMap(attributes);
}
}
return null;
}
}

View File

@@ -22,7 +22,7 @@ import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.webflow.executor.mvc.FlowController;
/**
* Test case that illustrates configuration of a FlowController and its associated artefacts using classic spring bean
* Test case that illustrates configuration of a FlowController and its associated artifacts using classic spring bean
* configuration information. This test case does not really test much but serves more as an example.
*
* @author Erwin Vervaet

View File

@@ -17,6 +17,7 @@ package org.springframework.webflow.config;
import junit.framework.TestCase;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.beans.factory.parsing.Problem;
import org.springframework.beans.factory.parsing.ProblemReporter;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
@@ -38,17 +39,12 @@ import org.springframework.webflow.executor.FlowExecutorImpl;
/**
* Unit tests for the WebFlowConfigNamespaceHandler and its BeanDefinitionParsers.
*
* @author Ben Hale
* @author Erwin Vervaet
* @author Christian Dupuis
*/
public class WebFlowConfigNamespaceHandlerTests extends TestCase {
private DefaultListableBeanFactory beanFactory;
protected void setUp() throws Exception {
super.setUp();
this.beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this.beanFactory);
reader.loadBeanDefinitions(new ClassPathResource(
@@ -58,16 +54,36 @@ public class WebFlowConfigNamespaceHandlerTests extends TestCase {
public void testRegistryWithPath() {
FlowDefinitionRegistry registry = (FlowDefinitionRegistry) this.beanFactory.getBean("withPath");
assertEquals("Incorrect number of flows loaded", 1, registry.getFlowDefinitionCount());
}
public void testRegistryWithoutPath() {
FlowDefinitionRegistry registry = (FlowDefinitionRegistry) this.beanFactory.getBean("withoutPath");
assertEquals("Incorrect number of flows loaded", 0, registry.getFlowDefinitionCount());
assertTrue("Incorrect flow name", registry.containsFlowDefinition("/flow1"));
}
public void testRegistryWithPathWithWildcards() {
FlowDefinitionRegistry registry = (FlowDefinitionRegistry) this.beanFactory.getBean("withPathWithWildcards");
assertEquals("Incorrect number of flows loaded", 0, registry.getFlowDefinitionCount());
assertEquals("Incorrect number of flows loaded", 3, registry.getFlowDefinitionCount());
assertTrue("Incorrect flow name", registry.containsFlowDefinition("/flow1"));
assertTrue("Incorrect flow name", registry.containsFlowDefinition("/flow2"));
}
public void testRegistryWithId() {
FlowDefinitionRegistry registry = (FlowDefinitionRegistry) this.beanFactory.getBean("withId");
assertEquals("Incorrect number of flows loaded", 1, registry.getFlowDefinitionCount());
assertTrue("Incorrect flow name", registry.containsFlowDefinition("/foo"));
}
public void testWithIdWithWildCards() {
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this.beanFactory);
try {
reader.loadBeanDefinitions(new ClassPathResource(
"org/springframework/webflow/config/webflow-config-namespace-bad.xml"));
fail("Should not have allowed id with wildcards");
} catch (BeanDefinitionParsingException e) {
}
}
public void testRegistryWithNamespace() {
FlowDefinitionRegistry registry = (FlowDefinitionRegistry) this.beanFactory.getBean("withNamespace");
assertEquals("Incorrect number of flows loaded", 1, registry.getFlowDefinitionCount());
assertTrue("Incorrect flow name", registry.containsFlowDefinition("namespace/flow1"));
}
public void testDefaultExecutor() {

View File

@@ -1,14 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<?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:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-2.0.xsd">
<!-- a sample flow registry -->
<bean id="flowRegistry" class="org.springframework.webflow.engine.builder.xml.XmlFlowRegistryFactoryBean">
<property name="flowLocations" value="classpath:test-flow.xml"/>
<bean id="flowRegistry" class="org.springframework.webflow.engine.builder.FlowRegistryFactoryBean">
<property name="xmlNamespaceFlowMappings">
<util:map key-type="java.lang.String">
<entry key="">
<util:set>
<bean class="org.springframework.webflow.definition.registry.FlowDefinitionResource">
<constructor-arg value="classpath:test-flow.xml" />
</bean>
</util:set>
</entry>
</util:map>
</property>
</bean>
<bean id="flowExecutor0" class="org.springframework.webflow.config.FlowExecutorFactoryBean">

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<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-1.0.xsd">
<start-state idref="start" />
<view-state id="start">
</view-state>
</flow>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<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-1.0.xsd">
<start-state idref="start" />
<view-state id="start">
</view-state>
</flow>

View File

@@ -1,104 +1,120 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
<?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:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<!-- a sample flow registry -->
<bean id="flowRegistry" class="org.springframework.webflow.engine.builder.xml.XmlFlowRegistryFactoryBean">
<property name="flowLocations" value="classpath:test-flow.xml"/>
</bean>
<!-- a sample flow execution listener and corresponding loader -->
<bean id="flowExecutionListener" class="org.springframework.webflow.execution.MockFlowExecutionListener"/>
<bean id="executionListenerLoader" class="org.springframework.webflow.execution.factory.ConditionalFlowExecutionListenerLoader">
<property name="listeners">
<map>
<entry key-ref="flowExecutionListener" value="*"/>
<entry key-ref="flowExecutionListener" value="test-flow"/>
<entry key-ref="flowExecutionListener">
<bean class="org.springframework.webflow.config.TestFlowExecutionListenerCriteria"/>
</entry>
</map>
</property>
</bean>
<!-- a sample conversation manager -->
<bean id="conversationManager" class="org.springframework.webflow.conversation.impl.SessionBindingConversationManager">
<property name="maxConversations" value="5"/>
</bean>
<!-- relatively simple flow controller definition using factory beans -->
<bean id="flowController" class="org.springframework.webflow.executor.mvc.FlowController">
<property name="flowExecutor" ref="flowExecutor"/>
<property name="defaultFlowId" value="test-flow"/>
</bean>
<bean id="flowExecutor" class="org.springframework.webflow.config.FlowExecutorFactoryBean">
<property name="definitionLocator" ref="flowRegistry"/>
<property name="executionAttributes">
<map>
<entry key="foo" value="bar"/>
</map>
</property>
<property name="executionListenerLoader" ref="executionListenerLoader"/>
<property name="repositoryType" value="SINGLEKEY"/>
<property name="conversationManager" ref="conversationManager"/>
<property name="defaults">
<bean class="org.springframework.webflow.config.FlowSystemDefaults">
<property name="alwaysRedirectOnPause" value="true"/>
</bean>
</property>
</bean>
<!-- very elaborate flow controller definition using straight low level bean definitions -->
<bean id="flowController-bean" class="org.springframework.webflow.executor.mvc.FlowController">
<property name="flowExecutor" ref="flowExecutor-bean"/>
<property name="defaultFlowId" value="test-flow"/>
</bean>
<bean id="flowExecutor-bean" class="org.springframework.webflow.executor.FlowExecutorImpl">
<constructor-arg ref="flowRegistry"/>
<constructor-arg>
<bean class="org.springframework.webflow.engine.impl.FlowExecutionImplFactory">
<property name="executionAttributesMap">
<map>
<entry key="foo" value="bar"/>
<entry key="alwaysRedirectOnPause">
<value type="java.lang.Boolean">true</value>
</entry>
</map>
</property>
<property name="executionListenerLoader" ref="executionListenerLoader"/>
</bean>
</constructor-arg>
<constructor-arg ref="executionRepository-bean"/>
</bean>
<bean id="executionRepository-bean" class="org.springframework.webflow.execution.repository.support.SimpleFlowExecutionRepository">
<constructor-arg>
<bean class="org.springframework.webflow.engine.impl.FlowExecutionImplStateRestorer">
<constructor-arg ref="flowRegistry"/>
<property name="executionAttributesMap">
<map>
<entry key="foo" value="bar"/>
<entry key="alwaysRedirectOnPause">
<value type="java.lang.Boolean">true</value>
</entry>
</map>
</property>
<property name="executionListenerLoader" ref="executionListenerLoader"/>
</bean>
</constructor-arg>
<constructor-arg ref="conversationManager"/>
<property name="alwaysGenerateNewNextKey" value="false"/>
</bean>
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-2.0.xsd">
<!-- a sample flow registry -->
<bean id="flowRegistry" class="org.springframework.webflow.engine.builder.FlowRegistryFactoryBean">
<property name="xmlNamespaceFlowMappings">
<util:map key-type="java.lang.String">
<entry key="">
<util:set>
<bean class="org.springframework.webflow.definition.registry.FlowDefinitionResource">
<constructor-arg value="classpath:test-flow.xml" />
</bean>
</util:set>
</entry>
</util:map>
</property>
</bean>
<!-- a sample flow execution listener and corresponding loader -->
<bean id="flowExecutionListener" class="org.springframework.webflow.execution.MockFlowExecutionListener" />
<bean id="executionListenerLoader"
class="org.springframework.webflow.execution.factory.ConditionalFlowExecutionListenerLoader">
<property name="listeners">
<map>
<entry key-ref="flowExecutionListener" value="*" />
<entry key-ref="flowExecutionListener" value="test-flow" />
<entry key-ref="flowExecutionListener">
<bean class="org.springframework.webflow.config.TestFlowExecutionListenerCriteria" />
</entry>
</map>
</property>
</bean>
<!-- a sample conversation manager -->
<bean id="conversationManager"
class="org.springframework.webflow.conversation.impl.SessionBindingConversationManager">
<property name="maxConversations" value="5" />
</bean>
<!-- relatively simple flow controller definition using factory beans -->
<bean id="flowController" class="org.springframework.webflow.executor.mvc.FlowController">
<property name="flowExecutor" ref="flowExecutor" />
<property name="defaultFlowId" value="test-flow" />
</bean>
<bean id="flowExecutor" class="org.springframework.webflow.config.FlowExecutorFactoryBean">
<property name="definitionLocator" ref="flowRegistry" />
<property name="executionAttributes">
<map>
<entry key="foo" value="bar" />
</map>
</property>
<property name="executionListenerLoader" ref="executionListenerLoader" />
<property name="repositoryType" value="SINGLEKEY" />
<property name="conversationManager" ref="conversationManager" />
<property name="defaults">
<bean class="org.springframework.webflow.config.FlowSystemDefaults">
<property name="alwaysRedirectOnPause" value="true" />
</bean>
</property>
</bean>
<!-- very elaborate flow controller definition using straight low level bean definitions -->
<bean id="flowController-bean" class="org.springframework.webflow.executor.mvc.FlowController">
<property name="flowExecutor" ref="flowExecutor-bean" />
<property name="defaultFlowId" value="test-flow" />
</bean>
<bean id="flowExecutor-bean" class="org.springframework.webflow.executor.FlowExecutorImpl">
<constructor-arg ref="flowRegistry" />
<constructor-arg>
<bean class="org.springframework.webflow.engine.impl.FlowExecutionImplFactory">
<property name="executionAttributesMap">
<map>
<entry key="foo" value="bar" />
<entry key="alwaysRedirectOnPause">
<value type="java.lang.Boolean">true</value>
</entry>
</map>
</property>
<property name="executionListenerLoader" ref="executionListenerLoader" />
</bean>
</constructor-arg>
<constructor-arg ref="executionRepository-bean" />
</bean>
<bean id="executionRepository-bean"
class="org.springframework.webflow.execution.repository.support.SimpleFlowExecutionRepository">
<constructor-arg>
<bean class="org.springframework.webflow.engine.impl.FlowExecutionImplStateRestorer">
<constructor-arg ref="flowRegistry" />
<property name="executionAttributesMap">
<map>
<entry key="foo" value="bar" />
<entry key="alwaysRedirectOnPause">
<value type="java.lang.Boolean">true</value>
</entry>
</map>
</property>
<property name="executionListenerLoader" ref="executionListenerLoader" />
</bean>
</constructor-arg>
<constructor-arg ref="conversationManager" />
<property name="alwaysGenerateNewNextKey" value="false" />
</bean>
</beans>

View File

@@ -0,0 +1,15 @@
<?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:flow="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/webflow-config
http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd">
<flow:registry id="withIdWithWildcards">
<flow:location id="foo" path="classpath*:org/springframework/webflow/config/flow*.xml"/>
</flow:registry>
</beans>

View File

@@ -6,19 +6,24 @@
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/webflow-config
http://www.springframework.org/schema/webflow-config/spring-webflow-config-1.0.xsd">
http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd">
<flow:registry id="withPath">
<flow:location path="classpath:org/springframework/webflow/registry/flow1.xml" />
</flow:registry>
<flow:registry id="withoutPath">
<flow:location path="" />
<flow:location path="classpath:org/springframework/webflow/config/flow1.xml" />
</flow:registry>
<flow:registry id="withPathWithWildcards">
<flow:location path="classpath*:org/springframework/webflow/engine/builder/registry/flow*.xml" />
<flow:location path="classpath*:org/springframework/webflow/engine/builder/registry/flows/**/flow*.xml" />
<flow:location path="classpath*:org/springframework/webflow/config/flow*.xml" />
</flow:registry>
<flow:registry id="withId">
<flow:location id="foo" path="classpath:org/springframework/webflow/config/flow1.xml"/>
</flow:registry>
<flow:registry id="withNamespace">
<flow:namespace name="namespace">
<flow:location path="classpath:org/springframework/webflow/config/flow1.xml"/>
</flow:namespace>
</flow:registry>
<flow:executor id="defaultExecutor" registry-ref="withPathWithWildcards"/>

View File

@@ -20,13 +20,17 @@ import java.io.ObjectOutputStream;
import junit.framework.TestCase;
import org.springframework.beans.factory.support.StaticListableBeanFactory;
import org.springframework.webflow.config.FlowExecutorFactoryBean;
import org.springframework.webflow.config.RepositoryType;
import org.springframework.webflow.core.collection.SharedAttributeMap;
import org.springframework.webflow.definition.registry.FlowDefinitionHolder;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistrar;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistryImpl;
import org.springframework.webflow.definition.registry.StaticFlowDefinitionHolder;
import org.springframework.webflow.engine.Flow;
import org.springframework.webflow.engine.builder.AbstractFlowBuilder;
import org.springframework.webflow.engine.builder.AbstractFlowBuilderFlowRegistryFactoryBean;
import org.springframework.webflow.engine.builder.FlowAssembler;
import org.springframework.webflow.engine.builder.FlowBuilderException;
import org.springframework.webflow.execution.support.ApplicationView;
import org.springframework.webflow.execution.support.FlowExecutionRedirect;
@@ -36,19 +40,16 @@ import org.springframework.webflow.test.MockExternalContext;
/**
* Test case that looks for the miminum conversation size.
*
* @author Erwin Vervaet
*/
public class ConversationSizeTests extends TestCase {
private SessionBindingConversationManager conversationManager;
private FlowExecutor flowExecutor;
protected void setUp() throws Exception {
AbstractFlowBuilderFlowRegistryFactoryBean flowRegistryFactory = new SizeTestFlowRegistry();
flowRegistryFactory.setBeanFactory(new StaticListableBeanFactory());
flowRegistryFactory.afterPropertiesSet();
FlowDefinitionRegistry flowRegistry = flowRegistryFactory.getRegistry();
FlowDefinitionRegistry flowRegistry = new FlowDefinitionRegistryImpl();
new SizeTestFlowRegistrar().registerFlowDefinitions(flowRegistry);
conversationManager = new SessionBindingConversationManager();
@@ -110,9 +111,6 @@ public class ConversationSizeTests extends TestCase {
oout.writeObject(obj);
oout.flush();
int objSize = bout.toByteArray().length;
// System.out.println(">>>> serialized size of '" + obj + "' is " + objSize);
return objSize;
}
@@ -123,9 +121,12 @@ public class ConversationSizeTests extends TestCase {
}
}
private static class SizeTestFlowRegistry extends AbstractFlowBuilderFlowRegistryFactoryBean {
protected void doPopulate(FlowDefinitionRegistry registry) {
registerFlowDefinition(registry, "size-test-flow", new SizeTestFlowBuilder());
private static class SizeTestFlowRegistrar implements FlowDefinitionRegistrar {
public void registerFlowDefinitions(FlowDefinitionRegistry registry) {
Flow flow = new FlowAssembler("size-test-flow", null, new SizeTestFlowBuilder()).assembleFlow();
FlowDefinitionHolder flowHolder = new StaticFlowDefinitionHolder(flow);
registry.registerFlowDefinition(flowHolder);
}
}
}

View File

@@ -17,32 +17,32 @@ package org.springframework.webflow.engine.builder;
import junit.framework.TestCase;
import org.springframework.beans.factory.support.StaticListableBeanFactory;
import org.springframework.webflow.core.collection.LocalAttributeMap;
import org.springframework.webflow.core.collection.MutableAttributeMap;
import org.springframework.webflow.definition.registry.FlowDefinitionHolder;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistrar;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistryImpl;
import org.springframework.webflow.definition.registry.StaticFlowDefinitionHolder;
import org.springframework.webflow.engine.Flow;
import org.springframework.webflow.execution.ViewSelection;
import org.springframework.webflow.execution.support.ApplicationView;
import org.springframework.webflow.test.MockFlowServiceLocator;
import org.springframework.webflow.test.MockRequestControlContext;
/**
* Test parameterization of flow built using an AbstractFlowBuilder when registering the flows with a
* FlowDefinitionRegistry.
*
* @author Erwin Vervaet
*/
public class AbstractFlowBuilderParameterizationTests extends TestCase {
private FlowDefinitionRegistry registry;
protected void setUp() throws Exception {
TestFlowRegistryFactoryBean registryFactory = new TestFlowRegistryFactoryBean();
StaticListableBeanFactory beanFactory = new StaticListableBeanFactory();
beanFactory.addBean("testAction", new ParameterizationTestAction());
registryFactory.setBeanFactory(beanFactory);
registryFactory.afterPropertiesSet();
registry = registryFactory.getRegistry();
registry = new FlowDefinitionRegistryImpl();
MockFlowServiceLocator flowServiceLocator = new MockFlowServiceLocator();
flowServiceLocator.registerBean("testAction", new ParameterizationTestAction());
new TestFlowRegistrar(flowServiceLocator).registerFlowDefinitions(registry);
}
public void testFlowParameterization() {
@@ -74,24 +74,40 @@ public class AbstractFlowBuilderParameterizationTests extends TestCase {
public class TestFlowBuilder extends AbstractFlowBuilder {
public TestFlowBuilder(FlowServiceLocator flowServiceLocator) {
setFlowServiceLocator(flowServiceLocator);
}
public void buildStates() throws FlowBuilderException {
addActionState("test", action("testAction"), transition(on(success()), to("finish")));
addEndState("finish", "${activeFlow.attributes['name']}");
}
}
public class TestFlowRegistryFactoryBean extends AbstractFlowBuilderFlowRegistryFactoryBean {
private class TestFlowRegistrar implements FlowDefinitionRegistrar {
protected void doPopulate(FlowDefinitionRegistry registry) {
MutableAttributeMap attributes = new LocalAttributeMap();
attributes.put("name", "A");
attributes.put("someKey", "someValue");
registerFlowDefinition(registry, "flowA", attributes, new TestFlowBuilder());
private FlowServiceLocator flowServiceLocator;
attributes = new LocalAttributeMap();
attributes.put("name", "B");
attributes.put("someOtherKey", "someOtherValue");
registerFlowDefinition(registry, "flowB", attributes, new TestFlowBuilder());
public TestFlowRegistrar(FlowServiceLocator flowServiceLocator) {
this.flowServiceLocator = flowServiceLocator;
}
public void registerFlowDefinitions(FlowDefinitionRegistry registry) {
MutableAttributeMap attributesA = new LocalAttributeMap();
attributesA.put("name", "A");
attributesA.put("someKey", "someValue");
Flow flowA = new FlowAssembler("flowA", attributesA, new TestFlowBuilder(flowServiceLocator))
.assembleFlow();
FlowDefinitionHolder flowHolderA = new StaticFlowDefinitionHolder(flowA);
registry.registerFlowDefinition(flowHolderA);
MutableAttributeMap attributesB = new LocalAttributeMap();
attributesB.put("name", "B");
attributesB.put("someOtherKey", "someOtherValue");
Flow flowB = new FlowAssembler("flowB", attributesB, new TestFlowBuilder(flowServiceLocator))
.assembleFlow();
FlowDefinitionHolder flowHolderB = new StaticFlowDefinitionHolder(flowB);
registry.registerFlowDefinition(flowHolderB);
}
}

View File

@@ -18,12 +18,14 @@ package org.springframework.webflow.engine.builder.xml;
import junit.framework.TestCase;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistryImpl;
import org.springframework.webflow.definition.registry.FlowDefinitionResource;
import org.springframework.webflow.engine.builder.BaseFlowServiceLocator;
public class XmlFlowRegistrarTests extends TestCase {
private XmlFlowRegistrar registrar;
private FlowDefinitionRegistry registry = new FlowDefinitionRegistryImpl();
@@ -33,19 +35,41 @@ public class XmlFlowRegistrarTests extends TestCase {
registrar = new XmlFlowRegistrar(locator);
}
public void testAddLocation() {
assertEquals(0, registry.getFlowDefinitionCount());
registrar.addLocation(new ClassPathResource("flow.xml", getClass()));
registrar.registerFlowDefinitions(registry);
assertEquals(1, registry.getFlowDefinitionCount());
assertEquals("flow", registry.getFlowDefinition("flow").getId());
}
public void testAddResource() {
assertEquals(0, registry.getFlowDefinitionCount());
registrar.addResource(new FlowDefinitionResource("foo", new ClassPathResource("flow.xml", getClass())));
registrar.addResource(new FlowDefinitionResource("foo", fromClassPath("flow.xml")), "namespace");
registrar.registerFlowDefinitions(registry);
assertEquals(1, registry.getFlowDefinitionCount());
assertEquals("foo", registry.getFlowDefinition("foo").getId());
assertEquals("foo", registry.getFlowDefinition("namespace/foo").getId());
}
public void testAddResourceDefaultNamespace() {
assertEquals(0, registry.getFlowDefinitionCount());
registrar.setDefaultNamespace("default");
registrar.addResource(new FlowDefinitionResource("foo", fromClassPath("flow.xml")));
registrar.registerFlowDefinitions(registry);
assertEquals(1, registry.getFlowDefinitionCount());
assertEquals("foo", registry.getFlowDefinition("default/foo").getId());
}
public void testAddLocation() {
assertEquals(0, registry.getFlowDefinitionCount());
registrar.addLocation(fromClassPath("flow.xml"), "namespace");
registrar.registerFlowDefinitions(registry);
assertEquals(1, registry.getFlowDefinitionCount());
assertEquals("flow", registry.getFlowDefinition("namespace/flow").getId());
}
public void testAddLocationDefaultNamespace() {
assertEquals(0, registry.getFlowDefinitionCount());
registrar.setDefaultNamespace("default");
registrar.addLocation(fromClassPath("flow.xml"));
registrar.registerFlowDefinitions(registry);
assertEquals(1, registry.getFlowDefinitionCount());
assertEquals("flow", registry.getFlowDefinition("default/flow").getId());
}
private Resource fromClassPath(String resourceName) {
return new ClassPathResource(resourceName, getClass());
}
}

View File

@@ -1,89 +0,0 @@
/*
* Copyright 2004-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.webflow.engine.builder.xml;
import junit.framework.TestCase;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
import org.springframework.webflow.engine.Flow;
import org.springframework.webflow.execution.ViewSelection;
import org.springframework.webflow.execution.support.ApplicationView;
import org.springframework.webflow.test.MockRequestControlContext;
/**
* Test flow parameterization using the XmlFlowRegistryFactoryBean.
*
* @author Erwin Vervaet
*/
public class XmlFlowRegistryFactoryBeanParameterizationTests extends TestCase {
private ApplicationContext applicationContext;
protected void setUp() throws Exception {
applicationContext = new ClassPathXmlApplicationContext("parameterizedFlowContext.xml", getClass());
}
public void testNoFlowParameterization() {
FlowDefinitionRegistry registry = (FlowDefinitionRegistry) applicationContext.getBean("flowRegistry0");
assertEquals(1, registry.getFlowDefinitionCount());
assertTrue(registry.containsFlowDefinition("parameterizedFlow"));
Flow parameterizedFlow = (Flow) registry.getFlowDefinition("parameterizedFlow");
assertEquals(0, parameterizedFlow.getAttributes().size());
assertNull(parameterizedFlow.getAttributes().get("foo"));
}
public void testSimpleFlowParameterization() {
FlowDefinitionRegistry registry = (FlowDefinitionRegistry) applicationContext.getBean("flowRegistry1");
assertEquals(1, registry.getFlowDefinitionCount());
assertTrue(registry.containsFlowDefinition("parameterizedFlow"));
Flow parameterizedFlow = (Flow) registry.getFlowDefinition("parameterizedFlow");
assertEquals(1, parameterizedFlow.getAttributes().size());
assertEquals("bar", parameterizedFlow.getAttributes().get("foo"));
}
public void testAdvancedParameterization() {
FlowDefinitionRegistry registry = (FlowDefinitionRegistry) applicationContext.getBean("flowRegistry2");
assertEquals(2, registry.getFlowDefinitionCount());
assertTrue(registry.containsFlowDefinition("flowA"));
Flow flowA = (Flow) registry.getFlowDefinition("flowA");
assertEquals(2, flowA.getAttributes().size());
assertEquals("A", flowA.getAttributes().get("name"));
assertEquals("someValue", flowA.getAttributes().get("someKey"));
assertNull(flowA.getAttributes().get("someOtherKey"));
assertTrue(registry.containsFlowDefinition("flowB"));
Flow flowB = (Flow) registry.getFlowDefinition("flowB");
assertEquals(2, flowB.getAttributes().size());
assertEquals("B", flowB.getAttributes().get("name"));
assertEquals("someOtherValue", flowB.getAttributes().get("someOtherKey"));
assertNull(flowB.getAttributes().get("someKey"));
}
public void testAdvancedParameterizationAtRuntime() {
FlowDefinitionRegistry registry = (FlowDefinitionRegistry) applicationContext.getBean("flowRegistry2");
Flow flowA = (Flow) registry.getFlowDefinition("flowA");
ViewSelection viewSelection = flowA.start(new MockRequestControlContext(flowA), null);
assertEquals("A", ((ApplicationView) viewSelection).getViewName());
Flow flowB = (Flow) registry.getFlowDefinition("flowB");
viewSelection = flowB.start(new MockRequestControlContext(flowB), null);
assertEquals("B", ((ApplicationView) viewSelection).getViewName());
}
}

View File

@@ -1,49 +0,0 @@
/*
* Copyright 2004-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.webflow.engine.builder.xml;
import java.util.Properties;
import junit.framework.TestCase;
import org.springframework.beans.factory.support.StaticListableBeanFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
public class XmlFlowRegistryFactoryBeanTests extends TestCase {
private XmlFlowRegistryFactoryBean factoryBean = new XmlFlowRegistryFactoryBean();
public void testCreateFromLocations() throws Exception {
ClassPathResource[] locations = new ClassPathResource[] { new ClassPathResource("flow.xml", getClass()) };
factoryBean.setFlowLocations(locations);
factoryBean.setBeanFactory(new StaticListableBeanFactory());
factoryBean.afterPropertiesSet();
FlowDefinitionRegistry registry = (FlowDefinitionRegistry) factoryBean.getObject();
assertEquals(1, registry.getFlowDefinitionCount());
assertEquals("flow", registry.getFlowDefinition("flow").getId());
}
public void testCreateFromDefinitions() throws Exception {
Properties properties = new Properties();
properties.put("foo", "classpath:/org/springframework/webflow/engine/builder/xml/flow.xml");
factoryBean.setFlowDefinitions(properties);
factoryBean.setBeanFactory(new StaticListableBeanFactory());
factoryBean.afterPropertiesSet();
FlowDefinitionRegistry registry = (FlowDefinitionRegistry) factoryBean.getObject();
assertEquals(1, registry.getFlowDefinitionCount());
assertEquals("foo", registry.getFlowDefinition("foo").getId());
}
}

View File

@@ -1,53 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="flowRegistry0"
class="org.springframework.webflow.engine.builder.xml.XmlFlowRegistryFactoryBean">
<property name="flowLocations" value="classpath:org/springframework/webflow/engine/builder/xml/parameterizedFlow.xml"/>
</bean>
<bean id="flowRegistry1"
class="org.springframework.webflow.engine.builder.xml.XmlFlowRegistryFactoryBean">
<property name="flowLocations" value="classpath:org/springframework/webflow/engine/builder/xml/parameterizedFlow.xml"/>
<property name="flowAttributes">
<map>
<entry key="parameterizedFlow">
<map>
<entry key="foo" value="bar"/>
</map>
</entry>
</map>
</property>
</bean>
<bean id="flowRegistry2"
class="org.springframework.webflow.engine.builder.xml.XmlFlowRegistryFactoryBean">
<property name="flowDefinitions">
<value>
flowA=classpath:org/springframework/webflow/engine/builder/xml/parameterizedFlow.xml
flowB=classpath:org/springframework/webflow/engine/builder/xml/parameterizedFlow.xml
</value>
</property>
<property name="flowAttributes">
<map>
<entry key="flowA">
<map>
<entry key="name" value="A"/>
<entry key="someKey" value="someValue"/>
</map>
</entry>
<entry key="flowB">
<map>
<entry key="name" value="B"/>
<entry key="someOtherKey" value="someOtherValue"/>
</map>
</entry>
</map>
</property>
</bean>
<bean id="testAction"
class="org.springframework.webflow.engine.builder.ParameterizationTestAction" />
</beans>

View File

@@ -1,27 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:flow="http://www.springframework.org/schema/webflow-config"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/webflow-config
http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd">
<bean id="executor" class="org.springframework.webflow.executor.FlowExecutorImpl">
<bean id="executor"
class="org.springframework.webflow.executor.FlowExecutorImpl">
<constructor-arg ref="registry" />
<constructor-arg ref="factory" />
<constructor-arg ref="repository" />
</bean>
<bean id="factory" class="org.springframework.webflow.engine.impl.FlowExecutionImplFactory" />
<bean id="factory"
class="org.springframework.webflow.engine.impl.FlowExecutionImplFactory" />
<bean id="stateRestorer" class="org.springframework.webflow.engine.impl.FlowExecutionImplStateRestorer">
<bean id="stateRestorer"
class="org.springframework.webflow.engine.impl.FlowExecutionImplStateRestorer">
<constructor-arg>
<ref bean="registry" />
</constructor-arg>
</bean>
<bean id="registry" class="org.springframework.webflow.engine.builder.xml.XmlFlowRegistryFactoryBean">
<property name="flowLocations" value="classpath:org/springframework/webflow/executor/flow.xml" />
</bean>
<flow:registry id="registry">
<flow:location
path="classpath:org/springframework/webflow/executor/flow.xml" />
</flow:registry>
<bean id="conversationManager" class="org.springframework.webflow.conversation.impl.SessionBindingConversationManager"/>
<bean id="conversationManager"
class="org.springframework.webflow.conversation.impl.SessionBindingConversationManager" />
</beans>