001: /*
002: * Copyright 2004-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.springframework.webflow.engine.builder.xml;
017:
018: import java.util.Iterator;
019: import java.util.Map;
020: import java.util.Properties;
021:
022: import org.springframework.core.io.Resource;
023: import org.springframework.util.Assert;
024: import org.springframework.webflow.core.collection.AttributeMap;
025: import org.springframework.webflow.core.collection.LocalAttributeMap;
026: import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
027: import org.springframework.webflow.definition.registry.FlowDefinitionResource;
028: import org.springframework.webflow.engine.builder.AbstractFlowBuildingFlowRegistryFactoryBean;
029: import org.springframework.webflow.engine.builder.DefaultFlowServiceLocator;
030: import org.springframework.webflow.engine.builder.FlowServiceLocator;
031:
032: /**
033: * A factory bean that produces a populated flow registry using an
034: * {@link XmlFlowRegistrar}. This is the simplest implementation to use when
035: * using a Spring BeanFactory to deploy an explicit registry of XML-based Flow
036: * definitions for execution.
037: * <p>
038: * By default, a configured flow definition will be assigned a registry
039: * identifier equal to the filename of the underlying definition resource, minus
040: * the filename extension. For example, an XML-based flow definition defined in
041: * the file <code>flow1.xml</code> will be identified as <code>flow1</code>
042: * in the registry created by this factory bean.
043: * <p>
044: * This class is also <code>BeanFactoryAware</code> and when used with Spring
045: * will automatically create a configured {@link DefaultFlowServiceLocator} for
046: * loading Flow artifacts like Actions from the Spring bean factory during the
047: * Flow registration process.
048: * <p>
049: * This class is also <code>ResourceLoaderAware</code>; when an instance is
050: * created by a Spring BeanFactory the factory will automatically configure the
051: * XmlFlowRegistrar with a context-relative resource loader for accessing other
052: * resources during Flow assembly.
053: *
054: * Usage example:
055: *
056: * <pre>
057: * <bean id="flowRegistry" class="org.springframework.webflow.engine.builder.registry.XmlFlowRegistryFactoryBean">
058: * <property name="flowLocations" value="/WEB-INF/flows/*-flow.xml"/>
059: * </bean>
060: * </pre>
061: *
062: * @author Keith Donald
063: */
064: public class XmlFlowRegistryFactoryBean extends
065: AbstractFlowBuildingFlowRegistryFactoryBean {
066:
067: /**
068: * The flow registrar that will perform the definition registrations.
069: */
070: private XmlFlowRegistrar flowRegistrar = new XmlFlowRegistrar();
071:
072: /**
073: * Temporary holder for flow definition locations.
074: */
075: private Resource[] locations;
076:
077: /**
078: * Temporary holder for flow definitions configured using a property map.
079: */
080: private Properties flowDefinitions;
081:
082: /**
083: * A map that contains a map (java.util.Map) of flow attributes keyed by flow id (String).
084: */
085: private Map flowAttributes;
086:
087: /**
088: * Returns the configured externalized XML flow registrar.
089: */
090: public XmlFlowRegistrar getXmlFlowRegistrar() {
091: return flowRegistrar;
092: }
093:
094: /**
095: * Set the configured externalized XML flow registrar.
096: * @since 1.0.1
097: */
098: public void setXmlFlowRegistrar(XmlFlowRegistrar flowRegistrar) {
099: Assert.notNull(flowRegistrar, "The flowRegistrar is required");
100: this .flowRegistrar = flowRegistrar;
101: }
102:
103: /**
104: * Sets the locations (resource file paths) pointing to XML-based flow
105: * definitions.
106: * <p>
107: * When configuring as a Spring bean definition, ANT-style resource
108: * patterns/wildcards are also supported, taking advantage of Spring's built
109: * in ResourceArrayPropertyEditor machinery.
110: * <p>
111: * For example:
112: *
113: * <pre>
114: * <bean id="flowRegistry" class="org.springframework.webflow.engine.builder.xml.XmlFlowRegistryFactoryBean">
115: * <property name="flowLocations" value="/WEB-INF/flows/*-flow.xml"/>
116: * </bean>
117: * </pre>
118: *
119: * Another example:
120: *
121: * <pre>
122: * <bean id="flowRegistry" class="org.springframework.webflow.engine.builder.xml.XmlFlowRegistryFactoryBean">
123: * <property name="flowLocations" value="classpath*:/example/flows/*-flow.xml"/>
124: * </bean>
125: * </pre>
126: *
127: * Flows registered from this set will be automatically assigned an id based
128: * on the filename of the matched XML resource.
129: * @param locations the resource locations
130: */
131: public void setFlowLocations(Resource[] locations) {
132: this .locations = locations;
133: }
134:
135: /**
136: * Convenience method for setting externalized flow definitions
137: * from a <code>java.util.Properties</code> map. Allows for more control
138: * over the definition, including which <code>flowId</code> is assigned.
139: * <p>
140: * Each property key is the <code>flowId</code> and each property value is
141: * the string encoded location of the externalized flow definition resource.
142: * <p>
143: * Here is the exact format:
144: *
145: * <pre>
146: * flowId=resource
147: * </pre>
148: *
149: * For example:
150: *
151: * <pre>
152: * <bean id="flowRegistry" class="org.springframework.webflow.engine.builder.xml.XmlFlowRegistryFactoryBean">
153: * <property name="flowDefinitions">
154: * <value>
155: * searchFlow=/WEB-INF/flows/search-flow.xml
156: * detailFlow=/WEB-INF/flows/detail-flow.xml
157: * </value>
158: * </property>
159: * </bean>
160: * </pre>
161: * @param flowDefinitions the flow definitions, defined within a properties
162: * map
163: */
164: public void setFlowDefinitions(Properties flowDefinitions) {
165: this .flowDefinitions = flowDefinitions;
166: }
167:
168: /**
169: * Sets flow attributes from an externalized <code>java.util.Map</code>. The keys in the
170: * map are String flow ids. The corresponding values should be <code>java.util.Map</code>
171: * maps containing flow attributes to be assigned to the flow. A flow with an id not
172: * contained in the provided map will get not externally defined flow attributes assigned.
173: * <p>
174: * Can be used in conjunction with both {@link #setFlowLocations(Resource[])}
175: * and {@link #setFlowDefinitions(Properties)}.
176: * @param flowAttributes the flow attributes, keyed by flow id
177: * @since 1.0.1
178: */
179: public void setFlowAttributes(Map flowAttributes) {
180: this .flowAttributes = flowAttributes;
181: }
182:
183: /**
184: * Sets the loader to load XML-based flow definition documents during flow
185: * definition assembly. Allows for customization over how flow definition
186: * documents are loaded. Optional.
187: * @param documentLoader the document loader
188: */
189: public void setDocumentLoader(DocumentLoader documentLoader) {
190: getXmlFlowRegistrar().setDocumentLoader(documentLoader);
191: }
192:
193: protected void init(FlowServiceLocator flowServiceLocator) {
194: // simply wire in the locator to the registrar
195: getXmlFlowRegistrar().setFlowServiceLocator(flowServiceLocator);
196: }
197:
198: protected void doPopulate(FlowDefinitionRegistry registry) {
199: addFlowDefinitionLocations();
200: addFlowDefinitionsFromProperties();
201: getXmlFlowRegistrar().registerFlowDefinitions(registry);
202: }
203:
204: /**
205: * Add configured flow definition locations to the flow definition
206: * registrar.
207: */
208: private void addFlowDefinitionLocations() {
209: if (locations != null) {
210: for (int i = 0; i < locations.length; i++) {
211: String flowId = FlowDefinitionResource
212: .conventionalFlowId(locations[i]);
213: getXmlFlowRegistrar()
214: .addResource(
215: new FlowDefinitionResource(flowId,
216: locations[i],
217: getFlowAttributes(flowId)));
218: }
219: }
220: }
221:
222: /**
223: * Add flow definitions configured using a property map to
224: * the flow definition registrar.
225: */
226: private void addFlowDefinitionsFromProperties() {
227: if (flowDefinitions != null) {
228: Iterator it = flowDefinitions.entrySet().iterator();
229: while (it.hasNext()) {
230: Map.Entry entry = (Map.Entry) it.next();
231: String flowId = (String) entry.getKey();
232: String location = (String) entry.getValue();
233: Resource resource = getFlowServiceLocator()
234: .getResourceLoader().getResource(location);
235: getXmlFlowRegistrar().addResource(
236: new FlowDefinitionResource(flowId, resource,
237: getFlowAttributes(flowId)));
238: }
239: }
240: }
241:
242: /**
243: * Returns the flow attributes to be assigned to the flow with given id. Returns
244: * null if no attributes should be assigned.
245: */
246: private AttributeMap getFlowAttributes(String flowId) {
247: if (flowAttributes != null) {
248: Map attributes = (Map) flowAttributes.get(flowId);
249: if (attributes != null) {
250: return new LocalAttributeMap(attributes);
251: }
252: }
253: return null;
254: }
255: }
|