SWF-94 Add support for Flow Inheritance

This commit is contained in:
Scott Andrews
2008-03-21 01:08:39 +00:00
parent 34907cd524
commit 1a6cf956ee
95 changed files with 9617 additions and 2457 deletions

View File

@@ -19,10 +19,15 @@ import org.springframework.webflow.definition.registry.FlowDefinitionRegistryImp
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.FlowModelFlowBuilder;
import org.springframework.webflow.engine.builder.RefreshableFlowDefinitionHolder;
import org.springframework.webflow.engine.builder.support.FlowBuilderContextImpl;
import org.springframework.webflow.engine.builder.support.FlowBuilderServices;
import org.springframework.webflow.engine.builder.xml.XmlFlowBuilder;
import org.springframework.webflow.engine.model.builder.FlowModelBuilder;
import org.springframework.webflow.engine.model.builder.xml.XmlFlowModelBuilder;
import org.springframework.webflow.engine.model.registry.DefaultFlowModelHolder;
import org.springframework.webflow.engine.model.registry.FlowModelHolder;
import org.springframework.webflow.engine.model.registry.FlowModelRegistryImpl;
/**
* A factory for a flow definition registry. Is a Spring FactoryBean, for provision by the flow definition registry bean
@@ -39,6 +44,11 @@ class FlowRegistryFactoryBean implements FactoryBean, InitializingBean {
*/
private FlowDefinitionRegistryImpl flowRegistry;
/**
* The model registry produced by this factory bean.
*/
private FlowModelRegistryImpl flowModelRegistry;
/**
* Flow definitions defined in external files that should be registered in the registry produced by this factory
* bean.
@@ -76,6 +86,7 @@ class FlowRegistryFactoryBean implements FactoryBean, InitializingBean {
public void afterPropertiesSet() throws Exception {
flowResourceFactory = new FlowDefinitionResourceFactory(flowBuilderServices.getResourceLoader());
flowRegistry = new FlowDefinitionRegistryImpl();
flowModelRegistry = new FlowModelRegistryImpl();
registerFlowLocations();
registerFlowBuilders();
}
@@ -137,8 +148,18 @@ class FlowRegistryFactoryBean implements FactoryBean, InitializingBean {
}
private FlowBuilder createFlowBuilder(FlowDefinitionResource resource) {
return new FlowModelFlowBuilder(createFlowModelHolder(resource), resource.getPath());
}
private FlowModelHolder createFlowModelHolder(FlowDefinitionResource resource) {
FlowModelHolder modelHolder = new DefaultFlowModelHolder(createFlowModelBuilder(resource), resource.getId());
flowModelRegistry.registerFlowModel(modelHolder);
return modelHolder;
}
private FlowModelBuilder createFlowModelBuilder(FlowDefinitionResource resource) {
if (isXml(resource.getPath())) {
return new XmlFlowBuilder(resource.getPath());
return new XmlFlowModelBuilder(resource.getPath(), flowModelRegistry);
} else {
throw new IllegalArgumentException(resource
+ " is not a supported resource type; supported types are [.xml]");

View File

@@ -23,11 +23,13 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.core.style.ToStringCreator;
import org.springframework.util.Assert;
import org.springframework.webflow.definition.FlowDefinition;
import org.springframework.webflow.engine.model.registry.FlowModelConstructionException;
/**
* A generic registry implementation for housing one or more flow definitions.
*
* @author Keith Donald
* @author Scott Andrews
*/
public class FlowDefinitionRegistryImpl implements FlowDefinitionRegistry {
@@ -65,6 +67,8 @@ public class FlowDefinitionRegistryImpl implements FlowDefinitionRegistry {
return parent.getFlowDefinition(id);
}
throw e;
} catch (FlowModelConstructionException e) {
throw new FlowDefinitionConstructionException(e.getMessage(), e);
}
}

View File

@@ -0,0 +1,877 @@
package org.springframework.webflow.engine.builder;
import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.binding.convert.ConversionException;
import org.springframework.binding.convert.ConversionExecutor;
import org.springframework.binding.convert.support.RuntimeBindingConversionExecutor;
import org.springframework.binding.expression.EvaluationException;
import org.springframework.binding.expression.Expression;
import org.springframework.binding.expression.ExpressionParser;
import org.springframework.binding.expression.ParserContext;
import org.springframework.binding.expression.support.ParserContextImpl;
import org.springframework.binding.mapping.Mapper;
import org.springframework.binding.mapping.impl.DefaultMapper;
import org.springframework.binding.mapping.impl.DefaultMapping;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.style.ToStringCreator;
import org.springframework.util.StringUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.GenericWebApplicationContext;
import org.springframework.webflow.action.ActionResultExposer;
import org.springframework.webflow.action.EvaluateAction;
import org.springframework.webflow.action.ExternalRedirectAction;
import org.springframework.webflow.action.FlowDefinitionRedirectAction;
import org.springframework.webflow.action.RenderAction;
import org.springframework.webflow.action.SetAction;
import org.springframework.webflow.action.ViewFactoryActionAdapter;
import org.springframework.webflow.core.collection.AttributeMap;
import org.springframework.webflow.core.collection.LocalAttributeMap;
import org.springframework.webflow.core.collection.MutableAttributeMap;
import org.springframework.webflow.definition.registry.FlowDefinitionLocator;
import org.springframework.webflow.engine.Flow;
import org.springframework.webflow.engine.FlowExecutionExceptionHandler;
import org.springframework.webflow.engine.FlowVariable;
import org.springframework.webflow.engine.SubflowAttributeMapper;
import org.springframework.webflow.engine.TargetStateResolver;
import org.springframework.webflow.engine.Transition;
import org.springframework.webflow.engine.TransitionCriteria;
import org.springframework.webflow.engine.VariableValueFactory;
import org.springframework.webflow.engine.ViewVariable;
import org.springframework.webflow.engine.builder.support.AbstractFlowBuilder;
import org.springframework.webflow.engine.builder.support.ActionExecutingViewFactory;
import org.springframework.webflow.engine.model.AbstractActionModel;
import org.springframework.webflow.engine.model.AbstractMappingModel;
import org.springframework.webflow.engine.model.AbstractStateModel;
import org.springframework.webflow.engine.model.ActionStateModel;
import org.springframework.webflow.engine.model.AttributeModel;
import org.springframework.webflow.engine.model.BeanImportModel;
import org.springframework.webflow.engine.model.DecisionStateModel;
import org.springframework.webflow.engine.model.EndStateModel;
import org.springframework.webflow.engine.model.EvaluateModel;
import org.springframework.webflow.engine.model.ExceptionHandlerModel;
import org.springframework.webflow.engine.model.FlowModel;
import org.springframework.webflow.engine.model.IfModel;
import org.springframework.webflow.engine.model.InputModel;
import org.springframework.webflow.engine.model.OutputModel;
import org.springframework.webflow.engine.model.PersistenceContextModel;
import org.springframework.webflow.engine.model.RenderModel;
import org.springframework.webflow.engine.model.SecuredModel;
import org.springframework.webflow.engine.model.SetModel;
import org.springframework.webflow.engine.model.SubflowStateModel;
import org.springframework.webflow.engine.model.TransitionModel;
import org.springframework.webflow.engine.model.VarModel;
import org.springframework.webflow.engine.model.ViewStateModel;
import org.springframework.webflow.engine.model.registry.FlowModelHolder;
import org.springframework.webflow.engine.support.BeanFactoryVariableValueFactory;
import org.springframework.webflow.engine.support.DefaultTransitionCriteria;
import org.springframework.webflow.engine.support.GenericSubflowAttributeMapper;
import org.springframework.webflow.engine.support.TransitionCriteriaChain;
import org.springframework.webflow.engine.support.TransitionExecutingFlowExecutionExceptionHandler;
import org.springframework.webflow.execution.Action;
import org.springframework.webflow.execution.RequestContext;
import org.springframework.webflow.execution.ScopeType;
import org.springframework.webflow.execution.ViewFactory;
import org.springframework.webflow.security.SecurityRule;
import org.springframework.webflow.util.ResourceHolder;
public class FlowModelFlowBuilder extends AbstractFlowBuilder implements ResourceHolder {
private FlowModelHolder flowModelHolder;
private FlowModel flowModel;
private LocalFlowBuilderContext localFlowBuilderContext;
private Resource resource;
public FlowModelFlowBuilder(FlowModelHolder flowModelHolder) {
this.flowModelHolder = flowModelHolder;
}
public FlowModelFlowBuilder(FlowModelHolder flowModelHolder, Resource resource) {
this.flowModelHolder = flowModelHolder;
this.resource = resource;
}
/**
* Initialize this builder. This could cause the builder to open a stream to an externalized resource representing
* the flow definition, for example.
* @throws FlowBuilderException an exception occurred building the flow
*/
public void doInit() throws FlowBuilderException {
flowModel = flowModelHolder.getFlowModel();
initLocalFlowContext();
}
/**
* Builds any variables initialized by the flow when it starts.
* @throws FlowBuilderException an exception occurred building the flow
*/
public void buildVariables() throws FlowBuilderException {
if (flowModel.getVars() != null) {
for (Iterator varIt = flowModel.getVars().iterator(); varIt.hasNext();) {
getFlow().addVariable(convertFlowVariable((VarModel) varIt.next()));
}
}
}
/**
* Builds the input mapper responsible for mapping flow input on start.
* @throws FlowBuilderException an exception occurred building the flow
*/
public void buildInputMapper() throws FlowBuilderException {
if (flowModel.getInputs() != null) {
getFlow().setInputMapper(convertFlowInputMapper(flowModel.getInputs()));
}
}
/**
* Builds any start actions to execute when the flow starts.
* @throws FlowBuilderException an exception occurred building the flow
*/
public void buildStartActions() throws FlowBuilderException {
if (flowModel.getOnStartActions() != null) {
getFlow().getStartActionList().addAll(convertActions(flowModel.getOnStartActions()));
}
}
/**
* Builds the states of the flow.
* @throws FlowBuilderException an exception occurred building the flow
*/
public void buildStates() throws FlowBuilderException {
if (flowModel.getStates() == null) {
throw new FlowBuilderException("At least one state is required to build a flow definition");
}
for (Iterator stateIt = flowModel.getStates().iterator(); stateIt.hasNext();) {
AbstractStateModel state = (AbstractStateModel) stateIt.next();
if (state instanceof ActionStateModel) {
convertActionState((ActionStateModel) state, getFlow());
} else if (state instanceof ViewStateModel) {
convertViewState((ViewStateModel) state, getFlow());
} else if (state instanceof DecisionStateModel) {
convertDecisionState((DecisionStateModel) state, getFlow());
} else if (state instanceof SubflowStateModel) {
convertSubflowState((SubflowStateModel) state, getFlow());
} else if (state instanceof EndStateModel) {
convertEndState((EndStateModel) state, getFlow());
}
}
if (flowModel.getStartStateId() != null) {
getFlow().setStartState(flowModel.getStartStateId());
} else {
// default to the identifier of the first state in the flow model
getFlow().setStartState(((AbstractStateModel) flowModel.getStates().get(0)).getId());
}
}
/**
* Builds any transitions shared by all states of the flow.
* @throws FlowBuilderException an exception occurred building the flow
*/
public void buildGlobalTransitions() throws FlowBuilderException {
if (flowModel.getGlobalTransitions() != null) {
getFlow().getGlobalTransitionSet().addAll(convertTransitions(flowModel.getGlobalTransitions()));
}
}
/**
* Builds any end actions to execute when the flow ends.
* @throws FlowBuilderException an exception occurred building the flow
*/
public void buildEndActions() throws FlowBuilderException {
if (flowModel.getOnEndActions() != null) {
getFlow().getEndActionList().addAll(convertActions(flowModel.getOnEndActions()));
}
}
/**
* Builds the output mapper responsible for mapping flow output on end.
* @throws FlowBuilderException an exception occurred building the flow
*/
public void buildOutputMapper() throws FlowBuilderException {
if (flowModel.getOutputs() != null) {
getFlow().setOutputMapper(convertFlowOutputMapper(flowModel.getOutputs()));
}
}
/**
* Creates and adds all exception handlers to the flow built by this builder.
* @throws FlowBuilderException an exception occurred building this flow
*/
public void buildExceptionHandlers() throws FlowBuilderException {
getFlow().getExceptionHandlerSet().addAll(
convertExceptionHandlers(flowModel.getExceptionHandlers(), flowModel.getGlobalTransitions()));
}
/**
* Shutdown the builder, releasing any resources it holds. A new flow construction process should start with another
* call to the {@link #init(FlowBuilderContext)} method.
* @throws FlowBuilderException an exception occurred building this flow
*/
public void doDispose() throws FlowBuilderException {
flowModel = null;
setLocalContext(null);
}
private void initLocalFlowContext() {
List resources = new LinkedList();
if (getFlowModel().getBeanImports() != null) {
if (getResource() == null) {
throw new FlowBuilderException("A resource must be defined in order to load bean-imports");
}
for (Iterator beanImportIt = getFlowModel().getBeanImports().iterator(); beanImportIt.hasNext();) {
BeanImportModel beanImport = (BeanImportModel) beanImportIt.next();
try {
resources.add(getResource().createRelative(beanImport.getResource()));
} catch (IOException e) {
throw new FlowBuilderException("Could not access flow-relative artifact resource '"
+ beanImport.getResource() + "'", e);
}
}
}
setLocalContext(new LocalFlowBuilderContext(getContext(), createFlowApplicationContext((Resource[]) resources
.toArray(new Resource[resources.size()]))));
}
protected Flow createFlow() {
Flow flow = convertFlow(flowModel);
flow.setBeanFactory(getLocalContext().getBeanFactory());
flow.setResourceLoader(getLocalContext().getResourceLoader());
return flow;
}
private Flow convertFlow(FlowModel flow) {
String flowId = getLocalContext().getFlowId();
AttributeMap externallyAssignedAttributes = getLocalContext().getFlowAttributes();
MutableAttributeMap flowAttributes = convertMetaAttributes(flow.getAttributes());
convertPersistenceContext(flow.getPersistenceContext(), flowAttributes);
convertSecured(flow.getSecured(), flowAttributes);
return this.getLocalContext().getFlowArtifactFactory().createFlow(flowId,
flowAttributes.union(externallyAssignedAttributes));
}
protected GenericApplicationContext createFlowApplicationContext(Resource[] resources) {
// see if this factory has a parent
BeanFactory parent = getContext().getBeanFactory();
// determine the context implementation based on the current environment
GenericApplicationContext flowContext;
if (parent instanceof WebApplicationContext) {
GenericWebApplicationContext webContext = new GenericWebApplicationContext();
webContext.setServletContext(((WebApplicationContext) parent).getServletContext());
flowContext = webContext;
} else {
flowContext = new GenericApplicationContext();
}
// set the parent if necessary
if (parent instanceof ApplicationContext) {
flowContext.setParent((ApplicationContext) parent);
} else {
if (parent != null) {
flowContext.getBeanFactory().setParentBeanFactory(parent);
}
}
flowContext.setResourceLoader(new FlowRelativeResourceLoader(resource));
AnnotationConfigUtils.registerAnnotationConfigProcessors(flowContext);
new XmlBeanDefinitionReader(flowContext).loadBeanDefinitions(resources);
registerFlowBeans(flowContext.getDefaultListableBeanFactory());
flowContext.refresh();
return flowContext;
}
/**
* Register beans in the bean factory local to the flow definition being built.
* <p>
* Subclasses may override this method to customize the population of the bean factory local to the flow definition
* being built; for example, to register mock implementations of services in a test environment.
* @param beanFactory the bean factory; register local beans with it using
* {@link ConfigurableBeanFactory#registerSingleton(String, Object)}
*/
protected void registerFlowBeans(ConfigurableBeanFactory beanFactory) {
}
private FlowVariable convertFlowVariable(VarModel var) {
Class clazz = (Class) fromStringTo(Class.class).execute(var.getClassName());
VariableValueFactory valueFactory = new BeanFactoryVariableValueFactory(clazz,
(AutowireCapableBeanFactory) getFlow().getBeanFactory());
ScopeType scope = convertScopeType(var.getScope(), ScopeType.FLOW);
if (!(scope == ScopeType.FLOW || scope == ScopeType.CONVERSATION)) {
throw new IllegalArgumentException("Only " + ScopeType.FLOW + " or " + ScopeType.CONVERSATION
+ " scope is allowed for flow variables");
}
return new FlowVariable(var.getName(), valueFactory, scope == ScopeType.FLOW ? true : false);
}
private Mapper convertFlowInputMapper(List inputs) {
DefaultMapper inputMapper = new DefaultMapper();
if (inputs != null) {
for (Iterator inputIt = inputs.iterator(); inputIt.hasNext();) {
inputMapper.addMapping(convertFlowInputMapping((InputModel) inputIt.next()));
}
}
return inputMapper;
}
private DefaultMapping convertFlowInputMapping(InputModel input) {
ExpressionParser parser = getLocalContext().getExpressionParser();
String name = input.getName();
String value = null;
if (StringUtils.hasText(input.getValue())) {
value = input.getValue();
} else {
value = name;
}
Expression source = parser.parseExpression(name, new ParserContextImpl().eval(MutableAttributeMap.class));
Expression target = parser.parseExpression(value, new ParserContextImpl().eval(RequestContext.class));
DefaultMapping mapping = new DefaultMapping(source, target);
convertMappingConversionExecutor(input, mapping);
convertMappingRequired(input, mapping);
return mapping;
}
private Mapper convertSubflowInputMapper(List inputs) {
DefaultMapper inputMapper = new DefaultMapper();
if (inputs != null) {
for (Iterator inputIt = inputs.iterator(); inputIt.hasNext();) {
inputMapper.addMapping(convertSubflowInputMapping((InputModel) inputIt.next()));
}
}
return inputMapper;
}
private DefaultMapping convertSubflowInputMapping(InputModel input) {
ExpressionParser parser = getLocalContext().getExpressionParser();
String name = input.getName();
String value = null;
if (StringUtils.hasText(input.getValue())) {
value = input.getValue();
} else {
value = name;
}
Expression source = parser.parseExpression(value, new ParserContextImpl().eval(RequestContext.class));
Expression target = parser.parseExpression(name, new ParserContextImpl().eval(MutableAttributeMap.class));
DefaultMapping mapping = new DefaultMapping(source, target);
convertMappingConversionExecutor(input, mapping);
convertMappingRequired(input, mapping);
return mapping;
}
private Mapper convertFlowOutputMapper(List outputs) {
DefaultMapper outputMapper = new DefaultMapper();
if (outputs != null) {
for (Iterator outputIt = outputs.iterator(); outputIt.hasNext();) {
outputMapper.addMapping(convertFlowOutputMapping((OutputModel) outputIt.next()));
}
}
return outputMapper;
}
private DefaultMapping convertFlowOutputMapping(OutputModel output) {
ExpressionParser parser = getLocalContext().getExpressionParser();
String name = output.getName();
String value = null;
if (StringUtils.hasText(output.getValue())) {
value = output.getValue();
} else {
value = name;
}
Expression source = parser.parseExpression(value, new ParserContextImpl().eval(RequestContext.class));
Expression target = parser.parseExpression(name, new ParserContextImpl().eval(MutableAttributeMap.class));
DefaultMapping mapping = new DefaultMapping(source, target);
convertMappingConversionExecutor(output, mapping);
convertMappingRequired(output, mapping);
return mapping;
}
private Mapper convertSubflowOutputMapper(List outputs) {
DefaultMapper outputMapper = new DefaultMapper();
if (outputs != null) {
for (Iterator outputIt = outputs.iterator(); outputIt.hasNext();) {
outputMapper.addMapping(convertSubflowOutputMapping((OutputModel) outputIt.next()));
}
}
return outputMapper;
}
private DefaultMapping convertSubflowOutputMapping(OutputModel output) {
ExpressionParser parser = getLocalContext().getExpressionParser();
String name = output.getName();
String value = null;
if (StringUtils.hasText(output.getValue())) {
value = output.getValue();
} else {
value = name;
}
Expression source = parser.parseExpression(name, new ParserContextImpl().eval(MutableAttributeMap.class));
Expression target = parser.parseExpression(value, new ParserContextImpl().eval(RequestContext.class));
DefaultMapping mapping = new DefaultMapping(source, target);
convertMappingConversionExecutor(output, mapping);
convertMappingRequired(output, mapping);
return mapping;
}
private void convertMappingConversionExecutor(AbstractMappingModel model, DefaultMapping mapping) {
if (StringUtils.hasText(model.getType())) {
Class type = (Class) fromStringTo(Class.class).execute(model.getType());
ConversionExecutor typeConverter = new RuntimeBindingConversionExecutor(type, getLocalContext()
.getConversionService());
mapping.setTypeConverter(typeConverter);
}
}
private void convertMappingRequired(AbstractMappingModel model, DefaultMapping mapping) {
if (StringUtils.hasText(model.getRequired())) {
boolean required = ((Boolean) fromStringTo(Boolean.class).execute(model.getRequired())).booleanValue();
mapping.setRequired(required);
}
}
private void convertActionState(ActionStateModel state, Flow flow) {
MutableAttributeMap attributes = convertMetaAttributes(state.getAttributes());
convertSecured(state.getSecured(), attributes);
getLocalContext().getFlowArtifactFactory().createActionState(state.getId(), flow,
convertActions(state.getOnEntryActions()), convertActions(state.getActions()),
convertTransitions(state.getTransitions()),
convertExceptionHandlers(state.getExceptionHandlers(), state.getTransitions()),
convertActions(state.getOnExitActions()), attributes);
}
private void convertViewState(ViewStateModel state, Flow flow) {
ViewFactory viewFactory = convertViewFactory(state.getView(), state.getId(), false);
Boolean redirect = null;
if (StringUtils.hasText(state.getRedirect())) {
redirect = (Boolean) fromStringTo(Boolean.class).execute(state.getRedirect());
}
boolean popup = false;
if (StringUtils.hasText(state.getPopup())) {
popup = ((Boolean) fromStringTo(Boolean.class).execute(state.getPopup())).booleanValue();
}
MutableAttributeMap attributes = convertMetaAttributes(state.getAttributes());
convertSecured(state.getSecured(), attributes);
getLocalContext().getFlowArtifactFactory().createViewState(state.getId(), flow,
convertViewVariables(state.getVars()), convertActions(state.getOnEntryActions()), viewFactory,
redirect, popup, convertActions(state.getOnRenderActions()),
convertTransitions(state.getTransitions()),
convertExceptionHandlers(state.getExceptionHandlers(), state.getTransitions()),
convertActions(state.getOnExitActions()), attributes);
}
private void convertDecisionState(DecisionStateModel state, Flow flow) {
MutableAttributeMap attributes = convertMetaAttributes(state.getAttributes());
convertSecured(state.getSecured(), attributes);
getLocalContext().getFlowArtifactFactory().createDecisionState(state.getId(), flow,
convertActions(state.getOnEntryActions()), convertIfs(state.getIfs()),
convertExceptionHandlers(state.getExceptionHandlers(), null), convertActions(state.getOnExitActions()),
attributes);
}
private void convertSubflowState(SubflowStateModel state, Flow flow) {
MutableAttributeMap attributes = convertMetaAttributes(state.getAttributes());
convertSecured(state.getSecured(), attributes);
getLocalContext().getFlowArtifactFactory().createSubflowState(state.getId(), flow,
convertActions(state.getOnEntryActions()), convertSubflowExpression(state.getSubflow()),
convertSubflowAttributeMapper(state), convertTransitions(state.getTransitions()),
convertExceptionHandlers(state.getExceptionHandlers(), state.getTransitions()),
convertActions(state.getOnExitActions()), attributes);
}
private Expression convertSubflowExpression(String subflow) {
Expression subflowId = getLocalContext().getExpressionParser().parseExpression(subflow,
new ParserContextImpl().template().eval(RequestContext.class).expect(String.class));
return new SubflowExpression(subflowId, getLocalContext().getFlowDefinitionLocator());
}
private SubflowAttributeMapper convertSubflowAttributeMapper(SubflowStateModel state) {
if (StringUtils.hasText(state.getSubflowAttributeMapper())) {
String attributeMapperBeanId = state.getSubflowAttributeMapper();
return (SubflowAttributeMapper) getLocalContext().getBeanFactory().getBean(attributeMapperBeanId,
SubflowAttributeMapper.class);
} else {
Mapper inputMapper = convertSubflowInputMapper(state.getInputs());
Mapper outputMapper = convertSubflowOutputMapper(state.getOutputs());
return new GenericSubflowAttributeMapper(inputMapper, outputMapper);
}
}
private void convertEndState(EndStateModel state, Flow flow) {
MutableAttributeMap attributes = convertMetaAttributes(state.getAttributes());
if (StringUtils.hasText(state.getCommit())) {
attributes.put("commit", fromStringTo(Boolean.class).execute(state.getCommit()));
}
convertSecured(state.getSecured(), attributes);
getLocalContext().getFlowArtifactFactory().createEndState(state.getId(), flow,
convertActions(state.getOnEntryActions()),
new ViewFactoryActionAdapter(convertViewFactory(state.getView(), state.getId(), true)),
convertFlowOutputMapper(state.getOutputs()),
convertExceptionHandlers(state.getExceptionHandlers(), null), attributes);
}
private ViewFactory convertViewFactory(String view, String stateId, boolean endState) {
if (!StringUtils.hasText(view)) {
if (endState) {
return null;
} else {
view = getLocalContext().getViewFactoryCreator().getViewIdByConvention(stateId);
Expression viewId = getLocalContext().getExpressionParser().parseExpression(view,
new ParserContextImpl().template().eval(RequestContext.class).expect(String.class));
return createViewFactory(viewId);
}
} else if (view.startsWith("externalRedirect:")) {
String encodedUrl = view.substring("externalRedirect:".length());
Expression externalUrl = getLocalContext().getExpressionParser().parseExpression(encodedUrl,
new ParserContextImpl().template().eval(RequestContext.class).expect(String.class));
return new ActionExecutingViewFactory(new ExternalRedirectAction(externalUrl));
} else if (view.startsWith("flowRedirect:")) {
String flowRedirect = view.substring("flowRedirect:".length());
Expression expression = getLocalContext().getExpressionParser().parseExpression(flowRedirect,
new ParserContextImpl().template().eval(RequestContext.class).expect(String.class));
return new ActionExecutingViewFactory(new FlowDefinitionRedirectAction(expression));
} else {
Expression viewId = getLocalContext().getExpressionParser().parseExpression(view,
new ParserContextImpl().template().eval(RequestContext.class).expect(String.class));
return createViewFactory(viewId);
}
}
private ViewFactory createViewFactory(Expression viewId) {
return getLocalContext().getViewFactoryCreator().createViewFactory(viewId,
getLocalContext().getExpressionParser(), getLocalContext().getFormatterRegistry(),
getLocalContext().getResourceLoader());
}
private ViewVariable[] convertViewVariables(List vars) {
List variables = new LinkedList();
if (vars != null) {
for (Iterator varIt = vars.iterator(); varIt.hasNext();) {
variables.add(convertViewVariable((VarModel) varIt.next()));
}
}
return (ViewVariable[]) variables.toArray(new ViewVariable[variables.size()]);
}
private ViewVariable convertViewVariable(VarModel var) {
Class clazz = (Class) fromStringTo(Class.class).execute(var.getClassName());
VariableValueFactory valueFactory = new BeanFactoryVariableValueFactory(clazz,
(AutowireCapableBeanFactory) getFlow().getBeanFactory());
return new ViewVariable(var.getName(), valueFactory);
}
private Transition[] convertIfs(List ifs) {
List transitions = new LinkedList();
if (ifs != null) {
for (Iterator ifIt = ifs.iterator(); ifIt.hasNext();) {
transitions.addAll(Arrays.asList(convertIf((IfModel) ifIt.next())));
}
}
return (Transition[]) transitions.toArray(new Transition[transitions.size()]);
}
private Transition[] convertIf(IfModel conditional) {
Transition thenTransition = convertThen(conditional);
if (StringUtils.hasText(conditional.getElse())) {
Transition elseTransition = convertElse(conditional);
return new Transition[] { thenTransition, elseTransition };
} else {
return new Transition[] { thenTransition };
}
}
private Transition convertThen(IfModel conditional) {
Expression expression = getLocalContext().getExpressionParser().parseExpression(conditional.getTest(),
new ParserContextImpl().eval(RequestContext.class).expect(Boolean.class));
TransitionCriteria matchingCriteria = new DefaultTransitionCriteria(expression);
TargetStateResolver targetStateResolver = (TargetStateResolver) fromStringTo(TargetStateResolver.class)
.execute(conditional.getThen());
return getLocalContext().getFlowArtifactFactory().createTransition(targetStateResolver, matchingCriteria, null,
null);
}
private Transition convertElse(IfModel conditional) {
TargetStateResolver targetStateResolver = (TargetStateResolver) fromStringTo(TargetStateResolver.class)
.execute(conditional.getElse());
return getLocalContext().getFlowArtifactFactory().createTransition(targetStateResolver, null, null, null);
}
private FlowExecutionExceptionHandler[] convertExceptionHandlers(List modelExceptionHandlers, List modelTransitions) {
FlowExecutionExceptionHandler[] transitionExecutingHandlers = convertTransitionExecutingExceptionHandlers(modelTransitions);
FlowExecutionExceptionHandler[] customHandlers = convertCustomExceptionHandlers(modelExceptionHandlers);
FlowExecutionExceptionHandler[] exceptionHandlers = new FlowExecutionExceptionHandler[transitionExecutingHandlers.length
+ customHandlers.length];
System.arraycopy(transitionExecutingHandlers, 0, exceptionHandlers, 0, transitionExecutingHandlers.length);
System.arraycopy(customHandlers, 0, exceptionHandlers, transitionExecutingHandlers.length,
customHandlers.length);
return exceptionHandlers;
}
private FlowExecutionExceptionHandler[] convertTransitionExecutingExceptionHandlers(List transitions) {
List exceptionHandlers = new LinkedList();
if (transitions != null) {
for (Iterator transitionIt = transitions.iterator(); transitionIt.hasNext();) {
TransitionModel transition = (TransitionModel) transitionIt.next();
if (StringUtils.hasText(transition.getOnException())) {
if (transition.getSecured() != null) {
throw new FlowBuilderException("Exception based transitions cannot be secured");
}
exceptionHandlers.add(convertTransitionExecutingExceptionHandler(transition));
}
}
}
return (FlowExecutionExceptionHandler[]) exceptionHandlers
.toArray(new FlowExecutionExceptionHandler[exceptionHandlers.size()]);
}
private FlowExecutionExceptionHandler convertTransitionExecutingExceptionHandler(TransitionModel transition) {
TransitionExecutingFlowExecutionExceptionHandler handler = new TransitionExecutingFlowExecutionExceptionHandler();
Class exceptionClass = (Class) fromStringTo(Class.class).execute(transition.getOnException());
TargetStateResolver targetStateResolver = (TargetStateResolver) fromStringTo(TargetStateResolver.class)
.execute(transition.getTo());
handler.add(exceptionClass, targetStateResolver);
handler.getActionList().addAll(convertActions(transition.getActions()));
return handler;
}
private FlowExecutionExceptionHandler[] convertCustomExceptionHandlers(List modelExceptionHandlers) {
List exceptionHandlers = new LinkedList();
if (modelExceptionHandlers != null) {
for (Iterator exceptionHandlerIt = modelExceptionHandlers.iterator(); exceptionHandlerIt.hasNext();) {
exceptionHandlers.add(convertCustomExceptionHandler((ExceptionHandlerModel) exceptionHandlerIt.next()));
}
}
return (FlowExecutionExceptionHandler[]) exceptionHandlers
.toArray(new FlowExecutionExceptionHandler[exceptionHandlers.size()]);
}
private FlowExecutionExceptionHandler convertCustomExceptionHandler(ExceptionHandlerModel exceptionHandler) {
return (FlowExecutionExceptionHandler) getLocalContext().getBeanFactory().getBean(
exceptionHandler.getBeanName(), FlowExecutionExceptionHandler.class);
}
private Transition[] convertTransitions(List modelTransactions) {
List transitions = new LinkedList();
if (modelTransactions != null) {
for (Iterator modelTransactionIt = modelTransactions.iterator(); modelTransactionIt.hasNext();) {
TransitionModel transition = (TransitionModel) modelTransactionIt.next();
if (!StringUtils.hasText(transition.getOnException())) {
transitions.add(convertTransition(transition));
}
}
}
return (Transition[]) transitions.toArray(new Transition[transitions.size()]);
}
private Transition convertTransition(TransitionModel transition) {
TransitionCriteria matchingCriteria = (TransitionCriteria) fromStringTo(TransitionCriteria.class).execute(
transition.getOn());
TargetStateResolver targetStateResolver = (TargetStateResolver) fromStringTo(TargetStateResolver.class)
.execute(transition.getTo());
TransitionCriteria executionCriteria = TransitionCriteriaChain.criteriaChainFor(convertActions(transition
.getActions()));
MutableAttributeMap attributes = convertMetaAttributes(transition.getAttributes());
convertSecured(transition.getSecured(), attributes);
return getLocalContext().getFlowArtifactFactory().createTransition(targetStateResolver, matchingCriteria,
executionCriteria, attributes);
}
private Action[] convertActions(List modelActions) {
List actions = new LinkedList();
if (modelActions != null) {
for (Iterator modelActionIt = modelActions.iterator(); modelActionIt.hasNext();) {
AbstractActionModel action = (AbstractActionModel) modelActionIt.next();
if (action instanceof EvaluateModel) {
actions.add(convertEvaluateAction((EvaluateModel) action));
} else if (action instanceof RenderModel) {
actions.add(convertRenderAction((RenderModel) action));
} else if (action instanceof SetModel) {
actions.add(convertSetAction((SetModel) action));
}
}
}
return (Action[]) actions.toArray(new Action[actions.size()]);
}
private Action convertEvaluateAction(EvaluateModel evaluate) {
String expressionString = evaluate.getExpression();
Expression expression = getLocalContext().getExpressionParser().parseExpression(expressionString,
new ParserContextImpl().eval(RequestContext.class));
return new EvaluateAction(expression, convertEvaluationActionResultExposer(evaluate));
}
private ActionResultExposer convertEvaluationActionResultExposer(EvaluateModel evaluate) {
if (StringUtils.hasText(evaluate.getResult())) {
Expression resultExpression = getLocalContext().getExpressionParser().parseExpression(evaluate.getResult(),
new ParserContextImpl().eval(RequestContext.class));
Class expectedResultType = null;
if (StringUtils.hasText(evaluate.getResultType())) {
expectedResultType = (Class) fromStringTo(Class.class).execute(evaluate.getResultType());
}
return new ActionResultExposer(resultExpression, expectedResultType, getLocalContext()
.getConversionService());
} else {
return null;
}
}
private Action convertRenderAction(RenderModel render) {
String[] fragmentExpressionStrings = StringUtils.commaDelimitedListToStringArray(render.getFragments());
fragmentExpressionStrings = StringUtils.trimArrayElements(fragmentExpressionStrings);
ParserContext context = new ParserContextImpl().template().eval(RequestContext.class).expect(String.class);
Expression[] fragments = new Expression[fragmentExpressionStrings.length];
for (int i = 0; i < fragmentExpressionStrings.length; i++) {
String fragment = fragmentExpressionStrings[i];
fragments[i] = getLocalContext().getExpressionParser().parseExpression(fragment, context);
}
return new RenderAction(fragments);
}
private Action convertSetAction(SetModel set) {
Expression nameExpression = getLocalContext().getExpressionParser().parseExpression(set.getName(),
new ParserContextImpl().eval(RequestContext.class));
Expression valueExpression = getLocalContext().getExpressionParser().parseExpression(set.getValue(),
new ParserContextImpl().eval(RequestContext.class));
Class expectedType = null;
if (StringUtils.hasText(set.getType())) {
expectedType = (Class) fromStringTo(Class.class).execute(set.getType());
}
return new SetAction(nameExpression, valueExpression, expectedType, getLocalContext().getConversionService());
}
private MutableAttributeMap convertMetaAttributes(List modelAttributes) {
LocalAttributeMap attributes = new LocalAttributeMap();
if (modelAttributes != null) {
for (Iterator modelAttributeIt = modelAttributes.iterator(); modelAttributeIt.hasNext();) {
convertMetaAttribute((AttributeModel) modelAttributeIt.next(), attributes);
}
}
return attributes;
}
private void convertMetaAttribute(AttributeModel attribute, MutableAttributeMap attributes) {
String name = attribute.getName();
String value = attribute.getValue();
attributes.put(name, convertAttributeValueIfNecessary(attribute, value));
}
private Object convertAttributeValueIfNecessary(AttributeModel attribute, String stringValue) {
if (StringUtils.hasText(attribute.getType())) {
Class targetClass = (Class) fromStringTo(Class.class).execute(attribute.getType());
return fromStringTo(targetClass).execute(stringValue);
} else {
return stringValue;
}
}
private void convertPersistenceContext(PersistenceContextModel persistenceContext, MutableAttributeMap attributes) {
if (persistenceContext != null) {
attributes.put("persistenceContext", Boolean.TRUE);
}
}
private void convertSecured(SecuredModel secured, MutableAttributeMap attributes) {
if (secured != null) {
SecurityRule rule = new SecurityRule();
rule.setAttributes(SecurityRule.convertAttributesFromCommaSeparatedString(secured.getAttributes()));
String comparisonType = secured.getMatch();
if ("any".equals(comparisonType)) {
rule.setComparisonType(SecurityRule.COMPARISON_ANY);
} else if ("all".equals(comparisonType)) {
rule.setComparisonType(SecurityRule.COMPARISON_ALL);
} else {
// default to any
rule.setComparisonType(SecurityRule.COMPARISON_ANY);
}
attributes.put(SecurityRule.SECURITY_ATTRIBUTE_NAME, rule);
}
}
private ScopeType convertScopeType(String scope, ScopeType defaultScope) {
if (StringUtils.hasText(scope)) {
return (ScopeType) fromStringTo(ScopeType.class).execute(scope);
} else {
return defaultScope;
}
}
private ConversionExecutor fromStringTo(Class targetType) throws ConversionException {
return getLocalContext().getConversionService().getConversionExecutor(String.class, targetType);
}
protected FlowModel getFlowModel() {
return flowModel;
}
protected LocalFlowBuilderContext getLocalContext() {
return localFlowBuilderContext;
}
public Resource getResource() {
return resource;
}
protected void setLocalContext(LocalFlowBuilderContext localFlowBuilderContext) {
this.localFlowBuilderContext = localFlowBuilderContext;
}
private static class FlowRelativeResourceLoader implements ResourceLoader {
private Resource resource;
public FlowRelativeResourceLoader(Resource resource) {
this.resource = resource;
}
public ClassLoader getClassLoader() {
return resource.getClass().getClassLoader();
}
public Resource getResource(String location) {
try {
return resource.createRelative(location);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
private static class SubflowExpression implements Expression {
private Expression subflowId;
private FlowDefinitionLocator flowDefinitionLocator;
public SubflowExpression(Expression subflowId, FlowDefinitionLocator flowDefinitionLocator) {
this.subflowId = subflowId;
this.flowDefinitionLocator = flowDefinitionLocator;
}
public Object getValue(Object context) throws EvaluationException {
String subflowId = (String) this.subflowId.getValue(context);
return flowDefinitionLocator.getFlowDefinition(subflowId);
}
public void setValue(Object context, Object value) throws EvaluationException {
throw new UnsupportedOperationException("Cannot set a subflow expression");
}
public Class getValueType(Object context) {
return null;
}
public String getExpressionString() {
return null;
}
}
public String toString() {
return new ToStringCreator(this).toString();
}
}

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.webflow.engine.builder.xml;
package org.springframework.webflow.engine.builder;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.binding.convert.ConversionService;
@@ -23,9 +23,6 @@ import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.io.ResourceLoader;
import org.springframework.webflow.core.collection.AttributeMap;
import org.springframework.webflow.definition.registry.FlowDefinitionLocator;
import org.springframework.webflow.engine.builder.FlowArtifactFactory;
import org.springframework.webflow.engine.builder.FlowBuilderContext;
import org.springframework.webflow.engine.builder.ViewFactoryCreator;
/**
* A builder context that delegates to a flow-local bean factory for builder services. Such builder services override

View File

@@ -138,6 +138,9 @@ public class RefreshableFlowDefinitionHolder implements FlowDefinitionHolder {
private void refreshIfChanged() {
long calculatedLastModified = calculateLastModified();
if (calculatedLastModified > lastModified) {
if (logger.isDebugEnabled()) {
logger.debug("Refreshing flow definition [" + flowDefinition.getId() + "]");
}
assembleFlow();
lastModified = calculatedLastModified;
}

View File

@@ -1,7 +0,0 @@
<html>
<body>
<p>
The XML-based flow builder implementation.
</p>
</body>
</html>

View File

@@ -0,0 +1,26 @@
/*
* 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.model;
/**
* Model support for actions.
*
* @author Scott Andrews
*/
public abstract class AbstractActionModel extends AbstractModel {
}

View File

@@ -0,0 +1,103 @@
/*
* 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.model;
import org.springframework.util.StringUtils;
/**
* Model support for mappings.
*
* @author Scott Andrews
*/
public abstract class AbstractMappingModel extends AbstractModel {
private String name;
private String value;
private String type;
private String required;
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
if (StringUtils.hasText(name)) {
this.name = name;
} else {
this.name = null;
}
}
/**
* @return the value
*/
public String getValue() {
return value;
}
/**
* @param value the value to set
*/
public void setValue(String value) {
if (StringUtils.hasText(value)) {
this.value = value;
} else {
this.value = null;
}
}
/**
* @return the type
*/
public String getType() {
return type;
}
/**
* @param type the type to set
*/
public void setType(String type) {
if (StringUtils.hasText(type)) {
this.type = type;
} else {
this.type = null;
}
}
/**
* @return the required
*/
public String getRequired() {
return required;
}
/**
* @param required the required to set
*/
public void setRequired(String required) {
if (StringUtils.hasText(required)) {
this.required = required;
} else {
this.required = null;
}
}
}

View File

@@ -0,0 +1,120 @@
/*
* 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.model;
import java.util.Iterator;
import java.util.LinkedList;
/**
* Contains basic merge functions that can be utilized by other models.
*
* @author Scott Andrews
*/
public abstract class AbstractModel implements Model {
/**
* Merge two objects. If the child is null, the parent will be returned. Else the child will be returned.
* @param child the child object to merge
* @param parent the parent object to merge
* @return the merged string
*/
protected Object merge(Object child, Object parent) {
if (child == null) {
return parent;
} else {
return child;
}
}
/**
* Merge two strings. If the child is null, the parent will be returned. Else the child will be returned.
* @param child the child string to merge
* @param parent the parent string to merge
* @return the merged string
*/
protected String merge(String child, String parent) {
return (String) merge((Object) child, (Object) parent);
}
/**
* Merge two model elements. If the child is null, the parent will be returned. Else the parent element will be
* merged into the child element with the result returned
* @param child the child model element to merge
* @param parent the parent model element to merge
* @return the merged element model
*/
protected Model merge(Model child, Model parent) {
if (child == null) {
return parent;
} else if (parent == null) {
return child;
} else {
child.merge(parent);
return child;
}
}
/**
* Merge two lists. All child element will be in the merged list. All parent elements not in the child list will be
* added. Mergeable elements in both lists will be merged according to that element merge rules. New items are added
* to the end of the list
* @param child the child list to merge
* @param parent the parent list to merge
* @return the merged list
*/
protected LinkedList merge(LinkedList child, LinkedList parent) {
return merge(child, parent, true);
}
/**
* Merge two lists. All child element will be in the merged list. All parent elements not in the child list will be
* added. Mergeable elements in both lists will be merged according to that element merge rules.
* @param child the child list to merge
* @param parent the parent list to merge
* @param addAtEnd if true new items will be added at the end of the list, otherwise the beginning
* @return the merged list
*/
protected LinkedList merge(LinkedList child, LinkedList parent, boolean addAtEnd) {
if (parent == null) {
return child;
} else if (child == null) {
return parent;
} else {
for (Iterator parentIt = parent.iterator(); parentIt.hasNext();) {
Model parentElement = (Model) parentIt.next();
if (!child.contains(parentElement)) {
boolean matchFound = false;
for (Iterator childIt = child.iterator(); !matchFound && childIt.hasNext();) {
Model childElement = (Model) childIt.next();
if (childElement.isMergeableWith(parentElement)) {
matchFound = true;
childElement.merge(parentElement);
}
}
if (!matchFound) {
if (addAtEnd) {
child.addLast(parentElement);
} else {
child.addFirst(parentElement);
}
}
}
}
}
return child;
}
}

View File

@@ -0,0 +1,185 @@
/*
* 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.model;
import java.util.LinkedList;
import org.springframework.util.StringUtils;
/**
* Model support for states.
*
* @author Scott Andrews
*/
public abstract class AbstractStateModel extends AbstractModel {
private String id;
private LinkedList attributes;
private SecuredModel secured;
private LinkedList onEntryActions;
private LinkedList exceptionHandlers;
/**
* @return the id
*/
public String getId() {
return id;
}
/**
* @param id the id to set
*/
public void setId(String id) {
if (StringUtils.hasText(id)) {
this.id = id;
} else {
this.id = null;
}
}
/**
* @return the attributes
*/
public LinkedList getAttributes() {
return attributes;
}
/**
* @param attributes the attributes to set
*/
public void setAttributes(LinkedList attributes) {
this.attributes = attributes;
}
/**
* @param attribute the attribute to add
*/
public void addAttribute(AttributeModel attribute) {
if (attribute == null) {
return;
}
if (attributes == null) {
attributes = new LinkedList();
}
attributes.add(attribute);
}
/**
* @param attributes the attributes to add
*/
public void addAttributes(LinkedList attributes) {
if (attributes == null || attributes.isEmpty()) {
return;
}
if (this.attributes == null) {
this.attributes = new LinkedList();
}
this.attributes.addAll(attributes);
}
/**
* @return the secured
*/
public SecuredModel getSecured() {
return secured;
}
/**
* @param secured the secured to set
*/
public void setSecured(SecuredModel secured) {
this.secured = secured;
}
/**
* @return the on entry actions
*/
public LinkedList getOnEntryActions() {
return onEntryActions;
}
/**
* @param onEntryActions the on entry actions to set
*/
public void setOnEntryActions(LinkedList onEntryActions) {
this.onEntryActions = onEntryActions;
}
/**
* @param onEntryAction the on entry action to add
*/
public void addOnEntryAction(AbstractActionModel onEntryAction) {
if (onEntryAction == null) {
return;
}
if (onEntryActions == null) {
onEntryActions = new LinkedList();
}
onEntryActions.add(onEntryAction);
}
/**
* @param onEntryActions the on entry actions to add
*/
public void addOnEntryActions(LinkedList onEntryActions) {
if (onEntryActions == null || onEntryActions.isEmpty()) {
return;
}
if (this.onEntryActions == null) {
this.onEntryActions = new LinkedList();
}
this.onEntryActions.addAll(onEntryActions);
}
/**
* @return the exception handlers
*/
public LinkedList getExceptionHandlers() {
return exceptionHandlers;
}
/**
* @param exceptionHandlers the exception handlers to set
*/
public void setExceptionHandlers(LinkedList exceptionHandlers) {
this.exceptionHandlers = exceptionHandlers;
}
/**
* @param exceptionHandler the exception handler to add
*/
public void addExceptionHandler(ExceptionHandlerModel exceptionHandler) {
if (exceptionHandler == null) {
return;
}
if (exceptionHandlers == null) {
exceptionHandlers = new LinkedList();
}
exceptionHandlers.add(exceptionHandler);
}
/**
* @param exceptionHandlers the exception handlers to add
*/
public void addExceptionHandlers(LinkedList exceptionHandlers) {
if (exceptionHandlers == null || exceptionHandlers.isEmpty()) {
return;
}
if (this.exceptionHandlers == null) {
this.exceptionHandlers = new LinkedList();
}
this.exceptionHandlers.addAll(exceptionHandlers);
}
}

View File

@@ -0,0 +1,108 @@
/*
* 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.model;
import java.util.LinkedList;
/**
* Model support for transitionable states.
*
* @author Scott Andrews
*/
public abstract class AbstractTransitionableStateModel extends AbstractStateModel {
private LinkedList transitions;
private LinkedList onExitActions;
/**
* @return the transitions
*/
public LinkedList getTransitions() {
return transitions;
}
/**
* @param transitions the transitions to set
*/
public void setTransitions(LinkedList transitions) {
this.transitions = transitions;
}
/**
* @param transition the transition to add
*/
public void addTransition(TransitionModel transition) {
if (transition == null) {
return;
}
if (transitions == null) {
transitions = new LinkedList();
}
transitions.add(transition);
}
/**
* @param transitions the transitions to add
*/
public void addTransitions(LinkedList transitions) {
if (transitions == null || transitions.isEmpty()) {
return;
}
if (this.transitions == null) {
this.transitions = new LinkedList();
}
this.transitions.addAll(transitions);
}
/**
* @return the on exit actions
*/
public LinkedList getOnExitActions() {
return onExitActions;
}
/**
* @param onExitActions the on exit actions to set
*/
public void setOnExitActions(LinkedList onExitActions) {
this.onExitActions = onExitActions;
}
/**
* @param onExitAction the on exit action to add
*/
public void addOnExitAction(AbstractActionModel onExitAction) {
if (onExitAction == null) {
return;
}
if (this.onExitActions == null) {
this.onExitActions = new LinkedList();
}
this.onExitActions.add(onExitAction);
}
/**
* @param onExitActions the on exit actions to add
*/
public void addOnExitsActions(LinkedList onExitActions) {
if (onExitActions == null || onExitActions.isEmpty()) {
return;
}
if (this.onExitActions == null) {
this.onExitActions = new LinkedList();
}
this.onExitActions.addAll(onExitActions);
}
}

View File

@@ -0,0 +1,174 @@
/*
* 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.model;
import java.util.LinkedList;
import org.springframework.util.ObjectUtils;
/**
* Model support for action states.
* <p>
* A state where one or more actions are executed. This state type is typically used to invoke application code. An
* action state is a transitionable state. A transition out of this state is driven by the result of action execution.
*
* @author Scott Andrews
*/
public class ActionStateModel extends AbstractTransitionableStateModel {
private LinkedList actions;
/**
* Create an action state model
* @param id the state identifier
*/
public ActionStateModel(String id) {
setId(id);
}
/**
* Create an action state model
* @param id the state identifier
* @param attributes meta attributes for the state
* @param secured security settings for the state
* @param onEntryActions actions to execute upon entry
* @param transitions transitions for the state
* @param onExitActions actions to execute before leaving the state
* @param actions actions to execute during the state
* @param exceptionHandlers exception handlers for the state
*/
public ActionStateModel(String id, LinkedList attributes, SecuredModel secured, LinkedList onEntryActions,
LinkedList transitions, LinkedList onExitActions, LinkedList actions, LinkedList exceptionHandlers) {
setId(id);
setAttributes(attributes);
setSecured(secured);
setOnEntryActions(onEntryActions);
setTransitions(transitions);
setOnExitActions(onExitActions);
setActions(actions);
setExceptionHandlers(exceptionHandlers);
}
/**
* Merge properties
* @param model the action state to merge into this state
*/
public void merge(Model model) {
if (isMergeableWith(model)) {
ActionStateModel state = (ActionStateModel) model;
setAttributes(merge(getAttributes(), state.getAttributes()));
setSecured((SecuredModel) merge(getSecured(), state.getSecured()));
setOnEntryActions(merge(getOnEntryActions(), state.getOnEntryActions(), false));
setExceptionHandlers(merge(getExceptionHandlers(), state.getExceptionHandlers()));
setTransitions(merge(getTransitions(), state.getTransitions()));
setOnExitActions(merge(getOnExitActions(), state.getOnExitActions(), false));
setActions(merge(getActions(), state.getActions(), false));
}
}
/**
* Tests if the model is able to be merged with this action state
* @param model the model to test
*/
public boolean isMergeableWith(Model model) {
if (model == null) {
return false;
}
if (!(model instanceof ActionStateModel)) {
return false;
}
ActionStateModel state = (ActionStateModel) model;
return ObjectUtils.nullSafeEquals(getId(), state.getId());
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (!(obj instanceof ActionStateModel)) {
return false;
}
ActionStateModel state = (ActionStateModel) obj;
if (state == null) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getId(), state.getId())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getAttributes(), state.getAttributes())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getSecured(), state.getSecured())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getOnEntryActions(), state.getOnEntryActions())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getExceptionHandlers(), state.getExceptionHandlers())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getTransitions(), state.getTransitions())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getOnExitActions(), state.getOnExitActions())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getActions(), state.getActions())) {
return false;
} else {
return true;
}
}
public int hashCode() {
return ObjectUtils.nullSafeHashCode(getId()) * 27 + ObjectUtils.nullSafeHashCode(getAttributes()) * 27
+ ObjectUtils.nullSafeHashCode(getSecured()) * 27 + ObjectUtils.nullSafeHashCode(getOnEntryActions())
* 27 + ObjectUtils.nullSafeHashCode(getExceptionHandlers()) * 27
+ ObjectUtils.nullSafeHashCode(getTransitions()) * 27
+ ObjectUtils.nullSafeHashCode(getOnExitActions()) * 27 + ObjectUtils.nullSafeHashCode(getActions())
* 27;
}
/**
* @return the actions
*/
public LinkedList getActions() {
return actions;
}
/**
* @param actions the actions to set
*/
public void setActions(LinkedList actions) {
this.actions = actions;
}
/**
* @param action the action to add
*/
public void addAction(AbstractActionModel action) {
if (action == null) {
return;
}
if (actions == null) {
actions = new LinkedList();
}
actions.add(action);
}
/**
* @param actions the actions to add
*/
public void addAction(LinkedList actions) {
if (actions == null || actions.isEmpty()) {
return;
}
if (this.actions == null) {
this.actions = new LinkedList();
}
this.actions.addAll(actions);
}
}

View File

@@ -0,0 +1,160 @@
/*
* 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.model;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Model support for attributes.
* <p>
* A meta attribute describing or otherwise annotating it's holder.
*
* @author Scott Andrews
*/
public class AttributeModel extends AbstractModel {
private String name;
private String type;
private String value;
/**
* Create an attribute model
* @param name the name of the attribute
* @param value the value of the attribute
*/
public AttributeModel(String name, String value) {
setName(name);
setValue(value);
}
/**
* Create an attribute model
* @param name the name of the attribute
* @param value the value of the attribute
* @param type the type of the value
*/
public AttributeModel(String name, String value, String type) {
setName(name);
setValue(value);
setType(type);
}
/**
* Merge properties
* @param model the attribute to merge into this attribute
*/
public void merge(Model model) {
if (isMergeableWith(model)) {
AttributeModel attribute = (AttributeModel) model;
setType(merge(getType(), attribute.getType()));
}
}
/**
* Tests if the model is able to be merged with this attribute
* @param model the model to test
*/
public boolean isMergeableWith(Model model) {
if (model == null) {
return false;
}
if (!(model instanceof AttributeModel)) {
return false;
}
AttributeModel attribute = (AttributeModel) model;
return ObjectUtils.nullSafeEquals(getName(), attribute.getName())
&& ObjectUtils.nullSafeEquals(getValue(), attribute.getValue());
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (!(obj instanceof AttributeModel)) {
return false;
}
AttributeModel attribute = (AttributeModel) obj;
if (attribute == null) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getName(), attribute.getName())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getType(), attribute.getType())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getValue(), attribute.getValue())) {
return false;
} else {
return super.equals(attribute);
}
}
public int hashCode() {
return ObjectUtils.nullSafeHashCode(getName()) * 27 + ObjectUtils.nullSafeHashCode(getType()) * 27
+ ObjectUtils.nullSafeHashCode(getValue()) * 27 + super.hashCode() * 27;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
if (StringUtils.hasText(name)) {
this.name = name;
} else {
this.name = null;
}
}
/**
* @return the type
*/
public String getType() {
return type;
}
/**
* @param type the type to set
*/
public void setType(String type) {
if (StringUtils.hasText(type)) {
this.type = type;
} else {
this.type = null;
}
}
/**
* @return the value
*/
public String getValue() {
return value;
}
/**
* @param value the value to set
*/
public void setValue(String value) {
if (StringUtils.hasText(value)) {
this.value = value;
} else {
this.value = null;
}
}
}

View File

@@ -0,0 +1,91 @@
/*
* 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.model;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Model support for bean imports.
* <p>
* Imports user-defined beans defined at a resource location. These beans become part of the flow's bean factory and are
* resolvable using flow expressions.
*
* @author Scott Andrews
*/
public class BeanImportModel extends AbstractModel {
private String resource;
/**
* Create a bean import model
* @param resource the resource containing beans to import
*/
public BeanImportModel(String resource) {
setResource(resource);
}
/**
* Bean imports are not mergeable
*/
public void merge(Model model) {
// not mergeable
}
/**
* Bean imports are not mergeable
*/
public boolean isMergeableWith(Model model) {
return false;
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (!(obj instanceof BeanImportModel)) {
return false;
}
BeanImportModel beanImport = (BeanImportModel) obj;
if (beanImport == null) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getResource(), beanImport.getResource())) {
return false;
} else {
return true;
}
}
public int hashCode() {
return ObjectUtils.nullSafeHashCode(getResource()) * 27;
}
/**
* @return the resource
*/
public String getResource() {
return resource;
}
/**
* @param resource the resource to set
*/
public void setResource(String resource) {
if (StringUtils.hasText(resource)) {
this.resource = resource;
} else {
this.resource = null;
}
}
}

View File

@@ -0,0 +1,211 @@
/*
* 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.model;
import java.util.LinkedList;
import org.springframework.util.ObjectUtils;
/**
* Model support for decision states.
* <p>
* Evaluates one or more expressions to decide what state to transition to next. Intended to be used as an idempotent
* 'navigation' or 'routing' state.
* <p>
* A decision state is a transitionable state. A decision state transition can be triggered by evaluating a boolean
* expression against the flow execution request context. To define transition expressions, use the 'if' element.
*
* @author Scott Andrews
*/
public class DecisionStateModel extends AbstractStateModel {
private LinkedList ifs;
private LinkedList onExitActions;
/**
* Create a decision state model
* @param id the state identifier
*/
public DecisionStateModel(String id) {
setId(id);
}
/**
* Create a decision state model
* @param id the state identifier
* @param ifs decision tests
* @param onExitActions actions to execute before exiting this state
* @param attributes meta attributes for this state
* @param secured security settings for this state
* @param onEntryActions actions to execute upon entering this state
* @param exceptionHandlers exception handlers for this state
*/
public DecisionStateModel(String id, LinkedList ifs, LinkedList onExitActions, LinkedList attributes,
SecuredModel secured, LinkedList onEntryActions, LinkedList exceptionHandlers) {
setId(id);
setIfs(ifs);
setOnExitActions(onExitActions);
setAttributes(attributes);
setSecured(secured);
setOnEntryActions(onEntryActions);
setExceptionHandlers(exceptionHandlers);
}
/**
* Merge properties
* @param model the decision state to merge into this state
*/
public void merge(Model model) {
if (isMergeableWith(model)) {
DecisionStateModel state = (DecisionStateModel) model;
setAttributes(merge(getAttributes(), state.getAttributes()));
setSecured((SecuredModel) merge(getSecured(), state.getSecured()));
setOnEntryActions(merge(getOnEntryActions(), state.getOnEntryActions(), false));
setExceptionHandlers(merge(getExceptionHandlers(), state.getExceptionHandlers()));
setIfs(merge(getIfs(), state.getIfs()));
setOnExitActions(merge(getOnExitActions(), state.getOnExitActions(), false));
}
}
/**
* Tests if the model is able to be merged with this decision state
* @param model the model to test
*/
public boolean isMergeableWith(Model model) {
if (model == null) {
return false;
}
if (!(model instanceof DecisionStateModel)) {
return false;
}
DecisionStateModel state = (DecisionStateModel) model;
return ObjectUtils.nullSafeEquals(getId(), state.getId());
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (!(obj instanceof DecisionStateModel)) {
return false;
}
DecisionStateModel state = (DecisionStateModel) obj;
if (state == null) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getId(), state.getId())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getAttributes(), state.getAttributes())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getSecured(), state.getSecured())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getOnEntryActions(), state.getOnEntryActions())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getExceptionHandlers(), state.getExceptionHandlers())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getIfs(), state.getIfs())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getOnExitActions(), state.getOnExitActions())) {
return false;
} else {
return true;
}
}
public int hashCode() {
return ObjectUtils.nullSafeHashCode(getId()) * 27 + ObjectUtils.nullSafeHashCode(getAttributes()) * 27
+ ObjectUtils.nullSafeHashCode(getSecured()) * 27 + ObjectUtils.nullSafeHashCode(getOnEntryActions())
* 27 + ObjectUtils.nullSafeHashCode(getExceptionHandlers()) * 27
+ ObjectUtils.nullSafeHashCode(getIfs()) * 27 + ObjectUtils.nullSafeHashCode(getOnExitActions()) * 27;
}
/**
* @return the ifs
*/
public LinkedList getIfs() {
return ifs;
}
/**
* @param ifs the ifs to set
*/
public void setIfs(LinkedList ifs) {
this.ifs = ifs;
}
/**
* @param conditional the if to add
*/
public void addIf(IfModel conditional) {
if (conditional == null) {
return;
}
if (ifs == null) {
ifs = new LinkedList();
}
ifs.add(conditional);
}
/**
* @param ifs the ifs to add
*/
public void addIf(LinkedList ifs) {
if (ifs == null || ifs.isEmpty()) {
return;
}
if (this.ifs == null) {
this.ifs = new LinkedList();
}
this.ifs.addAll(ifs);
}
/**
* @return the on exit actions
*/
public LinkedList getOnExitActions() {
return onExitActions;
}
/**
* @param onExitActions the on exit actions to set
*/
public void setOnExitActions(LinkedList onExitActions) {
this.onExitActions = onExitActions;
}
/**
* @param onExitAction the on exit action to add
*/
public void addOnExitAction(AbstractActionModel onExitAction) {
if (onExitAction == null) {
return;
}
if (onExitActions == null) {
onExitActions = new LinkedList();
}
onExitActions.add(onExitAction);
}
/**
* @param onExitActions the on exit actions to add
*/
public void addOnExitActions(LinkedList onExitActions) {
if (onExitActions == null || onExitActions.isEmpty()) {
return;
}
if (this.onExitActions == null) {
this.onExitActions = new LinkedList();
}
this.onExitActions.addAll(onExitActions);
}
}

View File

@@ -0,0 +1,217 @@
/*
* 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.model;
import java.util.LinkedList;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Model support for end states.
* <p>
* A state that terminates this flow when entered. Defines a flow outcome.
* <p>
* An end state is not transitionable; there are never transitions out of an end state. When an end-state is entered, an
* instance of this flow is terminated.
* <p>
* When this flow terminates, if it was the "root" flow the entire execution is terminated. If this flow was a subflow,
* its parent flow resumes.
*
* @author Scott Andrews
*/
public class EndStateModel extends AbstractStateModel {
private String view;
private String commit;
private LinkedList outputs;
/**
* Create an end state model
* @param id the state identifier
*/
public EndStateModel(String id) {
setId(id);
}
/**
* Create an end state model
* @param id the state identifier
* @param view the view to render
* @param commit indicate if the persistence context should be committed
* @param outputs output mappings
* @param attributes meta attributes for the state
* @param secured security settings for the state
* @param onEntryActions actions to execute when entering the state
* @param exceptionHandlers exception handlers for the state
*/
public EndStateModel(String id, String view, String commit, LinkedList outputs, LinkedList attributes,
SecuredModel secured, LinkedList onEntryActions, LinkedList exceptionHandlers) {
setId(id);
setView(view);
setCommit(commit);
setOutputs(outputs);
setAttributes(attributes);
setSecured(secured);
setOnEntryActions(onEntryActions);
setExceptionHandlers(exceptionHandlers);
}
/**
* Merge properties
* @param model the end state to merge into this state
*/
public void merge(Model model) {
if (isMergeableWith(model)) {
EndStateModel state = (EndStateModel) model;
setAttributes(merge(getAttributes(), state.getAttributes()));
setSecured((SecuredModel) merge(getSecured(), state.getSecured()));
setOnEntryActions(merge(getOnEntryActions(), state.getOnEntryActions(), false));
setExceptionHandlers(merge(getExceptionHandlers(), state.getExceptionHandlers()));
setView(merge(getView(), state.getView()));
setCommit(merge(getCommit(), state.getCommit()));
setOutputs(merge(getOutputs(), state.getOutputs(), false));
}
}
/**
* Tests if the model is able to be merged with this end state
* @param model the model to test
*/
public boolean isMergeableWith(Model model) {
if (model == null) {
return false;
}
if (!(model instanceof EndStateModel)) {
return false;
}
EndStateModel state = (EndStateModel) model;
return ObjectUtils.nullSafeEquals(getId(), state.getId());
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (!(obj instanceof EndStateModel)) {
return false;
}
EndStateModel state = (EndStateModel) obj;
if (state == null) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getId(), state.getId())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getAttributes(), state.getAttributes())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getSecured(), state.getSecured())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getOnEntryActions(), state.getOnEntryActions())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getExceptionHandlers(), state.getExceptionHandlers())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getView(), state.getView())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getCommit(), state.getCommit())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getOutputs(), state.getOutputs())) {
return false;
} else {
return true;
}
}
public int hasCode() {
return ObjectUtils.nullSafeHashCode(getId()) * 27 + ObjectUtils.nullSafeHashCode(getAttributes()) * 27
+ ObjectUtils.nullSafeHashCode(getSecured()) * 27 + ObjectUtils.nullSafeHashCode(getOnEntryActions())
* 27 + ObjectUtils.nullSafeHashCode(getExceptionHandlers()) * 27
+ ObjectUtils.nullSafeHashCode(getView()) * 27 + ObjectUtils.nullSafeHashCode(getCommit()) * 27
+ ObjectUtils.nullSafeHashCode(getOutputs()) * 27;
}
/**
* @return the view
*/
public String getView() {
return view;
}
/**
* @param view the view factory to set
*/
public void setView(String view) {
if (StringUtils.hasText(view)) {
this.view = view;
} else {
this.view = null;
}
}
/**
* @return the commit
*/
public String getCommit() {
return commit;
}
/**
* @param commit the commit to set
*/
public void setCommit(String commit) {
if (StringUtils.hasText(commit)) {
this.commit = commit;
} else {
this.commit = null;
}
}
/**
* @return the outputs
*/
public LinkedList getOutputs() {
return outputs;
}
/**
* @param outputs the outputs to set
*/
public void setOutputs(LinkedList outputs) {
this.outputs = outputs;
}
/**
* @param output the output mapping to add
*/
public void addOutput(OutputModel output) {
if (output == null) {
return;
}
if (outputs == null) {
outputs = new LinkedList();
}
outputs.add(output);
}
/**
* @param outputs the output mappings to add
*/
public void addOutputs(LinkedList outputs) {
if (outputs == null || outputs.isEmpty()) {
return;
}
if (this.outputs == null) {
this.outputs = new LinkedList();
}
this.outputs.addAll(outputs);
}
}

View File

@@ -0,0 +1,168 @@
/*
* 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.model;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Model support for evaluate actions.
* <p>
* Evaluates an expression against the flow request context.
*
* @author Scott Andrews
*/
public class EvaluateModel extends AbstractActionModel {
private String expression;
private String result;
private String resultType;
/**
* Create an evaluate action model
* @param expression the expression to evaluate
*/
public EvaluateModel(String expression) {
setExpression(expression);
}
/**
* Create an evaluate action model
* @param expression the expression to evaluate
* @param result where to store the result of the expressions
*/
public EvaluateModel(String expression, String result) {
setExpression(expression);
setResult(result);
}
/**
* Create an evaluate action model
* @param expression the expression to evaluate
* @param result where to store the result of the expressions
* @param resultType the type of the result
*/
public EvaluateModel(String expression, String result, String resultType) {
setExpression(expression);
setResult(result);
setResultType(resultType);
}
/**
* Merge properties
* @param model the evaluate action to merge into this evaluate
*/
public void merge(Model model) {
if (isMergeableWith(model)) {
EvaluateModel evaluate = (EvaluateModel) model;
setResult(merge(getResult(), evaluate.getResult()));
setResultType(merge(getResultType(), evaluate.getResultType()));
}
}
/**
* Tests if the model is able to be merged with this evaluate action
* @param model the model to test
*/
public boolean isMergeableWith(Model model) {
if (model == null) {
return false;
}
if (!(model instanceof EvaluateModel)) {
return false;
}
EvaluateModel evaluate = (EvaluateModel) model;
return ObjectUtils.nullSafeEquals(getExpression(), evaluate.getExpression());
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (!(obj instanceof EvaluateModel)) {
return false;
}
EvaluateModel evaluate = (EvaluateModel) obj;
if (evaluate == null) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getExpression(), evaluate.getExpression())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getResult(), evaluate.getResult())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getResultType(), evaluate.getResultType())) {
return false;
} else {
return true;
}
}
public int hashCode() {
return ObjectUtils.nullSafeHashCode(getExpression()) * 27 + ObjectUtils.nullSafeHashCode(getResult()) * 27
+ ObjectUtils.nullSafeHashCode(getResultType()) * 27;
}
/**
* @return the expression
*/
public String getExpression() {
return expression;
}
/**
* @param expression the expression to set
*/
public void setExpression(String expression) {
if (StringUtils.hasText(expression)) {
this.expression = expression;
} else {
this.expression = null;
}
}
/**
* @return the result
*/
public String getResult() {
return result;
}
/**
* @param result the result to set
*/
public void setResult(String result) {
if (StringUtils.hasText(result)) {
this.result = result;
} else {
this.result = null;
}
}
/**
* @return the result type
*/
public String getResultType() {
return resultType;
}
/**
* @param resultType the result type to set
*/
public void setResultType(String resultType) {
if (StringUtils.hasText(resultType)) {
this.resultType = resultType;
} else {
this.resultType = null;
}
}
}

View File

@@ -0,0 +1,87 @@
/*
* 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.model;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Model support for exception handlers.
* <p>
* Handles exceptions that occur during flow execution. Exception handlers may be attached at the state or flow level.
*
* @author Scott Andrews
*/
public class ExceptionHandlerModel extends AbstractModel {
private String beanName;
/**
* Create an exception handler model
* @param beanName the name of the bean to handle exceptions
*/
public ExceptionHandlerModel(String beanName) {
setBeanName(beanName);
}
/**
* Exception handlers are not mergeable
*/
public void merge(Model model) {
// not mergable
}
public boolean isMergeableWith(Model model) {
return false;
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (!(obj instanceof ExceptionHandlerModel)) {
return false;
}
ExceptionHandlerModel exceptionHandler = (ExceptionHandlerModel) obj;
if (exceptionHandler == null) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getBeanName(), exceptionHandler.getBeanName())) {
return false;
} else {
return true;
}
}
public int hashCode() {
return ObjectUtils.nullSafeHashCode(getBeanName()) * 27;
}
/**
* @return the bean name
*/
public String getBeanName() {
return beanName;
}
/**
* @param beanName the bean name to set
*/
public void setBeanName(String beanName) {
if (StringUtils.hasText(beanName)) {
this.beanName = beanName;
} else {
this.beanName = null;
}
}
}

View File

@@ -0,0 +1,705 @@
/*
* 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.model;
import java.util.LinkedList;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Model support for flows.
* <p>
* Defines exactly one flow definition. A flow is composed of one or more states that define the steps of a
* conversation. One of those steps is the start state, which defines the conversation's starting point. <br>
* A flow may also exhibit the following characteristics:
* <ul>
* <li>Be annotated with attributes that define descriptive properties that may affect flow execution. (See
* {@link AttributeModel})
* <li>Be secured (See {@link SecuredModel})
* <li>Be a persistence context for managing persistent objects during the course of flow execution. (See
* {@link PersistenceContextModel})
* <li>Instantiate a set of instance variables when started. (See {@link VarModel})
* <li>Map input provided by callers that start it (See {@link InputModel})
* <li>Return output to callers that end it. (See {@link OutputModel})
* <li>Execute actions at start time and end time. (See {@link EvaluateModel}, {@link RenderModel} and
* {@link SetModel})
* <li>Define transitions shared by all states. (See {@link TransitionModel})
* <li>Handle exceptions thrown by during flow execution. (See {@link ExceptionHandlerModel})
* <li>Import one or more local bean definition files defining custom flow artifacts (such as actions, exception
* handlers, view factories, transition criteria, etc). (See {@link BeanImportModel})
* </ul>
*
* @author Scott Andrews
*/
public class FlowModel extends AbstractModel {
// private String id;
private String parent;
private String startStateId;
private LinkedList attributes;
private SecuredModel secured;
private PersistenceContextModel persistenceContext;
private LinkedList vars;
private LinkedList inputs;
private LinkedList outputs;
private LinkedList onStartActions;
private LinkedList states;
private LinkedList globalTransitions;
private LinkedList onEndActions;
private LinkedList exceptionHandlers;
private LinkedList beanImports;
/**
* Create a flow model
*/
public FlowModel() {
}
/**
* Merge properties
* @param model the flow to merge into this flow
*/
public void merge(Model model) {
if (isMergeableWith(model)) {
FlowModel flow = (FlowModel) model;
setParent(null);
setStartStateId(merge(getStartStateId(), flow.getStartStateId()));
setAttributes(merge(getAttributes(), flow.getAttributes()));
setSecured((SecuredModel) merge(getSecured(), flow.getSecured()));
setPersistenceContext((PersistenceContextModel) merge(getPersistenceContext(), flow.getPersistenceContext()));
setVars(merge(getVars(), flow.getVars(), false));
setInputs(merge(getInputs(), flow.getInputs()));
setOutputs(merge(getOutputs(), flow.getOutputs()));
setOnStartActions(merge(getOnStartActions(), flow.getOnStartActions(), false));
setStates(merge(getStates(), flow.getStates()));
setGlobalTransitions(merge(getGlobalTransitions(), flow.getGlobalTransitions()));
setOnEndActions(merge(getOnEndActions(), flow.getOnEndActions(), false));
setExceptionHandlers(merge(getExceptionHandlers(), flow.getExceptionHandlers()));
setBeanImports(merge(getBeanImports(), flow.getBeanImports()));
}
}
/**
* Tests if the model is able to be merged with this flow
* @param model the model to test
*/
public boolean isMergeableWith(Model model) {
if (model == null) {
return false;
}
if ((model instanceof FlowModel)) {
return true;
} else {
return false;
}
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (!(obj instanceof FlowModel)) {
return false;
}
FlowModel flow = (FlowModel) obj;
if (flow == null) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getParent(), flow.getParent())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getStartStateId(), flow.getStartStateId())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getAttributes(), flow.getAttributes())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getSecured(), flow.getSecured())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getPersistenceContext(), flow.getPersistenceContext())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getVars(), flow.getVars())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getInputs(), flow.getInputs())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getOutputs(), flow.getOutputs())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getOnStartActions(), flow.getOnStartActions())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getStates(), flow.getStates())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getGlobalTransitions(), flow.getGlobalTransitions())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getOnEndActions(), flow.getOnEndActions())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getExceptionHandlers(), flow.getExceptionHandlers())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getBeanImports(), flow.getBeanImports())) {
return false;
} else {
return true;
}
}
public int hashCode() {
return ObjectUtils.nullSafeHashCode(getParent()) * 27 + ObjectUtils.nullSafeHashCode(getStartStateId()) * 27
+ ObjectUtils.nullSafeHashCode(getAttributes()) * 27 + ObjectUtils.nullSafeHashCode(getSecured()) * 27
+ ObjectUtils.nullSafeHashCode(getPersistenceContext()) * 27 + ObjectUtils.nullSafeHashCode(getVars())
* 27 + ObjectUtils.nullSafeHashCode(getInputs()) * 27 + ObjectUtils.nullSafeHashCode(getOutputs()) * 27
+ ObjectUtils.nullSafeHashCode(getOnStartActions()) * 27 + ObjectUtils.nullSafeHashCode(getStates())
* 27 + ObjectUtils.nullSafeHashCode(getGlobalTransitions()) * 27
+ ObjectUtils.nullSafeHashCode(getOnEndActions()) * 27
+ ObjectUtils.nullSafeHashCode(getExceptionHandlers()) * 27
+ ObjectUtils.nullSafeHashCode(getBeanImports()) * 27;
}
/**
* @return the parent
*/
public String getParent() {
return parent;
}
/**
* @param parent the parent to set
*/
public void setParent(String parent) {
if (StringUtils.hasText(parent)) {
this.parent = parent;
} else {
this.parent = null;
}
}
/**
* @return the id of the flow's start state
*/
public String getStartStateId() {
return startStateId;
}
/**
* @param startStateId the id of the flow's start state to set
*/
public void setStartStateId(String startStateId) {
if (StringUtils.hasText(startStateId)) {
this.startStateId = startStateId;
} else {
this.startStateId = null;
}
}
/**
* @param startState the flow's start state to set
*/
public void setStartState(AbstractStateModel startState) {
setStartStateId(startState.getId());
}
/**
* @return the attributes
*/
public LinkedList getAttributes() {
return attributes;
}
/**
* @param attributes the attributes to set
*/
public void setAttributes(LinkedList attributes) {
this.attributes = attributes;
}
/**
* @param attribute the attribute to add
*/
public void addAttribute(AttributeModel attribute) {
if (attribute == null) {
return;
}
if (attributes == null) {
attributes = new LinkedList();
}
attributes.add(attribute);
}
/**
* @param attributes the attributes to add
*/
public void addAttributes(LinkedList attributes) {
if (attributes == null || attributes.isEmpty()) {
return;
}
if (this.attributes == null) {
this.attributes = new LinkedList();
}
this.attributes.addAll(attributes);
}
/**
* @return the secured
*/
public SecuredModel getSecured() {
return secured;
}
/**
* @param secured the secured to set
*/
public void setSecured(SecuredModel secured) {
this.secured = secured;
}
/**
* @return the persistence context
*/
public PersistenceContextModel getPersistenceContext() {
return persistenceContext;
}
/**
* @param persistenceContext the persistence context to set
*/
public void setPersistenceContext(PersistenceContextModel persistenceContext) {
this.persistenceContext = persistenceContext;
}
/**
* @return the vars
*/
public LinkedList getVars() {
return vars;
}
/**
* @param vars the vars to set
*/
public void setVars(LinkedList vars) {
this.vars = vars;
}
/**
* @param var the var to add
*/
public void addVar(VarModel var) {
if (var == null) {
return;
}
if (vars == null) {
vars = new LinkedList();
}
vars.add(var);
}
/**
* @param vars the vars to add
*/
public void addVars(LinkedList vars) {
if (vars == null || vars.isEmpty()) {
return;
}
if (this.vars == null) {
this.vars = new LinkedList();
}
this.vars.addAll(vars);
}
/**
* @return the input mappings
*/
public LinkedList getInputs() {
return inputs;
}
/**
* @param inputs the input mappings to set
*/
public void setInputs(LinkedList inputs) {
this.inputs = inputs;
}
/**
* @param input the input mapping to add
*/
public void addInput(InputModel input) {
if (input == null) {
return;
}
if (inputs == null) {
inputs = new LinkedList();
}
inputs.add(input);
}
/**
* @param inputs the input mappings to add
*/
public void addInputs(LinkedList inputs) {
if (inputs == null || inputs.isEmpty()) {
return;
}
if (this.inputs == null) {
this.inputs = new LinkedList();
}
this.inputs.addAll(inputs);
}
/**
* @return the output mappings
*/
public LinkedList getOutputs() {
return outputs;
}
/**
* @param outputs the output mappings to set
*/
public void setOutputs(LinkedList outputs) {
this.outputs = outputs;
}
/**
* @param output the output mapping to add
*/
public void addOutput(OutputModel output) {
if (output == null) {
return;
}
if (outputs == null) {
outputs = new LinkedList();
}
outputs.add(output);
}
/**
* @param outputs the output mappings to add
*/
public void addOutputs(LinkedList outputs) {
if (outputs == null || outputs.isEmpty()) {
return;
}
if (this.outputs == null) {
this.outputs = new LinkedList();
}
this.outputs.addAll(outputs);
}
/**
* @return the on start actions
*/
public LinkedList getOnStartActions() {
return onStartActions;
}
/**
* @param onStartActions the on start actions to set
*/
public void setOnStartActions(LinkedList onStartActions) {
this.onStartActions = onStartActions;
}
/**
* @param onStartAction the on start action to add
*/
public void addOnStartAction(AbstractActionModel onStartAction) {
if (onStartAction == null) {
return;
}
if (onStartActions == null) {
onStartActions = new LinkedList();
}
onStartActions.add(onStartAction);
}
/**
* @param onStartActions the on start actions to add
*/
public void addOnStartActions(LinkedList onStartActions) {
if (onStartActions == null || onStartActions.isEmpty()) {
return;
}
if (this.onStartActions == null) {
this.onStartActions = new LinkedList();
}
this.onStartActions.addAll(onStartActions);
}
/**
* @return the states
*/
public LinkedList getStates() {
return states;
}
/**
* @param states the states to set
*/
public void setStates(LinkedList states) {
this.states = states;
}
/**
* @param state the state to add
*/
public void addState(AbstractStateModel state) {
if (state == null) {
return;
}
if (states == null) {
states = new LinkedList();
}
states.add(state);
}
/**
* @param states the states to add
*/
public void addStates(LinkedList states) {
if (states == null || states.isEmpty()) {
return;
}
if (this.states == null) {
this.states = new LinkedList();
}
this.states.addAll(states);
}
/**
* @param state the action state to add
*/
public void addActionState(ActionStateModel state) {
addState(state);
}
/**
* @param states the action states to add
*/
public void addActionStates(LinkedList states) {
addStates(states);
}
/**
* @param state the view state to add
*/
public void addViewState(ViewStateModel state) {
addState(state);
}
/**
* @param states the view states to add
*/
public void addViewStates(LinkedList states) {
addStates(states);
}
/**
* @param state the decision state to add
*/
public void addDecisionState(DecisionStateModel state) {
addState(state);
}
/**
* @param states the decision states to add
*/
public void addDecisionStates(LinkedList states) {
addStates(states);
}
/**
* @param state the subflow state to add
*/
public void addSubflowState(SubflowStateModel state) {
addState(state);
}
/**
* @param states the subflow states to add
*/
public void addSubflowStates(LinkedList states) {
addStates(states);
}
/**
* @param state the end state to add
*/
public void addEndState(EndStateModel state) {
addState(state);
}
/**
* @param states the end states to add
*/
public void addEndStates(LinkedList states) {
addStates(states);
}
/**
* @return the global transitions
*/
public LinkedList getGlobalTransitions() {
return globalTransitions;
}
/**
* @param globalTransitions the global transitions to set
*/
public void setGlobalTransitions(LinkedList globalTransitions) {
this.globalTransitions = globalTransitions;
}
/**
* @param globalTransition the global transition to add
*/
public void addGlobalTransition(TransitionModel globalTransition) {
if (globalTransition == null) {
return;
}
if (globalTransitions == null) {
globalTransitions = new LinkedList();
}
globalTransitions.add(globalTransition);
}
/**
* @param globalTransitions the global transitions to add
*/
public void addGlobalTransitions(LinkedList globalTransitions) {
if (globalTransitions == null || globalTransitions.isEmpty()) {
return;
}
if (this.globalTransitions == null) {
this.globalTransitions = new LinkedList();
}
this.globalTransitions.addAll(globalTransitions);
}
/**
* @return the on end actions
*/
public LinkedList getOnEndActions() {
return onEndActions;
}
/**
* @param onEndActions the on end actions to set
*/
public void setOnEndActions(LinkedList onEndActions) {
this.onEndActions = onEndActions;
}
/**
* @param onEndAction the on end action to add
*/
public void addOnEndAction(AbstractActionModel onEndAction) {
if (onEndAction == null) {
return;
}
if (onEndActions == null) {
onEndActions = new LinkedList();
}
onEndActions.add(onEndAction);
}
/**
* @param onEndActions the on end actions to add
*/
public void addOnEndActions(LinkedList onEndActions) {
if (onEndActions == null || onEndActions.isEmpty()) {
return;
}
if (this.onEndActions == null) {
this.onEndActions = new LinkedList();
}
this.onEndActions.addAll(onEndActions);
}
/**
* @return the exception handlers
*/
public LinkedList getExceptionHandlers() {
return exceptionHandlers;
}
/**
* @param exceptionHandlers the exception handlers to set
*/
public void setExceptionHandlers(LinkedList exceptionHandlers) {
this.exceptionHandlers = exceptionHandlers;
}
/**
* @param exceptionHandler the exception handler to add
*/
public void addExceptionHandler(ExceptionHandlerModel exceptionHandler) {
if (exceptionHandler == null) {
return;
}
if (exceptionHandlers == null) {
exceptionHandlers = new LinkedList();
}
exceptionHandlers.add(exceptionHandler);
}
/**
* @param exceptionHandlers the exception handlers to add
*/
public void addExceptionHandlers(LinkedList exceptionHandlers) {
if (exceptionHandlers == null || exceptionHandlers.isEmpty()) {
return;
}
if (this.exceptionHandlers == null) {
this.exceptionHandlers = new LinkedList();
}
this.exceptionHandlers.addAll(exceptionHandlers);
}
/**
* @return the bean imports
*/
public LinkedList getBeanImports() {
return beanImports;
}
/**
* @param beanImports the bean imports to set
*/
public void setBeanImports(LinkedList beanImports) {
this.beanImports = beanImports;
}
/**
* @param beanImport the bean import to add
*/
public void addBeanImport(BeanImportModel beanImport) {
if (beanImport == null) {
return;
}
if (beanImports == null) {
beanImports = new LinkedList();
}
beanImports.add(beanImport);
}
/**
* @param beanImports the bean imports to add
*/
public void addBeanImports(LinkedList beanImports) {
if (beanImports == null || beanImports.isEmpty()) {
return;
}
if (this.beanImports == null) {
this.beanImports = new LinkedList();
}
this.beanImports.addAll(beanImports);
}
}

View File

@@ -0,0 +1,162 @@
/*
* 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.model;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Model support for if elements.
* <p>
* Defines a boolean expression to evaluate a target state to transition to if that expression evaluates to true.
* Optionally, this element may define an 'else' attribute to define a state to transition to if the expression
* evaluates to false.
*
* @author Scott Andrews
*/
public class IfModel extends AbstractModel {
private String test;
private String then;
private String elze;
/**
* Create an if model
* @param test the boolean condition to test
* @param then the state to transition to if the boolean expression evaluates to true
*/
public IfModel(String test, String then) {
setTest(test);
setThen(then);
}
/**
* Create an if model
* @param test the boolean condition to test
* @param then the state to transition to if the boolean expression evaluates to true
* @param elze the state to transition to if the boolean expression evaluates to false
*/
public IfModel(String test, String then, String elze) {
setTest(test);
setThen(then);
setElse(elze);
}
/**
* Merge properties
* @param model the conditional to merge into this conditional
*/
public void merge(Model model) {
if (isMergeableWith(model)) {
IfModel conditional = (IfModel) model;
setThen(merge(getThen(), conditional.getThen()));
setElse(merge(getElse(), conditional.getElse()));
}
}
/**
* Tests if the model is able to be merged with this if action
* @param model the model to test
*/
public boolean isMergeableWith(Model model) {
if (model == null) {
return false;
}
if (!(model instanceof IfModel)) {
return false;
}
IfModel conditional = (IfModel) model;
return ObjectUtils.nullSafeEquals(getTest(), conditional.getTest());
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (!(obj instanceof IfModel)) {
return false;
}
IfModel conditional = (IfModel) obj;
if (conditional == null) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getTest(), conditional.getTest())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getThen(), conditional.getThen())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getElse(), conditional.getElse())) {
return false;
} else {
return true;
}
}
public int hashCode() {
return ObjectUtils.nullSafeHashCode(getTest()) * 27 + ObjectUtils.nullSafeHashCode(getThen()) * 27
+ ObjectUtils.nullSafeHashCode(getElse()) * 27;
}
/**
* @return the test
*/
public String getTest() {
return test;
}
/**
* @param test the test to set
*/
public void setTest(String test) {
if (StringUtils.hasText(test)) {
this.test = test;
} else {
this.test = null;
}
}
/**
* @return the then
*/
public String getThen() {
return then;
}
/**
* @param then the then to set
*/
public void setThen(String then) {
if (StringUtils.hasText(then)) {
this.then = then;
} else {
this.then = null;
}
}
/**
* @return the else
*/
public String getElse() {
return elze;
}
/**
* @param elze the else to set
*/
public void setElse(String elze) {
if (StringUtils.hasText(elze)) {
this.elze = elze;
} else {
this.elze = null;
}
}
}

View File

@@ -0,0 +1,108 @@
/*
* 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.model;
import org.springframework.util.ObjectUtils;
/**
* Model support for input mappings.
* <p>
* Maps a single input attribute into this flow or subflow.
*
* @author Scott Andrews
*/
public class InputModel extends AbstractMappingModel {
/**
* Create an input mapping model
* @param name the name of the mapping variable
* @param value the value to map
*/
public InputModel(String name, String value) {
setName(name);
setValue(value);
}
/**
* Create an input mapping model
* @param name the name of the mapping variable
* @param value the value to map
* @param type the type of the value
* @param required indicates if this mapping is required
*/
public InputModel(String name, String value, String type, String required) {
setName(name);
setValue(value);
setType(type);
setRequired(required);
}
/**
* Merge properties
* @param model the mapping to merge into this mapping
*/
public void merge(Model model) {
if (isMergeableWith(model)) {
InputModel input = (InputModel) model;
setValue(merge(getValue(), input.getValue()));
setType(merge(getType(), input.getType()));
setRequired(merge(getRequired(), input.getRequired()));
}
}
/**
* Tests if the model is able to be merged with this input mapping
* @param model the model to test
*/
public boolean isMergeableWith(Model model) {
if (model == null) {
return false;
}
if (!(model instanceof InputModel)) {
return false;
}
InputModel input = (InputModel) model;
return ObjectUtils.nullSafeEquals(getName(), input.getName());
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (!(obj instanceof InputModel)) {
return false;
}
InputModel input = (InputModel) obj;
if (input == null) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getName(), input.getName())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getValue(), input.getValue())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getType(), input.getType())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getRequired(), input.getRequired())) {
return false;
} else {
return true;
}
}
public int hashCode() {
return ObjectUtils.nullSafeHashCode(getName()) * 27 + ObjectUtils.nullSafeHashCode(getValue()) * 27
+ ObjectUtils.nullSafeHashCode(getType()) * 27 + ObjectUtils.nullSafeHashCode(getRequired()) * 27;
}
}

View File

@@ -0,0 +1,23 @@
package org.springframework.webflow.engine.model;
/**
* Interface defining models. All models must be able to handle merging of their content with an eligible model.
*
* @author Scott Andrews
*/
public interface Model {
/**
* Determine if the model is able to be merged into the current model
* @param model the model to compare
* @return true if able to merge
*/
public boolean isMergeableWith(Model model);
/**
* Merge the model into the current model
* @param model the model to merge with
*/
public void merge(Model model);
}

View File

@@ -0,0 +1,108 @@
/*
* 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.model;
import org.springframework.util.ObjectUtils;
/**
* Model support for output mappings.
* <p>
* Maps a single output attribute out of this flow or subflow.
*
* @author Scott Andrews
*/
public class OutputModel extends AbstractMappingModel {
/**
* Create an output mapping model
* @param name the name of the mapping variable
* @param value the value to map
*/
public OutputModel(String name, String value) {
setName(name);
setValue(value);
}
/**
* Create an output mapping model
* @param name the name of the mapping variable
* @param value the value to map
* @param type the type of the value
* @param required indicates if this mapping is required
*/
public OutputModel(String name, String value, String type, String required) {
setName(name);
setValue(value);
setType(type);
setRequired(required);
}
/**
* Merge properties
* @param model the mapping to merge into this mapping
*/
public void merge(Model model) {
if (isMergeableWith(model)) {
OutputModel output = (OutputModel) model;
setValue(merge(getValue(), output.getValue()));
setType(merge(getType(), output.getType()));
setRequired(merge(getRequired(), output.getRequired()));
}
}
/**
* Tests if the model is able to be merged with this output mapping
* @param model the model to test
*/
public boolean isMergeableWith(Model model) {
if (model == null) {
return false;
}
if (!(model instanceof OutputModel)) {
return false;
}
OutputModel output = (OutputModel) model;
return ObjectUtils.nullSafeEquals(getName(), output.getName());
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (!(obj instanceof InputModel)) {
return false;
}
OutputModel output = (OutputModel) obj;
if (output == null) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getName(), output.getName())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getValue(), output.getValue())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getType(), output.getType())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getRequired(), output.getRequired())) {
return false;
} else {
return true;
}
}
public int hashCode() {
return ObjectUtils.nullSafeHashCode(getName()) * 27 + ObjectUtils.nullSafeHashCode(getValue()) * 27
+ ObjectUtils.nullSafeHashCode(getType()) * 27 + ObjectUtils.nullSafeHashCode(getRequired()) * 27;
}
}

View File

@@ -0,0 +1,51 @@
/*
* 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.model;
/**
* Model support for persistence context elements.
* <p>
* Allocates a persistence context when this flow starts. The persistence context is closed when the flow ends. If the
* flow ends by reaching a "commit" end-state, changes made to managed persistent entities during the course of flow
* execution are flushed to the database in a transaction.
* <p>
* The persistence context can be referenced from within this flow by the "entityManager" variable.
*
* @author Scott Andrews
*/
public class PersistenceContextModel extends AbstractModel {
/**
* Create a persistence context model
*/
public PersistenceContextModel() {
}
/**
* Persistence contexts are not mergeable
*/
public void merge(Model model) {
// not mergeable
}
/**
* Persistence contexts are not mergeable
*/
public boolean isMergeableWith(Model model) {
return false;
}
}

View File

@@ -0,0 +1,93 @@
/*
* 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.model;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Model support for render actions.
* <p>
* Requests that the next view render a fragment of content. Multiple fragments may be specified using a comma
* delimiter.
*
* @author Scott Andrews
*/
public class RenderModel extends AbstractActionModel {
private String fragments;
/**
* Create a render action model
* @param fragments the fragments to render
*/
public RenderModel(String fragments) {
setFragments(fragments);
}
/**
* Render action models are not mergeable
* @param model the render action to merge into this render
*/
public void merge(Model model) {
// not mergeable
}
/**
* Render action models are not mergeable
* @param model the model to test
*/
public boolean isMergeableWith(Model model) {
return false;
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (!(obj instanceof RenderModel)) {
return false;
}
RenderModel render = (RenderModel) obj;
if (render == null) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getFragments(), render.getFragments())) {
return false;
} else {
return true;
}
}
public int hashCode() {
return ObjectUtils.nullSafeHashCode(getFragments()) * 27;
}
/**
* @return the fragments
*/
public String getFragments() {
return fragments;
}
/**
* @param fragments the fragments to set
*/
public void setFragments(String fragments) {
if (StringUtils.hasText(fragments)) {
this.fragments = fragments;
} else {
this.fragments = null;
}
}
}

View File

@@ -0,0 +1,139 @@
/*
* 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.model;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.webflow.security.SecurityFlowExecutionListener;
/**
* Model support for secured elements.
* <p>
* Secures a flow, state or transition. The user invoking this element must meet the required attributes otherwise
* access will be denied.
* <p>
* <b>Warning:</b> This model will only configure a security attribute in the definition. The flow execution must also
* be secured with a SecurityFlowExecutionListener.
*
* @see SecurityFlowExecutionListener
* @author Scott Andrews
*/
public class SecuredModel extends AbstractModel {
private String attributes;
private String match;
/**
* Create a security settings model
* @param attributes the security attributes
*/
public SecuredModel(String attributes) {
setAttributes(attributes);
}
/**
* Create a security settings model
* @param attributes the security attributes
* @param match the type of matching for the attributes
*/
public SecuredModel(String attributes, String match) {
setAttributes(attributes);
setMatch(match);
}
/**
* Merge properties
* @param model the secured to merge into this secured
*/
public void merge(Model model) {
if (isMergeableWith(model)) {
SecuredModel secured = (SecuredModel) model;
setMatch(merge(getMatch(), secured.getMatch()));
}
}
/**
* Tests if the model is able to be merged with this secured attribute
* @param model the model to test
*/
public boolean isMergeableWith(Model model) {
if (model == null) {
return false;
}
if (!(model instanceof SecuredModel)) {
return false;
}
SecuredModel secured = (SecuredModel) model;
return ObjectUtils.nullSafeEquals(getAttributes(), secured.getAttributes());
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (!(obj instanceof SecuredModel)) {
return false;
}
SecuredModel secured = (SecuredModel) obj;
if (secured == null) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getAttributes(), secured.getAttributes())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getMatch(), secured.getMatch())) {
return false;
} else {
return true;
}
}
public int hashCode() {
return ObjectUtils.nullSafeHashCode(getAttributes()) * 27 + ObjectUtils.nullSafeHashCode(getMatch()) * 27;
}
/**
* @return the attributes
*/
public String getAttributes() {
return attributes;
}
/**
* @param attributes the attributes to set
*/
public void setAttributes(String attributes) {
if (StringUtils.hasText(attributes)) {
this.attributes = attributes;
} else {
this.attributes = null;
}
}
/**
* @return the match
*/
public String getMatch() {
return match;
}
/**
* @param match the match to set
*/
public void setMatch(String match) {
if (StringUtils.hasText(match)) {
this.match = match;
} else {
this.match = null;
}
}
}

View File

@@ -0,0 +1,161 @@
/*
* 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.model;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Model support for set actions.
* <p>
* Sets an attribute value in a scope.
*
* @author Scott Andrews
*/
public class SetModel extends AbstractActionModel {
private String name;
private String value;
private String type;
/**
* Create a set action model
* @param name the name of the property to set
* @param value the value to set
*/
public SetModel(String name, String value) {
setName(name);
setValue(value);
}
/**
* Create a set action model
* @param name the name of the property to set
* @param value the value to set
* @param type the type of the property
*/
public SetModel(String name, String value, String type) {
setName(name);
setValue(value);
setType(type);
}
/**
* Merge properties
* @param model the set action to merge into this set
*/
public void merge(Model model) {
if (isMergeableWith(model)) {
SetModel set = (SetModel) model;
setValue(merge(getValue(), set.getValue()));
setType(merge(getType(), set.getType()));
}
}
/**
* Tests if the model is able to be merged with this set action
* @param model the model to test
*/
public boolean isMergeableWith(Model model) {
if (model == null) {
return false;
}
if (!(model instanceof SetModel)) {
return false;
}
SetModel set = (SetModel) model;
return ObjectUtils.nullSafeEquals(getName(), set.getName());
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof SetModel)) {
return false;
}
SetModel set = (SetModel) obj;
if (set == null) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getName(), set.getName())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getValue(), set.getValue())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getType(), set.getType())) {
return false;
} else {
return true;
}
}
public int hashCode() {
return ObjectUtils.nullSafeHashCode(getName()) * 27 + ObjectUtils.nullSafeHashCode(getValue()) * 27
+ ObjectUtils.nullSafeHashCode(getType()) * 27;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
if (StringUtils.hasText(name)) {
this.name = name;
} else {
this.name = null;
}
}
/**
* @return the value
*/
public String getValue() {
return value;
}
/**
* @param value the value to set
*/
public void setValue(String value) {
if (StringUtils.hasText(value)) {
this.value = value;
} else {
this.value = null;
}
}
/**
* @return the type
*/
public String getType() {
return type;
}
/**
* @param type the type to set
*/
public void setType(String type) {
if (StringUtils.hasText(type)) {
this.type = type;
} else {
this.type = null;
}
}
}

View File

@@ -0,0 +1,275 @@
/*
* 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.model;
import java.util.LinkedList;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Model support for subflow states.
* <p>
* Starts another flow as a subflow when entered. When the subflow ends, this state is expected to respond to its result
* by executing a transition.
* <p>
* A subflow state is a transitionable state. A transition is triggered by the subflow outcome that was reached.
*
* @author Scott Andrews
*/
public class SubflowStateModel extends AbstractTransitionableStateModel {
private String subflow;
private String subflowAttributeMapper;
private LinkedList inputs;
private LinkedList outputs;
/**
* Create a subflow state model
* @param id the identifier of the state
* @param subflow the identifier of the flow to launch as a subflow
*/
public SubflowStateModel(String id, String subflow) {
setId(id);
setSubflow(subflow);
}
/**
* Create a subflow state model
* @param id the identifier of the state
* @param subflow the identifier of the flow to launch as a subflow
* @param subflowAttributeMapper bean name of the attribute mapping
* @param inputs input mappings
* @param outputs output mappings
* @param attributes meta attributes for the state
* @param secured security settings for the state
* @param onEntryActions actions to be executed when entering the state
* @param exceptionHandlers exception handlers for the state
* @param transitions transitions for the state
* @param onExitActions actions to be executed before leaving the state.
*/
public SubflowStateModel(String id, String subflow, String subflowAttributeMapper, LinkedList inputs,
LinkedList outputs, LinkedList attributes, SecuredModel secured, LinkedList onEntryActions,
LinkedList exceptionHandlers, LinkedList transitions, LinkedList onExitActions) {
setId(id);
setSubflow(subflow);
setSubflowAttributeMapper(subflowAttributeMapper);
setInputs(inputs);
setOutputs(outputs);
setAttributes(attributes);
setSecured(secured);
setOnEntryActions(onEntryActions);
setExceptionHandlers(exceptionHandlers);
setTransitions(transitions);
setOnExitActions(onExitActions);
}
/**
* Merge properties
* @param model the subflow state to merge into this state
*/
public void merge(Model model) {
if (isMergeableWith(model)) {
SubflowStateModel state = (SubflowStateModel) model;
setAttributes(merge(getAttributes(), state.getAttributes()));
setSecured((SecuredModel) merge(getSecured(), state.getSecured()));
setOnEntryActions(merge(getOnEntryActions(), state.getOnEntryActions(), false));
setExceptionHandlers(merge(getExceptionHandlers(), state.getExceptionHandlers()));
setTransitions(merge(getTransitions(), state.getTransitions()));
setOnExitActions(merge(getOnExitActions(), state.getOnExitActions(), false));
setSubflow(merge(getSubflow(), state.getSubflow()));
setSubflowAttributeMapper(merge(getSubflowAttributeMapper(), state.getSubflowAttributeMapper()));
setInputs(merge(getInputs(), state.getInputs()));
setOutputs(merge(getOutputs(), state.getOutputs()));
}
}
/**
* Tests if the model is able to be merged with this subflow state
* @param model the model to test
*/
public boolean isMergeableWith(Model model) {
if (model == null) {
return false;
}
if (!(model instanceof SubflowStateModel)) {
return false;
}
SubflowStateModel state = (SubflowStateModel) model;
return ObjectUtils.nullSafeEquals(getId(), state.getId());
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (!(obj instanceof SubflowStateModel)) {
return false;
}
SubflowStateModel state = (SubflowStateModel) obj;
if (state == null) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getId(), state.getId())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getAttributes(), state.getAttributes())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getSecured(), state.getSecured())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getOnEntryActions(), state.getOnEntryActions())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getExceptionHandlers(), state.getExceptionHandlers())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getTransitions(), state.getTransitions())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getOnExitActions(), state.getOnExitActions())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getSubflow(), state.getSubflow())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getSubflowAttributeMapper(), state.getSubflowAttributeMapper())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getInputs(), state.getInputs())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getOutputs(), state.getOutputs())) {
return false;
} else {
return true;
}
}
public int hashCode() {
return ObjectUtils.nullSafeHashCode(getId()) * 27 + ObjectUtils.nullSafeHashCode(getAttributes()) * 27
+ ObjectUtils.nullSafeHashCode(getSecured()) * 27 + ObjectUtils.nullSafeHashCode(getOnEntryActions())
* 27 + ObjectUtils.nullSafeHashCode(getExceptionHandlers()) * 27
+ ObjectUtils.nullSafeHashCode(getTransitions()) * 27
+ ObjectUtils.nullSafeHashCode(getOnExitActions()) * 27 + ObjectUtils.nullSafeHashCode(getSubflow())
* 27 + ObjectUtils.nullSafeHashCode(getSubflowAttributeMapper()) * 27
+ ObjectUtils.nullSafeHashCode(getInputs()) * 27 + ObjectUtils.nullSafeHashCode(getOutputs()) * 27;
}
/**
* @return the subflow
*/
public String getSubflow() {
return subflow;
}
/**
* @param subflow the subflow to set
*/
public void setSubflow(String subflow) {
if (StringUtils.hasText(subflow)) {
this.subflow = subflow;
} else {
this.subflow = null;
}
}
/**
* @return the subflow attribute mapper
*/
public String getSubflowAttributeMapper() {
return subflowAttributeMapper;
}
/**
* @param subflowAttributeMapper the subflow attribute mapper to set
*/
public void setSubflowAttributeMapper(String subflowAttributeMapper) {
if (StringUtils.hasText(subflowAttributeMapper)) {
this.subflowAttributeMapper = subflowAttributeMapper;
} else {
this.subflowAttributeMapper = null;
}
}
/**
* @return the input mappings
*/
public LinkedList getInputs() {
return inputs;
}
/**
* @param inputs the input mappings to set
*/
public void setInputs(LinkedList inputs) {
this.inputs = inputs;
}
/**
* @param input the input mapping to add
*/
public void addInput(InputModel input) {
if (input == null) {
return;
}
if (inputs == null) {
inputs = new LinkedList();
}
inputs.add(input);
}
/**
* @param inputs the input mappings to add
*/
public void addInputs(LinkedList inputs) {
if (inputs == null || inputs.isEmpty()) {
return;
}
if (this.inputs == null) {
this.inputs = new LinkedList();
}
this.inputs.addAll(inputs);
}
/**
* @return the output mappings
*/
public LinkedList getOutputs() {
return outputs;
}
/**
* @param outputs the output mappings to set
*/
public void setOutputs(LinkedList outputs) {
this.outputs = outputs;
}
/**
* @param output the output mapping to add
*/
public void addOutput(OutputModel output) {
if (output == null) {
return;
}
if (outputs == null) {
outputs = new LinkedList();
}
outputs.add(output);
}
/**
* @param outputs the output mappings to add
*/
public void addOutputs(LinkedList outputs) {
if (outputs == null || outputs.isEmpty()) {
return;
}
if (this.outputs == null) {
this.outputs = new LinkedList();
}
this.outputs.addAll(outputs);
}
}

View File

@@ -0,0 +1,311 @@
/*
* 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.model;
import java.util.LinkedList;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Model support for transitions.
* <p>
* A path from this state to another state triggered by an event. Transitions may execute one or more actions. All
* transition actions must execute successfully for the transition itself to complete. If no transition target is
* specified, the transition acts as a simple event handler and does not change the state of the flow.
*
* @author Scott Andrews
*/
public class TransitionModel extends AbstractModel {
private String on;
private String onException;
private String to;
private String bind;
private LinkedList attributes;
private SecuredModel secured;
private LinkedList actions;
/**
* Create a transition model
* @param on the matching criteria
*/
public TransitionModel(String on) {
setOn(on);
}
/**
* Create a transition model
* @param on the matching criteria
* @param to the identifier of the state to target
*/
public TransitionModel(String on, String to) {
setOn(on);
setTo(to);
}
/**
* Create a transition model
* @param on the matching criteria
* @param to the identifier of the state to target
* @param onException class name of the exception to handle
* @param bind if the transition should bind to the defined model. Valid only for view state transitions
* @param attributes meta attributes for the transition
* @param secured security settings for the transition
* @param actions actions to be executed after matching the transition
*/
public TransitionModel(String on, String to, String onException, String bind, LinkedList attributes,
SecuredModel secured, LinkedList actions) {
setOn(on);
setTo(to);
setOnException(onException);
setBind(bind);
setAttributes(attributes);
setSecured(secured);
setActions(actions);
}
/**
* Merge properties
* @param model the transition to merge into this transition
*/
public void merge(Model model) {
if (isMergeableWith(model)) {
TransitionModel transition = (TransitionModel) model;
setOnException(merge(getOnException(), transition.getOnException()));
setTo(merge(getTo(), transition.getTo()));
setBind(merge(getBind(), transition.getBind()));
setAttributes(merge(getAttributes(), transition.getAttributes()));
setSecured((SecuredModel) merge(getSecured(), transition.getSecured()));
setActions(merge(getActions(), transition.getActions(), false));
}
}
/**
* Tests if the model is able to be merged with this transition
* @param model the model to test
*/
public boolean isMergeableWith(Model model) {
if (model == null) {
return false;
}
if (!(model instanceof TransitionModel)) {
return false;
}
TransitionModel transition = (TransitionModel) model;
return ObjectUtils.nullSafeEquals(getOn(), transition.getOn());
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (!(obj instanceof TransitionModel)) {
return false;
}
TransitionModel transition = (TransitionModel) obj;
if (transition == null) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getOn(), transition.getOn())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getOnException(), transition.getOnException())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getTo(), transition.getTo())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getBind(), transition.getBind())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getAttributes(), transition.getAttributes())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getSecured(), transition.getSecured())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getActions(), transition.getActions())) {
return false;
} else {
return true;
}
}
public int hashCode() {
return ObjectUtils.nullSafeHashCode(getOn()) * 27 + ObjectUtils.nullSafeHashCode(getOnException()) * 27
+ ObjectUtils.nullSafeHashCode(getTo()) * 27 + ObjectUtils.nullSafeHashCode(getBind()) * 27
+ ObjectUtils.nullSafeHashCode(getAttributes()) * 27 + ObjectUtils.nullSafeHashCode(getSecured()) * 27
+ ObjectUtils.nullSafeHashCode(getActions()) * 27;
}
/**
* @return the on
*/
public String getOn() {
return on;
}
/**
* @param on the on to set
*/
public void setOn(String on) {
if (StringUtils.hasText(on)) {
this.on = on;
} else {
this.on = null;
}
}
/**
* @return the on exception
*/
public String getOnException() {
return onException;
}
/**
* @param onException the on exception to set
*/
public void setOnException(String onException) {
if (StringUtils.hasText(onException)) {
this.onException = onException;
} else {
this.onException = null;
}
}
/**
* @return the to
*/
public String getTo() {
return to;
}
/**
* @param to the to to set
*/
public void setTo(String to) {
if (StringUtils.hasText(to)) {
this.to = to;
} else {
this.to = null;
}
}
/**
* @return the bind
*/
public String getBind() {
return bind;
}
/**
* @param bind the bind to set
*/
public void setBind(String bind) {
if (StringUtils.hasText(bind)) {
this.bind = bind;
} else {
this.bind = null;
}
}
/**
* @return the attributes
*/
public LinkedList getAttributes() {
return attributes;
}
/**
* @param attributes the attributes to set
*/
public void setAttributes(LinkedList attributes) {
this.attributes = attributes;
}
/**
* @param attribute the attribute to add
*/
public void addAttribute(AttributeModel attribute) {
if (attribute == null) {
return;
}
if (attributes == null) {
attributes = new LinkedList();
}
attributes.add(attribute);
}
/**
* @param attributes the attributes to add
*/
public void addAttributes(LinkedList attributes) {
if (attributes == null || attributes.isEmpty()) {
return;
}
if (this.attributes == null) {
this.attributes = new LinkedList();
}
this.attributes.addAll(attributes);
}
/**
* @return the secured
*/
public SecuredModel getSecured() {
return secured;
}
/**
* @param secured the secured to set
*/
public void setSecured(SecuredModel secured) {
this.secured = secured;
}
/**
* @return the actions
*/
public LinkedList getActions() {
return actions;
}
/**
* @param actions the actions to set
*/
public void setActions(LinkedList actions) {
this.actions = actions;
}
/**
* @param action the action to add
*/
public void addAction(AbstractActionModel action) {
if (action == null) {
return;
}
if (actions == null) {
actions = new LinkedList();
}
actions.add(action);
}
/**
* @param actions the actions to add
*/
public void addActions(LinkedList actions) {
if (actions == null || actions.isEmpty()) {
return;
}
if (this.actions == null) {
this.actions = new LinkedList();
}
this.actions.addAll(actions);
}
}

View File

@@ -0,0 +1,161 @@
/*
* 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.model;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Model support for var elements.
* <p>
* An instance variable. Variables are created when the flow starts or state enters and destroyed when the flow or state
* ends, respectively.
*
* @author Scott Andrews
*/
public class VarModel extends AbstractModel {
private String name;
private String className;
private String scope;
/**
* Create a variable model
* @param name the name of the variable
* @param className the class type of the variable
*/
public VarModel(String name, String className) {
setName(name);
setClassName(className);
}
/**
* Create a variable model
* @param name the name of the variable
* @param className the class type of the variable
* @param scope the scope to store the variable
*/
public VarModel(String name, String className, String scope) {
setName(name);
setClassName(className);
setScope(scope);
}
/**
* Merge properties
* @param model the var to merge into this var
*/
public void merge(Model model) {
if (isMergeableWith(model)) {
VarModel var = (VarModel) model;
setClassName(merge(getClassName(), var.getClassName()));
setScope(merge(getScope(), var.getScope()));
}
}
/**
* Tests if the model is able to be merged with this var
* @param model the model to test
*/
public boolean isMergeableWith(Model model) {
if (model == null) {
return false;
}
if (!(model instanceof VarModel)) {
return false;
}
VarModel var = (VarModel) model;
return ObjectUtils.nullSafeEquals(getName(), var.getName());
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (!(obj instanceof VarModel)) {
return false;
}
VarModel var = (VarModel) obj;
if (var == null) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getName(), var.getName())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getClassName(), var.getClassName())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getScope(), var.getScope())) {
return false;
} else {
return true;
}
}
public int hashCode() {
return ObjectUtils.nullSafeHashCode(getName()) * 27 + ObjectUtils.nullSafeHashCode(getClassName()) * 27
+ ObjectUtils.nullSafeHashCode(getScope()) * 27;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
if (StringUtils.hasText(name)) {
this.name = name;
} else {
this.name = null;
}
}
/**
* @return the class name
*/
public String getClassName() {
return className;
}
/**
* @param className the class name to set
*/
public void setClassName(String className) {
if (StringUtils.hasText(className)) {
this.className = className;
} else {
this.className = null;
}
}
/**
* @return the scope
*/
public String getScope() {
return scope;
}
/**
* @param scope the scope to set
*/
public void setScope(String scope) {
if (StringUtils.hasText(scope)) {
this.scope = scope;
} else {
this.scope = null;
}
}
}

View File

@@ -0,0 +1,339 @@
/*
* 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.model;
import java.util.LinkedList;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Model support for view states.
* <p>
* A state where the user participates. When a view state is entered, this flow pauses and control goes to the user.
* After some think time, the user resumes this flow at the view-state by signaling an event.
* <p>
* Once paused, a view-state may be 'refreshed' by the user. A refresh causes the response to be reissued and then
* returns control back to the user.
* <p>
* A view state may be configured with one or more render-actions using the 'on-render' element. Render actions are
* executed immediately before the view is rendered.
* <p>
* A view state is a transitionable state. A view state transition is triggered by a user event.
*
* @author Scott Andrews
*/
public class ViewStateModel extends AbstractTransitionableStateModel {
private String view;
private String redirect;
private String popup;
private String model;
private LinkedList vars;
private LinkedList onRenderActions;
/**
* Create a view state model
* @param id the identifier of the state
*/
public ViewStateModel(String id) {
setId(id);
}
/**
* Create a view state model
* @param id the identifier of the state
* @param view the view to render
*/
public ViewStateModel(String id, String view) {
setId(id);
setView(view);
}
/**
* Create a view state model
* @param id the identifier of the state
* @param view the view to render
* @param redirect request a flow execution redirect before render
* @param popup view should render in a popup dialog
* @param model the model object to bind for this view
* @param vars variables for this state
* @param onRenderActions actions to be executed before rendering
* @param attributes meta attributes for this state
* @param secured the security settings for this state
* @param onEntryActions actions to be executed on entry
* @param exceptionHandlers exception handlers for this state
* @param transitions transitions for this state
* @param onExitActions actions to be executed before exiting
*/
public ViewStateModel(String id, String view, String redirect, String popup, String model, LinkedList vars,
LinkedList onRenderActions, LinkedList attributes, SecuredModel secured, LinkedList onEntryActions,
LinkedList exceptionHandlers, LinkedList transitions, LinkedList onExitActions) {
setId(id);
setView(view);
setRedirect(redirect);
setPopup(popup);
setModel(model);
setVars(vars);
setOnRenderActions(onRenderActions);
setAttributes(attributes);
setSecured(secured);
setOnEntryActions(onEntryActions);
setExceptionHandlers(exceptionHandlers);
setTransitions(transitions);
setOnExitActions(onExitActions);
}
/**
* Merge properties
* @param model the view state to merge into this state
*/
public void merge(Model model) {
if (isMergeableWith(model)) {
ViewStateModel state = (ViewStateModel) model;
setAttributes(merge(getAttributes(), state.getAttributes()));
setSecured((SecuredModel) merge(getSecured(), state.getSecured()));
setOnEntryActions(merge(getOnEntryActions(), state.getOnEntryActions(), false));
setExceptionHandlers(merge(getExceptionHandlers(), state.getExceptionHandlers()));
setTransitions(merge(getTransitions(), state.getTransitions()));
setOnExitActions(merge(getOnExitActions(), state.getOnExitActions(), false));
setView(merge(getView(), state.getView()));
setRedirect(merge(getRedirect(), state.getRedirect()));
setPopup(merge(getPopup(), state.getPopup()));
setModel(merge(getModel(), state.getModel()));
setVars(merge(getVars(), state.getVars(), false));
setOnRenderActions(merge(getOnRenderActions(), state.getOnRenderActions(), false));
}
}
/**
* Tests if the model is able to be merged with this view state
* @param model the model to test
*/
public boolean isMergeableWith(Model model) {
if (model == null) {
return false;
}
if (!(model instanceof ViewStateModel)) {
return false;
}
ViewStateModel state = (ViewStateModel) model;
return ObjectUtils.nullSafeEquals(getId(), state.getId());
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (!(obj instanceof ViewStateModel)) {
return false;
}
ViewStateModel state = (ViewStateModel) obj;
if (state == null) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getId(), state.getId())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getAttributes(), state.getAttributes())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getSecured(), state.getSecured())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getOnEntryActions(), state.getOnEntryActions())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getExceptionHandlers(), state.getExceptionHandlers())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getTransitions(), state.getTransitions())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getOnExitActions(), state.getOnExitActions())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getView(), state.getView())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getRedirect(), state.getRedirect())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getPopup(), state.getPopup())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getModel(), state.getModel())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getVars(), state.getVars())) {
return false;
} else if (!ObjectUtils.nullSafeEquals(getOnRenderActions(), state.getOnRenderActions())) {
return false;
} else {
return true;
}
}
public int hashCode() {
return ObjectUtils.nullSafeHashCode(getId()) * 27 + ObjectUtils.nullSafeHashCode(getAttributes()) * 27
+ ObjectUtils.nullSafeHashCode(getSecured()) * 27 + ObjectUtils.nullSafeHashCode(getOnEntryActions())
* 27 + ObjectUtils.nullSafeHashCode(getExceptionHandlers()) * 27
+ ObjectUtils.nullSafeHashCode(getTransitions()) * 27
+ ObjectUtils.nullSafeHashCode(getOnExitActions()) * 27 + ObjectUtils.nullSafeHashCode(getView()) * 27
+ ObjectUtils.nullSafeHashCode(getRedirect()) * 27 + ObjectUtils.nullSafeHashCode(getPopup()) * 27
+ ObjectUtils.nullSafeHashCode(getModel()) * 27 + ObjectUtils.nullSafeHashCode(getVars()) * 27
+ ObjectUtils.nullSafeHashCode(getOnRenderActions()) * 27;
}
/**
* @return the view
*/
public String getView() {
return view;
}
/**
* @param view the view to set
*/
public void setView(String view) {
if (StringUtils.hasText(view)) {
this.view = view;
} else {
this.view = null;
}
}
/**
* @return the redirect
*/
public String getRedirect() {
return redirect;
}
/**
* @param redirect the redirect to set
*/
public void setRedirect(String redirect) {
if (StringUtils.hasText(redirect)) {
this.redirect = redirect;
} else {
this.redirect = null;
}
}
/**
* @return the popup
*/
public String getPopup() {
return popup;
}
/**
* @param popup the popup to set
*/
public void setPopup(String popup) {
if (StringUtils.hasText(popup)) {
this.popup = popup;
} else {
this.popup = null;
}
}
/**
* @return the model
*/
public String getModel() {
return model;
}
/**
* @param model the model to set
*/
public void setModel(String model) {
if (StringUtils.hasText(model)) {
this.model = model;
} else {
this.model = null;
}
}
/**
* @return the vars
*/
public LinkedList getVars() {
return vars;
}
/**
* @param vars the vars to set
*/
public void setVars(LinkedList vars) {
this.vars = vars;
}
/**
* @param var the var to add
*/
public void addVar(VarModel var) {
if (var == null) {
return;
}
if (vars == null) {
vars = new LinkedList();
}
vars.add(var);
}
/**
* @param vars the vars to add
*/
public void addVars(LinkedList vars) {
if (vars == null || vars.isEmpty()) {
return;
}
if (this.vars == null) {
this.vars = new LinkedList();
}
this.vars.addAll(vars);
}
/**
* @return the on render actions
*/
public LinkedList getOnRenderActions() {
return onRenderActions;
}
/**
* @param onRenderActions the on render actions to set
*/
public void setOnRenderActions(LinkedList onRenderActions) {
this.onRenderActions = onRenderActions;
}
/**
* @param onRenderAction the on render action to add
*/
public void addOnRenderAction(AbstractActionModel onRenderAction) {
if (onRenderAction == null) {
return;
}
if (this.onRenderActions == null) {
this.onRenderActions = new LinkedList();
}
this.onRenderActions.add(onRenderAction);
}
/**
* @param onRenderActions the on render actions to add
*/
public void addOnRenderActions(LinkedList onRenderActions) {
if (onRenderActions == null || onRenderActions.isEmpty()) {
return;
}
if (this.onRenderActions == null) {
this.onRenderActions = new LinkedList();
}
this.onRenderActions.addAll(onRenderActions);
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2004-2008 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.model.builder;
import org.springframework.webflow.engine.model.FlowModel;
/**
* Builder interface used to build a flow model. The process of building a flow model consists of the following steps:
* <ol>
* <li> Initialize this builder by calling {@link #init()}.
* <li> Call {@link #build()} to create the flow model.
* <li> Call {@link #getFlowModel()} to return the fully-built {@link FlowModel} model.
* <li> Dispose this builder, releasing any resources allocated during the building process by calling
* {@link #dispose()}.
* </ol>
* <p>
* Implementations should encapsulate flow construction logic, either for a specific kind of flow, for example, an
* <code>XmlFlowModelBuilder</code>, for building flows from an XML-definition.
* <p>
* This is a good example of the classic GoF builder pattern.
*
* @see FlowModel
*
* @author Keith Donald
* @author Erwin Vervaet
* @author Scott Andrews
*/
public interface FlowModelBuilder {
/**
* Initialize this builder. This could cause the builder to open a stream to an externalized resource representing
* the flow definition, for example.
* @throws FlowModelBuilderException an exception occurred building the flow
*/
public void init() throws FlowModelBuilderException;
/**
* Builds any variables initialized by the flow when it starts.
* @throws FlowModelBuilderException an exception occurred building the flow
*/
public void build() throws FlowModelBuilderException;
/**
* Get the fully constructed flow model. Called by the builder's assembler (director) after assembly. When this
* method is called by the assembler, it is expected flow construction has completed and the returned flow model is
* ready for use.
* @throws FlowModelBuilderException an exception occurred building this flow
*/
public FlowModel getFlowModel() throws FlowModelBuilderException;
/**
* Shutdown the builder, releasing any resources it holds. A new flow construction process should start with another
* call to the {@link #init()} method.
* @throws FlowModelBuilderException an exception occurred disposing this flow
*/
public void dispose() throws FlowModelBuilderException;
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2004-2008 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.model.builder;
import org.springframework.webflow.core.FlowException;
/**
* Exception thrown to indicate a problem while building a flow model.
*
* @see FlowModelBuilder
*
* @author Erwin Vervaet
* @author Scott Andrews
*/
public class FlowModelBuilderException extends FlowException {
/**
* Create a new flow model builder exception.
* @param message descriptive message
*/
public FlowModelBuilderException(String message) {
super(message);
}
/**
* Create a new flow model builder exception.
* @param message descriptive message
* @param cause the underlying cause of this exception
*/
public FlowModelBuilderException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.webflow.engine.builder.xml;
package org.springframework.webflow.engine.model.builder.xml;
import java.io.IOException;
import java.io.InputStream;

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.webflow.engine.builder.xml;
package org.springframework.webflow.engine.model.builder.xml;
import java.io.IOException;

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.webflow.engine.builder.xml;
package org.springframework.webflow.engine.model.builder.xml;
import java.io.IOException;

View File

@@ -0,0 +1,589 @@
/*
* 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.model.builder.xml;
import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import javax.xml.parsers.ParserConfigurationException;
import org.springframework.core.io.Resource;
import org.springframework.core.style.ToStringCreator;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.springframework.webflow.engine.model.AbstractActionModel;
import org.springframework.webflow.engine.model.AbstractStateModel;
import org.springframework.webflow.engine.model.ActionStateModel;
import org.springframework.webflow.engine.model.AttributeModel;
import org.springframework.webflow.engine.model.BeanImportModel;
import org.springframework.webflow.engine.model.DecisionStateModel;
import org.springframework.webflow.engine.model.EndStateModel;
import org.springframework.webflow.engine.model.EvaluateModel;
import org.springframework.webflow.engine.model.ExceptionHandlerModel;
import org.springframework.webflow.engine.model.FlowModel;
import org.springframework.webflow.engine.model.IfModel;
import org.springframework.webflow.engine.model.InputModel;
import org.springframework.webflow.engine.model.OutputModel;
import org.springframework.webflow.engine.model.PersistenceContextModel;
import org.springframework.webflow.engine.model.RenderModel;
import org.springframework.webflow.engine.model.SecuredModel;
import org.springframework.webflow.engine.model.SetModel;
import org.springframework.webflow.engine.model.SubflowStateModel;
import org.springframework.webflow.engine.model.TransitionModel;
import org.springframework.webflow.engine.model.VarModel;
import org.springframework.webflow.engine.model.ViewStateModel;
import org.springframework.webflow.engine.model.builder.FlowModelBuilder;
import org.springframework.webflow.engine.model.builder.FlowModelBuilderException;
import org.springframework.webflow.engine.model.registry.FlowModelRegistry;
import org.springframework.webflow.engine.model.registry.NoSuchFlowModelException;
import org.springframework.webflow.util.ResourceHolder;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
public class XmlFlowModelBuilder implements FlowModelBuilder, ResourceHolder {
/**
* The resource from which the document element being parsed was read. Used as a location for relative resource
* lookup.
*/
protected Resource resource;
/**
* The flow model registry used to lookup other flows
*/
protected FlowModelRegistry registry;
/**
* The loader for loading the flow definition resource XML document.
*/
private DocumentLoader documentLoader = new DefaultDocumentLoader();
/**
* The in-memory document object model (DOM) of the XML Document read from the flow definition resource.
*/
private Document document;
/**
* The flow model.
*/
private FlowModel flowModel;
/**
* Create a new XML flow builder parsing the document at the specified location, using the provided service locator
* to access externally managed flow artifacts.
*/
public XmlFlowModelBuilder(Resource resource, FlowModelRegistry registry) {
this.resource = resource;
this.registry = registry;
}
/**
* Sets the loader that will load the XML-based flow definition document. Optional, defaults to
* {@link DefaultDocumentLoader}.
* @param documentLoader the document loader
*/
public void setDocumentLoader(DocumentLoader documentLoader) {
Assert.notNull(documentLoader, "The XML document loader is required");
this.documentLoader = documentLoader;
}
public void init() throws FlowModelBuilderException {
try {
document = documentLoader.loadDocument(resource);
} catch (IOException e) {
throw new FlowModelBuilderException("Could not access the XML flow definition resource at " + resource, e);
} catch (ParserConfigurationException e) {
throw new FlowModelBuilderException("Could not configure the parser to parse the XML flow definition at "
+ resource, e);
} catch (SAXException e) {
throw new FlowModelBuilderException("Could not parse the XML flow definition document at " + resource, e);
}
}
public void build() throws FlowModelBuilderException {
if (getDocumentElement() == null) {
throw new FlowModelBuilderException("The FlowModelBuilder must be initialized first");
}
flowModel = parseFlow(getDocumentElement());
if (flowModel.getParent() != null) {
for (Iterator parentIt = Arrays.asList(StringUtils.trimArrayElements(flowModel.getParent().split(",")))
.iterator(); parentIt.hasNext();) {
String parentFlowId = (String) parentIt.next();
if (StringUtils.hasText(parentFlowId)) {
try {
flowModel.merge(registry.getFlowModel(parentFlowId));
} catch (NoSuchFlowModelException e) {
throw new FlowModelBuilderException("Unable to find flow '" + parentFlowId
+ "' to inherit from", e);
}
}
}
}
}
public FlowModel getFlowModel() throws FlowModelBuilderException {
return flowModel;
}
public void dispose() throws FlowModelBuilderException {
document = null;
flowModel = null;
}
protected FlowModel parseFlow(Element ele) {
FlowModel flow = new FlowModel();
flow.setParent(ele.getAttribute("parent"));
flow.setStartStateId(ele.getAttribute("start-state"));
flow.addAttributes(parseAttributes(ele));
flow.setSecured(parseSecured(ele));
flow.setPersistenceContext(parsePersistenceContext(ele));
flow.addVars(parseVars(ele));
flow.addInputs(parseInputs(ele));
flow.addOutputs(parseOutputs(ele));
flow.addOnStartActions(parseOnStartActions(ele));
flow.addStates(parseStates(ele));
flow.addGlobalTransitions(parseGlobalTransitions(ele));
flow.addOnEndActions(parseOnEndActions(ele));
flow.addExceptionHandlers(parseExceptionHandlers(ele));
flow.addBeanImports(parseBeanImports(ele));
return flow;
}
protected LinkedList parseAttributes(Element ele) {
LinkedList attributes = new LinkedList();
for (Iterator attributeIt = DomUtils.getChildElementsByTagName(ele, "attribute").iterator(); attributeIt
.hasNext();) {
attributes.add(parseAttribute((Element) attributeIt.next()));
}
return attributes;
}
protected LinkedList parseVars(Element ele) {
LinkedList vars = new LinkedList();
for (Iterator varIt = DomUtils.getChildElementsByTagName(ele, "var").iterator(); varIt.hasNext();) {
vars.add(parseVar((Element) varIt.next()));
}
return vars;
}
protected LinkedList parseInputs(Element ele) {
LinkedList inputs = new LinkedList();
for (Iterator inputIt = DomUtils.getChildElementsByTagName(ele, "input").iterator(); inputIt.hasNext();) {
inputs.add(parseInput((Element) inputIt.next()));
}
return inputs;
}
protected LinkedList parseOutputs(Element ele) {
LinkedList outputs = new LinkedList();
for (Iterator outputIt = DomUtils.getChildElementsByTagName(ele, "output").iterator(); outputIt.hasNext();) {
outputs.add(parseOutput((Element) outputIt.next()));
}
return outputs;
}
protected LinkedList parseActions(Element ele) {
LinkedList actions = new LinkedList();
for (Iterator actionIt = getChildElementsByTagNames(ele, new String[] { "evaluate", "render", "set" })
.iterator(); actionIt.hasNext();) {
actions.add(parseAction((Element) actionIt.next()));
}
return actions;
}
protected LinkedList parseStates(Element ele) {
LinkedList states = new LinkedList();
for (Iterator stateIt = getChildElementsByTagNames(ele,
new String[] { "action-state", "view-state", "decision-state", "subflow-state", "end-state" })
.iterator(); stateIt.hasNext();) {
states.add(parseState((Element) stateIt.next()));
}
return states;
}
protected LinkedList parseTransitions(Element ele) {
LinkedList transitions = new LinkedList();
for (Iterator transitionIt = DomUtils.getChildElementsByTagName(ele, "transition").iterator(); transitionIt
.hasNext();) {
transitions.add(parseTransition((Element) transitionIt.next()));
}
return transitions;
}
protected LinkedList parseExceptionHandlers(Element ele) {
LinkedList exceptionHandlers = new LinkedList();
for (Iterator exceptionHandlerIt = DomUtils.getChildElementsByTagName(ele, "exception-handler").iterator(); exceptionHandlerIt
.hasNext();) {
exceptionHandlers.add(parseExceptionHandler((Element) exceptionHandlerIt.next()));
}
return exceptionHandlers;
}
protected LinkedList parseBeanImports(Element ele) {
LinkedList beanImports = new LinkedList();
for (Iterator beanImportIt = DomUtils.getChildElementsByTagName(ele, "bean-import").iterator(); beanImportIt
.hasNext();) {
beanImports.add(parseBeanImport((Element) beanImportIt.next()));
}
return beanImports;
}
protected LinkedList parseIfs(Element ele) {
LinkedList ifs = new LinkedList();
for (Iterator ifIt = DomUtils.getChildElementsByTagName(ele, "if").iterator(); ifIt.hasNext();) {
ifs.add(parseIf((Element) ifIt.next()));
}
return ifs;
}
protected AbstractActionModel parseAction(Element ele) {
if (DomUtils.nodeNameEquals(ele, "evaluate")) {
return parseEvaluate(ele);
} else if (DomUtils.nodeNameEquals(ele, "render")) {
return parseRender(ele);
} else if (DomUtils.nodeNameEquals(ele, "set")) {
return parseSet(ele);
} else {
throw new UnsupportedOperationException("Unknown action element encountered '" + ele.getLocalName() + "'");
}
}
protected AbstractStateModel parseState(Element ele) {
if (DomUtils.nodeNameEquals(ele, "action-state")) {
return parseActionState(ele);
} else if (DomUtils.nodeNameEquals(ele, "view-state")) {
return parseViewState(ele);
} else if (DomUtils.nodeNameEquals(ele, "decision-state")) {
return parseDecisionState(ele);
} else if (DomUtils.nodeNameEquals(ele, "subflow-state")) {
return parseSubflowState(ele);
} else if (DomUtils.nodeNameEquals(ele, "end-state")) {
return parseEndState(ele);
} else {
throw new UnsupportedOperationException("Unknown state element encountered '" + ele.getLocalName() + "'");
}
}
protected LinkedList parseGlobalTransitions(Element ele) {
if (ele == null) {
return null;
} else if (!DomUtils.nodeNameEquals(ele, "global-transitions")) {
return parseGlobalTransitions(DomUtils.getChildElementByTagName(ele, "global-transitions"));
} else {
return parseTransitions(ele);
}
}
protected AttributeModel parseAttribute(Element ele) {
if (ele == null) {
return null;
} else if (!DomUtils.nodeNameEquals(ele, "attribute")) {
return parseAttribute(DomUtils.getChildElementByTagName(ele, "attribute"));
} else {
return new AttributeModel(ele.getAttribute("name"), parseValue(ele), ele.getAttribute("type"));
}
}
protected String parseValue(Element ele) {
if (ele.hasAttribute("value")) {
return ele.getAttribute("value");
} else {
Element valueEle = DomUtils.getChildElementByTagName(ele, "value");
return valueEle != null ? DomUtils.getTextValue(valueEle) : null;
}
}
protected SecuredModel parseSecured(Element ele) {
if (ele == null) {
return null;
} else if (!DomUtils.nodeNameEquals(ele, "secured")) {
return parseSecured(DomUtils.getChildElementByTagName(ele, "secured"));
} else {
return new SecuredModel(ele.getAttribute("attributes"), ele.getAttribute("match"));
}
}
protected PersistenceContextModel parsePersistenceContext(Element ele) {
if (ele == null) {
return null;
} else if (!DomUtils.nodeNameEquals(ele, "persistence-context")) {
return parsePersistenceContext(DomUtils.getChildElementByTagName(ele, "persistence-context"));
} else {
return new PersistenceContextModel();
}
}
protected VarModel parseVar(Element ele) {
if (ele == null) {
return null;
} else if (!DomUtils.nodeNameEquals(ele, "var")) {
return parseVar(DomUtils.getChildElementByTagName(ele, "var"));
} else {
return new VarModel(ele.getAttribute("name"), ele.getAttribute("class"), ele.getAttribute("scope"));
}
}
protected InputModel parseInput(Element ele) {
if (ele == null) {
return null;
} else if (!DomUtils.nodeNameEquals(ele, "input")) {
return parseInput(DomUtils.getChildElementByTagName(ele, "input"));
} else {
return new InputModel(ele.getAttribute("name"), ele.getAttribute("value"), ele.getAttribute("type"), ele
.getAttribute("required"));
}
}
protected OutputModel parseOutput(Element ele) {
if (ele == null) {
return null;
} else if (!DomUtils.nodeNameEquals(ele, "output")) {
return parseOutput(DomUtils.getChildElementByTagName(ele, "output"));
} else {
return new OutputModel(ele.getAttribute("name"), ele.getAttribute("value"), ele.getAttribute("type"), ele
.getAttribute("required"));
}
}
protected TransitionModel parseTransition(Element ele) {
if (ele == null) {
return null;
} else if (!DomUtils.nodeNameEquals(ele, "transition")) {
return parseTransition(DomUtils.getChildElementByTagName(ele, "transition"));
} else {
return new TransitionModel(ele.getAttribute("on"), ele.getAttribute("to"),
ele.getAttribute("on-exception"), ele.getAttribute("bind"), parseAttributes(ele),
parseSecured(ele), parseActions(ele));
}
}
protected ExceptionHandlerModel parseExceptionHandler(Element ele) {
if (ele == null) {
return null;
} else if (!DomUtils.nodeNameEquals(ele, "exception-handler")) {
return parseExceptionHandler(DomUtils.getChildElementByTagName(ele, "exception-handler"));
} else {
return new ExceptionHandlerModel(ele.getAttribute("bean-name"));
}
}
protected BeanImportModel parseBeanImport(Element ele) {
if (ele == null) {
return null;
} else if (!DomUtils.nodeNameEquals(ele, "bean-import")) {
return parseBeanImport(DomUtils.getChildElementByTagName(ele, "bean-import"));
} else {
return new BeanImportModel(ele.getAttribute("resource"));
}
}
protected IfModel parseIf(Element ele) {
if (ele == null) {
return null;
} else if (!DomUtils.nodeNameEquals(ele, "if")) {
return parseIf(DomUtils.getChildElementByTagName(ele, "if"));
} else {
return new IfModel(ele.getAttribute("test"), ele.getAttribute("then"), ele.getAttribute("else"));
}
}
protected LinkedList parseOnStartActions(Element ele) {
if (ele == null) {
return null;
} else if (!DomUtils.nodeNameEquals(ele, "on-start")) {
return parseOnStartActions(DomUtils.getChildElementByTagName(ele, "on-start"));
} else {
return parseActions(ele);
}
}
protected LinkedList parseOnEntryActions(Element ele) {
if (ele == null) {
return null;
} else if (!DomUtils.nodeNameEquals(ele, "on-entry")) {
return parseOnEntryActions(DomUtils.getChildElementByTagName(ele, "on-entry"));
} else {
return parseActions(ele);
}
}
protected LinkedList parseOnExitActions(Element ele) {
if (ele == null) {
return null;
} else if (!DomUtils.nodeNameEquals(ele, "on-exit")) {
return parseOnExitActions(DomUtils.getChildElementByTagName(ele, "on-exit"));
} else {
return parseActions(ele);
}
}
protected LinkedList parseOnRenderActions(Element ele) {
if (ele == null) {
return null;
} else if (!DomUtils.nodeNameEquals(ele, "on-render")) {
return parseOnRenderActions(DomUtils.getChildElementByTagName(ele, "on-render"));
} else {
return parseActions(ele);
}
}
protected LinkedList parseOnEndActions(Element ele) {
if (ele == null) {
return null;
} else if (!DomUtils.nodeNameEquals(ele, "on-end")) {
return parseOnEndActions(DomUtils.getChildElementByTagName(ele, "on-end"));
} else {
return parseActions(ele);
}
}
protected EvaluateModel parseEvaluate(Element ele) {
if (ele == null) {
return null;
} else if (!DomUtils.nodeNameEquals(ele, "evaluate")) {
return parseEvaluate(DomUtils.getChildElementByTagName(ele, "evaluate"));
} else {
return new EvaluateModel(ele.getAttribute("expression"), ele.getAttribute("result"), ele
.getAttribute("result-type"));
}
}
protected RenderModel parseRender(Element ele) {
if (ele == null) {
return null;
} else if (!DomUtils.nodeNameEquals(ele, "render")) {
return parseRender(DomUtils.getChildElementByTagName(ele, "render"));
} else {
return new RenderModel(ele.getAttribute("fragments"));
}
}
protected SetModel parseSet(Element ele) {
if (ele == null) {
return null;
} else if (!DomUtils.nodeNameEquals(ele, "set")) {
return parseSet(DomUtils.getChildElementByTagName(ele, "set"));
} else {
return new SetModel(ele.getAttribute("name"), ele.getAttribute("value"), ele.getAttribute("type"));
}
}
protected ActionStateModel parseActionState(Element ele) {
if (ele == null) {
return null;
} else if (!DomUtils.nodeNameEquals(ele, "action-state")) {
return parseActionState(DomUtils.getChildElementByTagName(ele, "action-state"));
} else {
return new ActionStateModel(ele.getAttribute("id"), parseAttributes(ele), parseSecured(ele),
parseOnEntryActions(ele), parseTransitions(ele), parseOnExitActions(ele), parseActions(ele),
parseExceptionHandlers(ele));
}
}
protected ViewStateModel parseViewState(Element ele) {
if (ele == null) {
return null;
} else if (!DomUtils.nodeNameEquals(ele, "view-state")) {
return parseViewState(DomUtils.getChildElementByTagName(ele, "view-state"));
} else {
return new ViewStateModel(ele.getAttribute("id"), ele.getAttribute("view"), ele.getAttribute("redirect"),
ele.getAttribute("popup"), ele.getAttribute("model"), parseVars(ele), parseOnRenderActions(ele),
parseAttributes(ele), parseSecured(ele), parseOnEntryActions(ele), parseExceptionHandlers(ele),
parseTransitions(ele), parseOnExitActions(ele));
}
}
protected DecisionStateModel parseDecisionState(Element ele) {
if (ele == null) {
return null;
} else if (!DomUtils.nodeNameEquals(ele, "decision-state")) {
return parseDecisionState(DomUtils.getChildElementByTagName(ele, "decision-state"));
} else {
return new DecisionStateModel(ele.getAttribute("id"), parseIfs(ele), parseOnExitActions(ele),
parseAttributes(ele), parseSecured(ele), parseOnEntryActions(ele), parseExceptionHandlers(ele));
}
}
protected SubflowStateModel parseSubflowState(Element ele) {
if (ele == null) {
return null;
} else if (!DomUtils.nodeNameEquals(ele, "subflow-state")) {
return parseSubflowState(DomUtils.getChildElementByTagName(ele, "subflow-state"));
} else {
return new SubflowStateModel(ele.getAttribute("id"), ele.getAttribute("subflow"), ele
.getAttribute("subflow-attribute-mapper"), parseInputs(ele), parseOutputs(ele),
parseAttributes(ele), parseSecured(ele), parseOnEntryActions(ele), parseExceptionHandlers(ele),
parseTransitions(ele), parseOnExitActions(ele));
}
}
protected EndStateModel parseEndState(Element ele) {
if (ele == null) {
return null;
} else if (!DomUtils.nodeNameEquals(ele, "end-state")) {
return parseEndState(DomUtils.getChildElementByTagName(ele, "end-state"));
} else {
return new EndStateModel(ele.getAttribute("id"), ele.getAttribute("view-factory"), ele
.getAttribute("commit"), parseOutputs(ele), parseAttributes(ele), parseSecured(ele),
parseOnEntryActions(ele), parseExceptionHandlers(ele));
}
}
// TODO: submit this to DomUtils
private static List getChildElementsByTagNames(Element ele, String[] childEleNames) {
List names = Arrays.asList(childEleNames);
NodeList nl = ele.getChildNodes();
List childEles = new LinkedList();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element && (names.contains(node.getLocalName()) || names.contains(node.getNodeName()))) {
childEles.add(node);
}
}
return childEles;
}
public Resource getResource() {
return resource;
}
/**
* Returns the DOM document parsed from the XML file.
*/
protected Document getDocument() {
return document;
}
/**
* Returns the root document element.
*/
protected Element getDocumentElement() {
return document != null ? document.getDocumentElement() : null;
}
public String toString() {
return new ToStringCreator(this).append("location", resource).toString();
}
}

View File

@@ -0,0 +1,168 @@
/*
* Copyright 2004-2008 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.model.registry;
import java.io.IOException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.Resource;
import org.springframework.webflow.engine.model.FlowModel;
import org.springframework.webflow.engine.model.builder.FlowModelBuilder;
import org.springframework.webflow.engine.model.builder.FlowModelBuilderException;
import org.springframework.webflow.util.ResourceHolder;
/**
* A flow model holder that can detect changes on an underlying flow model resource and refresh that resource
* automatically.
* <p>
* This class is thread-safe.
* <p>
* Note that this {@link FlowModel} holder uses a {@link FlowModelBuilder}.
*
* @see FlowModel
*
* @author Keith Donald
* @author Scott Andrews
*/
public class DefaultFlowModelHolder implements FlowModelHolder {
private static final Log logger = LogFactory.getLog(DefaultFlowModelHolder.class);
/**
* The flow model assembled by this assembler.
*/
private FlowModel flowModel;
/**
* The flow mode identifier
*/
private String flowModelId;
/**
* The flow model builder.
*/
private FlowModelBuilder builder;
/**
* A last modified date for the backing flow definition resource, used to support automatic reassembly on resource
* change.
*/
private long lastModified;
/**
* Creates a new refreshable flow model holder that uses the configured assembler (GOF director) to drive flow
* assembly, on initial use and on any resource change or refresh.
* @param builder the flow model builder to use
* @param flowModelId the identifier of the flow model
*/
public DefaultFlowModelHolder(FlowModelBuilder builder, String flowModelId) {
this.builder = builder;
this.flowModelId = flowModelId;
}
/**
* Creates a new static flow model holder
* @param flowModel the flow model to hold
* @param flowModelId the identifier of the flow model
*/
public DefaultFlowModelHolder(FlowModel flowModel, String flowModelId) {
this.flowModel = flowModel;
this.flowModelId = flowModelId;
}
public String getFlowModelId() {
return flowModelId;
}
public synchronized FlowModel getFlowModel() throws FlowModelConstructionException {
if (flowModel == null) {
lastModified = calculateLastModified();
logger.debug("Assembling the flow model for the first time");
assembleFlow();
} else {
refreshIfChanged();
}
return flowModel;
}
public synchronized void refresh() throws FlowModelConstructionException {
assembleFlow();
}
// internal helpers
/**
* Helper that retrieves the last modified date by querying the backing flow resource.
* @return the last modified date, or 0L if it could not be retrieved
*/
private long calculateLastModified() {
if (getFlowModelBuilder() instanceof ResourceHolder) {
Resource resource = ((ResourceHolder) getFlowModelBuilder()).getResource();
try {
long lastModified = resource.getFile().lastModified();
if (logger.isDebugEnabled()) {
logger.debug("Flow definition [" + resource + "] was last modified on " + lastModified);
}
return lastModified;
} catch (IOException e) {
// ignore, last modified checks not supported
}
}
return 0L;
}
/**
* Assemble the held flow definition, delegating to the configured FlowAssembler (director).
*/
private void assembleFlow() throws FlowModelConstructionException {
try {
builder.init();
builder.build();
flowModel = builder.getFlowModel();
} catch (FlowModelBuilderException e) {
throw new FlowModelConstructionException(flowModelId, e);
} finally {
builder.dispose();
}
}
/**
* Reassemble the flow if its underlying resource has changed.
*/
private void refreshIfChanged() {
long calculatedLastModified = calculateLastModified();
if (calculatedLastModified > lastModified) {
if (logger.isDebugEnabled()) {
logger.debug("Refreshing flow definition [" + flowModelId + "]");
}
assembleFlow();
lastModified = calculatedLastModified;
}
}
/**
* Returns the flow builder that actually builds the Flow definition.
*/
private FlowModelBuilder getFlowModelBuilder() {
return builder;
}
public String toString() {
return "'" + getFlowModelId() + "'";
}
}

View File

@@ -0,0 +1,51 @@
/*
* 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.model.registry;
import org.springframework.webflow.core.FlowException;
/**
* Thrown when a flow model was found during a lookup operation but could not be constructed.
*
* @author Keith Donald
* @author Erwin Vervaet
* @author Scott Andrews
*/
public class FlowModelConstructionException extends FlowException {
/**
* The id of the flow that could not be constructed.
*/
private String flowModelId;
/**
* Creates an exception indicating a flow model could not be constructed.
* @param flowModelId the flow model identifier
* @param cause the underlying cause of the exception
*/
public FlowModelConstructionException(String flowModelId, Throwable cause) {
super("An exception occurred constructing the flow '" + flowModelId + "'", cause);
this.flowModelId = flowModelId;
}
/**
* Returns the id of the flow model that could not be constructed.
* @return the flow id
*/
public String getFlowModelId() {
return flowModelId;
}
}

View File

@@ -0,0 +1,51 @@
/*
* 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.model.registry;
import org.springframework.webflow.engine.model.FlowModel;
/**
* A holder holding a reference to a Flow model. Provides a layer of indirection, enabling things like "hot-reloadable"
* flow models.
*
* @see FlowModelRegistry#registerFlowModel(FlowModelHolder)
*
* @author Keith Donald
* @author Scott Andrews
*/
public interface FlowModelHolder {
/**
* Returns the <code>id</code> of the flow model held by this holder. This is a <i>lightweight</i> method callers
* may call to obtain the id of the flow without triggering full flow definition assembly (which may be an expensive
* operation).
*/
public String getFlowModelId();
/**
* Returns the flow model held by this holder. Calling this method the first time may trigger flow assembly (which
* may be expensive).
* @throws FlowModelConstructionException if there is a problem constructing the target flow model
*/
public FlowModel getFlowModel() throws FlowModelConstructionException;
/**
* Refresh the flow model held by this holder. Calling this method typically triggers flow re-assembly, which may
* include a refresh from an externalized resource such as a file.
* @throws FlowModelConstructionException if there is a problem constructing the target flow model
*/
public void refresh() throws FlowModelConstructionException;
}

View File

@@ -0,0 +1,38 @@
/*
* 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.model.registry;
import org.springframework.webflow.engine.model.FlowModel;
/**
* A runtime service locator interface for retrieving flow definitions by <code>id</code>. Flow locators are needed
* by flow executors at runtime to retrieve flow models to support loading flow definitions.
*
* @author Keith Donald
* @author Erwin Vervaet
* @author Scott Andrews
*/
public interface FlowModelLocator {
/**
* Lookup the flow model with the specified id.
* @param id the flow model identifier
* @return the flow mode
* @throws NoSuchFlowModelException when the flow model with the specified id does not exist
* @throws FlowModelConstructionException if there is a problem constructing the identified flow model
*/
public FlowModel getFlowModel(String id) throws NoSuchFlowModelException, FlowModelConstructionException;
}

View File

@@ -0,0 +1,45 @@
/*
* 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.model.registry;
/**
* A container of flow models. Extends {@link FlowModelLocator} for accessing registered Flow models for conversion to
* flow definitions.
* <p>
* Flow model registries can be configured with a "parent" registry to provide a hook into a larger flow model registry
* hierarchy.
*
* @author Keith Donald
* @author Scott Andrews
*/
public interface FlowModelRegistry extends FlowModelLocator {
/**
* Sets this registry's parent registry. When asked by a client to locate a flow model this registry will query it's
* parent if it cannot fulfill the lookup request itself.
* @param parent the parent flow model registry, may be null
*/
public void setParent(FlowModelRegistry parent);
/**
* Register a flow model in this registry. Registers a "holder", not the Flow model itself. This allows the actual
* Flow model to be loaded lazily only when needed, and also rebuilt at runtime when its underlying resource changes
* without re-deploy.
* @param modelHolder a holder holding the flow model to register
*/
public void registerFlowModel(FlowModelHolder modelHolder);
}

View File

@@ -0,0 +1,102 @@
/*
* 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.model.registry;
import java.util.Map;
import java.util.TreeMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.style.ToStringCreator;
import org.springframework.util.Assert;
import org.springframework.webflow.engine.model.FlowModel;
/**
* A generic registry implementation for housing one or more flow models.
*
* @author Keith Donald
* @author Scott Andrews
*/
public class FlowModelRegistryImpl implements FlowModelRegistry {
private static final Log logger = LogFactory.getLog(FlowModelRegistryImpl.class);
/**
* The map of loaded Flow models maintained in this registry.
*/
private Map flowModels;
/**
* An optional parent flow model registry.
*/
private FlowModelRegistry parent;
public FlowModelRegistryImpl() {
flowModels = new TreeMap();
}
// implementing FlowModelLocator
public FlowModel getFlowModel(String id) throws NoSuchFlowModelException, FlowModelConstructionException {
try {
if (id == null) {
throw new IllegalArgumentException("The id of the flow to lookup is required");
}
if (logger.isDebugEnabled()) {
logger.debug("Getting flow model with id '" + id + "'");
}
return getFlowModelHolder(id).getFlowModel();
} catch (NoSuchFlowModelException e) {
if (parent != null) {
// try parent
return parent.getFlowModel(id);
}
throw e;
}
}
// implementing FlowModelRegistry
public void setParent(FlowModelRegistry parent) {
this.parent = parent;
}
public void registerFlowModel(FlowModelHolder modelHolder) {
Assert.notNull(modelHolder, "The holder of the flow model to register is required");
if (logger.isDebugEnabled()) {
logger.debug("Registering flow model " + modelHolder);
}
flowModels.put(modelHolder.getFlowModelId(), modelHolder);
}
// internal helpers
/**
* Returns the identified flow model holder. Throws an exception if it cannot be found.
*/
private FlowModelHolder getFlowModelHolder(String id) throws NoSuchFlowModelException {
FlowModelHolder holder = (FlowModelHolder) flowModels.get(id);
if (holder == null) {
throw new NoSuchFlowModelException(id);
}
return holder;
}
public String toString() {
return new ToStringCreator(this).append("flowModels", flowModels).append("parent", parent).toString();
}
}

View File

@@ -0,0 +1,49 @@
/*
* 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.model.registry;
import org.springframework.webflow.core.FlowException;
/**
* Thrown when no flow model was found during a lookup operation by a flow locator.
*
* @author Keith Donald
* @author Erwin Vervaet
* @author Scott Andrews
*/
public class NoSuchFlowModelException extends FlowException {
/**
* The id of the flow model that could not be located.
*/
private String flowModelId;
/**
* Creates an exception indicating a flow model could not be found.
* @param flowModelId the flow model id
*/
public NoSuchFlowModelException(String flowModelId) {
super("No flow model '" + flowModelId + "' found");
this.flowModelId = flowModelId;
}
/**
* Returns the id of the flow model that could not be found.
*/
public String getFlowModelId() {
return flowModelId;
}
}

View File

@@ -15,7 +15,6 @@
*/
package org.springframework.webflow.test.execution;
import org.springframework.core.io.Resource;
import org.springframework.webflow.config.FlowDefinitionResource;
import org.springframework.webflow.config.FlowDefinitionResourceFactory;
import org.springframework.webflow.core.collection.AttributeMap;
@@ -33,6 +32,7 @@ import org.springframework.webflow.test.MockFlowBuilderContext;
* caching of the flow definition built from an externalized resource to speed up test execution.
*
* @author Keith Donald
* @author Scott Andrews
*/
public abstract class AbstractExternalizedFlowExecutionTests extends AbstractFlowExecutionTests {
@@ -145,7 +145,7 @@ public abstract class AbstractExternalizedFlowExecutionTests extends AbstractFlo
FlowDefinitionResource resource = getResource(resourceFactory);
MockFlowBuilderContext builderContext = new MockFlowBuilderContext(resource.getId(), resource.getAttributes());
configureFlowBuilderContext(builderContext);
FlowBuilder builder = createFlowBuilder(resource.getPath());
FlowBuilder builder = createFlowBuilder(resource);
FlowAssembler assembler = new FlowAssembler(builder, builderContext);
return assembler.assembleFlow();
}
@@ -168,9 +168,9 @@ public abstract class AbstractExternalizedFlowExecutionTests extends AbstractFlo
/**
* Create the flow builder to build the flow at the specified resource location.
* @param path the location of the flow definition
* @param resource the resource location of the flow definition
* @return the flow builder that can build the flow definition
*/
protected abstract FlowBuilder createFlowBuilder(Resource path);
protected abstract FlowBuilder createFlowBuilder(FlowDefinitionResource resource);
}

View File

@@ -16,9 +16,15 @@
package org.springframework.webflow.test.execution;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.core.io.Resource;
import org.springframework.webflow.config.FlowDefinitionResource;
import org.springframework.webflow.engine.builder.FlowBuilder;
import org.springframework.webflow.engine.builder.xml.XmlFlowBuilder;
import org.springframework.webflow.engine.builder.FlowModelFlowBuilder;
import org.springframework.webflow.engine.model.builder.FlowModelBuilder;
import org.springframework.webflow.engine.model.builder.xml.XmlFlowModelBuilder;
import org.springframework.webflow.engine.model.registry.DefaultFlowModelHolder;
import org.springframework.webflow.engine.model.registry.FlowModelHolder;
import org.springframework.webflow.engine.model.registry.FlowModelRegistry;
import org.springframework.webflow.engine.model.registry.FlowModelRegistryImpl;
/**
* Base class for flow integration tests that verify an XML flow definition executes as expected.
@@ -47,15 +53,19 @@ import org.springframework.webflow.engine.builder.xml.XmlFlowBuilder;
*
* @author Keith Donald
* @author Erwin Vervaet
* @author Scott Andrews
*/
public abstract class AbstractXmlFlowExecutionTests extends AbstractExternalizedFlowExecutionTests {
private FlowModelRegistry flowModelRegistry;
/**
* Constructs a default XML flow execution test.
* @see #setName(String)
*/
public AbstractXmlFlowExecutionTests() {
super();
flowModelRegistry = new FlowModelRegistryImpl();
}
/**
@@ -64,10 +74,14 @@ public abstract class AbstractXmlFlowExecutionTests extends AbstractExternalized
*/
public AbstractXmlFlowExecutionTests(String name) {
super(name);
flowModelRegistry = new FlowModelRegistryImpl();
}
protected FlowBuilder createFlowBuilder(Resource resource) {
return new XmlFlowBuilder(resource) {
protected FlowBuilder createFlowBuilder(FlowDefinitionResource resource) {
FlowModelBuilder modelBuilder = new XmlFlowModelBuilder(resource.getPath(), flowModelRegistry);
FlowModelHolder modelHolder = new DefaultFlowModelHolder(modelBuilder, resource.getId());
flowModelRegistry.registerFlowModel(modelHolder);
return new FlowModelFlowBuilder(modelHolder, resource.getPath()) {
protected void registerFlowBeans(ConfigurableBeanFactory flowBeanFactory) {
registerMockFlowBeans(flowBeanFactory);
}

View File

@@ -0,0 +1,299 @@
package org.springframework.webflow.engine.builder;
import junit.framework.TestCase;
import org.springframework.beans.factory.support.StaticListableBeanFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.webflow.action.ExternalRedirectAction;
import org.springframework.webflow.action.FlowDefinitionRedirectAction;
import org.springframework.webflow.core.collection.LocalAttributeMap;
import org.springframework.webflow.core.collection.MutableAttributeMap;
import org.springframework.webflow.engine.Flow;
import org.springframework.webflow.engine.FlowInputMappingException;
import org.springframework.webflow.engine.FlowOutputMappingException;
import org.springframework.webflow.engine.ViewState;
import org.springframework.webflow.engine.builder.support.ActionExecutingViewFactory;
import org.springframework.webflow.engine.impl.FlowExecutionImplFactory;
import org.springframework.webflow.engine.model.AttributeModel;
import org.springframework.webflow.engine.model.EndStateModel;
import org.springframework.webflow.engine.model.FlowModel;
import org.springframework.webflow.engine.model.InputModel;
import org.springframework.webflow.engine.model.OutputModel;
import org.springframework.webflow.engine.model.PersistenceContextModel;
import org.springframework.webflow.engine.model.SecuredModel;
import org.springframework.webflow.engine.model.TransitionModel;
import org.springframework.webflow.engine.model.VarModel;
import org.springframework.webflow.engine.model.ViewStateModel;
import org.springframework.webflow.engine.model.builder.xml.XmlFlowModelBuilder;
import org.springframework.webflow.engine.model.builder.xml.XmlFlowModelBuilderTests;
import org.springframework.webflow.engine.model.registry.DefaultFlowModelHolder;
import org.springframework.webflow.engine.model.registry.FlowModelHolder;
import org.springframework.webflow.engine.model.registry.FlowModelRegistryImpl;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.FlowExecution;
import org.springframework.webflow.execution.ViewFactory;
import org.springframework.webflow.security.SecurityRule;
import org.springframework.webflow.test.MockExternalContext;
import org.springframework.webflow.test.MockFlowBuilderContext;
public class FlowModelFlowBuilderTests extends TestCase {
private FlowModel model;
protected void setUp() {
StaticListableBeanFactory beanFactory = new StaticListableBeanFactory();
beanFactory.addBean("bean", new Object());
model = new FlowModel();
}
public void testBuildIncompleteFlow() {
try {
getFlow(model);
fail("Should have failed");
} catch (FlowBuilderException e) {
}
}
public void testBuildFlowWithEndState() {
model.addEndState(new EndStateModel("end"));
Flow flow = getFlow(model);
assertEquals("flow", flow.getId());
assertEquals("end", flow.getStartState().getId());
}
public void testBuildFlowWithDefaultStartState() {
model.addEndState(new EndStateModel("end"));
Flow flow = getFlow(model);
assertEquals("flow", flow.getId());
assertEquals("end", flow.getStartState().getId());
}
public void testBuildFlowWithStartStateAttribute() {
model.setStartStateId("end");
model.addEndState(new EndStateModel("foo"));
model.addEndState(new EndStateModel("end"));
Flow flow = getFlow(model);
assertEquals("flow", flow.getId());
assertEquals("end", flow.getStartState().getId());
}
public void testCustomFlowAttribute() {
model.addAttribute(new AttributeModel("foo", "bar"));
model.addAttribute(new AttributeModel("number", "1", "integer"));
model.addEndState(new EndStateModel("end"));
Flow flow = getFlow(model);
assertEquals("bar", flow.getAttributes().get("foo"));
assertEquals(new Integer(1), flow.getAttributes().get("number"));
}
public void testPersistenceContextFlow() {
model.setPersistenceContext(new PersistenceContextModel());
model.addEndState(new EndStateModel("end"));
Flow flow = getFlow(model);
assertNotNull(flow.getAttributes().get("persistenceContext"));
assertTrue(((Boolean) flow.getAttributes().get("persistenceContext")).booleanValue());
}
public void testFlowInputOutputMapping() {
model.addInput(new InputModel("foo", "flowScope.foo"));
model.addInput(new InputModel("foo", "flowScope.bar"));
model.addInput(new InputModel("number", "flowScope.baz", "integer", null));
model.addInput(new InputModel("required", "flowScope.boop", null, "true"));
EndStateModel end = new EndStateModel("end");
end.addOutput(new OutputModel("foo", "flowScope.foo"));
model.addEndState(end);
EndStateModel notReached = new EndStateModel("notReached");
notReached.addOutput(new OutputModel("notReached", "flowScope.foo"));
model.addEndState(notReached);
model.addOutput(new OutputModel("differentName", "flowScope.bar"));
model.addOutput(new OutputModel("number", "flowScope.baz", "integer", null));
model.addOutput(new OutputModel("required", "flowScope.baz", "integer", "true"));
model.addOutput(new OutputModel("literal", "'a literal'"));
Flow flow = getFlow(model);
FlowExecutionImplFactory factory = new FlowExecutionImplFactory();
FlowExecution execution = factory.createFlowExecution(flow);
MockExternalContext context = new MockExternalContext();
MutableAttributeMap input = new LocalAttributeMap();
input.put("foo", "bar");
input.put("number", "3");
input.put("required", "9");
execution.start(input, context);
Event outcome = execution.getOutcome();
assertEquals("end", outcome.getId());
assertEquals("bar", outcome.getAttributes().get("foo"));
assertEquals("bar", outcome.getAttributes().get("differentName"));
assertEquals(new Integer(3), outcome.getAttributes().get("number"));
assertEquals(new Integer(3), outcome.getAttributes().get("required"));
assertEquals("a literal", outcome.getAttributes().get("literal"));
assertNull(outcome.getAttributes().get("notReached"));
}
public void testFlowRequiredInputMapping() {
model.addInput(new InputModel("foo", "flowScope.foo"));
model.addInput(new InputModel("foo", "flowScope.bar"));
model.addInput(new InputModel("number", "flowScope.baz", "integer", null));
model.addInput(new InputModel("required", "flowScope.boop", null, "true"));
EndStateModel end = new EndStateModel("end");
end.addOutput(new OutputModel("foo", "flowScope.foo"));
model.addEndState(end);
EndStateModel notReached = new EndStateModel("notReached");
notReached.addOutput(new OutputModel("notReached", "flowScope.foo"));
model.addEndState(notReached);
model.addOutput(new OutputModel("differentName", "flowScope.bar"));
model.addOutput(new OutputModel("number", "flowScope.baz", "integer", null));
model.addOutput(new OutputModel("required", "flowScope.baz", "integer", "true"));
model.addOutput(new OutputModel("literal", "'a literal'"));
Flow flow = getFlow(model);
FlowExecutionImplFactory factory = new FlowExecutionImplFactory();
FlowExecution execution = factory.createFlowExecution(flow);
MockExternalContext context = new MockExternalContext();
MutableAttributeMap input = new LocalAttributeMap();
try {
execution.start(input, context);
fail("Should have failed");
} catch (FlowInputMappingException e) {
}
}
public void testFlowRequiredOutputMapping() {
model.addInput(new InputModel("foo", "flowScope.foo"));
model.addInput(new InputModel("foo", "flowScope.bar"));
model.addInput(new InputModel("number", "flowScope.baz", "integer", null));
model.addInput(new InputModel("required", "flowScope.boop", null, "true"));
EndStateModel end = new EndStateModel("end");
end.addOutput(new OutputModel("foo", "flowScope.foo"));
model.addEndState(end);
EndStateModel notReached = new EndStateModel("notReached");
notReached.addOutput(new OutputModel("notReached", "flowScope.foo"));
model.addEndState(notReached);
model.addOutput(new OutputModel("differentName", "flowScope.bar"));
model.addOutput(new OutputModel("number", "flowScope.baz", "integer", null));
model.addOutput(new OutputModel("required", "flowScope.baz", "integer", "true"));
model.addOutput(new OutputModel("literal", "'a literal'"));
Flow flow = getFlow(model);
FlowExecutionImplFactory factory = new FlowExecutionImplFactory();
FlowExecution execution = factory.createFlowExecution(flow);
MockExternalContext context = new MockExternalContext();
MutableAttributeMap input = new LocalAttributeMap();
input.put("required", "yo");
try {
execution.start(input, context);
fail("Should have failed");
} catch (FlowOutputMappingException e) {
}
}
public void testFlowSecured() {
model.setSecured(new SecuredModel("ROLE_USER"));
model.addEndState(new EndStateModel("end"));
Flow flow = getFlow(model);
SecurityRule rule = (SecurityRule) flow.getAttributes().get(SecurityRule.SECURITY_ATTRIBUTE_NAME);
assertNotNull(rule);
assertEquals(SecurityRule.COMPARISON_ANY, rule.getComparisonType());
assertEquals(1, rule.getAttributes().size());
assertTrue(rule.getAttributes().contains("ROLE_USER"));
}
public void testFlowSecuredState() {
EndStateModel end = new EndStateModel("end");
end.setSecured(new SecuredModel("ROLE_USER"));
model.addEndState(end);
Flow flow = getFlow(model);
SecurityRule rule = (SecurityRule) flow.getState("end").getAttributes().get(
SecurityRule.SECURITY_ATTRIBUTE_NAME);
assertNotNull(rule);
assertEquals(SecurityRule.COMPARISON_ANY, rule.getComparisonType());
assertEquals(1, rule.getAttributes().size());
assertTrue(rule.getAttributes().contains("ROLE_USER"));
}
public void testFlowSecuredTransition() {
model.addEndState(new EndStateModel("end"));
TransitionModel transition = new TransitionModel(null, "end");
transition.setSecured(new SecuredModel("ROLE_USER"));
model.addGlobalTransition(transition);
Flow flow = getFlow(model);
SecurityRule rule = (SecurityRule) flow.getGlobalTransitionSet().toArray()[0].getAttributes().get(
SecurityRule.SECURITY_ATTRIBUTE_NAME);
assertNotNull(rule);
assertEquals(SecurityRule.COMPARISON_ANY, rule.getComparisonType());
assertEquals(1, rule.getAttributes().size());
assertTrue(rule.getAttributes().contains("ROLE_USER"));
}
public void testFlowVariable() {
model.addVar(new VarModel("flow-foo", "org.springframework.webflow.TestBean"));
model.addVar(new VarModel("conversation-foo", "org.springframework.webflow.TestBean", "conversation"));
model.addEndState(new EndStateModel("end"));
Flow flow = getFlow(model);
assertEquals("flow-foo", flow.getVariable("flow-foo").getName());
assertEquals(true, flow.getVariable("flow-foo").isLocal());
assertEquals("conversation-foo", flow.getVariables()[1].getName());
assertEquals(false, flow.getVariables()[1].isLocal());
}
public void testViewStateVariable() {
ViewStateModel view = new ViewStateModel("view");
view.addVar(new VarModel("foo", "org.springframework.webflow.TestBean"));
model.addViewState(view);
Flow flow = getFlow(model);
assertNotNull(((ViewState) flow.getStateInstance("view")).getVariable("foo"));
}
public void testViewStateRedirect() {
ViewStateModel view = new ViewStateModel("view");
view.setRedirect("true");
model.addViewState(view);
Flow flow = getFlow(model);
assertTrue(((ViewState) flow.getStateInstance("view")).getRedirect());
}
public void testViewStatePopup() {
ViewStateModel view = new ViewStateModel("view");
view.setPopup("true");
model.addViewState(view);
Flow flow = getFlow(model);
assertTrue(((ViewState) flow.getStateInstance("view")).getPopup());
}
public void testViewStateFlowRedirect() {
model.addViewState(new ViewStateModel("view", "flowRedirect:myFlow?input=#{flowScope.foo}"));
Flow flow = getFlow(model);
ViewFactory vf = ((ViewState) flow.getStateInstance("view")).getViewFactory();
assertTrue(vf instanceof ActionExecutingViewFactory);
ActionExecutingViewFactory avf = (ActionExecutingViewFactory) vf;
assertTrue(avf.getAction() instanceof FlowDefinitionRedirectAction);
}
public void testViewStateExternalRedirect() {
model.addViewState(new ViewStateModel("view",
"externalRedirect:http://www.paypal.com?_callbackUrl=#{flowExecutionUri}"));
Flow flow = getFlow(model);
ViewFactory vf = ((ViewState) flow.getStateInstance("view")).getViewFactory();
assertTrue(vf instanceof ActionExecutingViewFactory);
ActionExecutingViewFactory avf = (ActionExecutingViewFactory) vf;
assertTrue(avf.getAction() instanceof ExternalRedirectAction);
}
public void testResourceBackedFlowBuilder() {
ClassPathResource resource = new ClassPathResource("flow-endstate.xml", XmlFlowModelBuilderTests.class);
Flow flow = getFlow(resource);
assertEquals("flow", flow.getId());
assertEquals("end", flow.getStartState().getId());
}
private Flow getFlow(FlowModel model) {
FlowModelHolder holder = new DefaultFlowModelHolder(model, "flow");
FlowModelFlowBuilder builder = new FlowModelFlowBuilder(holder);
FlowAssembler assembler = new FlowAssembler(builder, new MockFlowBuilderContext("flow"));
return assembler.assembleFlow();
}
private Flow getFlow(ClassPathResource resource) {
FlowModelHolder holder = new DefaultFlowModelHolder(new XmlFlowModelBuilder(resource,
new FlowModelRegistryImpl()), "flow");
FlowModelFlowBuilder builder = new FlowModelFlowBuilder(holder, resource);
FlowAssembler assembler = new FlowAssembler(builder, new MockFlowBuilderContext("flow"));
return assembler.assembleFlow();
}
}

View File

@@ -1,241 +0,0 @@
package org.springframework.webflow.engine.builder.xml;
import junit.framework.TestCase;
import org.springframework.beans.factory.support.StaticListableBeanFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.webflow.action.ExternalRedirectAction;
import org.springframework.webflow.action.FlowDefinitionRedirectAction;
import org.springframework.webflow.core.collection.LocalAttributeMap;
import org.springframework.webflow.core.collection.MutableAttributeMap;
import org.springframework.webflow.engine.Flow;
import org.springframework.webflow.engine.FlowInputMappingException;
import org.springframework.webflow.engine.FlowOutputMappingException;
import org.springframework.webflow.engine.ViewState;
import org.springframework.webflow.engine.builder.FlowAssembler;
import org.springframework.webflow.engine.builder.FlowBuilderException;
import org.springframework.webflow.engine.builder.support.ActionExecutingViewFactory;
import org.springframework.webflow.engine.impl.FlowExecutionImplFactory;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.FlowExecution;
import org.springframework.webflow.execution.ViewFactory;
import org.springframework.webflow.security.SecurityRule;
import org.springframework.webflow.test.MockExternalContext;
import org.springframework.webflow.test.MockFlowBuilderContext;
public class XmlFlowBuilderTests extends TestCase {
private XmlFlowBuilder builder;
protected void setUp() {
StaticListableBeanFactory beanFactory = new StaticListableBeanFactory();
beanFactory.addBean("bean", new Object());
}
public void testBuildIncompleteFlow() {
ClassPathResource resource = new ClassPathResource("flow-incomplete.xml", getClass());
builder = new XmlFlowBuilder(resource);
FlowAssembler assembler = new FlowAssembler(builder, new MockFlowBuilderContext("flow"));
try {
assembler.assembleFlow();
fail("Should have failed");
} catch (FlowBuilderException e) {
}
}
public void testBuildFlowWithEndState() {
ClassPathResource resource = new ClassPathResource("flow-endstate.xml", getClass());
builder = new XmlFlowBuilder(resource);
FlowAssembler assembler = new FlowAssembler(builder, new MockFlowBuilderContext("flow"));
Flow flow = assembler.assembleFlow();
assertEquals("flow", flow.getId());
assertEquals("end", flow.getStartState().getId());
}
public void testBuildFlowWithDefaultStartState() {
ClassPathResource resource = new ClassPathResource("flow-startstate-default.xml", getClass());
builder = new XmlFlowBuilder(resource);
FlowAssembler assembler = new FlowAssembler(builder, new MockFlowBuilderContext("flow"));
Flow flow = assembler.assembleFlow();
assertEquals("flow", flow.getId());
assertEquals("end", flow.getStartState().getId());
}
public void testBuildFlowWithStartStateAttribute() {
ClassPathResource resource = new ClassPathResource("flow-startstate-attribute.xml", getClass());
builder = new XmlFlowBuilder(resource);
FlowAssembler assembler = new FlowAssembler(builder, new MockFlowBuilderContext("flow"));
Flow flow = assembler.assembleFlow();
assertEquals("flow", flow.getId());
assertEquals("end", flow.getStartState().getId());
}
public void testCustomFlowAttribute() {
ClassPathResource resource = new ClassPathResource("flow-custom-attribute.xml", getClass());
builder = new XmlFlowBuilder(resource);
FlowAssembler assembler = new FlowAssembler(builder, new MockFlowBuilderContext("flow"));
Flow flow = assembler.assembleFlow();
assertEquals("bar", flow.getAttributes().get("foo"));
assertEquals(new Integer(1), flow.getAttributes().get("number"));
}
public void testPersistenceContextFlow() {
ClassPathResource resource = new ClassPathResource("flow-persistencecontext.xml", getClass());
builder = new XmlFlowBuilder(resource);
FlowAssembler assembler = new FlowAssembler(builder, new MockFlowBuilderContext("flow"));
Flow flow = assembler.assembleFlow();
assertNotNull(flow.getAttributes().get("persistenceContext"));
assertTrue(((Boolean) flow.getAttributes().get("persistenceContext")).booleanValue());
}
public void testFlowInputOutputMapping() {
ClassPathResource resource = new ClassPathResource("flow-inputoutput.xml", getClass());
builder = new XmlFlowBuilder(resource);
FlowAssembler assembler = new FlowAssembler(builder, new MockFlowBuilderContext("flow"));
Flow flow = assembler.assembleFlow();
FlowExecutionImplFactory factory = new FlowExecutionImplFactory();
FlowExecution execution = factory.createFlowExecution(flow);
MockExternalContext context = new MockExternalContext();
MutableAttributeMap input = new LocalAttributeMap();
input.put("foo", "bar");
input.put("number", "3");
input.put("required", "9");
execution.start(input, context);
Event outcome = execution.getOutcome();
assertEquals("end", outcome.getId());
assertEquals("bar", outcome.getAttributes().get("foo"));
assertEquals("bar", outcome.getAttributes().get("differentName"));
assertEquals(new Integer(3), outcome.getAttributes().get("number"));
assertEquals(new Integer(3), outcome.getAttributes().get("required"));
assertEquals("a literal", outcome.getAttributes().get("literal"));
assertNull(outcome.getAttributes().get("notReached"));
}
public void testFlowRequiredInputMapping() {
ClassPathResource resource = new ClassPathResource("flow-inputoutput.xml", getClass());
builder = new XmlFlowBuilder(resource);
FlowAssembler assembler = new FlowAssembler(builder, new MockFlowBuilderContext("flow"));
Flow flow = assembler.assembleFlow();
FlowExecutionImplFactory factory = new FlowExecutionImplFactory();
FlowExecution execution = factory.createFlowExecution(flow);
MockExternalContext context = new MockExternalContext();
MutableAttributeMap input = new LocalAttributeMap();
try {
execution.start(input, context);
fail("Should have failed");
} catch (FlowInputMappingException e) {
}
}
public void testFlowRequiredOutputMapping() {
ClassPathResource resource = new ClassPathResource("flow-inputoutput.xml", getClass());
builder = new XmlFlowBuilder(resource);
FlowAssembler assembler = new FlowAssembler(builder, new MockFlowBuilderContext("flow"));
Flow flow = assembler.assembleFlow();
FlowExecutionImplFactory factory = new FlowExecutionImplFactory();
FlowExecution execution = factory.createFlowExecution(flow);
MockExternalContext context = new MockExternalContext();
MutableAttributeMap input = new LocalAttributeMap();
input.put("required", "yo");
try {
execution.start(input, context);
fail("Should have failed");
} catch (FlowOutputMappingException e) {
}
}
public void testFlowSecured() {
ClassPathResource resource = new ClassPathResource("flow-secured.xml", getClass());
builder = new XmlFlowBuilder(resource);
FlowAssembler assembler = new FlowAssembler(builder, new MockFlowBuilderContext("flow"));
Flow flow = assembler.assembleFlow();
SecurityRule rule = (SecurityRule) flow.getAttributes().get(SecurityRule.SECURITY_ATTRIBUTE_NAME);
assertNotNull(rule);
assertEquals(SecurityRule.COMPARISON_ANY, rule.getComparisonType());
assertEquals(1, rule.getAttributes().size());
assertTrue(rule.getAttributes().contains("ROLE_USER"));
}
public void testFlowSecuredState() {
ClassPathResource resource = new ClassPathResource("flow-secured-state.xml", getClass());
builder = new XmlFlowBuilder(resource);
FlowAssembler assembler = new FlowAssembler(builder, new MockFlowBuilderContext("flow"));
Flow flow = assembler.assembleFlow();
SecurityRule rule = (SecurityRule) flow.getState("end").getAttributes().get(
SecurityRule.SECURITY_ATTRIBUTE_NAME);
assertNotNull(rule);
assertEquals(SecurityRule.COMPARISON_ANY, rule.getComparisonType());
assertEquals(1, rule.getAttributes().size());
assertTrue(rule.getAttributes().contains("ROLE_USER"));
}
public void testFlowSecuredTransition() {
ClassPathResource resource = new ClassPathResource("flow-secured-transition.xml", getClass());
builder = new XmlFlowBuilder(resource);
FlowAssembler assembler = new FlowAssembler(builder, new MockFlowBuilderContext("flow"));
Flow flow = assembler.assembleFlow();
SecurityRule rule = (SecurityRule) flow.getGlobalTransitionSet().toArray()[0].getAttributes().get(
SecurityRule.SECURITY_ATTRIBUTE_NAME);
assertNotNull(rule);
assertEquals(SecurityRule.COMPARISON_ANY, rule.getComparisonType());
assertEquals(1, rule.getAttributes().size());
assertTrue(rule.getAttributes().contains("ROLE_USER"));
}
public void testFlowVariable() {
ClassPathResource resource = new ClassPathResource("flow-var.xml", getClass());
builder = new XmlFlowBuilder(resource);
FlowAssembler assembler = new FlowAssembler(builder, new MockFlowBuilderContext("flow"));
Flow flow = assembler.assembleFlow();
assertEquals("flow-foo", flow.getVariable("flow-foo").getName());
assertEquals(true, flow.getVariable("flow-foo").isLocal());
assertEquals("conversation-foo", flow.getVariables()[1].getName());
assertEquals(false, flow.getVariables()[1].isLocal());
}
public void testViewStateVariable() {
ClassPathResource resource = new ClassPathResource("flow-viewstate-var.xml", getClass());
builder = new XmlFlowBuilder(resource);
FlowAssembler assembler = new FlowAssembler(builder, new MockFlowBuilderContext("flow"));
Flow flow = assembler.assembleFlow();
assertNotNull(((ViewState) flow.getStateInstance("view")).getVariable("foo"));
}
public void testViewStateRedirect() {
ClassPathResource resource = new ClassPathResource("flow-viewstate-redirect.xml", getClass());
builder = new XmlFlowBuilder(resource);
FlowAssembler assembler = new FlowAssembler(builder, new MockFlowBuilderContext("flow"));
Flow flow = assembler.assembleFlow();
assertTrue(((ViewState) flow.getStateInstance("view")).getRedirect());
}
public void testViewStatePopup() {
ClassPathResource resource = new ClassPathResource("flow-viewstate-popup.xml", getClass());
builder = new XmlFlowBuilder(resource);
FlowAssembler assembler = new FlowAssembler(builder, new MockFlowBuilderContext("flow"));
Flow flow = assembler.assembleFlow();
assertTrue(((ViewState) flow.getStateInstance("view")).getPopup());
}
public void testViewStateFlowRedirect() {
ClassPathResource resource = new ClassPathResource("flow-viewstate-flowredirect.xml", getClass());
builder = new XmlFlowBuilder(resource);
FlowAssembler assembler = new FlowAssembler(builder, new MockFlowBuilderContext("flow"));
Flow flow = assembler.assembleFlow();
ViewFactory vf = ((ViewState) flow.getStateInstance("view")).getViewFactory();
assertTrue(vf instanceof ActionExecutingViewFactory);
ActionExecutingViewFactory avf = (ActionExecutingViewFactory) vf;
assertTrue(avf.getAction() instanceof FlowDefinitionRedirectAction);
}
public void testViewStateExternalRedirect() {
ClassPathResource resource = new ClassPathResource("flow-viewstate-externalredirect.xml", getClass());
builder = new XmlFlowBuilder(resource);
FlowAssembler assembler = new FlowAssembler(builder, new MockFlowBuilderContext("flow"));
Flow flow = assembler.assembleFlow();
ViewFactory vf = ((ViewState) flow.getStateInstance("view")).getViewFactory();
assertTrue(vf instanceof ActionExecutingViewFactory);
ActionExecutingViewFactory avf = (ActionExecutingViewFactory) vf;
assertTrue(avf.getAction() instanceof ExternalRedirectAction);
}
}

View File

@@ -0,0 +1,94 @@
/*
* 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.model;
import java.util.LinkedList;
import junit.framework.TestCase;
/**
* Unit tests for {@link AbstractModel}.
*/
public class AbstractModelTests extends TestCase {
public void testStringMerge() {
AbstractModel obj = new PersistenceContextModel();
String child = "child";
String parent = "parent";
assertEquals("child", obj.merge(child, parent));
}
public void testStringMergeNullParent() {
AbstractModel obj = new PersistenceContextModel();
String child = "child";
String parent = null;
assertEquals("child", obj.merge(child, parent));
}
public void testStringMergeNullChild() {
AbstractModel obj = new PersistenceContextModel();
String child = null;
String parent = "parent";
assertEquals("parent", obj.merge(child, parent));
}
public void testStringMergeNulls() {
AbstractModel obj = new PersistenceContextModel();
String child = null;
String parent = null;
assertEquals(null, obj.merge(child, parent));
}
public void testListMerge() {
AbstractModel obj = new PersistenceContextModel();
LinkedList child = new LinkedList();
child.add(new SecuredModel("1"));
LinkedList parent = new LinkedList();
parent.add(new SecuredModel("2"));
LinkedList result = obj.merge(child, parent);
assertEquals(2, result.size());
assertEquals("1", ((SecuredModel) result.get(0)).getAttributes());
assertEquals("2", ((SecuredModel) result.get(1)).getAttributes());
}
public void testListMergeNullParent() {
AbstractModel obj = new PersistenceContextModel();
LinkedList child = new LinkedList();
child.add("1");
LinkedList parent = null;
LinkedList result = obj.merge(child, parent);
assertEquals(1, result.size());
assertEquals("1", result.get(0));
}
public void testListMergeNullChild() {
AbstractModel obj = new PersistenceContextModel();
LinkedList child = null;
LinkedList parent = new LinkedList();
parent.add("2");
LinkedList result = obj.merge(child, parent);
assertEquals(1, result.size());
assertEquals("2", result.get(0));
}
public void testListMergeNulls() {
AbstractModel obj = new PersistenceContextModel();
LinkedList child = null;
LinkedList parent = null;
LinkedList result = obj.merge(child, parent);
assertEquals(null, result);
}
}

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.model;
import org.springframework.webflow.engine.model.ActionStateModel;
import org.springframework.webflow.engine.model.EvaluateModel;
import junit.framework.TestCase;
/**
* Unit tests for {@link ActionStateModel}.
*/
public class ActionStateModelTests extends TestCase {
public void testMerge() {
ActionStateModel child = new ActionStateModel("child");
ActionStateModel parent = new ActionStateModel("parent");
child.merge(parent);
assertEquals("child", child.getId());
}
public void testMergeNullParent() {
ActionStateModel child = new ActionStateModel("child");
ActionStateModel parent = null;
child.merge(parent);
assertEquals("child", child.getId());
}
public void testMergeOverrideMatch() {
ActionStateModel child = new ActionStateModel("child");
ActionStateModel parent = new ActionStateModel("child");
parent.addAction(new EvaluateModel("eval1"));
child.merge(parent);
assertEquals(1, child.getActions().size());
}
public void testMergeOverrideMatchFailed() {
ActionStateModel child = new ActionStateModel("child");
ActionStateModel parent = new ActionStateModel("parent");
parent.addAction(new EvaluateModel("eval1"));
child.merge(parent);
assertEquals(null, child.getActions());
}
}

View File

@@ -0,0 +1,55 @@
/*
* 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.model;
import org.springframework.webflow.engine.model.AttributeModel;
import junit.framework.TestCase;
/**
* Unit tests for {@link AttributeModel}.
*/
public class AttributeModelTests extends TestCase {
public void testMerge() {
AttributeModel child = new AttributeModel("child", "childvalue");
AttributeModel parent = new AttributeModel("parent", "parentvalue");
child.merge(parent);
assertEquals("child", child.getName());
}
public void testMergeNullParent() {
AttributeModel child = new AttributeModel("child", "childvalue");
AttributeModel parent = null;
child.merge(parent);
assertEquals("child", child.getName());
}
public void testMergeOverrideMatch() {
AttributeModel child = new AttributeModel("child", "childvalue");
AttributeModel parent = new AttributeModel("child", "childvalue", "string");
child.merge(parent);
assertEquals("string", child.getType());
}
public void testMergeOverrideMatchFailed() {
AttributeModel child = new AttributeModel("child", "childvalue");
AttributeModel parent = new AttributeModel("parent", "parentvalue", "string");
child.merge(parent);
assertEquals(null, child.getType());
}
}

View File

@@ -0,0 +1,49 @@
/*
* 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.model;
import org.springframework.webflow.engine.model.BeanImportModel;
import junit.framework.TestCase;
/**
* Unit tests for {@link BeanImportModel}.
*/
public class BeanImportModelTests extends TestCase {
public void testMerge() {
BeanImportModel child = new BeanImportModel("child");
BeanImportModel parent = new BeanImportModel("parent");
child.merge(parent);
assertEquals("child", child.getResource());
}
public void testMergeNullParent() {
BeanImportModel child = new BeanImportModel("child");
BeanImportModel parent = null;
child.merge(parent);
assertEquals("child", child.getResource());
}
public void testMergeOverrideMatch() {
// bean import will never merge
}
public void testMergeOverrideMatchFailed() {
// bean import will never merge
}
}

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.model;
import org.springframework.webflow.engine.model.DecisionStateModel;
import org.springframework.webflow.engine.model.IfModel;
import junit.framework.TestCase;
/**
* Unit tests for {@link DecisionStateModel}.
*/
public class DecisionStateModelTests extends TestCase {
public void testMerge() {
DecisionStateModel child = new DecisionStateModel("child");
DecisionStateModel parent = new DecisionStateModel("parent");
child.merge(parent);
assertEquals("child", child.getId());
}
public void testMergeNullParent() {
DecisionStateModel child = new DecisionStateModel("child");
DecisionStateModel parent = null;
child.merge(parent);
assertEquals("child", child.getId());
}
public void testMergeOverrideMatch() {
DecisionStateModel child = new DecisionStateModel("child");
DecisionStateModel parent = new DecisionStateModel("child");
parent.addIf(new IfModel("test", "then"));
child.merge(parent);
assertEquals(1, child.getIfs().size());
}
public void testMergeOverrideMatchFailed() {
DecisionStateModel child = new DecisionStateModel("child");
DecisionStateModel parent = new DecisionStateModel("parent");
parent.addIf(new IfModel("test", "then"));
child.merge(parent);
assertEquals(null, child.getIfs());
}
}

View File

@@ -0,0 +1,57 @@
/*
* 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.model;
import org.springframework.webflow.engine.model.EndStateModel;
import junit.framework.TestCase;
/**
* Unit tests for {@link EndStateModel}.
*/
public class EndStateModelTests extends TestCase {
public void testMerge() {
EndStateModel child = new EndStateModel("child");
EndStateModel parent = new EndStateModel("parent");
child.merge(parent);
assertEquals("child", child.getId());
}
public void testMergeNullParent() {
EndStateModel child = new EndStateModel("child");
EndStateModel parent = null;
child.merge(parent);
assertEquals("child", child.getId());
}
public void testMergeOverrideMatch() {
EndStateModel child = new EndStateModel("child");
EndStateModel parent = new EndStateModel("child");
parent.setCommit("true");
child.merge(parent);
assertEquals("true", child.getCommit());
}
public void testMergeOverrideMatchFailed() {
EndStateModel child = new EndStateModel("child");
EndStateModel parent = new EndStateModel("parent");
parent.setCommit("true");
child.merge(parent);
assertEquals(null, child.getCommit());
}
}

View File

@@ -0,0 +1,55 @@
/*
* 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.model;
import org.springframework.webflow.engine.model.EvaluateModel;
import junit.framework.TestCase;
/**
* Unit tests for {@link EvaluateModel}.
*/
public class EvaluateModelTests extends TestCase {
public void testMerge() {
EvaluateModel child = new EvaluateModel("child");
EvaluateModel parent = new EvaluateModel("parent");
child.merge(parent);
assertEquals("child", child.getExpression());
}
public void testMergeNullParent() {
EvaluateModel child = new EvaluateModel("child");
EvaluateModel parent = null;
child.merge(parent);
assertEquals("child", child.getExpression());
}
public void testMergeOverrideMatch() {
EvaluateModel child = new EvaluateModel("child");
EvaluateModel parent = new EvaluateModel("child", "end");
child.merge(parent);
assertEquals("end", child.getResult());
}
public void testMergeOverrideMatchFailed() {
EvaluateModel child = new EvaluateModel("child");
EvaluateModel parent = new EvaluateModel("parent", "end");
child.merge(parent);
assertEquals(null, child.getResult());
}
}

View File

@@ -0,0 +1,49 @@
/*
* 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.model;
import org.springframework.webflow.engine.model.ExceptionHandlerModel;
import junit.framework.TestCase;
/**
* Unit tests for {@link ExceptionHandlerModel}.
*/
public class ExceptionHandlerModelTests extends TestCase {
public void testMerge() {
ExceptionHandlerModel child = new ExceptionHandlerModel("child");
ExceptionHandlerModel parent = new ExceptionHandlerModel("parent");
child.merge(parent);
assertEquals("child", child.getBeanName());
}
public void testMergeNullParent() {
ExceptionHandlerModel child = new ExceptionHandlerModel("child");
ExceptionHandlerModel parent = null;
child.merge(parent);
assertEquals("child", child.getBeanName());
}
public void testMergeOverrideMatch() {
// exception handler will never merge
}
public void testMergeOverrideMatchFailed() {
// exception handler will never merge
}
}

View File

@@ -0,0 +1,188 @@
/*
* 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.model;
import junit.framework.TestCase;
/**
* Unit tests for {@link FlowModel}.
*/
public class FlowModelTests extends TestCase {
public void testMerge() {
FlowModel child = new FlowModel();
child.setStartStateId("child");
FlowModel parent = new FlowModel();
parent.setStartStateId("parent");
child.merge(parent);
assertEquals("child", child.getStartStateId());
}
public void testMergeNullParent() {
FlowModel child = new FlowModel();
child.setStartStateId("child");
FlowModel parent = null;
child.merge(parent);
assertEquals("child", child.getStartStateId());
}
public void testMergeOverrideMatch() {
FlowModel child = new FlowModel();
FlowModel parent = new FlowModel();
parent.addViewState(new ViewStateModel("view"));
child.merge(parent);
assertEquals(1, child.getStates().size());
}
public void testMergeOverrideMatchFailed() {
FlowModel child = new FlowModel();
FlowModel parent = new FlowModel();
parent.addViewState(new ViewStateModel("view"));
child.merge(parent);
// flows will always merge, regardless of likeness
assertEquals(1, child.getStates().size());
}
public void testIntegrationAttributes() {
FlowModel child = new FlowModel();
FlowModel parent = new FlowModel();
child.addAttribute(new AttributeModel("name", "value"));
parent.addAttribute(new AttributeModel("name", "value", "type"));
parent.addAttribute(new AttributeModel("name2", "value2", "type2"));
child.merge(parent);
assertEquals(2, child.getAttributes().size());
assertEquals("name", ((AttributeModel) child.getAttributes().get(0)).getName());
assertEquals("type", ((AttributeModel) child.getAttributes().get(0)).getType());
assertEquals("name2", ((AttributeModel) child.getAttributes().get(1)).getName());
assertEquals("type2", ((AttributeModel) child.getAttributes().get(1)).getType());
}
public void testIntegrationSecured() {
FlowModel child = new FlowModel();
FlowModel parent = new FlowModel();
child.setSecured(new SecuredModel("secured"));
parent.setSecured(new SecuredModel("secured", "all"));
child.merge(parent);
assertEquals("all", child.getSecured().getMatch());
}
public void testIntegrationPersistenceContext() {
FlowModel child = new FlowModel();
FlowModel parent = new FlowModel();
parent.setPersistenceContext(new PersistenceContextModel());
child.merge(parent);
assertNotNull(child.getPersistenceContext());
}
public void testIntegrationVars() {
FlowModel child = new FlowModel();
FlowModel parent = new FlowModel();
child.addVar(new VarModel("name", "value"));
parent.addVar(new VarModel("name", "", "scope"));
parent.addVar(new VarModel("name2", "value2"));
child.merge(parent);
assertEquals(2, child.getVars().size());
assertEquals("scope", ((VarModel) child.getVars().get(1)).getScope());
}
public void testIntegrationMappings() {
FlowModel child = new FlowModel();
FlowModel parent = new FlowModel();
child.addInput(new InputModel("name", "value"));
child.addInput(new InputModel("name2", "value2", "type2", "required2"));
child.addInput(new InputModel("name3", "value3", "type3", "required3"));
parent.addInput(new InputModel("name", "value", "type", "required"));
parent.addInput(new InputModel("name3", "value3", "type3", "required3"));
child.merge(parent);
assertEquals(3, child.getInputs().size());
}
public void testIntegrationOnStart() {
FlowModel child = new FlowModel();
FlowModel parent = new FlowModel();
child.addOnStartAction(new EvaluateModel("expression"));
child.addOnStartAction(new RenderModel("expression"));
child.addOnStartAction(new SetModel("expression", "value"));
parent.addOnStartAction(new EvaluateModel("expression", "result"));
parent.addOnStartAction(new RenderModel("expression"));
parent.addOnStartAction(new SetModel("expression", "value"));
child.merge(parent);
assertEquals(3, child.getOnStartActions().size());
assertEquals("result", ((EvaluateModel) child.getOnStartActions().get(0)).getResult());
}
public void testIntegrationStates() {
FlowModel child = new FlowModel();
FlowModel parent = new FlowModel();
child.addViewState(new ViewStateModel("view"));
child.addEndState(new EndStateModel("end"));
parent.addViewState(new ViewStateModel("view", "jsp"));
parent.addState(new DecisionStateModel("decider"));
parent.addActionState(new ActionStateModel("end"));
child.merge(parent);
assertEquals(4, child.getStates().size());
assertEquals("jsp", ((ViewStateModel) child.getStates().get(0)).getView());
}
public void testIntegrationGlobalTransitions() {
FlowModel child = new FlowModel();
FlowModel parent = new FlowModel();
child.addGlobalTransition(new TransitionModel("end"));
child.addGlobalTransition(new TransitionModel("start"));
parent.addGlobalTransition(new TransitionModel("search"));
parent.addGlobalTransition(new TransitionModel("end", "theend"));
child.merge(parent);
assertEquals(3, child.getGlobalTransitions().size());
assertEquals("theend", ((TransitionModel) child.getGlobalTransitions().get(0)).getTo());
}
public void testIntegrationOnEnd() {
FlowModel child = new FlowModel();
FlowModel parent = new FlowModel();
child.addOnEndAction(new EvaluateModel("expression"));
child.addOnEndAction(new RenderModel("expression"));
child.addOnEndAction(new SetModel("expression", "value"));
parent.addOnEndAction(new EvaluateModel("expression", "result"));
parent.addOnEndAction(new RenderModel("expression"));
parent.addOnEndAction(new SetModel("expression", "value"));
child.merge(parent);
assertEquals(3, child.getOnEndActions().size());
assertEquals("result", ((EvaluateModel) child.getOnEndActions().get(0)).getResult());
}
public void testIntegrationExceptionHandlers() {
FlowModel child = new FlowModel();
FlowModel parent = new FlowModel();
child.addExceptionHandler(new ExceptionHandlerModel("bean1"));
child.addExceptionHandler(new ExceptionHandlerModel("bean2"));
parent.addExceptionHandler(new ExceptionHandlerModel("bean2"));
parent.addExceptionHandler(new ExceptionHandlerModel("bean3"));
child.merge(parent);
assertEquals(3, child.getExceptionHandlers().size());
}
public void testIntegrationBeanImports() {
FlowModel child = new FlowModel();
FlowModel parent = new FlowModel();
child.addBeanImport(new BeanImportModel("path1"));
child.addBeanImport(new BeanImportModel("path2"));
parent.addBeanImport(new BeanImportModel("path2"));
parent.addBeanImport(new BeanImportModel("path3"));
child.merge(parent);
assertEquals(3, child.getBeanImports().size());
}
}

View File

@@ -0,0 +1,55 @@
/*
* 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.model;
import org.springframework.webflow.engine.model.IfModel;
import junit.framework.TestCase;
/**
* Unit tests for {@link IfModel}.
*/
public class IfModelTests extends TestCase {
public void testMerge() {
IfModel child = new IfModel("child", "childthen");
IfModel parent = new IfModel("parent", "parentthen");
child.merge(parent);
assertEquals("childthen", child.getThen());
}
public void testMergeNullParent() {
IfModel child = new IfModel("child", "childthen");
IfModel parent = null;
child.merge(parent);
assertEquals("childthen", child.getThen());
}
public void testMergeOverrideMatch() {
IfModel child = new IfModel("child", "childthen");
IfModel parent = new IfModel("child", "childthen", "childelse");
child.merge(parent);
assertEquals("childelse", child.getElse());
}
public void testMergeOverrideMatchFailed() {
IfModel child = new IfModel("child", "childthen");
IfModel parent = new IfModel("parent", "parentthen", "parentelse");
child.merge(parent);
assertEquals(null, child.getElse());
}
}

View File

@@ -0,0 +1,57 @@
/*
* 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.model;
import org.springframework.webflow.engine.model.InputModel;
import junit.framework.TestCase;
/**
* Unit tests for {@link InputModel}.
*/
public class InputModelTests extends TestCase {
public void testMerge() {
InputModel child = new InputModel("child", "childvalue");
InputModel parent = new InputModel("parent", "parentvalue");
child.merge(parent);
assertEquals("childvalue", child.getValue());
}
public void testMergeNullParent() {
InputModel child = new InputModel("child", "childvalue");
InputModel parent = null;
child.merge(parent);
assertEquals("childvalue", child.getValue());
}
public void testMergeOverrideMatch() {
InputModel child = new InputModel("child", "childvalue");
InputModel parent = new InputModel("child", "childvalue");
parent.setType("long");
child.merge(parent);
assertEquals("long", child.getType());
}
public void testMergeOverrideMatchFailed() {
InputModel child = new InputModel("child", "childvalue");
InputModel parent = new InputModel("parent", "parentvalue");
parent.setType("long");
child.merge(parent);
assertEquals(null, child.getType());
}
}

View File

@@ -0,0 +1,57 @@
/*
* 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.model;
import org.springframework.webflow.engine.model.OutputModel;
import junit.framework.TestCase;
/**
* Unit tests for {@link OutputModel}.
*/
public class OutputModelTests extends TestCase {
public void testMerge() {
OutputModel child = new OutputModel("child", "childvalue");
OutputModel parent = new OutputModel("parent", "parentvalue");
child.merge(parent);
assertEquals("childvalue", child.getValue());
}
public void testMergeNullParent() {
OutputModel child = new OutputModel("child", "childvalue");
OutputModel parent = null;
child.merge(parent);
assertEquals("childvalue", child.getValue());
}
public void testMergeOverrideMatch() {
OutputModel child = new OutputModel("child", "childvalue");
OutputModel parent = new OutputModel("child", "childvalue");
parent.setType("long");
child.merge(parent);
assertEquals("long", child.getType());
}
public void testMergeOverrideMatchFailed() {
OutputModel child = new OutputModel("child", "childvalue");
OutputModel parent = new OutputModel("parent", "parentvalue");
parent.setType("long");
child.merge(parent);
assertEquals(null, child.getType());
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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.model;
import org.springframework.webflow.engine.model.PersistenceContextModel;
import junit.framework.TestCase;
/**
* Unit tests for {@link PersistenceContextModel}.
*/
public class PersistenceContextModelTests extends TestCase {
public void test() {
// no op
}
}

View File

@@ -0,0 +1,49 @@
/*
* 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.model;
import org.springframework.webflow.engine.model.RenderModel;
import junit.framework.TestCase;
/**
* Unit tests for {@link RenderModel}.
*/
public class RenderModelTests extends TestCase {
public void testMerge() {
RenderModel child = new RenderModel("child");
RenderModel parent = new RenderModel("parent");
child.merge(parent);
assertEquals("child", child.getFragments());
}
public void testMergeNullParent() {
RenderModel child = new RenderModel("child");
RenderModel parent = null;
child.merge(parent);
assertEquals("child", child.getFragments());
}
public void testMergeOverrideMatch() {
// render will never merge
}
public void testMergeOverrideMatchFailed() {
// render will never merge
}
}

View File

@@ -0,0 +1,55 @@
/*
* 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.model;
import org.springframework.webflow.engine.model.SecuredModel;
import junit.framework.TestCase;
/**
* Unit tests for {@link SecuredModel}.
*/
public class SecuredModelTests extends TestCase {
public void testMerge() {
SecuredModel child = new SecuredModel("child");
SecuredModel parent = new SecuredModel("parent");
child.merge(parent);
assertEquals("child", child.getAttributes());
}
public void testMergeNullParent() {
SecuredModel child = new SecuredModel("child");
SecuredModel parent = null;
child.merge(parent);
assertEquals("child", child.getAttributes());
}
public void testMergeOverrideMatch() {
SecuredModel child = new SecuredModel("child");
SecuredModel parent = new SecuredModel("child", "all");
child.merge(parent);
assertEquals("all", child.getMatch());
}
public void testMergeOverrideMatchFailed() {
SecuredModel child = new SecuredModel("child");
SecuredModel parent = new SecuredModel("parent", "all");
child.merge(parent);
assertEquals(null, child.getMatch());
}
}

View File

@@ -0,0 +1,55 @@
/*
* 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.model;
import org.springframework.webflow.engine.model.SetModel;
import junit.framework.TestCase;
/**
* Unit tests for {@link SetModel}.
*/
public class SetModelTests extends TestCase {
public void testMerge() {
SetModel child = new SetModel("child", "childvalue");
SetModel parent = new SetModel("parent", "parentvalue");
child.merge(parent);
assertEquals("child", child.getName());
}
public void testMergeNullParent() {
SetModel child = new SetModel("child", "childvalue");
SetModel parent = null;
child.merge(parent);
assertEquals("child", child.getName());
}
public void testMergeOverrideMatch() {
SetModel child = new SetModel("child", "childvalue");
SetModel parent = new SetModel("child", "childvalue", "childtype");
child.merge(parent);
assertEquals("childtype", child.getType());
}
public void testMergeOverrideMatchFailed() {
SetModel child = new SetModel("child", "childvalue");
SetModel parent = new SetModel("parent", "parentvalue", "parenttype");
child.merge(parent);
assertEquals(null, child.getType());
}
}

View File

@@ -0,0 +1,55 @@
/*
* 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.model;
import junit.framework.TestCase;
/**
* Unit tests for {@link SubflowStateModel}.
*/
public class SubflowStateModelTests extends TestCase {
public void testMerge() {
SubflowStateModel child = new SubflowStateModel("child", "childflow");
SubflowStateModel parent = new SubflowStateModel("parent", "parentflow");
child.merge(parent);
assertEquals("child", child.getId());
}
public void testMergeNullParent() {
SubflowStateModel child = new SubflowStateModel("child", "childflow");
SubflowStateModel parent = null;
child.merge(parent);
assertEquals("child", child.getId());
}
public void testMergeOverrideMatch() {
SubflowStateModel child = new SubflowStateModel("child", "childflow");
SubflowStateModel parent = new SubflowStateModel("child", "parentflow");
parent.addInput(new InputModel("inname", "invalue"));
child.merge(parent);
assertEquals(1, child.getInputs().size());
}
public void testMergeOverrideMatchFailed() {
SubflowStateModel child = new SubflowStateModel("child", "childflow");
SubflowStateModel parent = new SubflowStateModel("parent", "parentflow");
parent.addInput(new InputModel("inname", "invalue"));
child.merge(parent);
assertEquals(null, child.getInputs());
}
}

View File

@@ -0,0 +1,55 @@
/*
* 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.model;
import org.springframework.webflow.engine.model.TransitionModel;
import junit.framework.TestCase;
/**
* Unit tests for {@link TransitionModel}.
*/
public class TransitionModelTests extends TestCase {
public void testMerge() {
TransitionModel child = new TransitionModel("child");
TransitionModel parent = new TransitionModel("parent");
child.merge(parent);
assertEquals("child", child.getOn());
}
public void testMergeNullParent() {
TransitionModel child = new TransitionModel("child");
TransitionModel parent = null;
child.merge(parent);
assertEquals("child", child.getOn());
}
public void testMergeOverrideMatch() {
TransitionModel child = new TransitionModel("child");
TransitionModel parent = new TransitionModel("child", "end");
child.merge(parent);
assertEquals("end", child.getTo());
}
public void testMergeOverrideMatchFailed() {
TransitionModel child = new TransitionModel("child");
TransitionModel parent = new TransitionModel("parent", "end");
child.merge(parent);
assertEquals(null, child.getTo());
}
}

View File

@@ -0,0 +1,55 @@
/*
* 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.model;
import org.springframework.webflow.engine.model.VarModel;
import junit.framework.TestCase;
/**
* Unit tests for {@link VarModel}.
*/
public class VarModelTests extends TestCase {
public void testMerge() {
VarModel child = new VarModel("child", "childclass");
VarModel parent = new VarModel("parent", "parentclass");
child.merge(parent);
assertEquals("child", child.getName());
}
public void testMergeNullParent() {
VarModel child = new VarModel("child", "childclass");
VarModel parent = null;
child.merge(parent);
assertEquals("child", child.getName());
}
public void testMergeOverrideMatch() {
VarModel child = new VarModel("child", "childclass");
VarModel parent = new VarModel("child", "childclass", "childscope");
child.merge(parent);
assertEquals("childscope", child.getScope());
}
public void testMergeOverrideMatchFailed() {
VarModel child = new VarModel("child", "childclass");
VarModel parent = new VarModel("parent", "parentclass", "parentscope");
child.merge(parent);
assertEquals(null, child.getScope());
}
}

View File

@@ -0,0 +1,55 @@
/*
* 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.model;
import org.springframework.webflow.engine.model.ViewStateModel;
import junit.framework.TestCase;
/**
* Unit tests for {@link ViewStateModel}.
*/
public class ViewStateModelTests extends TestCase {
public void testMerge() {
ViewStateModel child = new ViewStateModel("child", "childview");
ViewStateModel parent = new ViewStateModel("parent", "parentview");
child.merge(parent);
assertEquals("childview", child.getView());
}
public void testMergeNullParent() {
ViewStateModel child = new ViewStateModel("child", "childview");
ViewStateModel parent = null;
child.merge(parent);
assertEquals("childview", child.getView());
}
public void testMergeOverrideMatch() {
ViewStateModel child = new ViewStateModel("child");
ViewStateModel parent = new ViewStateModel("child", "parentview");
child.merge(parent);
assertEquals("parentview", child.getView());
}
public void testMergeOverrideMatchFailed() {
ViewStateModel child = new ViewStateModel("child");
ViewStateModel parent = new ViewStateModel("parent", "parentview");
child.merge(parent);
assertEquals(null, child.getView());
}
}

View File

@@ -0,0 +1,275 @@
package org.springframework.webflow.engine.model.builder.xml;
import junit.framework.TestCase;
import org.springframework.beans.factory.support.StaticListableBeanFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.webflow.engine.model.AbstractStateModel;
import org.springframework.webflow.engine.model.AttributeModel;
import org.springframework.webflow.engine.model.FlowModel;
import org.springframework.webflow.engine.model.SecuredModel;
import org.springframework.webflow.engine.model.TransitionModel;
import org.springframework.webflow.engine.model.VarModel;
import org.springframework.webflow.engine.model.ViewStateModel;
import org.springframework.webflow.engine.model.builder.FlowModelBuilder;
import org.springframework.webflow.engine.model.registry.DefaultFlowModelHolder;
import org.springframework.webflow.engine.model.registry.FlowModelConstructionException;
import org.springframework.webflow.engine.model.registry.FlowModelRegistry;
import org.springframework.webflow.engine.model.registry.FlowModelRegistryImpl;
public class XmlFlowModelBuilderTests extends TestCase {
private FlowModelRegistry registry;
protected void setUp() {
StaticListableBeanFactory beanFactory = new StaticListableBeanFactory();
beanFactory.addBean("bean", new Object());
registry = new FlowModelRegistryImpl();
}
// public void testBuildIncompleteFlow() {
// ClassPathResource resource = new ClassPathResource("flow-incomplete.xml", getClass());
// builder = new XmlModelBuilder(resource);
// try {
// builder.parse();
// fail("Should have failed");
// } catch (FlowBuilderException e) {
// }
// }
public void testBuildFlowWithEndState() {
ClassPathResource resource = new ClassPathResource("flow-endstate.xml", getClass());
FlowModelBuilder builder = new XmlFlowModelBuilder(resource, registry);
builder.init();
builder.build();
FlowModel flow = builder.getFlowModel();
assertNull(flow.getStartStateId());
assertEquals("end", ((AbstractStateModel) flow.getStates().get(0)).getId());
}
public void testBuildFlowWithDefaultStartState() {
ClassPathResource resource = new ClassPathResource("flow-startstate-default.xml", getClass());
FlowModelBuilder builder = new XmlFlowModelBuilder(resource, registry);
builder.init();
builder.build();
FlowModel flow = builder.getFlowModel();
assertNull(flow.getStartStateId());
assertEquals("end", ((AbstractStateModel) flow.getStates().get(0)).getId());
}
public void testBuildFlowWithStartStateAttribute() {
ClassPathResource resource = new ClassPathResource("flow-startstate-attribute.xml", getClass());
FlowModelBuilder builder = new XmlFlowModelBuilder(resource, registry);
builder.init();
builder.build();
FlowModel flow = builder.getFlowModel();
assertEquals("end", flow.getStartStateId());
}
public void testCustomFlowAttribute() {
ClassPathResource resource = new ClassPathResource("flow-custom-attribute.xml", getClass());
FlowModelBuilder builder = new XmlFlowModelBuilder(resource, registry);
builder.init();
builder.build();
FlowModel flow = builder.getFlowModel();
assertEquals("bar", ((AttributeModel) flow.getAttributes().get(0)).getValue());
assertEquals("number", ((AttributeModel) flow.getAttributes().get(1)).getName());
}
public void testPersistenceContextFlow() {
ClassPathResource resource = new ClassPathResource("flow-persistencecontext.xml", getClass());
FlowModelBuilder builder = new XmlFlowModelBuilder(resource, registry);
builder.init();
builder.build();
FlowModel flow = builder.getFlowModel();
assertNotNull(flow.getPersistenceContext());
}
// public void testFlowInputOutputMapping() {
// ClassPathResource resource = new ClassPathResource("flow-inputoutput.xml", getClass());
// FlowModelBuilder builder = new XmlFlowModelBuilder(resource, registry);
// builder.init();
// builder.build();
// FlowModel flow = builder.getFlowModel();
// FlowExecutionImplFactory factory = new FlowExecutionImplFactory();
// FlowExecution execution = factory.createFlowExecution(flow);
// MockExternalContext context = new MockExternalContext();
// MutableAttributeMap input = new LocalAttributeMap();
// input.put("foo", "bar");
// input.put("number", "3");
// input.put("required", "9");
// execution.start(input, context);
// Event outcome = execution.getOutcome();
// assertEquals("end", outcome.getId());
// assertEquals("bar", outcome.getAttributes().get("foo"));
// assertEquals("bar", outcome.getAttributes().get("differentName"));
// assertEquals(new Integer(3), outcome.getAttributes().get("number"));
// assertEquals(new Integer(3), outcome.getAttributes().get("required"));
// assertEquals("a literal", outcome.getAttributes().get("literal"));
// assertNull(outcome.getAttributes().get("notReached"));
// }
// public void testFlowRequiredInputMapping() {
// ClassPathResource resource = new ClassPathResource("flow-inputoutput.xml", getClass());
// FlowModelBuilder builder = new XmlFlowModelBuilder(resource, registry);
// builder.init();
// builder.build();
// FlowModel flow = builder.getFlowModel();
// FlowExecutionImplFactory factory = new FlowExecutionImplFactory();
// FlowExecution execution = factory.createFlowExecution(flow);
// MockExternalContext context = new MockExternalContext();
// MutableAttributeMap input = new LocalAttributeMap();
// try {
// execution.start(input, context);
// fail("Should have failed");
// } catch (FlowExecutionException e) {
// RequiredMappingException me = (RequiredMappingException) e.getRootCause();
// }
// }
// public void testFlowRequiredOutputMapping() {
// ClassPathResource resource = new ClassPathResource("flow-inputoutput.xml", getClass());
// FlowModelBuilder builder = new XmlFlowModelBuilder(resource, registry);
// builder.init();
// builder.build();
// FlowModel flow = builder.getFlowModel();
// FlowExecutionImplFactory factory = new FlowExecutionImplFactory();
// FlowExecution execution = factory.createFlowExecution(flow);
// MockExternalContext context = new MockExternalContext();
// MutableAttributeMap input = new LocalAttributeMap();
// input.put("required", "yo");
// try {
// execution.start(input, context);
// fail("Should have failed");
// } catch (FlowExecutionException e) {
// RequiredMappingException me = (RequiredMappingException) e.getRootCause();
// }
// }
public void testFlowSecured() {
ClassPathResource resource = new ClassPathResource("flow-secured.xml", getClass());
FlowModelBuilder builder = new XmlFlowModelBuilder(resource, registry);
builder.init();
builder.build();
FlowModel flow = builder.getFlowModel();
SecuredModel secured = flow.getSecured();
assertNotNull(secured);
assertEquals("ROLE_USER", secured.getAttributes());
}
public void testFlowSecuredState() {
ClassPathResource resource = new ClassPathResource("flow-secured-state.xml", getClass());
FlowModelBuilder builder = new XmlFlowModelBuilder(resource, registry);
builder.init();
builder.build();
FlowModel flow = builder.getFlowModel();
SecuredModel secured = ((AbstractStateModel) flow.getStates().get(0)).getSecured();
assertNotNull(secured);
assertEquals("ROLE_USER", secured.getAttributes());
}
public void testFlowSecuredTransition() {
ClassPathResource resource = new ClassPathResource("flow-secured-transition.xml", getClass());
FlowModelBuilder builder = new XmlFlowModelBuilder(resource, registry);
builder.init();
builder.build();
FlowModel flow = builder.getFlowModel();
SecuredModel secured = ((TransitionModel) flow.getGlobalTransitions().get(0)).getSecured();
assertNotNull(secured);
assertEquals("ROLE_USER", secured.getAttributes());
}
public void testFlowVariable() {
ClassPathResource resource = new ClassPathResource("flow-var.xml", getClass());
FlowModelBuilder builder = new XmlFlowModelBuilder(resource, registry);
builder.init();
builder.build();
FlowModel flow = builder.getFlowModel();
assertEquals("flow-foo", ((VarModel) flow.getVars().get(0)).getName());
assertEquals(null, ((VarModel) flow.getVars().get(0)).getScope());
assertEquals("conversation-foo", ((VarModel) flow.getVars().get(1)).getName());
assertEquals("conversation", ((VarModel) flow.getVars().get(1)).getScope());
}
public void testViewStateVariable() {
ClassPathResource resource = new ClassPathResource("flow-viewstate-var.xml", getClass());
FlowModelBuilder builder = new XmlFlowModelBuilder(resource, registry);
builder.init();
builder.build();
FlowModel flow = builder.getFlowModel();
assertEquals("foo", ((VarModel) ((ViewStateModel) flow.getStates().get(0)).getVars().get(0)).getName());
}
public void testViewStateRedirect() {
ClassPathResource resource = new ClassPathResource("flow-viewstate-redirect.xml", getClass());
FlowModelBuilder builder = new XmlFlowModelBuilder(resource, registry);
builder.init();
builder.build();
FlowModel flow = builder.getFlowModel();
assertEquals("true", ((ViewStateModel) flow.getStates().get(0)).getRedirect());
}
public void testViewStatePopup() {
ClassPathResource resource = new ClassPathResource("flow-viewstate-popup.xml", getClass());
FlowModelBuilder builder = new XmlFlowModelBuilder(resource, registry);
builder.init();
builder.build();
FlowModel flow = builder.getFlowModel();
assertEquals("true", ((ViewStateModel) flow.getStates().get(0)).getPopup());
}
// public void testViewStateFlowRedirect() {
// ClassPathResource resource = new ClassPathResource("flow-viewstate-flowredirect.xml",
// getClass());
// FlowModelBuilder builder = new XmlFlowModelBuilder(resource, registry);
// builder.init();
// builder.build();
// FlowModel flow = builder.getFlowModel();
// ViewFactory vf = ((ViewState) flow.getStateInstance("view")).getViewFactory();
// assertTrue(vf instanceof ActionExecutingViewFactory);
// ActionExecutingViewFactory avf = (ActionExecutingViewFactory) vf;
// assertTrue(avf.getAction() instanceof FlowDefinitionRedirectAction);
// }
// public void testViewStateExternalRedirect() {
// ClassPathResource resource = new ClassPathResource("flow-viewstate-externalredirect.xml",
// getClass());
// FlowModelBuilder builder = new XmlFlowModelBuilder(resource, registry);
// builder.init();
// builder.build();
// FlowModel flow = builder.getFlowModel();
// ViewFactory vf = ((ViewState) flow.getStateInstance("view")).getViewFactory();
// assertTrue(vf instanceof ActionExecutingViewFactory);
// ActionExecutingViewFactory avf = (ActionExecutingViewFactory) vf;
// assertTrue(avf.getAction() instanceof ExternalRedirectAction);
// }
public void testMerge() {
ClassPathResource resourceChild = new ClassPathResource("flow-inheritance-child.xml", getClass());
ClassPathResource resourceParent = new ClassPathResource("flow-inheritance-parent.xml", getClass());
registry
.registerFlowModel(new DefaultFlowModelHolder(new XmlFlowModelBuilder(resourceChild, registry), "child"));
registry.registerFlowModel(new DefaultFlowModelHolder(new XmlFlowModelBuilder(resourceParent, registry),
"parent"));
FlowModel flow = registry.getFlowModel("child");
assertEquals(1, flow.getGlobalTransitions().size());
assertEquals(2, flow.getStates().size());
assertEquals("view", ((AbstractStateModel) flow.getStates().get(0)).getId());
}
public void testMergeParentNotFound() {
ClassPathResource resourceChild = new ClassPathResource("flow-inheritance-child.xml", getClass());
ClassPathResource resourceParent = new ClassPathResource("flow-inheritance-parent.xml", getClass());
registry
.registerFlowModel(new DefaultFlowModelHolder(new XmlFlowModelBuilder(resourceChild, registry), "child"));
registry.registerFlowModel(new DefaultFlowModelHolder(new XmlFlowModelBuilder(resourceParent, registry),
"parent-id-not-matching"));
try {
registry.getFlowModel("child");
fail("A FlowModelConstructionException was expected");
} catch (FlowModelConstructionException e) {
// we want this
}
}
}

View File

@@ -0,0 +1,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-2.0.xsd"
parent="parent">
<view-state id="view" view="myCustomView" />
</flow>

View File

@@ -0,0 +1,13 @@
<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-2.0.xsd">
<end-state id="end"/>
<global-transitions>
<transition to="end">
<secured attributes="ROLE_USER"/>
</transition>
</global-transitions>
</flow>

View File

@@ -0,0 +1,71 @@
package org.springframework.webflow.engine.model.registry;
import junit.framework.TestCase;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.webflow.engine.model.AbstractStateModel;
import org.springframework.webflow.engine.model.EndStateModel;
import org.springframework.webflow.engine.model.FlowModel;
import org.springframework.webflow.engine.model.builder.FlowModelBuilder;
import org.springframework.webflow.engine.model.builder.FlowModelBuilderException;
import org.springframework.webflow.util.ResourceHolder;
public class DefaultFlowModelHolderTests extends TestCase {
private DefaultFlowModelHolder holder;
private FlowModelBuilder builder;
protected void setUp() {
builder = new SimpleFlowBuilder();
holder = new DefaultFlowModelHolder(builder, "flowId");
}
public void testGetFlowDefinition() {
FlowModel flow = holder.getFlowModel();
assertNull(flow.getStartStateId());
assertEquals("end", ((AbstractStateModel) flow.getStates().get(0)).getId());
}
public void testGetFlowDefinitionWithChangesRefreshed() {
FlowModel flow = holder.getFlowModel();
holder.refresh();
flow = holder.getFlowModel();
assertNull(flow.getStartStateId());
assertEquals("end", ((AbstractStateModel) flow.getStates().get(0)).getId());
}
public class SimpleFlowBuilder implements FlowModelBuilder {
public FlowModel getFlowModel() throws FlowModelBuilderException {
FlowModel flow = new FlowModel();
flow.addEndState(new EndStateModel("end"));
return flow;
}
public void build() throws FlowModelBuilderException {
// no-op
}
public void mergeParent() throws FlowModelBuilderException {
// no-op
}
public void dispose() throws FlowModelBuilderException {
// no-op
}
public void init() throws FlowModelBuilderException {
// no-op
}
}
public class ChangeDetectableFlowBuilder extends SimpleFlowBuilder implements ResourceHolder {
private FileSystemResource resource = new FileSystemResource("file.txt");
public Resource getResource() {
return resource;
}
}
}

View File

@@ -0,0 +1,58 @@
package org.springframework.webflow.engine.model.registry;
import junit.framework.TestCase;
import org.springframework.webflow.engine.model.FlowModel;
public class FlowModelRegistryImplTests extends TestCase {
private FlowModelRegistryImpl registry = new FlowModelRegistryImpl();
private FlowModel fooFlow;
private FlowModel barFlow;
protected void setUp() {
fooFlow = new FlowModel();
barFlow = new FlowModel();
}
public void testNoSuchFlowDefinition() {
try {
registry.getFlowModel("bogus");
fail("Should've bombed with NoSuchFlow");
} catch (NoSuchFlowModelException e) {
}
}
public void testRegisterFlow() {
registry.registerFlowModel(new DefaultFlowModelHolder(fooFlow, "foo"));
assertEquals(fooFlow, registry.getFlowModel("foo"));
}
public void testRegisterFlowSameIds() {
registry.registerFlowModel(new DefaultFlowModelHolder(fooFlow, "foo"));
FlowModel newFlow = new FlowModel();
registry.registerFlowModel(new DefaultFlowModelHolder(newFlow, "foo"));
assertSame(newFlow, registry.getFlowModel("foo"));
}
public void testRegisterMultipleFlows() {
registry.registerFlowModel(new DefaultFlowModelHolder(fooFlow, "foo"));
registry.registerFlowModel(new DefaultFlowModelHolder(barFlow, "bar"));
assertEquals(fooFlow, registry.getFlowModel("foo"));
assertEquals(barFlow, registry.getFlowModel("bar"));
}
public void testParentHierarchy() {
testRegisterMultipleFlows();
FlowModelRegistryImpl child = new FlowModelRegistryImpl();
child.setParent(registry);
FlowModel fooFlow = new FlowModel();
child.registerFlowModel(new DefaultFlowModelHolder(fooFlow, "foo"));
assertSame(fooFlow, child.getFlowModel("foo"));
assertEquals(barFlow, child.getFlowModel("bar"));
}
}

View File

@@ -0,0 +1 @@
a changeable file