made start state optional

This commit is contained in:
Keith Donald
2008-02-28 22:23:23 +00:00
parent 27bfc1883b
commit f9a40441f1
25 changed files with 374 additions and 144 deletions

View File

@@ -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);
}
}

View File

@@ -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.

View File

@@ -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;
*
* <pre>
* FlowBuilder builder = ...;
* Flow flow = new FlowAssembler(&quot;myFlow&quot;, builder, null).assembleFlow();
* FlowBuilder context = ...;
* Flow flow = new FlowAssembler(builder, builderContext).assembleFlow();
* </pre>
*
* @see org.springframework.webflow.engine.builder.FlowBuilder

View File

@@ -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;
* <li> Call {@link #buildInputMapper()} to create and set the input mapper for the flow.
* <li> Call {@link #buildStartActions()} to create and add any start actions to the flow.
* <li> Call {@link #buildStates()} to create the states of the flow and add them to the flow definition.
* <li> Call {@link #buildGlobalTransitions()} to create the any transitions shared by all states of the flow and add
* them to the flow definition.
* <li> Call {@link #buildGlobalTransitions()} to create any transitions shared by all states of the flow and add them
* to the flow definition.
* <li> Call {@link #buildEndActions()} to create and add any end actions to the flow.
* <li> Call {@link #buildOutputMapper()} to create and set the output mapper for the flow.
* <li> 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.
* <p>
* 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;
}

View File

@@ -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 {
/**

View File

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

View File

@@ -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.

View File

@@ -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);
}

View File

@@ -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() {
}

View File

@@ -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);
}
}
}

View File

@@ -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
}
}
}
}

View File

@@ -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;
}
}

View File

@@ -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 <code>null</code>
* 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;

View File

@@ -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;

View File

@@ -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;
* &lt;flow xmlns=&quot;http://www.springframework.org/schema/webflow&quot;
* xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
* xsi:schemaLocation=&quot;http://www.springframework.org/schema/webflow
* http://www.springframework.org/schema/webflow/spring-webflow-1.0.xsd&quot;&gt;
* http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd&quot;&gt;
* &lt;!-- Define your states here --&gt;
* &lt;/flow&gt;
* </pre>
*
* <p>
* Consult the <a href="http://www.springframework.org/schema/webflow/spring-webflow-1.0.xsd">web flow XML schema</a>
* Consult the <a href="http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">web flow XML schema</a>
* for more information on the XML-based flow definition format.
* <p>
* 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()));
}
}

View File

@@ -280,11 +280,16 @@ Defines flow startup logic to execute. This logic will always execute when this
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element ref="start-state">
<xsd:element ref="start-state" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
Identifies the starting point of this flow. The start state is the point where flow execution begins.
Identifies the starting point of this flow. The start state is the point where flow execution begins.
This element is an alternative to the 'start-state' attribute. In general, the 'start-state' attribute
is performed when you wish to explictly denote the flow start-state.
If not specified, the default start state is the first state defined in the document.
]]>
</xsd:documentation>
</xsd:annotation>
@@ -453,6 +458,16 @@ definition resource location.
</xsd:annotation>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="start-state" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
Identifies the starting point of this flow. The start state is the point where flow execution begins.
If not specified, the default start state is the first state defined in the document.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>

View File

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

View File

@@ -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));
}

View File

@@ -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));
}

View File

@@ -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");
}

View File

@@ -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());
}
}

View File

@@ -1,10 +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">
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd" start-state="end">
<start-state idref="end"/>
<end-state id="end"/>
</flow>

View File

@@ -0,0 +1,9 @@
<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" start-state="end">
<end-state id="foo"/>
<end-state id="end"/>
</flow>

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">
<end-state id="end"/>
</flow>

View File

@@ -0,0 +1,11 @@
<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">
<start-state idref="end"/>
<end-state id="foo"/>
<end-state id="end"/>
</flow>