org.springframework.webflow.config.FlowDefinitionRegistryBuilder Maven / Gradle / Ivy
/*
* Copyright 2004-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.webflow.config;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.context.ApplicationContext;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
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.engine.builder.DefaultFlowHolder;
import org.springframework.webflow.engine.builder.FlowAssembler;
import org.springframework.webflow.engine.builder.FlowBuilder;
import org.springframework.webflow.engine.builder.FlowBuilderContext;
import org.springframework.webflow.engine.builder.model.FlowModelFlowBuilder;
import org.springframework.webflow.engine.builder.support.FlowBuilderContextImpl;
import org.springframework.webflow.engine.builder.support.FlowBuilderServices;
import org.springframework.webflow.engine.model.builder.DefaultFlowModelHolder;
import org.springframework.webflow.engine.model.builder.FlowModelBuilder;
import org.springframework.webflow.engine.model.builder.xml.XmlFlowModelBuilder;
import org.springframework.webflow.engine.model.registry.FlowModelHolder;
/**
* A builder for creating {@link FlowDefinitionRegistry} instances designed for programmatic
* use in {@code @Bean} factory methods. For XML configuration consider using the
* {@code webflow-config} XML namespace.
*
* @author Rossen Stoyanchev
* @since 2.4
*/
public class FlowDefinitionRegistryBuilder {
private final List flowLocations = new ArrayList();
private final List flowLocationPatterns = new ArrayList();
private final List flowBuilderInfos = new ArrayList();
private FlowBuilderServices flowBuilderServices;
private FlowDefinitionRegistry parent;
private FlowDefinitionResourceFactory flowResourceFactory;
/**
* Create a new instance with the given ApplicationContext.
*
* @param appContext the ApplicationContext to use for initializing the
* FlowDefinitionResourceFactory and FlowBuilderServices instances with
*/
public FlowDefinitionRegistryBuilder(ApplicationContext appContext) {
this(appContext, null);
}
/**
* Create a new instance with the given ApplicationContext and {@link FlowBuilderServices}.
*
* @param appContext the ApplicationContext to use for initializing the
* FlowDefinitionResourceFactory and FlowBuilderServices instances with
* @param builderServices a {@link FlowBuilderServices} instance to configure
* on the FlowDefinitionRegistry
*/
public FlowDefinitionRegistryBuilder(ApplicationContext appContext, FlowBuilderServices builderServices) {
Assert.notNull(appContext, "applicationContext is required");
this.flowResourceFactory = new FlowDefinitionResourceFactory(appContext);
if (builderServices != null) {
this.flowBuilderServices = builderServices;
}
else {
this.flowBuilderServices = new FlowBuilderServicesBuilder(appContext).build();
this.flowBuilderServices.setApplicationContext(appContext);
}
}
/**
* Configure the base path where flow definitions are found. When specified, all
* flow locations are relative to this path. Also when specified, by default flows
* are assigned an id equal to the the path segment between their base path and
* file name.
*
* For example, if a flow definition is located at
* '/WEB-INF/hotels/booking/booking-flow.xml' and the base path is '/WEB-INF', the
* remaining path to this flow is 'hotels/booking' which then becomes the flow id.
*
* If a flow definition is found directly on the base path, the file name minus
* its extension is used as the flow id.
* @param basePath the base path to use
*/
public FlowDefinitionRegistryBuilder setBasePath(String basePath) {
if (basePath != null) {
this.flowResourceFactory.setBasePath(basePath);
}
return this;
}
/**
* Register a flow defined at the following location as an .xml file.
* This may be a path to a single resource or a ANT-style path expression that
* matches multiple resources.
* @param path the resource path to the externalized flow definition resource.
*/
public FlowDefinitionRegistryBuilder addFlowLocation(String path) {
this.addFlowLocation(path, null, null);
return this;
}
/**
* Register a flow defined at the following location as an .xml file.
* This may be a path to a single resource or a ANT-style path expression that
* matches multiple resources.
* @param path the resource path to the externalized flow definition resource.
* @param id the unique id to assign to the added flow definition in the registry
* Specify only if you wish to provide a custom flow definition identifier.
*/
public FlowDefinitionRegistryBuilder addFlowLocation(String path, String id) {
this.flowLocations.add(new FlowLocation(path, id, null));
return this;
}
/**
* Register a flow defined at the following location as an .xml file.
* This may be a path to a single resource or a ANT-style path expression that
* matches multiple resources.
* @param path the resource path to the externalized flow definition resource.
* @param id the unique id to assign to the added flow definition in the registry
* Specify only if you wish to provide a custom flow definition identifier.
* @param attributes meta-attributes to assign to the flow definition
*/
public FlowDefinitionRegistryBuilder addFlowLocation(String path, String id, Map attributes) {
this.flowLocations.add(new FlowLocation(path, id, attributes));
return this;
}
/**
* Registers a set of flows resolved from a resource location pattern.
* @param pattern the pattern to use
*/
public FlowDefinitionRegistryBuilder addFlowLocationPattern(String pattern) {
this.flowLocationPatterns.add(pattern);
return this;
}
/**
* Set the {@link FlowBuilderServices} to use for defining custom services needed
* to build the flows registered in this registry.
* @param flowBuilderServices the {@link FlowBuilderServices} instance
*/
public FlowDefinitionRegistryBuilder setFlowBuilderServices(FlowBuilderServices flowBuilderServices) {
this.flowBuilderServices = flowBuilderServices;
return this;
}
/**
* Register a custom {@link FlowBuilder} instance.
* @param builder the FlowBuilder to configure
*/
public FlowDefinitionRegistryBuilder addFlowBuilder(FlowBuilder builder) {
addFlowBuilder(builder, null, null);
return this;
}
/**
* Register a custom {@link FlowBuilder} instance with the given flow id.
* @param builder the FlowBuilder to configure
* @param id the id assign to the flow definition in this registry.
* Specify when you wish to provide a custom flow definition identifier.
*/
public FlowDefinitionRegistryBuilder addFlowBuilder(FlowBuilder builder, String id) {
addFlowBuilder(builder, id, null);
return this;
}
/**
* Register a custom {@link FlowBuilder} instance with the given flow id.
* @param builder the FlowBuilder to configure
* @param id the id assign to the flow definition in this registry.
* Specify when you wish to provide a custom flow definition identifier.
* @param attributes attributes to assign to the flow definition.
*/
public FlowDefinitionRegistryBuilder addFlowBuilder(FlowBuilder builder, String id, Map attributes) {
if (!StringUtils.hasText(id)) {
id = StringUtils.uncapitalize(StringUtils.delete(
ClassUtils.getShortName(builder.getClass()), "FlowBuilder"));
}
this.flowBuilderInfos.add(new FlowBuilderInfo(builder, id, attributes));
return this;
}
/**
* Configure a parent registry. Registries can be organized in a hierarchy.
* If a child registry does not contain a flow, its parent registry is queried.
* @param parent the parent registry
*/
public FlowDefinitionRegistryBuilder setParent(FlowDefinitionRegistry parent) {
this.parent = parent;
return this;
}
/**
* Create and return a {@link FlowDefinitionRegistry} instance.
*/
public FlowDefinitionRegistry build() {
DefaultFlowRegistry flowRegistry = new DefaultFlowRegistry();
flowRegistry.setParent(this.parent);
registerFlowLocations(flowRegistry);
registerFlowLocationPatterns(flowRegistry);
registerFlowBuilders(flowRegistry);
return flowRegistry;
}
private void registerFlowLocations(DefaultFlowRegistry flowRegistry) {
for (FlowLocation location : this.flowLocations) {
String path = location.getPath();
String id = location.getId();
AttributeMap