Added setFlowAttributes method to XmlFlowRegistryFactoryBean to make it easier to configure externally defined flow definition attributes (SWF-200)

This commit is contained in:
Erwin Vervaet
2006-12-27 16:15:15 +00:00
parent bf951c7e35
commit fe1efa591a
7 changed files with 273 additions and 41 deletions

View File

@@ -49,6 +49,8 @@ Package org.springframework.webflow.engine
* TransitionExecutingStateExceptionHandler now does full logging of the exceptions that it handles.
* Fixed bug in BaseFlowServiceLocator where it was not allowing user to override the default web flow
converters (SWF-220).
* Added setFlowAttributes method to XmlFlowRegistryFactoryBean to make it easier to configure
externally defined flow definition attributes (SWF-200).
Package org.springframework.webflow.execution.factory
* The getHolder method on ConditionalFlowExecutionListenerLoader is now private. It has a package

View File

@@ -15,13 +15,13 @@
*/
package org.springframework.webflow.engine.builder.xml;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.springframework.core.io.Resource;
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;
@@ -29,14 +29,14 @@ import org.springframework.webflow.engine.builder.DefaultFlowServiceLocator;
import org.springframework.webflow.engine.builder.FlowServiceLocator;
/**
* A factory bean that produces a populated flow registry using a
* 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, a XML-based flow definition defined in
* 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>
@@ -66,11 +66,21 @@ public class XmlFlowRegistryFactoryBean extends AbstractFlowBuildingFlowRegistry
* 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.
@@ -108,7 +118,7 @@ public class XmlFlowRegistryFactoryBean extends AbstractFlowBuildingFlowRegistry
* @param locations the resource locations
*/
public void setFlowLocations(Resource[] locations) {
getXmlFlowRegistrar().setLocations(locations);
this.locations = locations;
}
/**
@@ -122,7 +132,7 @@ public class XmlFlowRegistryFactoryBean extends AbstractFlowBuildingFlowRegistry
* Here is the exact format:
*
* <pre>
* flow id=resource
* flowId=resource
* </pre>
*
* For example:
@@ -143,6 +153,20 @@ public class XmlFlowRegistryFactoryBean extends AbstractFlowBuildingFlowRegistry
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
*/
public void setFlowAttributes(Map flowAttributes) {
this.flowAttributes = flowAttributes;
}
/**
* Sets the loader to load XML-based flow definition documents during flow
@@ -160,29 +184,54 @@ public class XmlFlowRegistryFactoryBean extends AbstractFlowBuildingFlowRegistry
}
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 && flowDefinitions.size() > 0) {
List flows = new ArrayList(flowDefinitions.size());
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);
flows.add(new FlowDefinitionResource(flowId, resource));
getXmlFlowRegistrar().addResource(
new FlowDefinitionResource(flowId, resource, getFlowAttributes(flowId)));
}
getXmlFlowRegistrar().addResources(
(FlowDefinitionResource[])flows.toArray(new FlowDefinitionResource[flows.size()]));
// cleanup
flowDefinitions = null;
}
}
/**
* 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

@@ -18,7 +18,6 @@ package org.springframework.webflow.engine.builder;
import junit.framework.TestCase;
import org.springframework.beans.factory.support.StaticListableBeanFactory;
import org.springframework.webflow.action.AbstractAction;
import org.springframework.webflow.core.collection.AttributeMap;
import org.springframework.webflow.core.collection.LocalAttributeMap;
import org.springframework.webflow.core.collection.MutableAttributeMap;
@@ -26,8 +25,6 @@ import org.springframework.webflow.definition.FlowDefinition;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
import org.springframework.webflow.definition.registry.StaticFlowDefinitionHolder;
import org.springframework.webflow.engine.Flow;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;
import org.springframework.webflow.execution.ViewSelection;
import org.springframework.webflow.execution.support.ApplicationView;
import org.springframework.webflow.test.MockRequestControlContext;
@@ -45,7 +42,7 @@ public class AbstractFlowBuilderParameterizationTests extends TestCase {
protected void setUp() throws Exception {
TestFlowRegistryFactoryBean registryFactory = new TestFlowRegistryFactoryBean();
StaticListableBeanFactory beanFactory = new StaticListableBeanFactory();
beanFactory.addBean("testAction", new TestAction());
beanFactory.addBean("testAction", new ParameterizationTestAction());
registryFactory.setBeanFactory(beanFactory);
registryFactory.afterPropertiesSet();
registry = registryFactory.getRegistry();
@@ -90,30 +87,6 @@ public class AbstractFlowBuilderParameterizationTests extends TestCase {
}
}
public class TestAction extends AbstractAction {
protected Event doExecute(RequestContext context) throws Exception {
if ("flowA".equals(context.getActiveFlow().getId())) {
Flow flowA = (Flow)context.getActiveFlow();
assertEquals(2, flowA.getAttributes().size());
assertEquals("A", flowA.getAttributes().get("name"));
assertEquals("someValue", flowA.getAttributes().get("someKey"));
assertNull(flowA.getAttributes().get("someOtherKey"));
}
else if ("flowB".equals(context.getActiveFlow().getId())) {
Flow flowB = (Flow)context.getActiveFlow();
assertEquals(2, flowB.getAttributes().size());
assertEquals("B", flowB.getAttributes().get("name"));
assertEquals("someOtherValue", flowB.getAttributes().get("someOtherKey"));
assertNull(flowB.getAttributes().get("someKey"));
}
else {
throw new IllegalStateException();
}
return success();
}
}
public class TestFlowRegistryFactoryBean extends AbstractFlowBuildingFlowRegistryFactoryBean {
protected void doPopulate(FlowDefinitionRegistry registry) {

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2002-2006 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 junit.framework.Assert;
import org.springframework.webflow.action.AbstractAction;
import org.springframework.webflow.engine.Flow;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;
/**
* Test action used by some unit tests.
*
* @author Erwin Vervaet
*/
public class ParameterizationTestAction extends AbstractAction {
protected Event doExecute(RequestContext context) throws Exception {
if ("flowA".equals(context.getActiveFlow().getId())) {
Flow flowA = (Flow)context.getActiveFlow();
Assert.assertEquals(2, flowA.getAttributes().size());
Assert.assertEquals("A", flowA.getAttributes().get("name"));
Assert.assertEquals("someValue", flowA.getAttributes().get("someKey"));
Assert.assertNull(flowA.getAttributes().get("someOtherKey"));
}
else if ("flowB".equals(context.getActiveFlow().getId())) {
Flow flowB = (Flow)context.getActiveFlow();
Assert.assertEquals(2, flowB.getAttributes().size());
Assert.assertEquals("B", flowB.getAttributes().get("name"));
Assert.assertEquals("someOtherValue", flowB.getAttributes().get("someOtherKey"));
Assert.assertNull(flowB.getAttributes().get("someKey"));
}
else {
throw new IllegalStateException();
}
return success();
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright 2002-2006 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

@@ -0,0 +1,13 @@
<?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="test" />
<action-state id="test">
<action bean="testAction" />
<transition on="success" to="finish" />
</action-state>
<end-state id="finish" view="${activeFlow.attributes['name']}"/>
</flow>

View File

@@ -0,0 +1,53 @@
<?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>