From f9a40441f14b25b6b533466edae98ed55b8fbf66 Mon Sep 17 00:00:00 2001 From: Keith Donald Date: Thu, 28 Feb 2008 22:23:23 +0000 Subject: [PATCH] made start state optional --- .../springframework/webflow/engine/Flow.java | 116 +++++++++++------- .../engine/builder/FlowArtifactFactory.java | 2 +- .../webflow/engine/builder/FlowAssembler.java | 5 +- .../webflow/engine/builder/FlowBuilder.java | 19 +-- .../engine/builder/FlowBuilderContext.java | 19 +++ .../engine/builder/FlowBuilderException.java | 4 +- .../RefreshableFlowDefinitionHolder.java | 2 +- .../engine/builder/ViewFactoryCreator.java | 30 ++++- .../builder/support/AbstractFlowBuilder.java | 24 +++- .../support/ActionExecutingViewFactory.java | 73 +++++++++++ .../support/ActionInvokingViewFactory.java | 50 -------- .../support/FlowBuilderContextImpl.java | 20 ++- .../builder/support/FlowBuilderServices.java | 17 ++- .../builder/xml/LocalFlowBuilderContext.java | 5 + .../engine/builder/xml/XmlFlowBuilder.java | 41 +++++-- .../engine/builder/xml/spring-webflow-2.0.xsd | 19 ++- .../engine/impl/FlowExecutionImpl.java | 6 +- .../webflow/mvc/MvcViewFactoryCreator.java | 2 +- .../webflow/test/MockViewFactoryCreator.java | 2 +- ...lderServicesBeanDefinitionParserTests.java | 2 +- .../builder/xml/XmlFlowBuilderTests.java | 28 +++++ .../engine/builder/xml/flow-endstate.xml | 4 +- .../builder/xml/flow-startstate-attribute.xml | 9 ++ .../builder/xml/flow-startstate-default.xml | 8 ++ .../builder/xml/flow-startstate-element.xml | 11 ++ 25 files changed, 374 insertions(+), 144 deletions(-) create mode 100644 spring-webflow/src/main/java/org/springframework/webflow/engine/builder/support/ActionExecutingViewFactory.java delete mode 100644 spring-webflow/src/main/java/org/springframework/webflow/engine/builder/support/ActionInvokingViewFactory.java create mode 100644 spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/flow-startstate-attribute.xml create mode 100644 spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/flow-startstate-default.xml create mode 100644 spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/flow-startstate-element.xml diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/Flow.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/Flow.java index f7b7db97..6fd1af75 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/Flow.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/Flow.java @@ -27,6 +27,9 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.support.StaticListableBeanFactory; import org.springframework.binding.mapping.AttributeMapper; import org.springframework.binding.mapping.MappingContext; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; import org.springframework.core.style.StylerUtils; import org.springframework.core.style.ToStringCreator; import org.springframework.util.Assert; @@ -105,7 +108,7 @@ import org.springframework.webflow.execution.RequestContext; * @author Colin Sampaleanu * @author Jeremy Grelle */ -public class Flow extends AnnotatedObject implements FlowDefinition, BeanFactory { +public class Flow extends AnnotatedObject implements FlowDefinition, BeanFactory, ResourceLoader { /** * Logger, can be used in subclasses. @@ -166,9 +169,14 @@ public class Flow extends AnnotatedObject implements FlowDefinition, BeanFactory private FlowExecutionExceptionHandlerSet exceptionHandlerSet = new FlowExecutionExceptionHandlerSet(); /** - * The local bean factory for this flow + * An optional bean factory hosting services needed by this flow. */ - private BeanFactory localBeanFactory = new StaticListableBeanFactory(); + private BeanFactory beanFactory = new StaticListableBeanFactory(); + + /** + * An optional resource loader capable of loading resources relative to this flow. + */ + private ResourceLoader resourceLoader = new DefaultResourceLoader(); /** * Construct a new flow definition with the given id. The id should be unique among all flows. @@ -433,6 +441,22 @@ public class Flow extends AnnotatedObject implements FlowDefinition, BeanFactory return globalTransitionSet; } + /** + * Sets a reference to a bean factory hosting application objects needed by this flow. + * @param beanFactory the bean factory + */ + public void setBeanFactory(BeanFactory beanFactory) { + this.beanFactory = beanFactory; + } + + /** + * Sets a reference to a resource loader capable of loading resources relative to this flow. + * @param resourceLoader the resource loader + */ + public void setResourceLoader(ResourceLoader resourceLoader) { + this.resourceLoader = resourceLoader; + } + // id based equality public boolean equals(Object o) { @@ -536,6 +560,52 @@ public class Flow extends AnnotatedObject implements FlowDefinition, BeanFactory return getExceptionHandlerSet().handleException(exception, context); } + // implementing bean factory + + public boolean containsBean(String name) { + return beanFactory.containsBean(name); + } + + public String[] getAliases(String name) { + return beanFactory.getAliases(name); + } + + public Object getBean(String name, Class requiredType) throws BeansException { + return beanFactory.getBean(name, requiredType); + } + + public Object getBean(String name, Object[] args) throws BeansException { + return beanFactory.getBean(name, args); + } + + public Object getBean(String name) throws BeansException { + return beanFactory.getBean(name); + } + + public Class getType(String name) throws NoSuchBeanDefinitionException { + return beanFactory.getType(name); + } + + public boolean isPrototype(String name) throws NoSuchBeanDefinitionException { + return beanFactory.isPrototype(name); + } + + public boolean isSingleton(String name) throws NoSuchBeanDefinitionException { + return beanFactory.isSingleton(name); + } + + public boolean isTypeMatch(String name, Class targetType) throws NoSuchBeanDefinitionException { + return beanFactory.isTypeMatch(name, targetType); + } + + public ClassLoader getClassLoader() { + return resourceLoader.getClassLoader(); + } + + public Resource getResource(String name) { + return resourceLoader.getResource(name); + } + // internal helpers private void assertStartStateSet() { @@ -616,44 +686,4 @@ public class Flow extends AnnotatedObject implements FlowDefinition, BeanFactory "outputMapper", outputMapper).toString(); } - public void setLocalBeanFactory(BeanFactory localBeanFactory) { - this.localBeanFactory = localBeanFactory; - } - - public boolean containsBean(String name) { - return localBeanFactory.containsBean(name); - } - - public String[] getAliases(String name) { - return localBeanFactory.getAliases(name); - } - - public Object getBean(String name, Class requiredType) throws BeansException { - return localBeanFactory.getBean(name, requiredType); - } - - public Object getBean(String name, Object[] args) throws BeansException { - return localBeanFactory.getBean(name, args); - } - - public Object getBean(String name) throws BeansException { - return localBeanFactory.getBean(name); - } - - public Class getType(String name) throws NoSuchBeanDefinitionException { - return localBeanFactory.getType(name); - } - - public boolean isPrototype(String name) throws NoSuchBeanDefinitionException { - return localBeanFactory.isPrototype(name); - } - - public boolean isSingleton(String name) throws NoSuchBeanDefinitionException { - return localBeanFactory.isSingleton(name); - } - - public boolean isTypeMatch(String name, Class targetType) throws NoSuchBeanDefinitionException { - return localBeanFactory.isTypeMatch(name, targetType); - } - } \ No newline at end of file diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/FlowArtifactFactory.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/FlowArtifactFactory.java index 202c92ba..42477416 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/FlowArtifactFactory.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/FlowArtifactFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2007 the original author or authors. + * 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. diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/FlowAssembler.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/FlowAssembler.java index a142bd54..149eb521 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/FlowAssembler.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/FlowAssembler.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2007 the original author or authors. + * 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. @@ -27,7 +27,8 @@ import org.springframework.webflow.engine.Flow; * *
  *     FlowBuilder builder = ...;
- *     Flow flow = new FlowAssembler("myFlow", builder, null).assembleFlow();
+ *     FlowBuilder context = ...;
+ *     Flow flow = new FlowAssembler(builder, builderContext).assembleFlow();
  * 
* * @see org.springframework.webflow.engine.builder.FlowBuilder diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/FlowBuilder.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/FlowBuilder.java index ee8ab56e..6bcd870a 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/FlowBuilder.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/FlowBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2007 the original author or authors. + * 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. @@ -25,8 +25,8 @@ import org.springframework.webflow.engine.Flow; *
  • Call {@link #buildInputMapper()} to create and set the input mapper for the flow. *
  • Call {@link #buildStartActions()} to create and add any start actions to the flow. *
  • Call {@link #buildStates()} to create the states of the flow and add them to the flow definition. - *
  • Call {@link #buildGlobalTransitions()} to create the any transitions shared by all states of the flow and add - * them to the flow definition. + *
  • Call {@link #buildGlobalTransitions()} to create any transitions shared by all states of the flow and add them + * to the flow definition. *
  • Call {@link #buildEndActions()} to create and add any end actions to the flow. *
  • Call {@link #buildOutputMapper()} to create and set the output mapper for the flow. *
  • Call {@link #buildExceptionHandlers()} to create the exception handlers of the flow and add them to the flow @@ -44,9 +44,10 @@ import org.springframework.webflow.engine.Flow; * reused, however, exercise caution when doing this as these objects are not thread safe. Also, for each use be sure to * call init, followed by the build* methods, getFlow, and dispose completely in that order. *

    - * This is an example of the classic GoF builder pattern. + * This is a good example of the classic GoF builder pattern. * * @see Flow + * @see FlowBuilderContext * @see FlowAssembler * * @author Keith Donald @@ -111,15 +112,17 @@ public interface FlowBuilder { public void buildExceptionHandlers() throws FlowBuilderException; /** - * Get the fully constructed and configured Flow object - called by the builder's assembler (director) after + * Get the fully constructed and configured Flow object. 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 is ready for use. + * returned flow is fully configured and ready for use. + * @throws FlowBuilderException an exception occurred building this flow */ - public Flow getFlow(); + public Flow getFlow() throws FlowBuilderException; /** * 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 dispose(); + public void dispose() throws FlowBuilderException; } \ No newline at end of file diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/FlowBuilderContext.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/FlowBuilderContext.java index 1a2614be..b16a4e52 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/FlowBuilderContext.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/FlowBuilderContext.java @@ -1,3 +1,18 @@ +/* + * 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.builder; import org.springframework.beans.factory.BeanFactory; @@ -8,6 +23,10 @@ import org.springframework.webflow.action.BeanInvokingActionFactory; import org.springframework.webflow.core.collection.AttributeMap; import org.springframework.webflow.definition.registry.FlowDefinitionLocator; +/** + * Provides services needed to a direct a flow builder through building a flow definition. + * @author Keith Donald + */ public interface FlowBuilderContext { /** diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/FlowBuilderException.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/FlowBuilderException.java index 0a689151..c9abe42a 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/FlowBuilderException.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/FlowBuilderException.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2007 the original author or authors. + * 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. @@ -20,7 +20,7 @@ import org.springframework.webflow.core.FlowException; /** * Exception thrown to indicate a problem while building a flow. * - * @see org.springframework.webflow.engine.builder.FlowBuilder + * @see FlowBuilder * * @author Erwin Vervaet */ diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/RefreshableFlowDefinitionHolder.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/RefreshableFlowDefinitionHolder.java index 522ee7d5..77e85024 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/RefreshableFlowDefinitionHolder.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/RefreshableFlowDefinitionHolder.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2007 the original author or authors. + * 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. diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/ViewFactoryCreator.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/ViewFactoryCreator.java index fef563c6..3c6ce9ca 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/ViewFactoryCreator.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/ViewFactoryCreator.java @@ -1,14 +1,38 @@ +/* + * 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.builder; import org.springframework.binding.expression.Expression; import org.springframework.core.io.ResourceLoader; -import org.springframework.webflow.execution.Action; +import org.springframework.webflow.execution.View; import org.springframework.webflow.execution.ViewFactory; +/** + * A factory for ViewFactory objects. This is an SPI interface and conceals specific types of view factories from the + * flow builder infrastructure. + */ public interface ViewFactoryCreator { + /** + * Create a view factory capable of creating {@link View} objects that can render the view template with the + * provided identifier. + * @param viewId an expression that resolves the id of the view template + * @param viewResourceLoader an optional resource loader to use to load the view template from an input stream + * @return the view factory + */ public ViewFactory createViewFactory(Expression viewId, ResourceLoader viewResourceLoader); - public Action createFinalResponseAction(Expression viewId, ResourceLoader viewResourceLoader); - } diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/support/AbstractFlowBuilder.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/support/AbstractFlowBuilder.java index 776495ac..af3ab269 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/support/AbstractFlowBuilder.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/support/AbstractFlowBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2007 the original author or authors. + * 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. @@ -36,8 +36,15 @@ public abstract class AbstractFlowBuilder implements FlowBuilder { */ private Flow flow; + /** + * The flow builder context providing access to services needed to build the flow. + */ private FlowBuilderContext context; + /** + * Returns this flow builder's context. + * @return the flow builder context + */ protected FlowBuilderContext getContext() { return context; } @@ -48,10 +55,18 @@ public abstract class AbstractFlowBuilder implements FlowBuilder { this.flow = createFlow(); } + /** + * Flow builder initialization hook. Does nothing by default. May be overridden by subclasses. + */ protected void doInit() { } + /** + * Factory method that initially creates the flow implementation during flow builder initialization. Simply + * delegates to the configured flow artifact factory by default. + * @return the flow instance, initially created but not yet built + */ protected Flow createFlow() { String id = getContext().getFlowId(); AttributeMap attributes = getContext().getFlowAttributes(); @@ -81,15 +96,18 @@ public abstract class AbstractFlowBuilder implements FlowBuilder { public void buildExceptionHandlers() throws FlowBuilderException { } - public Flow getFlow() { + public Flow getFlow() throws FlowBuilderException { return flow; } - public void dispose() { + public void dispose() throws FlowBuilderException { flow = null; doDispose(); } + /** + * Flow builder destruction hook. Does nothing by default. May be overridden by subclasses. + */ protected void doDispose() { } diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/support/ActionExecutingViewFactory.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/support/ActionExecutingViewFactory.java new file mode 100644 index 00000000..21a1ad47 --- /dev/null +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/support/ActionExecutingViewFactory.java @@ -0,0 +1,73 @@ +/* + * 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.builder.support; + +import org.springframework.webflow.engine.ActionExecutor; +import org.springframework.webflow.execution.Action; +import org.springframework.webflow.execution.Event; +import org.springframework.webflow.execution.RequestContext; +import org.springframework.webflow.execution.View; +import org.springframework.webflow.execution.ViewFactory; + +/** + * A view factory implementation that creates views that execute an action when rendered. Used mainly to encapsulate an + * action that renders a response. Examples include flow redirect and external redirect actions. + */ +public class ActionExecutingViewFactory implements ViewFactory { + + /** + * The action to execute. + */ + private Action action; + + /** + * Create a new action invoking view factory + * @param action the action to execute + */ + public ActionExecutingViewFactory(Action action) { + this.action = action; + } + + public View getView(RequestContext context) { + return new ActionExecutingView(action, context); + } + + private static class ActionExecutingView implements View { + + private Action action; + + private RequestContext context; + + private ActionExecutingView(Action action, RequestContext context) { + this.action = action; + this.context = context; + } + + public boolean eventSignaled() { + return context.getExternalContext().getRequestParameterMap().contains("_eventId"); + } + + public Event getEvent() { + return new Event(this, context.getExternalContext().getRequestParameterMap().get("_eventId")); + } + + public void render() { + ActionExecutor.execute(action, context); + } + + } + +} \ No newline at end of file diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/support/ActionInvokingViewFactory.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/support/ActionInvokingViewFactory.java deleted file mode 100644 index d34dad86..00000000 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/support/ActionInvokingViewFactory.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.springframework.webflow.engine.builder.support; - -import org.springframework.webflow.execution.Action; -import org.springframework.webflow.execution.Event; -import org.springframework.webflow.execution.RequestContext; -import org.springframework.webflow.execution.View; -import org.springframework.webflow.execution.ViewFactory; - -public class ActionInvokingViewFactory implements ViewFactory { - - private Action action; - - public ActionInvokingViewFactory(Action action) { - this.action = action; - } - - public View getView(RequestContext context) { - return new ActionExecutingView(action, context); - } - - private static class ActionExecutingView implements View { - - private Action action; - - private RequestContext context; - - private ActionExecutingView(Action action, RequestContext context) { - this.action = action; - this.context = context; - } - - public boolean eventSignaled() { - return context.getExternalContext().getRequestParameterMap().contains("_eventId"); - } - - public Event getEvent() { - return new Event(this, context.getExternalContext().getRequestParameterMap().get("_eventId")); - } - - public void render() { - try { - action.execute(context); - } catch (Exception e) { - // TODO - } - } - - } - -} \ No newline at end of file diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/support/FlowBuilderContextImpl.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/support/FlowBuilderContextImpl.java index 05e2d02e..add4358e 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/support/FlowBuilderContextImpl.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/support/FlowBuilderContextImpl.java @@ -12,6 +12,10 @@ import org.springframework.webflow.engine.builder.FlowArtifactFactory; import org.springframework.webflow.engine.builder.FlowBuilderContext; import org.springframework.webflow.engine.builder.ViewFactoryCreator; +/** + * Generic implementation of a flow builder context, suitable for use by most flow assembly systems. + * @author Keith Donald + */ public class FlowBuilderContextImpl implements FlowBuilderContext { private String flowId; @@ -24,6 +28,13 @@ public class FlowBuilderContextImpl implements FlowBuilderContext { private GenericConversionService flowConversionService; + /** + * Creates a new flow builder context. + * @param flowId the id to assign the flow being built + * @param flowAttributes attributes to assign the flow being built + * @param flowDefinitionLocator a locator to find dependent subflows + * @param flowBuilderServices a parameter object providing access to additional services needed by the flow builder + */ public FlowBuilderContextImpl(String flowId, AttributeMap flowAttributes, FlowDefinitionLocator flowDefinitionLocator, FlowBuilderServices flowBuilderServices) { this.flowId = flowId; @@ -36,6 +47,12 @@ public class FlowBuilderContextImpl implements FlowBuilderContext { flowConversionService.setParent(this.flowBuilderServices.getConversionService()); } + public FlowBuilderServices getFlowBuilderServices() { + return flowBuilderServices; + } + + // implementing flow builder context + public String getFlowId() { return flowId; } @@ -76,7 +93,4 @@ public class FlowBuilderContextImpl implements FlowBuilderContext { return flowDefinitionLocator; } - public FlowBuilderServices getFlowBuilderServices() { - return flowBuilderServices; - } } diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/support/FlowBuilderServices.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/support/FlowBuilderServices.java index 1563a5f5..49499ddd 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/support/FlowBuilderServices.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/support/FlowBuilderServices.java @@ -14,9 +14,16 @@ import org.springframework.webflow.core.expression.DefaultExpressionParserFactor import org.springframework.webflow.engine.Flow; import org.springframework.webflow.engine.State; import org.springframework.webflow.engine.builder.FlowArtifactFactory; +import org.springframework.webflow.engine.builder.FlowBuilderContext; import org.springframework.webflow.engine.builder.ViewFactoryCreator; import org.springframework.webflow.execution.Action; +/** + * A simple holder for services needed by a flow builder. These services are typically exposed via a Flow Builder's + * {@link FlowBuilderContext}. + * + * @author Keith Donald + */ public class FlowBuilderServices implements ResourceLoaderAware, BeanFactoryAware { /** @@ -32,17 +39,19 @@ public class FlowBuilderServices implements ResourceLoaderAware, BeanFactoryAwar private BeanInvokingActionFactory beanInvokingActionFactory = new BeanInvokingActionFactory(); /** - * The view factory creator. + * The view factory creator for creating views to render during flow execution. The default is null + * and this service must be configured externally. */ private ViewFactoryCreator viewFactoryCreator; /** - * The conversion service. + * The conversion service for converting from one object type to another. */ private ConversionService conversionService = new DefaultConversionService(); /** - * The parser for parsing expression strings into expression objects. + * The parser for parsing expression strings into expression objects. The default is Web Flow's default expression + * parser implementation. */ private ExpressionParser expressionParser = DefaultExpressionParserFactory.getExpressionParser(); @@ -52,7 +61,7 @@ public class FlowBuilderServices implements ResourceLoaderAware, BeanFactoryAwar private ResourceLoader resourceLoader; /** - * The Spring bean factory used. + * The Spring bean factory that provides access to the services of the user application. */ private BeanFactory beanFactory; diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/LocalFlowBuilderContext.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/LocalFlowBuilderContext.java index 42e1eaae..22926c22 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/LocalFlowBuilderContext.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/LocalFlowBuilderContext.java @@ -26,6 +26,11 @@ 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 + * the services of the external "parent" context. + * @author Keith Donald + */ class LocalFlowBuilderContext implements FlowBuilderContext { private FlowBuilderContext parent; diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/XmlFlowBuilder.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/XmlFlowBuilder.java index d7060d2b..94c8682a 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/XmlFlowBuilder.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/XmlFlowBuilder.java @@ -57,6 +57,7 @@ import org.springframework.webflow.action.EvaluateAction; import org.springframework.webflow.action.ExternalRedirectAction; import org.springframework.webflow.action.FlowDefinitionRedirectAction; 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; @@ -71,7 +72,7 @@ import org.springframework.webflow.engine.TransitionCriteria; import org.springframework.webflow.engine.builder.FlowArtifactFactory; import org.springframework.webflow.engine.builder.FlowBuilderException; import org.springframework.webflow.engine.builder.support.AbstractFlowBuilder; -import org.springframework.webflow.engine.builder.support.ActionInvokingViewFactory; +import org.springframework.webflow.engine.builder.support.ActionExecutingViewFactory; import org.springframework.webflow.engine.support.BeanFactoryFlowVariable; import org.springframework.webflow.engine.support.BooleanExpressionTransitionCriteria; import org.springframework.webflow.engine.support.SimpleFlowVariable; @@ -96,13 +97,13 @@ import org.xml.sax.SAXException; * <flow xmlns="http://www.springframework.org/schema/webflow" * xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" * xsi:schemaLocation="http://www.springframework.org/schema/webflow - * http://www.springframework.org/schema/webflow/spring-webflow-1.0.xsd"> + * http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"> * <!-- Define your states here --> * </flow> * * *

    - * Consult the web flow XML schema + * Consult the web flow XML schema * for more information on the XML-based flow definition format. *

    * This builder will setup a flow-local bean factory for the flow being constructed. That flow-local bean factory will @@ -125,8 +126,6 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde private static final String FLOW_ELEMENT = "flow"; - private static final String START_STATE_ELEMENT = "start-state"; - private static final String ACTION_STATE_ELEMENT = "action-state"; private static final String ACTION_ELEMENT = "action"; @@ -323,7 +322,8 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde protected Flow createFlow() { Flow flow = parseFlow(getDocumentElement()); - flow.setLocalBeanFactory(getLocalContext().getBeanFactory()); + flow.setBeanFactory(getLocalContext().getBeanFactory()); + flow.setResourceLoader(getLocalContext().getResourceLoader()); return flow; } @@ -542,12 +542,27 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde private void parseAndSetStartState(Element element, Flow flow) { String startStateId = getStartStateId(element); - flow.setStartState(startStateId); + if (StringUtils.hasText(startStateId)) { + flow.setStartState(startStateId); + } } private String getStartStateId(Element element) { - Element startStateElement = DomUtils.getChildElementByTagName(element, START_STATE_ELEMENT); - return startStateElement.getAttribute(IDREF_ATTRIBUTE); + String startState = "start-state"; + if (element.hasAttribute(startState)) { + Element startStateElement = DomUtils.getChildElementByTagName(element, startState); + Assert + .isNull(startStateElement, + "Define either a flow 'start-state' attribute or use the classic 'start-state' element. Do not use both."); + return element.getAttribute(startState); + } else { + Element startStateElement = DomUtils.getChildElementByTagName(element, startState); + if (startStateElement != null) { + return startStateElement.getAttribute(IDREF_ATTRIBUTE); + } else { + return null; + } + } } private void parseAndAddActionState(Element element, Flow flow) { @@ -614,11 +629,11 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde String encodedUrl = encodedView.substring(EXTERNAL_REDIRECT_PREFIX.length()); Expression externalUrl = getExpressionParser().parseExpression(encodedUrl, new ParserContextImpl().eval(RequestContext.class).expect(String.class)); - ViewFactory viewFactory = new ActionInvokingViewFactory(new ExternalRedirectAction(externalUrl)); + ViewFactory viewFactory = new ActionExecutingViewFactory(new ExternalRedirectAction(externalUrl)); return new ViewInfo(viewFactory, Boolean.FALSE); } else if (encodedView.startsWith(FLOW_DEFINITION_REDIRECT_PREFIX)) { String flowRedirect = encodedView.substring(FLOW_DEFINITION_REDIRECT_PREFIX.length()); - ViewFactory viewFactory = new ActionInvokingViewFactory(FlowDefinitionRedirectAction.create(flowRedirect)); + ViewFactory viewFactory = new ActionExecutingViewFactory(FlowDefinitionRedirectAction.create(flowRedirect)); return new ViewInfo(viewFactory, Boolean.FALSE); } else if (encodedView.startsWith(BEAN_PREFIX)) { ViewFactory viewFactory = (ViewFactory) getLocalContext().getBeanFactory().getBean( @@ -652,8 +667,8 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde } else { Expression viewName = getExpressionParser().parseExpression(encodedView, new ParserContextImpl().eval(RequestContext.class).expect(String.class)); - return getLocalContext().getViewFactoryCreator().createFinalResponseAction(viewName, - getLocalContext().getResourceLoader()); + return new ViewFactoryActionAdapter(getLocalContext().getViewFactoryCreator().createViewFactory(viewName, + getLocalContext().getResourceLoader())); } } diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/spring-webflow-2.0.xsd b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/spring-webflow-2.0.xsd index 8ea7c86d..4129d309 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/spring-webflow-2.0.xsd +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/spring-webflow-2.0.xsd @@ -280,11 +280,16 @@ Defines flow startup logic to execute. This logic will always execute when this - + @@ -453,6 +458,16 @@ definition resource location. + + + + + + + diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionImpl.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionImpl.java index b6cfedfc..7109b7a3 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionImpl.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionImpl.java @@ -367,11 +367,11 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable { } FlowExecutionKey assignKey() { - this.key = keyFactory.getKey(this); + key = keyFactory.getKey(this); if (logger.isDebugEnabled()) { - logger.debug("Assigned key " + this.key); + logger.debug("Assigned key " + key); } - return this.key; + return key; } // package private setters for restoring transient state used by FlowExecutionImplServicesConfigurer diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/MvcViewFactoryCreator.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/MvcViewFactoryCreator.java index 86f0d379..6c4a1770 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/MvcViewFactoryCreator.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/MvcViewFactoryCreator.java @@ -46,7 +46,7 @@ public class MvcViewFactoryCreator implements ViewFactoryCreator, ApplicationCon private ApplicationContext applicationContext; - public Action createFinalResponseAction(Expression viewId, ResourceLoader viewResourceLoader) { + public Action createRenderViewAction(Expression viewId, ResourceLoader viewResourceLoader) { return new ViewFactoryActionAdapter(createViewFactory(viewId, viewResourceLoader)); } diff --git a/spring-webflow/src/main/java/org/springframework/webflow/test/MockViewFactoryCreator.java b/spring-webflow/src/main/java/org/springframework/webflow/test/MockViewFactoryCreator.java index f80b9e89..0d864b66 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/test/MockViewFactoryCreator.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/test/MockViewFactoryCreator.java @@ -32,7 +32,7 @@ import org.springframework.webflow.execution.ViewFactory; * @author Keith Donald */ class MockViewFactoryCreator implements ViewFactoryCreator { - public Action createFinalResponseAction(Expression viewId, ResourceLoader viewResourceLoader) { + public Action createRenderViewAction(Expression viewId, ResourceLoader viewResourceLoader) { return new ViewFactoryActionAdapter(createViewFactory(viewId, viewResourceLoader)); } diff --git a/spring-webflow/src/test/java/org/springframework/webflow/config/FlowBuilderServicesBeanDefinitionParserTests.java b/spring-webflow/src/test/java/org/springframework/webflow/config/FlowBuilderServicesBeanDefinitionParserTests.java index 5da9255b..53f82dfd 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/config/FlowBuilderServicesBeanDefinitionParserTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/config/FlowBuilderServicesBeanDefinitionParserTests.java @@ -43,7 +43,7 @@ public class FlowBuilderServicesBeanDefinitionParserTests extends TestCase { public static class TestViewFactoryCreator implements ViewFactoryCreator { - public Action createFinalResponseAction(Expression viewId, ResourceLoader viewResourceLoader) { + public Action createRenderViewAction(Expression viewId, ResourceLoader viewResourceLoader) { throw new UnsupportedOperationException("Auto-generated method stub"); } diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/XmlFlowBuilderTests.java b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/XmlFlowBuilderTests.java index 5097d050..b3f916e8 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/XmlFlowBuilderTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/XmlFlowBuilderTests.java @@ -37,4 +37,32 @@ public class XmlFlowBuilderTests extends TestCase { 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 testBuildFlowWithStartStateElement() { + ClassPathResource resource = new ClassPathResource("flow-startstate-element.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()); + } + } diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/flow-endstate.xml b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/flow-endstate.xml index 9bdfa89c..04150939 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/flow-endstate.xml +++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/flow-endstate.xml @@ -1,10 +1,8 @@ + http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd" start-state="end"> - - \ No newline at end of file diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/flow-startstate-attribute.xml b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/flow-startstate-attribute.xml new file mode 100644 index 00000000..70961fbd --- /dev/null +++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/flow-startstate-attribute.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/flow-startstate-default.xml b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/flow-startstate-default.xml new file mode 100644 index 00000000..64a24bae --- /dev/null +++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/flow-startstate-default.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/flow-startstate-element.xml b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/flow-startstate-element.xml new file mode 100644 index 00000000..0a1fa809 --- /dev/null +++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/xml/flow-startstate-element.xml @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file