view variables
popup attribute redirect attribute
This commit is contained in:
@@ -46,7 +46,6 @@ public class FlowDefinitionRedirectAction extends AbstractAction {
|
||||
}
|
||||
|
||||
public static FlowDefinitionRedirectAction create(String encodedFlowRedirect) {
|
||||
// TODO
|
||||
return null;
|
||||
throw new UnsupportedOperationException("Not yet implemented");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -65,7 +65,9 @@ public class ActionExecutingViewFactory implements ViewFactory {
|
||||
}
|
||||
|
||||
public void render() {
|
||||
ActionExecutor.execute(action, context);
|
||||
if (action != null) {
|
||||
ActionExecutor.execute(action, context);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user