diff --git a/spring-webflow/changelog.txt b/spring-webflow/changelog.txt index 3b8154c7..31792566 100644 --- a/spring-webflow/changelog.txt +++ b/spring-webflow/changelog.txt @@ -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 diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/XmlFlowRegistryFactoryBean.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/XmlFlowRegistryFactoryBean.java index 99f12f51..a0d5a12d 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/XmlFlowRegistryFactoryBean.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/XmlFlowRegistryFactoryBean.java @@ -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. *

* 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 flow1.xml will be identified as flow1 * in the registry created by this factory bean. *

@@ -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: * *

-	 *      flow id=resource
+	 *      flowId=resource
 	 * 
* * 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 java.util.Map. The keys in the + * map are String flow ids. The corresponding values should be java.util.Map + * 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. + *

+ * 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; + } } \ No newline at end of file diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/AbstractFlowBuilderParameterizationTests.java b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/AbstractFlowBuilderParameterizationTests.java index baf96a9e..5d7bed3b 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/AbstractFlowBuilderParameterizationTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/AbstractFlowBuilderParameterizationTests.java @@ -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) { diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/ParameterizationTestAction.java b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/ParameterizationTestAction.java new file mode 100644 index 00000000..67c3ecc2 --- /dev/null +++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/ParameterizationTestAction.java @@ -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(); + } + +} diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/XmlFlowRegistryFactoryBeanParameterizationTests.java b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/XmlFlowRegistryFactoryBeanParameterizationTests.java new file mode 100644 index 00000000..ec573d20 --- /dev/null +++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/XmlFlowRegistryFactoryBeanParameterizationTests.java @@ -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()); + } +} diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/parameterizedFlow.xml b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/parameterizedFlow.xml new file mode 100644 index 00000000..ae8367a6 --- /dev/null +++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/parameterizedFlow.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/parameterizedFlowContext.xml b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/parameterizedFlowContext.xml new file mode 100644 index 00000000..486d2137 --- /dev/null +++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/parameterizedFlowContext.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + flowA=classpath:org/springframework/webflow/engine/builder/xml/parameterizedFlow.xml + flowB=classpath:org/springframework/webflow/engine/builder/xml/parameterizedFlow.xml + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file