view variables

popup attribute
redirect attribute
This commit is contained in:
Keith Donald
2008-02-29 22:31:57 +00:00
parent 635bd56b79
commit 7800a315a0
19 changed files with 443 additions and 89 deletions

View File

@@ -46,7 +46,6 @@ public class FlowDefinitionRedirectAction extends AbstractAction {
}
public static FlowDefinitionRedirectAction create(String encodedFlowRedirect) {
// TODO
return null;
throw new UnsupportedOperationException("Not yet implemented");
}
}

View File

@@ -16,7 +16,9 @@
package org.springframework.webflow.engine;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
@@ -129,7 +131,7 @@ public class Flow extends AnnotatedObject implements FlowDefinition {
/**
* The set of flow variables created by this flow.
*/
private Set variables = new LinkedHashSet(3);
private Map variables = new LinkedHashMap();
/**
* The mapper to map flow input attributes.
@@ -350,7 +352,7 @@ public class Flow extends AnnotatedObject implements FlowDefinition {
* @param variable the variable
*/
public void addVariable(FlowVariable variable) {
variables.add(variable);
variables.put(variable.getName(), variable);
}
/**
@@ -366,11 +368,19 @@ public class Flow extends AnnotatedObject implements FlowDefinition {
}
}
/**
* Returns the flow variable with the given name.
* @param name the name of the variable
*/
public FlowVariable getVariable(String name) {
return (FlowVariable) variables.get(name);
}
/**
* Returns the flow variables.
*/
public FlowVariable[] getVariables() {
return (FlowVariable[]) variables.toArray(new FlowVariable[variables.size()]);
return (FlowVariable[]) variables.values().toArray(new FlowVariable[variables.size()]);
}
/**
@@ -513,6 +523,7 @@ public class Flow extends AnnotatedObject implements FlowDefinition {
* @throws FlowExecutionException when an exception occurs during the resume operation
*/
public void resume(RequestControlContext context) throws FlowExecutionException {
restoreVariables(context);
getCurrentViewState(context).resume(context);
}
@@ -575,11 +586,8 @@ public class Flow extends AnnotatedObject implements FlowDefinition {
}
}
/**
* Create (setup) all known flow variables in flow scope.
*/
private void createVariables(RequestContext context) {
Iterator it = variables.iterator();
Iterator it = variables.values().iterator();
while (it.hasNext()) {
FlowVariable variable = (FlowVariable) it.next();
if (logger.isDebugEnabled()) {
@@ -589,9 +597,17 @@ public class Flow extends AnnotatedObject implements FlowDefinition {
}
}
/**
* Returns the current state and makes sure it is a view state.
*/
private void restoreVariables(RequestContext context) {
Iterator it = variables.values().iterator();
while (it.hasNext()) {
FlowVariable variable = (FlowVariable) it.next();
if (logger.isDebugEnabled()) {
logger.debug("Restoring " + variable);
}
variable.restore(context);
}
}
private ViewState getCurrentViewState(RequestControlContext context) {
State currentState = (State) context.getCurrentState();
if (!(currentState instanceof ViewState)) {
@@ -601,9 +617,6 @@ public class Flow extends AnnotatedObject implements FlowDefinition {
return (ViewState) currentState;
}
/**
* Returns the current state and makes sure it is transitionable.
*/
private TransitionableState getCurrentTransitionableState(RequestControlContext context) {
State currentState = (State) context.getCurrentState();
if (!(currentState instanceof TransitionableState)) {

View File

@@ -17,7 +17,6 @@ package org.springframework.webflow.engine;
import org.springframework.core.style.ToStringCreator;
import org.springframework.util.Assert;
import org.springframework.webflow.execution.FlowSession;
import org.springframework.webflow.execution.RequestContext;
/**
@@ -85,10 +84,6 @@ public class FlowVariable extends AnnotatedObject {
return name.hashCode() + valueFactory.hashCode() + local.hashCode();
}
/**
* Creates a new instance of this flow variable in the configured scope.
* @param context the flow execution request context
*/
public final void create(RequestContext context) {
Object value = valueFactory.createVariableValue(context);
if (local == Boolean.TRUE) {
@@ -98,10 +93,10 @@ public class FlowVariable extends AnnotatedObject {
}
}
public final Object restore(FlowSession session, RequestContext context) {
public final Object restore(RequestContext context) {
Object value;
if (local == Boolean.TRUE) {
value = session.getScope().get(name);
value = context.getFlowScope().get(name);
} else {
value = context.getConversationScope().get(name);
}

View File

@@ -184,10 +184,21 @@ public abstract class State extends AnnotatedObject implements StateDefinition {
logger.debug("Entering state '" + getId() + "' of flow '" + getFlow().getId() + "'");
}
context.setCurrentState(this);
doPreEntryActions(context);
entryActionList.execute(context);
doEnter(context);
}
/**
* Hook method to execute before running state entry actions upon state entry. Does nothing by default. Subclasses
* may override.
* @param context the request control context
* @throws FlowExecutionException if an exception occurs
*/
protected void doPreEntryActions(RequestControlContext context) throws FlowExecutionException {
}
/**
* Hook method to execute custom behavior as a result of entering this state. By implementing this method subclasses
* specialize the behavior of the state.

View File

@@ -15,10 +15,15 @@
*/
package org.springframework.webflow.engine;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.core.style.ToStringCreator;
import org.springframework.util.Assert;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.FlowExecutionException;
import org.springframework.webflow.execution.RequestContext;
import org.springframework.webflow.execution.View;
import org.springframework.webflow.execution.ViewFactory;
@@ -33,25 +38,33 @@ import org.springframework.webflow.execution.ViewFactory;
*/
public class ViewState extends TransitionableState {
private static final String FLOW_MODAL_VIEW_HEADER = "Flow-Modal-View";
private static final String MODAL_ATTR = "modal";
private static final String POPUP_VIEW_HEADER = "Flow-Modal-View";
/**
* The list of actions to be executed when this state is entered.
*/
private ActionList renderActionList = new ActionList();
/**
* Whether or not a redirect should occur before the view is rendered.
*/
private boolean redirect;
/**
* A factory for creating and restoring the view rendered by this view state.
*/
private ViewFactory viewFactory;
/**
* The set of view variables created by this view state.
*/
private Map variables = new LinkedHashMap();
/**
* Whether or not a redirect should occur before the view is rendered.
*/
private Boolean redirect;
/**
* Whether or not the view should render as a popup.
*/
private boolean popup;
/**
* Create a new view state.
* @param flow the owning flow
@@ -65,12 +78,53 @@ public class ViewState extends TransitionableState {
this.viewFactory = viewFactory;
}
/**
* Adds a view variable.
* @param variable the variable
*/
public void addVariable(ViewVariable variable) {
variables.put(variable.getName(), variable);
}
/**
* Adds a set of view variables.
* @param variables the variables
*/
public void addVariables(ViewVariable[] variables) {
for (int i = 0; i < variables.length; i++) {
addVariable(variables[i]);
}
}
/**
* Returns the view variable with the given name.
* @param name the name of the variable
*/
public ViewVariable getVariable(String name) {
return (ViewVariable) variables.get(name);
}
/**
* Returns the flow variables.
*/
public ViewVariable[] getVariables() {
return (ViewVariable[]) variables.values().toArray(new ViewVariable[variables.size()]);
}
/**
* Sets whether this view state should send a flow execution redirect when entered.
* @param redirect the redirect flag
*/
public void setRedirect(boolean redirect) {
this.redirect = redirect;
this.redirect = Boolean.valueOf(redirect);
}
/**
* Sets whether this view state should render as a popup.
* @param popup the popup flag
*/
public void setPopup(boolean popup) {
this.popup = popup;
}
/**
@@ -81,13 +135,16 @@ public class ViewState extends TransitionableState {
return renderActionList;
}
protected void doPreEntryActions(RequestControlContext context) throws FlowExecutionException {
createVariables(context);
}
protected void doEnter(RequestControlContext context) throws FlowExecutionException {
context.assignFlowExecutionKey();
if (context.getExternalContext().isAjaxRequest()
&& Boolean.TRUE.equals(context.getCurrentState().getAttributes().getBoolean(MODAL_ATTR))) {
context.getExternalContext().setResponseHeader(FLOW_MODAL_VIEW_HEADER, "true");
}
if (shouldRedirect(context)) {
if (context.getExternalContext().isAjaxRequest() && popup) {
context.getExternalContext().setResponseHeader(POPUP_VIEW_HEADER, "true");
}
context.sendFlowExecutionRedirect();
} else {
View view = viewFactory.getView(context);
@@ -103,6 +160,7 @@ public class ViewState extends TransitionableState {
public void resume(RequestControlContext context) {
View view = viewFactory.getView(context);
restoreVariables(context);
if (view.eventSignaled()) {
Event event = view.getEvent();
if (logger.isDebugEnabled()) {
@@ -120,12 +178,58 @@ public class ViewState extends TransitionableState {
}
}
public void exit(RequestControlContext context) {
destroyVariables(context);
super.exit(context);
}
// internal helpers
private boolean shouldRedirect(RequestControlContext context) {
return redirect || context.getAlwaysRedirectOnPause();
if (redirect != null) {
return redirect.booleanValue();
} else {
return context.getAlwaysRedirectOnPause();
}
}
private void createVariables(RequestContext context) {
Iterator it = variables.values().iterator();
while (it.hasNext()) {
ViewVariable variable = (ViewVariable) it.next();
if (logger.isDebugEnabled()) {
logger.debug("Creating " + variable);
}
variable.create(context);
}
}
private void restoreVariables(RequestContext context) {
Iterator it = variables.values().iterator();
while (it.hasNext()) {
ViewVariable variable = (ViewVariable) it.next();
if (logger.isDebugEnabled()) {
logger.debug("Destroying " + variable);
}
variable.restore(context);
}
}
private void destroyVariables(RequestContext context) {
Iterator it = variables.values().iterator();
while (it.hasNext()) {
ViewVariable variable = (ViewVariable) it.next();
if (logger.isDebugEnabled()) {
logger.debug("Destroying " + variable);
}
variable.destroy(context);
}
}
protected void appendToString(ToStringCreator creator) {
super.appendToString(creator);
creator.append("viewManager", viewFactory);
creator.append("viewFactory", viewFactory).append("variables", variables);
}
}

View File

@@ -0,0 +1,50 @@
package org.springframework.webflow.engine;
import org.springframework.webflow.execution.RequestContext;
public class ViewVariable extends AnnotatedObject {
private String name;
private VariableValueFactory valueFactory;
public ViewVariable(String name, VariableValueFactory valueFactory) {
this.name = name;
this.valueFactory = valueFactory;
}
public String getName() {
return name;
}
public VariableValueFactory getValueFactory() {
return valueFactory;
}
// name and scope based equality
public boolean equals(Object o) {
if (!(o instanceof ViewVariable)) {
return false;
}
ViewVariable other = (ViewVariable) o;
return name.equals(other.name) && valueFactory.equals(other.valueFactory);
}
public int hashCode() {
return name.hashCode() + valueFactory.hashCode();
}
public final void create(RequestContext context) {
Object value = valueFactory.createVariableValue(context);
context.getFlowScope().put(name, value);
}
public final Object restore(RequestContext context) {
Object value = context.getFlowScope().get(name);
return valueFactory.restoreReferences(value, context);
}
public final Object destroy(RequestContext context) {
return context.getFlowScope().remove(name);
}
}

View File

@@ -30,6 +30,7 @@ import org.springframework.webflow.engine.Transition;
import org.springframework.webflow.engine.TransitionCriteria;
import org.springframework.webflow.engine.TransitionableState;
import org.springframework.webflow.engine.ViewState;
import org.springframework.webflow.engine.ViewVariable;
import org.springframework.webflow.execution.Action;
import org.springframework.webflow.execution.ViewFactory;
@@ -38,7 +39,7 @@ import org.springframework.webflow.execution.ViewFactory;
* {@link Transition transitions}.
* <p>
* This factory encapsulates the construction of each Flow implementation as well as each core artifact type. Subclasses
* may customize how the core elements are created, useful for plugging in custom implementations.
* may customize how the core elements are created.
*
* @author Keith Donald
* @author Erwin Vervaet
@@ -69,6 +70,7 @@ public class FlowArtifactFactory {
* @param entryActions any state entry actions; may be null
* @param viewFactory the state view factory strategy
* @param redirect whether to send a flow execution redirect before rendering
* @param popup whether to display the view in a popup window
* @param renderActions any 'render actions' to execute on entry and refresh; may be null
* @param transitions any transitions (paths) out of this state; may be null
* @param exceptionHandlers any exception handlers; may be null
@@ -77,11 +79,13 @@ public class FlowArtifactFactory {
* null
* @return the fully initialized view state instance
*/
public State createViewState(String id, Flow flow, Action[] entryActions, ViewFactory viewFactory,
boolean redirect, Action[] renderActions, Transition[] transitions,
public State createViewState(String id, Flow flow, ViewVariable[] variables, Action[] entryActions,
ViewFactory viewFactory, boolean redirect, boolean popup, Action[] renderActions, Transition[] transitions,
FlowExecutionExceptionHandler[] exceptionHandlers, Action[] exitActions, AttributeMap attributes) {
ViewState viewState = new ViewState(flow, id, viewFactory);
viewState.addVariables(variables);
viewState.setRedirect(redirect);
viewState.setPopup(popup);
viewState.getRenderActionList().addAll(renderActions);
configureCommonProperties(viewState, entryActions, transitions, exceptionHandlers, exitActions, attributes);
return viewState;

View File

@@ -65,7 +65,9 @@ public class ActionExecutingViewFactory implements ViewFactory {
}
public void render() {
ActionExecutor.execute(action, context);
if (action != null) {
ActionExecutor.execute(action, context);
}
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.webflow.engine.builder.xml;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
@@ -43,6 +44,7 @@ import org.springframework.binding.method.MethodSignature;
import org.springframework.binding.method.Parameter;
import org.springframework.binding.method.Parameters;
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;
@@ -70,6 +72,7 @@ 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.FlowArtifactFactory;
import org.springframework.webflow.engine.builder.FlowBuilderException;
import org.springframework.webflow.engine.builder.support.AbstractFlowBuilder;
@@ -435,7 +438,8 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde
+ importElement.getAttribute(RESOURCE_ATTRIBUTE) + "'", e);
}
}
this.localFlowBuilderContext = new LocalFlowBuilderContext(getContext(), createFlowApplicationContext(resources));
this.localFlowBuilderContext = new LocalFlowBuilderContext(getContext(),
createFlowApplicationContext(resources));
}
private GenericApplicationContext createFlowApplicationContext(Resource[] resources) {
@@ -459,6 +463,7 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde
}
}
flowContext.setResourceLoader(new FlowRelativeResourceLoader(resource));
AnnotationConfigUtils.registerAnnotationConfigProcessors(flowContext);
new XmlBeanDefinitionReader(flowContext).loadBeanDefinitions(resources);
registerFlowBeans(flowContext.getDefaultListableBeanFactory());
flowContext.refresh();
@@ -561,14 +566,35 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde
}
private void parseAndAddViewState(Element element, Flow flow) {
ViewInfo viewInfo = parseViewInfo(element);
ViewFactory viewFactory = parseViewFactory(element);
boolean redirect = false;
if (viewInfo.redirect != null) {
redirect = viewInfo.redirect.booleanValue();
if (element.hasAttribute("redirect")) {
redirect = ((Boolean) fromStringTo(Boolean.class).execute(element.getAttribute("redirect"))).booleanValue();
}
getFlowArtifactFactory().createViewState(parseId(element), flow, parseEntryActions(element),
viewInfo.viewFactory, redirect, parseRenderActions(element), parseTransitions(element),
parseExceptionHandlers(element), parseExitActions(element), parseAttributes(element));
boolean popup = false;
if (element.hasAttribute("popup")) {
popup = ((Boolean) fromStringTo(Boolean.class).execute(element.getAttribute("popup"))).booleanValue();
}
getFlowArtifactFactory().createViewState(parseId(element), flow, parseViewVariables(element),
parseEntryActions(element), viewFactory, redirect, popup, parseRenderActions(element),
parseTransitions(element), parseExceptionHandlers(element), parseExitActions(element),
parseAttributes(element));
}
private ViewVariable[] parseViewVariables(Element viewStateElement) {
List varElements = DomUtils.getChildElementsByTagName(viewStateElement, VAR_ELEMENT);
List variables = new ArrayList(varElements.size());
for (Iterator it = varElements.iterator(); it.hasNext();) {
variables.add(parseViewVariable((Element) it.next()));
}
return (ViewVariable[]) variables.toArray(new ViewVariable[variables.size()]);
}
private ViewVariable parseViewVariable(Element element) {
Class clazz = (Class) fromStringTo(Class.class).execute(element.getAttribute(CLASS_ATTRIBUTE));
VariableValueFactory valueFactory = new BeanFactoryVariableValueFactory(clazz,
(AutowireCapableBeanFactory) getFlow().getBeanFactory());
return new ViewVariable(element.getAttribute("name"), valueFactory);
}
private void parseAndAddDecisionState(Element element, Flow flow) {
@@ -602,41 +628,38 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde
}
}
private ViewInfo parseViewInfo(Element element) {
private ViewFactory parseViewFactory(Element element) {
String encodedView = element.getAttribute(VIEW_ATTRIBUTE);
if (encodedView == null || encodedView.length() == 0) {
// TODO what to do here?
return null;
} else if (encodedView.startsWith(REDIRECT_PREFIX)) {
String encodedViewName = encodedView.substring(REDIRECT_PREFIX.length());
Expression viewName = getExpressionParser().parseExpression(encodedViewName,
if (!StringUtils.hasText(encodedView)) {
encodedView = createViewId(element.getAttribute(ID_ATTRIBUTE));
Expression viewName = getExpressionParser().parseExpression(encodedView,
new ParserContextImpl().eval(RequestContext.class).expect(String.class));
ViewFactory viewFactory = getLocalContext().getViewFactoryCreator().createViewFactory(viewName,
return getLocalContext().getViewFactoryCreator().createViewFactory(viewName,
getLocalContext().getResourceLoader());
return new ViewInfo(viewFactory, Boolean.TRUE);
} else if (encodedView.startsWith(EXTERNAL_REDIRECT_PREFIX)) {
String encodedUrl = encodedView.substring(EXTERNAL_REDIRECT_PREFIX.length());
Expression externalUrl = getExpressionParser().parseExpression(encodedUrl,
new ParserContextImpl().eval(RequestContext.class).expect(String.class));
ViewFactory viewFactory = new ActionExecutingViewFactory(new ExternalRedirectAction(externalUrl));
return new ViewInfo(viewFactory, Boolean.FALSE);
return new ActionExecutingViewFactory(new ExternalRedirectAction(externalUrl));
} else if (encodedView.startsWith(FLOW_DEFINITION_REDIRECT_PREFIX)) {
String flowRedirect = encodedView.substring(FLOW_DEFINITION_REDIRECT_PREFIX.length());
ViewFactory viewFactory = new ActionExecutingViewFactory(FlowDefinitionRedirectAction.create(flowRedirect));
return new ViewInfo(viewFactory, Boolean.FALSE);
return new ActionExecutingViewFactory(FlowDefinitionRedirectAction.create(flowRedirect));
} else if (encodedView.startsWith(BEAN_PREFIX)) {
ViewFactory viewFactory = (ViewFactory) getLocalContext().getBeanFactory().getBean(
return (ViewFactory) getLocalContext().getBeanFactory().getBean(
encodedView.substring(BEAN_PREFIX.length()), ViewFactory.class);
return new ViewInfo(viewFactory, Boolean.FALSE);
} else {
Expression viewName = getExpressionParser().parseExpression(encodedView,
new ParserContextImpl().eval(RequestContext.class).expect(String.class));
ViewFactory viewFactory = getLocalContext().getViewFactoryCreator().createViewFactory(viewName,
return getLocalContext().getViewFactoryCreator().createViewFactory(viewName,
getLocalContext().getResourceLoader());
return new ViewInfo(viewFactory, null);
}
}
// TODO - make configurable
private String createViewId(String viewStateId) {
return viewStateId + ".html";
}
private Action parseFinalResponseAction(Element element) {
String encodedView = element.getAttribute(VIEW_ATTRIBUTE);
if (encodedView == null || encodedView.length() == 0) {
@@ -1123,12 +1146,14 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde
private ViewFactory viewFactory;
private Boolean redirect;
public ViewInfo(ViewFactory viewFactory, Boolean redirect) {
public ViewInfo(ViewFactory viewFactory) {
this.viewFactory = viewFactory;
this.redirect = redirect;
}
public ViewFactory getViewFactory() {
return viewFactory;
}
}
private static class FlowRelativeResourceLoader implements ResourceLoader {

View File

@@ -1294,7 +1294,17 @@ An attribute describing this state.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:element>
<xsd:element ref="var" minOccurs="0" maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
A view variable definition. View variables are automatically created when a view-state is entered and
destroyed when the view-state exits. A view variable is stored in flow scope.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element ref="entry-actions" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
@@ -1365,13 +1375,6 @@ This value may also be a view name expression evaluated against the request cont
<pre>
${flowScope.view}
</pre>
Use of the "redirect:" prefix indicates this view state should trigger a redirect to a
unique "flow execution URL". This causes the application view to render on the
redirected request to that URL. This allows browsers to refresh a specific
state of the conversation while it remains active:
<pre>
redirect:priceForm
</pre>
Use of the "externalRedirect:" prefix indicates this view state should trigger a
redirect to an absolute external URL, typically to interface with an external system.
External redirect query parameters may be specified using ${expressions} that evaluate
@@ -1379,27 +1382,43 @@ against the request context:
<pre>
externalRedirect:/someOtherSystem.htm?orderId=${flowScope.order.id}
</pre>
Use of the "flowRedirect:" prefix has this view state generate a redirect to a URL
that launches a new flow execution of an identified flow:
Use of the "flowRedirect:" prefix requests a redirect to a new execution of another flow:
<pre>
flowRedirect:editOrderFlow?orderId=${flowScope.order.id}
flowRedirect:myOtherFlow?someData=${flowScope.data}
</pre>
Use of the "bean:" prefix references a custom ViewSelector implementation you define,
Use of the "bean:" prefix references a custom ViewFactory implementation you define,
exposed by id in either a flow-local context using the "import" element or in the parent
context.
<pre>
bean:myCustomViewSelector
bean:myCustomViewFactory
</pre>
The exact semantics regarding the interpretation of this value are determined by the
installed TextToViewSelector converter.
<br>
Note when no view name is provided, this view state will make a "null" view selection. A null
view does not request the rendering of a view, it only pauses the flow and returns control
the client. Use a null view when another state is expected to generate the response.
Note when no view name is provided, the view to render will be determined by convention.
The default convention is to treat the id of this view state as the view identifier.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="redirect" type="xsd:boolean">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
Determines if this view state should request a flow execution redirect instead of rendering.
Default is false.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="popup" type="xsd:boolean">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
Determines if this view state should render its view in a modal popup dialog.
Default is false.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>

View File

@@ -0,0 +1,61 @@
package org.springframework.webflow.engine;
import junit.framework.TestCase;
import org.springframework.webflow.execution.RequestContext;
import org.springframework.webflow.test.MockRequestContext;
public class FlowVariableTests extends TestCase {
private boolean restoreCalled;
public void testCreateVariable() {
FlowVariable var = new FlowVariable("foo", new VariableValueFactory() {
public Object createVariableValue(RequestContext context) {
return "bar";
}
public Object restoreReferences(Object value, RequestContext context) {
return value;
}
}, true);
MockRequestContext context = new MockRequestContext();
var.create(context);
assertEquals("bar", context.getFlowScope().get("foo"));
}
public void testCreateConversationVariable() {
FlowVariable var = new FlowVariable("foo", new VariableValueFactory() {
public Object createVariableValue(RequestContext context) {
return "bar";
}
public Object restoreReferences(Object value, RequestContext context) {
return value;
}
}, false);
MockRequestContext context = new MockRequestContext();
var.create(context);
assertEquals("bar", context.getConversationScope().get("foo"));
}
public void testCreateRestoreVariable() {
FlowVariable var = new FlowVariable("foo", new VariableValueFactory() {
public Object createVariableValue(RequestContext context) {
return "bar";
}
public Object restoreReferences(Object value, RequestContext context) {
restoreCalled = true;
assertEquals("bar", value);
return value;
}
}, false);
MockRequestContext context = new MockRequestContext();
var.create(context);
var.restore(context);
assertEquals("bar", context.getConversationScope().get("foo"));
assertTrue(restoreCalled);
}
}

View File

@@ -5,6 +5,7 @@ import junit.framework.TestCase;
import org.springframework.beans.factory.support.StaticListableBeanFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.webflow.engine.Flow;
import org.springframework.webflow.engine.ViewState;
import org.springframework.webflow.engine.builder.FlowAssembler;
import org.springframework.webflow.engine.builder.FlowBuilderException;
import org.springframework.webflow.test.MockFlowBuilderContext;
@@ -79,9 +80,17 @@ public class XmlFlowBuilderTests extends TestCase {
builder = new XmlFlowBuilder(resource);
FlowAssembler assembler = new FlowAssembler(builder, new MockFlowBuilderContext("flow"));
Flow flow = assembler.assembleFlow();
assertEquals("flow-foo", flow.getVariables()[0].getName());
assertEquals(true, flow.getVariables()[0].isLocal());
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"));
}
}

View File

@@ -0,0 +1,7 @@
<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">
<view-state id="view" view="externalRedirect:http://www.paypal.com?_callbackUrl=#{flowExecutionUri}" />
</flow>

View File

@@ -0,0 +1,7 @@
<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">
<view-state id="view" view="flowRedirect:myFlow?input=#{flowScope.foo}" />
</flow>

View File

@@ -0,0 +1,7 @@
<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">
<view-state id="view" popup="true" />
</flow>

View File

@@ -0,0 +1,7 @@
<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">
<view-state id="view" redirect="true"/>
</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">
<view-state id="view">
<var name="foo" class="org.springframework.webflow.TestBean"/>
</view-state>
</flow>

View File

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

View File

@@ -0,0 +1,18 @@
package org.springframework.webflow.engine.support;
import junit.framework.TestCase;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.webflow.TestBean;
import org.springframework.webflow.test.MockRequestContext;
public class BeanFactoryVariableValueFactoryTests extends TestCase {
private BeanFactoryVariableValueFactory factory;
public void testCreateValue() {
factory = new BeanFactoryVariableValueFactory(TestBean.class, new DefaultListableBeanFactory());
MockRequestContext context = new MockRequestContext();
Object value = factory.createVariableValue(context);
assertTrue(value instanceof TestBean);
}
}