diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/FlowViewStateManager.java b/spring-faces/src/main/java/org/springframework/faces/webflow/FlowViewStateManager.java
index 74037264..a0490807 100644
--- a/spring-faces/src/main/java/org/springframework/faces/webflow/FlowViewStateManager.java
+++ b/spring-faces/src/main/java/org/springframework/faces/webflow/FlowViewStateManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2008 the original author or authors.
+ * Copyright 2004-2011 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.
@@ -30,6 +30,7 @@ import org.springframework.webflow.execution.RequestContextHolder;
* Custom {@link StateManager} that manages the JSF component state in web flow's view scope.
*
* @author Jeremy Grelle
+ * @author Rossen Stoyanchev
*/
public class FlowViewStateManager extends StateManager {
@@ -150,31 +151,35 @@ public class FlowViewStateManager extends StateManager {
/**
*
* JSF 1.2 (or higher) version of state saving.
- *
*
*
- * In JSF 2 where a partial state saving algorithm is used, this method merely delegates to the next
- * ViewStateManager. Thus partial state saving is handled by the JSF 2 runtime. However, a
- * {@link FlowViewResponseStateManager} plugged in via {@link FlowRenderKit} will ensure the state is saved in a Web
- * Flow view-scoped variable.
- *
+ * In JSF 2, if partial state saving is enabled this method delegates in order to obtain the serialized view state.
+ * During rendering JSF calls this method to prepare the state and then calls {@link FlowViewResponseStateManager}
+ * which writes it to Web Flow's view scope.
+ *
+ *
+ * Nevertheless this method always writes the serialized state to Web Flow's view scope to ensure it is up-to-date
+ * for cases outside of rendering (e.g. ViewState.updateHistory()) or when the render phase doesn't call
+ * {@link FlowViewResponseStateManager} such as when processing a partial request.
*/
public Object saveView(FacesContext context) {
if (context.getViewRoot().isTransient()) {
return null;
}
+ FlowSerializedView view = null;
if ((!JsfUtils.isFlowRequest()) || JsfRuntimeInformation.isPartialStateSavingSupported()) {
- return delegate.saveView(context);
+ Object[] state = (Object[]) delegate.saveView(context);
+ view = new FlowSerializedView(context.getViewRoot().getViewId(), state[0], state[1]);
} else {
- RequestContext requestContext = RequestContextHolder.getRequestContext();
- if (logger.isDebugEnabled()) {
- logger.debug("Saving view root '" + context.getViewRoot().getViewId() + "' in view scope");
- }
- FlowSerializedView view = new FlowSerializedView(context.getViewRoot().getViewId(),
- getTreeStructureToSave(context), getComponentStateToSave(context));
- requestContext.getViewScope().put(SERIALIZED_VIEW_STATE, view);
- return view;
+ view = new FlowSerializedView(context.getViewRoot().getViewId(), getTreeStructureToSave(context),
+ getComponentStateToSave(context));
}
+ RequestContext requestContext = RequestContextHolder.getRequestContext();
+ if (logger.isDebugEnabled()) {
+ logger.debug("Saving view root '" + context.getViewRoot().getViewId() + "' in view scope");
+ }
+ requestContext.getViewScope().put(SERIALIZED_VIEW_STATE, view);
+ return view;
}
/**
diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/JsfView.java b/spring-faces/src/main/java/org/springframework/faces/webflow/JsfView.java
index ed7cfb63..d270b2c0 100644
--- a/spring-faces/src/main/java/org/springframework/faces/webflow/JsfView.java
+++ b/spring-faces/src/main/java/org/springframework/faces/webflow/JsfView.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2008 the original author or authors.
+ * Copyright 2004-2011 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.
@@ -88,6 +88,13 @@ public class JsfView implements View {
try {
logger.debug("Asking faces lifecycle to render");
facesLifecycle.render(facesContext);
+
+ /* Ensure serialized view state is always updated even if JSF didn't call StateManager.writeState(). */
+ if (JsfRuntimeInformation.isAtLeastJsf20()) {
+ if (requestContext.getExternalContext().isAjaxRequest()) {
+ saveState();
+ }
+ }
} finally {
logger.debug("View rendering complete");
facesContext.responseComplete();
@@ -134,9 +141,10 @@ public class JsfView implements View {
// Set the temporary UIViewRoot state so that it will be available across the redirect
return new ViewRootHolder(getViewRoot());
} else {
- // In JSF 2 the partial state saving algorithm attaches a system event listener to the UIViewRoot with
- // a reference to the FacesContext instance. The FacesContext instance is released at end of each request.
- // Hence, keeping the UIViewRoot across the redirect is not feasible.
+ // In JSF 2 the partial state saving algorithm attaches a system event listener to the UIViewRoot which
+ // holds on to a reference to the FacesContext instance. The FacesContext instance is released at end of
+ // each request. Hence, keeping the UIViewRoot across the redirect is not feasible.
+ // @see com.sun.faces.context.StateContext$AddRemoveListener
logger.debug("User event state requested but not saved.");
return null;
}
diff --git a/spring-webflow-samples/booking-mvc/.springBeans b/spring-webflow-samples/booking-mvc/.springBeans
index 79d41891..3cc7edfd 100755
--- a/spring-webflow-samples/booking-mvc/.springBeans
+++ b/spring-webflow-samples/booking-mvc/.springBeans
@@ -1,12 +1,16 @@
1
-
+
+ src/main/webapp/WEB-INF/config/data-access-config.xml
+ src/main/webapp/WEB-INF/config/security-config.xml
+ src/main/webapp/WEB-INF/config/webflow-config.xml
+ src/main/webapp/WEB-INF/config/webmvc-config.xml
diff --git a/spring-webflow-samples/booking-mvc/pom.xml b/spring-webflow-samples/booking-mvc/pom.xml
index aaf7ff42..e142911d 100644
--- a/spring-webflow-samples/booking-mvc/pom.xml
+++ b/spring-webflow-samples/booking-mvc/pom.xml
@@ -17,7 +17,7 @@
org.springframework
- spring-context
+ spring-webmvc
${org.springframework-version}
@@ -37,11 +37,6 @@
spring-orm
${org.springframework-version}
-
- org.springframework
- spring-webmvc
- ${org.springframework-version}
-
org.springframework.webflow
spring-webflow
diff --git a/spring-webflow-samples/booking-mvc/src/main/webapp/WEB-INF/hotels/booking/booking-flow.xml b/spring-webflow-samples/booking-mvc/src/main/webapp/WEB-INF/hotels/booking/booking-flow.xml
index d746efef..82841d80 100644
--- a/spring-webflow-samples/booking-mvc/src/main/webapp/WEB-INF/hotels/booking/booking-flow.xml
+++ b/spring-webflow-samples/booking-mvc/src/main/webapp/WEB-INF/hotels/booking/booking-flow.xml
@@ -1,12 +1,14 @@
-
-
-
+ xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow.xsd">
+
+
+
+
+
-
+
@@ -23,11 +25,17 @@
+
+
+
-
+
+
+
+
@@ -36,7 +44,7 @@
-
+
\ No newline at end of file
diff --git a/spring-webflow-samples/booking-mvc/src/main/webapp/WEB-INF/hotels/booking/enterBookingDetails.jsp b/spring-webflow-samples/booking-mvc/src/main/webapp/WEB-INF/hotels/booking/enterBookingDetails.jsp
index 4416b2b0..1f95919d 100644
--- a/spring-webflow-samples/booking-mvc/src/main/webapp/WEB-INF/hotels/booking/enterBookingDetails.jsp
+++ b/spring-webflow-samples/booking-mvc/src/main/webapp/WEB-INF/hotels/booking/enterBookingDetails.jsp
@@ -27,7 +27,7 @@
-
+
Book Hotel
@@ -187,7 +187,7 @@
diff --git a/spring-webflow-samples/booking-mvc/src/main/webapp/WEB-INF/hotels/booking/reviewBooking.jsp b/spring-webflow-samples/booking-mvc/src/main/webapp/WEB-INF/hotels/booking/reviewBooking.jsp
index f7ecf680..9efe4faa 100644
--- a/spring-webflow-samples/booking-mvc/src/main/webapp/WEB-INF/hotels/booking/reviewBooking.jsp
+++ b/spring-webflow-samples/booking-mvc/src/main/webapp/WEB-INF/hotels/booking/reviewBooking.jsp
@@ -15,7 +15,7 @@
-
+
Confirm Booking Details
@@ -49,9 +49,14 @@
-
Confirm
-
Revise
-
Cancel
+
+ Confirm
+ Revise
+ Cancel
+
+
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/config/FlowExecutorBeanDefinitionParser.java b/spring-webflow/src/main/java/org/springframework/webflow/config/FlowExecutorBeanDefinitionParser.java
index 55918341..bd108a86 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/config/FlowExecutorBeanDefinitionParser.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/config/FlowExecutorBeanDefinitionParser.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2008 the original author or authors.
+ * Copyright 2004-2011 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.
@@ -89,16 +89,22 @@ class FlowExecutorBeanDefinitionParser extends AbstractSingleBeanDefinitionParse
}
}
- private Set parseFlowExecutionAttributes(Element element) {
+ private Set parseFlowExecutionAttributes(Element element) {
Element executionAttributesElement = DomUtils.getChildElementByTagName(element, "flow-execution-attributes");
if (executionAttributesElement != null) {
- HashSet attributes = new HashSet();
+ HashSet attributes = new HashSet();
Element redirectElement = DomUtils.getChildElementByTagName(executionAttributesElement,
"always-redirect-on-pause");
if (redirectElement != null) {
String value = redirectElement.getAttribute("value");
attributes.add(new FlowElementAttribute("alwaysRedirectOnPause", value, "boolean"));
}
+ Element redirectInSameStateElement = DomUtils.getChildElementByTagName(executionAttributesElement,
+ "redirect-in-same-state");
+ if (redirectInSameStateElement != null) {
+ String value = redirectInSameStateElement.getAttribute("value");
+ attributes.add(new FlowElementAttribute("redirectInSameState", value, "boolean"));
+ }
List attributeElements = DomUtils.getChildElementsByTagName(executionAttributesElement, "attribute");
for (Iterator it = attributeElements.iterator(); it.hasNext();) {
Element attributeElement = (Element) it.next();
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/config/FlowExecutorFactoryBean.java b/spring-webflow/src/main/java/org/springframework/webflow/config/FlowExecutorFactoryBean.java
index a4f7a7b5..43aee271 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/config/FlowExecutorFactoryBean.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/config/FlowExecutorFactoryBean.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2008 the original author or authors.
+ * Copyright 2004-2011 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.
@@ -62,6 +62,8 @@ class FlowExecutorFactoryBean implements FactoryBean, ApplicationContextAware, B
private static final String ALWAYS_REDIRECT_ON_PAUSE = "alwaysRedirectOnPause";
+ private static final String REDIRECT_IN_SAME_STATE = "redirectInSameState";
+
private FlowDefinitionLocator flowDefinitionLocator;
private Integer maxFlowExecutions;
@@ -185,11 +187,12 @@ class FlowExecutorFactoryBean implements FactoryBean, ApplicationContextAware, B
private void putDefaultFlowExecutionAttributes(LocalAttributeMap executionAttributes) {
if (!executionAttributes.contains(ALWAYS_REDIRECT_ON_PAUSE)) {
- if (environment == MvcEnvironment.PORTLET) {
- executionAttributes.put(ALWAYS_REDIRECT_ON_PAUSE, Boolean.FALSE);
- } else {
- executionAttributes.put(ALWAYS_REDIRECT_ON_PAUSE, Boolean.TRUE);
- }
+ boolean redirect = (environment == MvcEnvironment.PORTLET) ? Boolean.FALSE : Boolean.TRUE;
+ executionAttributes.put(ALWAYS_REDIRECT_ON_PAUSE, redirect);
+ }
+ if (!executionAttributes.contains(REDIRECT_IN_SAME_STATE)) {
+ boolean redirect = (environment == MvcEnvironment.PORTLET) ? Boolean.FALSE : Boolean.TRUE;
+ executionAttributes.put(REDIRECT_IN_SAME_STATE, redirect);
}
}
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/config/spring-webflow-config-2.3.xsd b/spring-webflow/src/main/java/org/springframework/webflow/config/spring-webflow-config-2.3.xsd
index f187db5c..a9640aed 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/config/spring-webflow-config-2.3.xsd
+++ b/spring-webflow/src/main/java/org/springframework/webflow/config/spring-webflow-config-2.3.xsd
@@ -392,6 +392,16 @@ Example: 'flow1,flow2,flow3'.
+
+
+
+
+
+
+
@@ -420,6 +430,19 @@ true = always redirect on pause; false = do not, only redirect when explicitly i
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/RequestControlContext.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/RequestControlContext.java
index f12f0194..2382f4dd 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/RequestControlContext.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/RequestControlContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2008 the original author or authors.
+ * Copyright 2004-2011 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.
@@ -150,4 +150,11 @@ public interface RequestControlContext extends RequestContext {
*/
public boolean getRedirectOnPause();
+ /**
+ * Returns the value of the 'redirect in same state' flow execution attribute if set or otherwise it falls back on
+ * the value returned by {@link #getRedirectOnPause()}.
+ * @return true or false
+ */
+ public boolean getRedirectInSameState();
+
}
\ No newline at end of file
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/ViewState.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/ViewState.java
index 27586b5c..ca2bd931 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/ViewState.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/ViewState.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2008 the original author or authors.
+ * Copyright 2004-2011 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.
@@ -41,6 +41,11 @@ import org.springframework.webflow.execution.ViewFactory;
*/
public class ViewState extends TransitionableState {
+ /**
+ * The name of the attribute indicating an Ajax-driven Flow Definition.
+ */
+ private static final String AJAX_DRIVEN_ATTRIBUTE_NAME = "ajaxDriven";
+
/**
* The list of actions to be executed before the view is rendered.
*/
@@ -61,12 +66,6 @@ public class ViewState extends TransitionableState {
*/
private Boolean redirect;
- /**
- * Whether or not a redirect should occur when the state is not exited (e.g. invalid form submission, a transition
- * without a "to" attribute).
- */
- private Boolean redirectInSameState = Boolean.FALSE;
-
/**
* Whether or not the view should render as a popup.
*/
@@ -139,22 +138,6 @@ public class ViewState extends TransitionableState {
this.redirect = redirect;
}
- /**
- * Returns whether this view state should request a flow execution redirect when the state hasn't been exited.
- */
- public boolean getRedirectInSameState() {
- return (redirectInSameState != null) ? redirectInSameState.booleanValue() : false;
- }
-
- /**
- * Sets whether this view state should requests a flow execution redirect when entered when processing is done but
- * the state hasn't been exited (e.g. invalid form submissions).
- * @param redirectInSameState the redirect flag
- */
- public void setRedirectInSameState(Boolean redirectInSameState) {
- this.redirectInSameState = redirectInSameState;
- }
-
/**
* Returns whether this view state should render as a popup.
*/
@@ -289,17 +272,29 @@ public class ViewState extends TransitionableState {
private boolean shouldRedirect(RequestControlContext context) {
if (redirect != null) {
return redirect.booleanValue();
- } else {
- return context.getRedirectOnPause();
}
+ if (getAjaxDriven(context) != null) {
+ if (context.getExternalContext().isAjaxRequest()) {
+ return false;
+ }
+ }
+ return context.getRedirectOnPause();
}
private boolean shouldRedirectInSameState(RequestControlContext context) {
- if (redirectInSameState != null) {
- return redirectInSameState.booleanValue();
- } else {
- return shouldRedirect(context);
+ if (redirect != null) {
+ return redirect.booleanValue();
}
+ if (getAjaxDriven(context) != null) {
+ if (context.getExternalContext().isAjaxRequest()) {
+ return false;
+ }
+ }
+ return context.getRedirectInSameState();
+ }
+
+ private Boolean getAjaxDriven(RequestControlContext context) {
+ return context.getActiveFlow().getAttributes().getBoolean(AJAX_DRIVEN_ATTRIBUTE_NAME);
}
private void render(RequestControlContext context, View view) throws ViewRenderingException {
@@ -367,8 +362,8 @@ public class ViewState extends TransitionableState {
protected void appendToString(ToStringCreator creator) {
super.appendToString(creator);
- creator.append("viewFactory", viewFactory).append("variables", variables).append("redirect", redirect).append(
- "popup", popup);
+ creator.append("viewFactory", viewFactory).append("variables", variables).append("redirect", redirect)
+ .append("popup", popup);
}
}
\ No newline at end of file
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/FlowArtifactFactory.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/FlowArtifactFactory.java
index 06e5fa56..988e263d 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/FlowArtifactFactory.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/FlowArtifactFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2008 the original author or authors.
+ * Copyright 2004-2011 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.
@@ -86,7 +86,6 @@ public class FlowArtifactFactory {
ViewState viewState = new ViewState(flow, id, viewFactory);
viewState.addVariables(variables);
viewState.setRedirect(redirect);
- viewState.setRedirectInSameState(Boolean.FALSE);
viewState.setPopup(popup);
viewState.getRenderActionList().addAll(renderActions);
configureCommonProperties(viewState, entryActions, transitions, exceptionHandlers, exitActions, attributes);
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/model/FlowModelFlowBuilder.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/model/FlowModelFlowBuilder.java
index 1609d946..8eb6d253 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/model/FlowModelFlowBuilder.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/model/FlowModelFlowBuilder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2008 the original author or authors.
+ * Copyright 2004-2011 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.
@@ -76,6 +76,7 @@ import org.springframework.webflow.engine.model.AbstractActionModel;
import org.springframework.webflow.engine.model.AbstractMappingModel;
import org.springframework.webflow.engine.model.AbstractStateModel;
import org.springframework.webflow.engine.model.ActionStateModel;
+import org.springframework.webflow.engine.model.AjaxDrivenModel;
import org.springframework.webflow.engine.model.AttributeModel;
import org.springframework.webflow.engine.model.BeanImportModel;
import org.springframework.webflow.engine.model.BinderModel;
@@ -382,6 +383,7 @@ public class FlowModelFlowBuilder extends AbstractFlowBuilder {
MutableAttributeMap flowAttributes = parseMetaAttributes(flow.getAttributes());
parseAndPutPersistenceContext(flow.getPersistenceContext(), flowAttributes);
parseAndPutSecured(flow.getSecured(), flowAttributes);
+ parseAndPutAjaxDriven(flow.getAjaxDriven(), flowAttributes);
return flowAttributes;
}
@@ -927,6 +929,12 @@ public class FlowModelFlowBuilder extends AbstractFlowBuilder {
}
}
+ private void parseAndPutAjaxDriven(AjaxDrivenModel ajaxDrivenModel, MutableAttributeMap attributes) {
+ if (ajaxDrivenModel != null) {
+ attributes.put("ajaxDriven", Boolean.TRUE);
+ }
+ }
+
private void parseAndPutSecured(SecuredModel secured, MutableAttributeMap attributes) {
if (secured != null) {
SecurityRule rule = new SecurityRule();
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/RequestControlContextImpl.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/RequestControlContextImpl.java
index 260612f7..0db51f2a 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/RequestControlContextImpl.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/RequestControlContextImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2008 the original author or authors.
+ * Copyright 2004-2011 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.
@@ -246,10 +246,22 @@ class RequestControlContextImpl implements RequestControlContext {
return redirectOnPause != null ? redirectOnPause.booleanValue() : false;
}
+ public boolean getRedirectInSameState() {
+ if (!getExternalContext().isResponseAllowed()) {
+ return true;
+ }
+ Boolean redirectInSameState = flowExecution.getAttributes().getBoolean("redirectInSameState");
+ if (redirectInSameState != null) {
+ return redirectInSameState.booleanValue();
+ } else {
+ return getRedirectOnPause();
+ }
+ }
+
public String toString() {
return new ToStringCreator(this).append("externalContext", externalContext)
- .append("currentEvent", currentEvent).append("requestScope", requestScope).append("attributes",
- attributes).append("messageContext", messageContext).append("flowExecution", flowExecution)
- .toString();
+ .append("currentEvent", currentEvent).append("requestScope", requestScope)
+ .append("attributes", attributes).append("messageContext", messageContext)
+ .append("flowExecution", flowExecution).toString();
}
}
\ No newline at end of file
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/model/AjaxDrivenModel.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/model/AjaxDrivenModel.java
new file mode 100644
index 00000000..dea32826
--- /dev/null
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/model/AjaxDrivenModel.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2004-2011 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.webflow.engine.model;
+
+/**
+ * Model support for the ajax-driven element.
+ *
+ * @author Rossen Stoyanchev
+ * @since 2.3
+ */
+public class AjaxDrivenModel extends AbstractModel {
+
+ public AjaxDrivenModel() {
+ }
+
+ public boolean isMergeableWith(Model model) {
+ return false;
+ }
+
+ public void merge(Model model) {
+
+ }
+
+ public Model createCopy() {
+ return new AjaxDrivenModel();
+ }
+
+}
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/model/FlowModel.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/model/FlowModel.java
index 4607a06b..e7ff0e3b 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/model/FlowModel.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/model/FlowModel.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2008 the original author or authors.
+ * Copyright 2004-2011 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.
@@ -58,6 +58,8 @@ public class FlowModel extends AbstractModel {
private PersistenceContextModel persistenceContext;
+ private AjaxDrivenModel ajaxDriven;
+
private LinkedList vars;
private LinkedList inputs;
@@ -224,6 +226,20 @@ public class FlowModel extends AbstractModel {
this.persistenceContext = persistenceContext;
}
+ /**
+ * @return the ajaxDriven model
+ */
+ public AjaxDrivenModel getAjaxDriven() {
+ return ajaxDriven;
+ }
+
+ /**
+ * @param ajaxDriven the ajaxDriven model to set
+ */
+ public void setAjaxDriven(AjaxDrivenModel ajaxDriven) {
+ this.ajaxDriven = ajaxDriven;
+ }
+
/**
* @return the vars
*/
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/model/builder/xml/WebFlowEntityResolver.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/model/builder/xml/WebFlowEntityResolver.java
index 6425c168..8eb89788 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/model/builder/xml/WebFlowEntityResolver.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/model/builder/xml/WebFlowEntityResolver.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2008 the original author or authors.
+ * Copyright 2004-2011 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.
@@ -24,7 +24,7 @@ import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/**
- * EntityResolver implementation for the Spring Web Flow 2.0 XML Schema. This will load the XSD from the classpath.
+ * EntityResolver implementation for the Spring Web Flow XML Schema. This will load the XSD from the classpath.
*
* The xmlns of the XSD expected to be resolved:
*
@@ -33,7 +33,7 @@ import org.xml.sax.SAXException;
* <flow xmlns="http://www.springframework.org/schema/webflow"
* xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
* xsi:schemaLocation="http://www.springframework.org/schema/webflow
- * http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
+ * http://www.springframework.org/schema/webflow/spring-webflow.xsd">
*
*
* @author Erwin Vervaet
@@ -41,22 +41,32 @@ import org.xml.sax.SAXException;
*/
class WebFlowEntityResolver implements EntityResolver {
- private static final String WEBFLOW_ELEMENT = "spring-webflow-2.0";
+ private static final String[] WEBFLOW_VERSIONS = new String[] { "spring-webflow-2.3", "spring-webflow-2.0" };
public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
- if (systemId != null && systemId.indexOf(WEBFLOW_ELEMENT) > systemId.lastIndexOf("/")) {
- String filename = systemId.substring(systemId.indexOf(WEBFLOW_ELEMENT));
- try {
- Resource resource = new ClassPathResource(filename, getClass());
- InputSource source = new InputSource(resource.getInputStream());
- source.setPublicId(publicId);
- source.setSystemId(systemId);
- return source;
- } catch (IOException ex) {
- // fall through below
+ if (systemId != null && systemId.indexOf("spring-webflow.xsd") > -1) {
+ return createInputSource(publicId, systemId, WEBFLOW_VERSIONS[0] + ".xsd");
+ }
+ for (int i = 0; i < WEBFLOW_VERSIONS.length; i++) {
+ if (systemId != null && systemId.indexOf(WEBFLOW_VERSIONS[i]) > systemId.lastIndexOf("/")) {
+ String fileName = systemId.substring(systemId.indexOf(WEBFLOW_VERSIONS[i]));
+ return createInputSource(publicId, systemId, fileName);
}
}
// let the parser handle it
return null;
}
-}
\ No newline at end of file
+
+ private InputSource createInputSource(String publicId, String systemId, String fileName) {
+ try {
+ Resource resource = new ClassPathResource(fileName, getClass());
+ InputSource source = new InputSource(resource.getInputStream());
+ source.setPublicId(publicId);
+ source.setSystemId(systemId);
+ return source;
+ } catch (IOException ex) {
+ // fall through below
+ }
+ return null;
+ }
+}
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/model/builder/xml/XmlFlowModelBuilder.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/model/builder/xml/XmlFlowModelBuilder.java
index b0d0128b..acedabfb 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/model/builder/xml/XmlFlowModelBuilder.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/model/builder/xml/XmlFlowModelBuilder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2008 the original author or authors.
+ * Copyright 2004-2011 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.
@@ -31,6 +31,7 @@ import org.springframework.util.xml.DomUtils;
import org.springframework.webflow.engine.model.AbstractActionModel;
import org.springframework.webflow.engine.model.AbstractStateModel;
import org.springframework.webflow.engine.model.ActionStateModel;
+import org.springframework.webflow.engine.model.AjaxDrivenModel;
import org.springframework.webflow.engine.model.AttributeModel;
import org.springframework.webflow.engine.model.BeanImportModel;
import org.springframework.webflow.engine.model.BinderModel;
@@ -201,6 +202,7 @@ public class XmlFlowModelBuilder implements FlowModelBuilder {
flow.setAttributes(parseAttributes(element));
flow.setSecured(parseSecured(element));
flow.setPersistenceContext(parsePersistenceContext(element));
+ flow.setAjaxDriven(parseAjaxDriven(element));
flow.setVars(parseVars(element));
flow.setInputs(parseInputs(element));
flow.setOnStartActions(parseOnStartActions(element));
@@ -410,6 +412,15 @@ public class XmlFlowModelBuilder implements FlowModelBuilder {
}
}
+ private AjaxDrivenModel parseAjaxDriven(Element element) {
+ element = DomUtils.getChildElementByTagName(element, "ajax-driven");
+ if (element == null) {
+ return null;
+ } else {
+ return new AjaxDrivenModel();
+ }
+ }
+
private VarModel parseVar(Element element) {
return new VarModel(element.getAttribute("name"), element.getAttribute("class"));
}
@@ -507,8 +518,8 @@ public class XmlFlowModelBuilder implements FlowModelBuilder {
}
private BindingModel parseBinding(Element element) {
- return new BindingModel(element.getAttribute("property"), element.getAttribute("converter"), element
- .getAttribute("required"));
+ return new BindingModel(element.getAttribute("property"), element.getAttribute("converter"),
+ element.getAttribute("required"));
}
private LinkedList parseOnExitActions(Element element) {
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/model/builder/xml/spring-webflow-2.3.xsd b/spring-webflow/src/main/java/org/springframework/webflow/engine/model/builder/xml/spring-webflow-2.3.xsd
new file mode 100644
index 00000000..ed1894dc
--- /dev/null
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/model/builder/xml/spring-webflow-2.3.xsd
@@ -0,0 +1,1596 @@
+
+
+
+
+
+
+This schema defines Spring Web Flow's XML-based flow definition language.
+
+The root "flow" element in this document defines exactly one flow definition.
+A flow definition is a blueprint for a carrying out a conversation with a single user.
+
+A flow is composed of one or more states that form the steps of the flow.
+Each state executes a behavior when entered. What behavior is executed is a
+function of the state's type. Core state types include view states,
+action states, subflow states, decision states, and end states.
+
+A flow definition has exactly one start state.
+Events that occur within states drive state transitions.
+]]>
+
+
+
+
+
+
+
+
+
+
+A flow may also exhibit the following characteristics:
+
+Be annotated with attributes that define descriptive properties that may affect flow execution.
+(See the <attribute/> element)
+
+ Be secured
+(See the <secured/> element)
+
+ Be a persistence context for managing persistent objects during the course of flow execution.
+(See the <persistence-context/> element)
+
+ Instantiate a set of instance variables when started.
+(See the <var/> element)
+
+ Map input provided by callers that start it
+(See the <input/> element)
+
+ Return output to callers that end it.
+(See the <output/> element)
+
+ Execute actions at start time and end time.
+(See the <on-start/> and <on-end/> elements)
+
+ Define transitions shared by all states.
+(See the <global-transitions/> element)
+
+ Handle exceptions thrown by during flow execution.
+(See the <exception-handler/> element)
+
+ Import one or more local bean definition files defining custom flow artifacts
+(such as actions, exception handlers, view factories, transition criteria, etc).
+(See the <bean-import/> element)
+
+]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+The persistence context can be referenced from within this flow by the "entityManager" variable.
+]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+If inheriting from a state defined in another flow, the flowId prefix is required.
+If inheriting from a state defined in this flow, the flowId should not be specified.
+For example, <action-state id="state" parent="myParentFlow#myParentState"> or <action-state id="state" parent="#myLocalParentState">
+]]>
+
+
+
+
+
+
+
+
+
+Once paused, a view-state may be 'refreshed' by the user.
+A refresh causes the response to be reissued and then returns control back to the user.
+
+A view state may be configured with one or more render-actions using the 'on-render' element.
+Render actions are executed immediately before the view is rendered.
+
+A view state is a transitionable state.
+A view state transition is triggered by a user event.
+]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+If inheriting from a state defined in another flow, the flowId prefix is required.
+If inheriting from a state defined in this flow, the flowId should not be specified.
+For example, <view-state id="state" parent="myParentFlow#myParentState"> or <view-state id="state" parent="#myLocalParentState">
+]]>
+
+
+
+
+
+
+
+ priceForm.jsp
+
+Can also be an evaluatable expression:
+
+ ${flowScope.myViewExpression}
+
+The externalRedirect: prefix may be used to redirect to an external location, typically to interface with an external system or controller.
+External redirect query parameters may be specified using ${expressions} that evaluate against the request context.
+The supported formats for an encoded externalRedirect expression are:
+
+ externalRedirect:
+ externalRedirect:contextRelative:
+ externalRedirect:serverRelative:
+ externalRedirect:
+
+For example:
+
+ externalRedirect:/hotels/index
+ externalRedirect:http://someOtherSystem?orderId=${order.id}&callbackUrl=${flowExecutionUrl}
+
+The flowRedirect: prefix may be used to redirect to another flow:
+
+ flowRedirect:myOtherFlow?someData=${flowScope.data}
+
+For exotic usages, you may plug in a custom ViewFactory bean you define:
+
+ ${myCustomViewFactory}
+
+When this attribute is not specified, 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.
+]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+A decision state is a transitionable state.
+A decision state transition can be triggered by evaluating a boolean expression against the flow execution request context.
+To define transition expressions, use the 'if' element.
+
+Examples:
+
+A simple boolean expression test, using the convenient 'if' element:
+
+ <decision-state id="requiresShipping">
+ <if test="sale.requiresShipping" then="enterShippingDetails" else="processSale"/>
+ </decision-state>
+
+]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+The form is:
+
+ <if test="criteriaExpression" then="trueStateId" else="falseStateId"/>
+
+]]>
+
+
+
+
+
+
+
+ <if test="sale.requiresShipping" then="enterShippingDetails"/>
+
+]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+If inheriting from a state defined in another flow, the flowId prefix is required.
+If inheriting from a state defined in this flow, the flowId should not be specified.
+For example, <decision-state id="state" parent="myParentFlow#myParentState"> or <decision-state id="state" parent="#myLocalParentState">
+]]>
+
+
+
+
+
+
+
+
+
+A subflow state is a transitionable state.
+A transition is triggered by the subflow outcome that was reached.
+]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+If inheriting from a state defined in another flow, the flowId prefix is required.
+If inheriting from a state defined in this flow, the flowId should not be specified.
+For example, <subflow-state id="state" parent="myParentFlow#myParentState"> or <subflow-state id="state" parent="#myLocalParentState">
+]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+An end state is not transitionable; there are never transitions out of an end state.
+When an end-state is entered, an instance of this flow is terminated.
+
+When this flow terminates, if it was the "root" flow the entire execution is terminated.
+If this flow was a subflow, its parent flow resumes.
+]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+If inheriting from a state defined in another flow, the flowId prefix is required.
+If inheriting from a state defined in this flow, the flowId should not be specified.
+For example, <end-state id="state" parent="myParentFlow#myParentState"> or <end-state id="state" parent="#myLocalParentState">
+]]>
+
+
+
+
+
+
+
+ priceForm.jsp
+
+It can also be an evaluatable expression:
+
+ ${flowScope.myViewExpression}
+
+The externalRedirect: prefix may be used to request a redirect to an external location, typically to interface with an external system or controller.
+External redirect query parameters may be specified using ${expressions} that evaluate against the request context.
+The supported formats for an encoded externalRedirect expression are:
+
+ externalRedirect:
+ externalRedirect:contextRelative:
+ externalRedirect:serverRelative:
+ externalRedirect:
+
+For example:
+
+ externalRedirect:/hotels/index
+ externalRedirect:http://someOtherSystem?orderId=${order.id}
+
+The flowRedirect: prefix may be used to redirect to another flow:
+
+ flowRedirect:myOtherFlow?someData=${flowScope.data}
+
+For exotic usages, you may plug in a custom ViewFactory bean you define:
+
+ ${myCustomViewFactory}
+
+
+When this attribute is not specified, no final response will be issued.
+In this case, the caller is expected to handle this flow outcome.
+]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+For example:
+
+ <bean-import resource="orderitem-flow-beans.xml"/>
+
+... would look for 'orderitem-flow-beans.xml' in the same directory as this document.
+]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Note: Cannot be used in conjunction with an exception based transition.
+]]>
+
+
+
+
+
+
+
+
+
+ <transition on="submit" to="state"/>
+
+... which reads "on the occurrence of the 'submit' event, transition to 'state'"
+
+Sophisticated transitional expressions are also supported when enclosed in a delimited expression:
+
+ <transition on="${currentEvent.id == 'submit' &;amp;& flowScope.attribute == 'foo'}" to="state"/>
+
+]]>
+
+
+
+
+
+
+
+The value of this attribute must be a fully-qualified java.lang.Exception class name (e.g. example.booking.ItineraryExpiredException).
+Superclasses of the configured exception class match by default. Use this attribute or the 'on' attribute, not both.
+
+Note: Cannot be used in conjunction with a secured element.
+]]>
+
+
+
+
+
+
+
+The value of this attribute may be a static state identifier (e.g. to="displayForm") or a dynamic expression (e.g. to="${flowScope.previousViewState}").
+If no value is specified, this transition acts as a simple event handler and will not change the state of the flow.
+]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Note: This element configures a meta-attribute.
+For the attribute to be enforced, the flow execution must be observed by a SecurityFlowExecutionListener.
+]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/test/MockRequestControlContext.java b/spring-webflow/src/main/java/org/springframework/webflow/test/MockRequestControlContext.java
index 35c1c7ef..9ccfe3e7 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/test/MockRequestControlContext.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/test/MockRequestControlContext.java
@@ -125,6 +125,18 @@ public class MockRequestControlContext extends MockRequestContext implements Req
return redirectOnPause != null ? redirectOnPause.booleanValue() : false;
}
+ public boolean getRedirectInSameState() {
+ if (!getExternalContext().isResponseAllowed()) {
+ return true;
+ }
+ Boolean redirectInSameState = getMockFlowExecutionContext().getAttributes().getBoolean("redirectInSameState");
+ if (redirectInSameState != null) {
+ return redirectInSameState.booleanValue();
+ } else {
+ return getRedirectOnPause();
+ }
+ }
+
// implementation specific accessors for testing
public void setAlwaysRedirectOnPause(boolean alwaysRedirectOnPause) {
@@ -132,4 +144,9 @@ public class MockRequestControlContext extends MockRequestContext implements Req
Boolean.valueOf(alwaysRedirectOnPause));
}
+ public void setRedirectInSameState(boolean redirectInSameState) {
+ getMockFlowExecutionContext().getAttributeMap()
+ .put("redirectInSameState", Boolean.valueOf(redirectInSameState));
+ }
+
}
\ No newline at end of file
diff --git a/spring-webflow/src/test/java/org/springframework/webflow/config/FlowExecutorBeanDefinitionParserTests.java b/spring-webflow/src/test/java/org/springframework/webflow/config/FlowExecutorBeanDefinitionParserTests.java
index 7942af48..c8c0e1d3 100644
--- a/spring-webflow/src/test/java/org/springframework/webflow/config/FlowExecutorBeanDefinitionParserTests.java
+++ b/spring-webflow/src/test/java/org/springframework/webflow/config/FlowExecutorBeanDefinitionParserTests.java
@@ -40,9 +40,11 @@ public class FlowExecutorBeanDefinitionParserTests extends TestCase {
public static class ConfigurationListener extends FlowExecutionListenerAdapter {
public void sessionCreating(RequestContext context, FlowDefinition definition) {
- assertEquals(3, context.getFlowExecutionContext().getAttributes().size());
+ assertEquals(4, context.getFlowExecutionContext().getAttributes().size());
assertEquals(Boolean.FALSE,
context.getFlowExecutionContext().getAttributes().getBoolean("alwaysRedirectOnPause"));
+ assertEquals(Boolean.TRUE,
+ context.getFlowExecutionContext().getAttributes().getBoolean("redirectInSameState"));
assertEquals("bar", context.getFlowExecutionContext().getAttributes().get("foo"));
assertEquals(new Integer(2), context.getFlowExecutionContext().getAttributes().get("bar"));
}
diff --git a/spring-webflow/src/test/java/org/springframework/webflow/config/flow-executor.xml b/spring-webflow/src/test/java/org/springframework/webflow/config/flow-executor.xml
index e66f344e..a398e386 100644
--- a/spring-webflow/src/test/java/org/springframework/webflow/config/flow-executor.xml
+++ b/spring-webflow/src/test/java/org/springframework/webflow/config/flow-executor.xml
@@ -12,6 +12,7 @@
+
diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/ViewStateTests.java b/spring-webflow/src/test/java/org/springframework/webflow/engine/ViewStateTests.java
index fc442dc8..bd78fbd1 100644
--- a/spring-webflow/src/test/java/org/springframework/webflow/engine/ViewStateTests.java
+++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/ViewStateTests.java
@@ -297,7 +297,6 @@ public class ViewStateTests extends TestCase {
Flow flow = new Flow("myFlow");
StubViewFactory viewFactory = new StubViewFactory();
ViewState state = new ViewState(flow, "viewState", viewFactory);
- state.setRedirectInSameState(Boolean.TRUE);
Transition t = new Transition(on("submit"), null);
state.getTransitionSet().add(t);
MockRequestControlContext context = new MockRequestControlContext(flow);
@@ -320,7 +319,6 @@ public class ViewStateTests extends TestCase {
Flow flow = new Flow("myFlow");
StubViewFactory viewFactory = new StubViewFactory();
ViewState state = new ViewState(flow, "viewState", viewFactory);
- state.setRedirectInSameState(Boolean.TRUE);
Transition t = new Transition(on("submit"), null);
TestAction action = new TestAction();
t.setExecutionCriteria(new ActionTransitionCriteria(action));
@@ -428,6 +426,62 @@ public class ViewStateTests extends TestCase {
assertEquals(StubViewFactory.USER_EVENT_STATE, context.getFlashScope().get(View.USER_EVENT_STATE_ATTRIBUTE));
}
+ public void testRedirectInSameStateOverridesAlwaysRedirectOnPause() {
+ Flow flow = new Flow("myFlow");
+ StubViewFactory viewFactory = new StubViewFactory();
+ ViewState state = new ViewState(flow, "viewState", viewFactory);
+ Transition t = new Transition(on("submit"), null);
+ state.getTransitionSet().add(t);
+ MockRequestControlContext context = new MockRequestControlContext(flow);
+ state.enter(context);
+ context = new MockRequestControlContext(context.getFlowExecutionContext());
+ context.setAlwaysRedirectOnPause(false);
+ context.setRedirectInSameState(true);
+ context.getFlowScope().remove("renderCalled");
+ context.putRequestParameter("_eventId", "submit");
+ state.resume(context);
+ assertTrue(context.getMockExternalContext().getFlowExecutionRedirectRequested());
+ }
+
+ public void testAjaxDrivenAttributeOverridesRedirectInSameState() {
+ Flow flow = new Flow("myFlow");
+ flow.getAttributes().put("ajaxDriven", Boolean.TRUE);
+ StubViewFactory viewFactory = new StubViewFactory();
+ ViewState state = new ViewState(flow, "viewState", viewFactory);
+ Transition t = new Transition(on("submit"), null);
+ state.getTransitionSet().add(t);
+ MockRequestControlContext context = new MockRequestControlContext(flow);
+ state.enter(context);
+ context = new MockRequestControlContext(context.getFlowExecutionContext());
+ context.getMockExternalContext().setAjaxRequest(true);
+ context.setAlwaysRedirectOnPause(true);
+ context.setRedirectInSameState(true);
+ context.getFlowScope().remove("renderCalled");
+ context.putRequestParameter("_eventId", "submit");
+ state.resume(context);
+ assertFalse(context.getMockExternalContext().getFlowExecutionRedirectRequested());
+ }
+
+ public void testViewStateRedirectOverridesAjaxDrivenAttribute() {
+ Flow flow = new Flow("myFlow");
+ flow.getAttributes().put("ajaxDriven", Boolean.TRUE);
+ StubViewFactory viewFactory = new StubViewFactory();
+ ViewState state = new ViewState(flow, "viewState", viewFactory);
+ state.setRedirect(false);
+ Transition t = new Transition(on("submit"), null);
+ state.getTransitionSet().add(t);
+ MockRequestControlContext context = new MockRequestControlContext(flow);
+ state.enter(context);
+ context = new MockRequestControlContext(context.getFlowExecutionContext());
+ context.getMockExternalContext().setAjaxRequest(true);
+ context.setAlwaysRedirectOnPause(true);
+ context.setRedirectInSameState(true);
+ context.getFlowScope().remove("renderCalled");
+ context.putRequestParameter("_eventId", "submit");
+ state.resume(context);
+ assertFalse(context.getMockExternalContext().getFlowExecutionRedirectRequested());
+ }
+
public void testResumeViewStateForEventDestroyVariables() {
Flow flow = new Flow("myFlow");
StubViewFactory viewFactory = new StubViewFactory();
diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/model/FlowModelFlowBuilderTests.java b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/model/FlowModelFlowBuilderTests.java
index f9f63cc0..d211a18b 100644
--- a/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/model/FlowModelFlowBuilderTests.java
+++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/model/FlowModelFlowBuilderTests.java
@@ -18,6 +18,7 @@ import org.springframework.webflow.engine.ViewState;
import org.springframework.webflow.engine.builder.FlowAssembler;
import org.springframework.webflow.engine.builder.FlowBuilderException;
import org.springframework.webflow.engine.impl.FlowExecutionImplFactory;
+import org.springframework.webflow.engine.model.AjaxDrivenModel;
import org.springframework.webflow.engine.model.AttributeModel;
import org.springframework.webflow.engine.model.EndStateModel;
import org.springframework.webflow.engine.model.EvaluateModel;
@@ -129,6 +130,14 @@ public class FlowModelFlowBuilderTests extends TestCase {
assertTrue(((Boolean) flow.getAttributes().get("persistenceContext")).booleanValue());
}
+ public void testAjaxDrivenFlow() {
+ model.setAjaxDriven(new AjaxDrivenModel());
+ model.setStates(singleList(new EndStateModel("end")));
+ Flow flow = getFlow(model);
+ assertNotNull(flow.getAttributes().get("ajaxDriven"));
+ assertTrue(((Boolean) flow.getAttributes().get("ajaxDriven")).booleanValue());
+ }
+
public void testFlowInputOutputMapping() {
InputModel input1 = new InputModel("foo", "flowScope.foo");
InputModel input2 = new InputModel("foo", "flowScope.bar");
@@ -190,8 +199,8 @@ public class FlowModelFlowBuilderTests extends TestCase {
end.setSecured(new SecuredModel("ROLE_USER"));
model.setStates(singleList(end));
Flow flow = getFlow(model);
- SecurityRule rule = (SecurityRule) flow.getState("end").getAttributes().get(
- SecurityRule.SECURITY_ATTRIBUTE_NAME);
+ SecurityRule rule = (SecurityRule) flow.getState("end").getAttributes()
+ .get(SecurityRule.SECURITY_ATTRIBUTE_NAME);
assertNotNull(rule);
assertEquals(SecurityRule.COMPARISON_ANY, rule.getComparisonType());
assertEquals(1, rule.getAttributes().size());
diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/model/builder/xml/WebFlowEntityResolverTests.java b/spring-webflow/src/test/java/org/springframework/webflow/engine/model/builder/xml/WebFlowEntityResolverTests.java
new file mode 100644
index 00000000..e2c49dd4
--- /dev/null
+++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/model/builder/xml/WebFlowEntityResolverTests.java
@@ -0,0 +1,32 @@
+package org.springframework.webflow.engine.model.builder.xml;
+
+import junit.framework.TestCase;
+
+import org.xml.sax.InputSource;
+
+public class WebFlowEntityResolverTests extends TestCase {
+
+ private static final String PUBLIC_ID = "http://www.springframework.org/schema/webflow";
+
+ public void testResolve23() throws Exception {
+ WebFlowEntityResolver resolver = new WebFlowEntityResolver();
+ InputSource source = resolver.resolveEntity(PUBLIC_ID,
+ "http://www.springframework.org/schema/webflow/spring-webflow-2.3.xsd");
+ assertNotNull(source);
+ }
+
+ public void testResolve20() throws Exception {
+ WebFlowEntityResolver resolver = new WebFlowEntityResolver();
+ InputSource source = resolver.resolveEntity(PUBLIC_ID,
+ "http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd");
+ assertNotNull(source);
+ }
+
+ public void testResolveLatest() throws Exception {
+ WebFlowEntityResolver resolver = new WebFlowEntityResolver();
+ InputSource source = resolver.resolveEntity(PUBLIC_ID,
+ "http://www.springframework.org/schema/webflow/spring-webflow.xsd");
+ assertNotNull(source);
+ }
+
+}
diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/model/builder/xml/XmlFlowModelBuilderTests.java b/spring-webflow/src/test/java/org/springframework/webflow/engine/model/builder/xml/XmlFlowModelBuilderTests.java
index f43497da..967b46fb 100644
--- a/spring-webflow/src/test/java/org/springframework/webflow/engine/model/builder/xml/XmlFlowModelBuilderTests.java
+++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/model/builder/xml/XmlFlowModelBuilderTests.java
@@ -13,6 +13,7 @@ import org.springframework.webflow.engine.builder.FlowAssembler;
import org.springframework.webflow.engine.builder.model.FlowModelFlowBuilder;
import org.springframework.webflow.engine.impl.FlowExecutionImplFactory;
import org.springframework.webflow.engine.model.AbstractStateModel;
+import org.springframework.webflow.engine.model.AjaxDrivenModel;
import org.springframework.webflow.engine.model.AttributeModel;
import org.springframework.webflow.engine.model.BindingModel;
import org.springframework.webflow.engine.model.ExceptionHandlerModel;
@@ -103,6 +104,16 @@ public class XmlFlowModelBuilderTests extends TestCase {
assertEquals("ROLE_USER", secured.getAttributes());
}
+ public void testFlowAjaxDriven() {
+ ClassPathResource resource = new ClassPathResource("flow-ajax-driven.xml", getClass());
+ FlowModelBuilder builder = new XmlFlowModelBuilder(resource, registry);
+ builder.init();
+ builder.build();
+ FlowModel flow = builder.getFlowModel();
+ AjaxDrivenModel ajaxDriven = flow.getAjaxDriven();
+ assertNotNull(ajaxDriven);
+ }
+
public void testFlowSecuredState() {
ClassPathResource resource = new ClassPathResource("flow-secured-state.xml", getClass());
FlowModelBuilder builder = new XmlFlowModelBuilder(resource, registry);
@@ -290,16 +301,16 @@ public class XmlFlowModelBuilderTests extends TestCase {
DefaultFlowModelHolder holder = new DefaultFlowModelHolder(builder);
FlowModel model = holder.getFlowModel();
assertEquals("foo1", ((ExceptionHandlerModel) model.getExceptionHandlers().get(0)).getBean());
- assertEquals("foo2", ((ExceptionHandlerModel) model.getStateById("state1").getExceptionHandlers().get(0))
- .getBean());
- assertEquals("foo3", ((ExceptionHandlerModel) model.getStateById("state2").getExceptionHandlers().get(0))
- .getBean());
- assertEquals("foo4", ((ExceptionHandlerModel) model.getStateById("state3").getExceptionHandlers().get(0))
- .getBean());
- assertEquals("foo5", ((ExceptionHandlerModel) model.getStateById("state4").getExceptionHandlers().get(0))
- .getBean());
- assertEquals("foo6", ((ExceptionHandlerModel) model.getStateById("state5").getExceptionHandlers().get(0))
- .getBean());
+ assertEquals("foo2",
+ ((ExceptionHandlerModel) model.getStateById("state1").getExceptionHandlers().get(0)).getBean());
+ assertEquals("foo3",
+ ((ExceptionHandlerModel) model.getStateById("state2").getExceptionHandlers().get(0)).getBean());
+ assertEquals("foo4",
+ ((ExceptionHandlerModel) model.getStateById("state3").getExceptionHandlers().get(0)).getBean());
+ assertEquals("foo5",
+ ((ExceptionHandlerModel) model.getStateById("state4").getExceptionHandlers().get(0)).getBean());
+ assertEquals("foo6",
+ ((ExceptionHandlerModel) model.getStateById("state5").getExceptionHandlers().get(0)).getBean());
}
public void testFormActionValidatorMethod() {
diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/model/builder/xml/flow-ajax-driven.xml b/spring-webflow/src/test/java/org/springframework/webflow/engine/model/builder/xml/flow-ajax-driven.xml
new file mode 100644
index 00000000..85ce29a6
--- /dev/null
+++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/model/builder/xml/flow-ajax-driven.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
\ No newline at end of file