SWF-991, SWF-1413 - Add ajax-driven flow attribute and redirect-in-same-view-state flow execution attribute
This commit is contained in:
@@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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.
|
* Custom {@link StateManager} that manages the JSF component state in web flow's view scope.
|
||||||
*
|
*
|
||||||
* @author Jeremy Grelle
|
* @author Jeremy Grelle
|
||||||
|
* @author Rossen Stoyanchev
|
||||||
*/
|
*/
|
||||||
public class FlowViewStateManager extends StateManager {
|
public class FlowViewStateManager extends StateManager {
|
||||||
|
|
||||||
@@ -150,31 +151,35 @@ public class FlowViewStateManager extends StateManager {
|
|||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* JSF 1.2 (or higher) version of state saving.
|
* JSF 1.2 (or higher) version of state saving.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* In JSF 2 where a partial state saving algorithm is used, this method merely delegates to the next
|
* In JSF 2, if partial state saving is enabled this method delegates in order to obtain the serialized view state.
|
||||||
* ViewStateManager. Thus partial state saving is handled by the JSF 2 runtime. However, a
|
* During rendering JSF calls this method to prepare the state and then calls {@link FlowViewResponseStateManager}
|
||||||
* {@link FlowViewResponseStateManager} plugged in via {@link FlowRenderKit} will ensure the state is saved in a Web
|
* which writes it to Web Flow's view scope.
|
||||||
* Flow view-scoped variable.
|
*
|
||||||
* </p>
|
* <p>
|
||||||
|
* 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) {
|
public Object saveView(FacesContext context) {
|
||||||
if (context.getViewRoot().isTransient()) {
|
if (context.getViewRoot().isTransient()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
FlowSerializedView view = null;
|
||||||
if ((!JsfUtils.isFlowRequest()) || JsfRuntimeInformation.isPartialStateSavingSupported()) {
|
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 {
|
} else {
|
||||||
RequestContext requestContext = RequestContextHolder.getRequestContext();
|
view = new FlowSerializedView(context.getViewRoot().getViewId(), getTreeStructureToSave(context),
|
||||||
if (logger.isDebugEnabled()) {
|
getComponentStateToSave(context));
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -88,6 +88,13 @@ public class JsfView implements View {
|
|||||||
try {
|
try {
|
||||||
logger.debug("Asking faces lifecycle to render");
|
logger.debug("Asking faces lifecycle to render");
|
||||||
facesLifecycle.render(facesContext);
|
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 {
|
} finally {
|
||||||
logger.debug("View rendering complete");
|
logger.debug("View rendering complete");
|
||||||
facesContext.responseComplete();
|
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
|
// Set the temporary UIViewRoot state so that it will be available across the redirect
|
||||||
return new ViewRootHolder(getViewRoot());
|
return new ViewRootHolder(getViewRoot());
|
||||||
} else {
|
} else {
|
||||||
// In JSF 2 the partial state saving algorithm attaches a system event listener to the UIViewRoot with
|
// In JSF 2 the partial state saving algorithm attaches a system event listener to the UIViewRoot which
|
||||||
// a reference to the FacesContext instance. The FacesContext instance is released at end of each request.
|
// holds on to a reference to the FacesContext instance. The FacesContext instance is released at end of
|
||||||
// Hence, keeping the UIViewRoot across the redirect is not feasible.
|
// 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.");
|
logger.debug("User event state requested but not saved.");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<beansProjectDescription>
|
<beansProjectDescription>
|
||||||
<version>1</version>
|
<version>1</version>
|
||||||
<pluginVersion><![CDATA[2.0.5.v200805032123]]></pluginVersion>
|
<pluginVersion><![CDATA[2.5.2.201101081000-RELEASE]]></pluginVersion>
|
||||||
<configSuffixes>
|
<configSuffixes>
|
||||||
<configSuffix><![CDATA[xml]]></configSuffix>
|
<configSuffix><![CDATA[xml]]></configSuffix>
|
||||||
</configSuffixes>
|
</configSuffixes>
|
||||||
<enableImports><![CDATA[true]]></enableImports>
|
<enableImports><![CDATA[true]]></enableImports>
|
||||||
<configs>
|
<configs>
|
||||||
|
<config>src/main/webapp/WEB-INF/config/data-access-config.xml</config>
|
||||||
|
<config>src/main/webapp/WEB-INF/config/security-config.xml</config>
|
||||||
|
<config>src/main/webapp/WEB-INF/config/webflow-config.xml</config>
|
||||||
|
<config>src/main/webapp/WEB-INF/config/webmvc-config.xml</config>
|
||||||
</configs>
|
</configs>
|
||||||
<configSets>
|
<configSets>
|
||||||
</configSets>
|
</configSets>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
<!-- Spring -->
|
<!-- Spring -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework</groupId>
|
<groupId>org.springframework</groupId>
|
||||||
<artifactId>spring-context</artifactId>
|
<artifactId>spring-webmvc</artifactId>
|
||||||
<version>${org.springframework-version}</version>
|
<version>${org.springframework-version}</version>
|
||||||
<exclusions>
|
<exclusions>
|
||||||
<!-- Exclude Commons Logging in favor of SLF4j -->
|
<!-- Exclude Commons Logging in favor of SLF4j -->
|
||||||
@@ -37,11 +37,6 @@
|
|||||||
<artifactId>spring-orm</artifactId>
|
<artifactId>spring-orm</artifactId>
|
||||||
<version>${org.springframework-version}</version>
|
<version>${org.springframework-version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework</groupId>
|
|
||||||
<artifactId>spring-webmvc</artifactId>
|
|
||||||
<version>${org.springframework-version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.webflow</groupId>
|
<groupId>org.springframework.webflow</groupId>
|
||||||
<artifactId>spring-webflow</artifactId>
|
<artifactId>spring-webflow</artifactId>
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<flow xmlns="http://www.springframework.org/schema/webflow"
|
<flow xmlns="http://www.springframework.org/schema/webflow"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
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">
|
xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow.xsd">
|
||||||
|
|
||||||
<secured attributes="ROLE_USER" />
|
<secured attributes="ROLE_USER" />
|
||||||
|
|
||||||
|
<ajax-driven />
|
||||||
|
|
||||||
<input name="hotelId" required="true" />
|
<input name="hotelId" required="true" />
|
||||||
|
|
||||||
<on-start>
|
<on-start>
|
||||||
<evaluate expression="bookingService.createBooking(hotelId, currentUser.name)" result="flowScope.booking" />
|
<evaluate expression="bookingService.createBooking(hotelId, currentUser.name)" result="flowScope.booking" />
|
||||||
</on-start>
|
</on-start>
|
||||||
@@ -23,11 +25,17 @@
|
|||||||
<binding property="creditCardExpiryYear" />
|
<binding property="creditCardExpiryYear" />
|
||||||
<binding property="amenities" />
|
<binding property="amenities" />
|
||||||
</binder>
|
</binder>
|
||||||
|
<on-render>
|
||||||
|
<render fragments="body" />
|
||||||
|
</on-render>
|
||||||
<transition on="proceed" to="reviewBooking" />
|
<transition on="proceed" to="reviewBooking" />
|
||||||
<transition on="cancel" to="cancel" bind="false" />
|
<transition on="cancel" to="cancel" bind="false" />
|
||||||
</view-state>
|
</view-state>
|
||||||
|
|
||||||
<view-state id="reviewBooking" model="booking">
|
<view-state id="reviewBooking" model="booking">
|
||||||
|
<on-render>
|
||||||
|
<render fragments="body" />
|
||||||
|
</on-render>
|
||||||
<transition on="confirm" to="bookingConfirmed">
|
<transition on="confirm" to="bookingConfirmed">
|
||||||
<evaluate expression="bookingService.persistBooking(booking)" />
|
<evaluate expression="bookingService.persistBooking(booking)" />
|
||||||
</transition>
|
</transition>
|
||||||
@@ -36,7 +44,7 @@
|
|||||||
</view-state>
|
</view-state>
|
||||||
|
|
||||||
<end-state id="bookingConfirmed"/>
|
<end-state id="bookingConfirmed"/>
|
||||||
|
|
||||||
<end-state id="cancel" />
|
<end-state id="cancel" />
|
||||||
|
|
||||||
</flow>
|
</flow>
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
</spring:bind>
|
</spring:bind>
|
||||||
</div>
|
</div>
|
||||||
</spring:hasBindErrors>
|
</spring:hasBindErrors>
|
||||||
<form:form modelAttribute="booking">
|
<form:form modelAttribute="booking" action="${flowExecutionUrl}">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Book Hotel</legend>
|
<legend>Book Hotel</legend>
|
||||||
<div>
|
<div>
|
||||||
@@ -187,7 +187,7 @@
|
|||||||
</p>
|
</p>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
Spring.addDecoration(new Spring.ValidateAllDecoration({elementId:'proceed', event:'onclick'}));
|
Spring.addDecoration(new Spring.ValidateAllDecoration({elementId:'proceed', event:'onclick'}));
|
||||||
Spring.addDecoration(new Spring.AjaxEventDecoration({elementId:'proceed',event:'onclick',formId:'booking',params:{fragments:'body'}}));
|
Spring.addDecoration(new Spring.AjaxEventDecoration({elementId:'proceed',event:'onclick',formId:'booking'}));
|
||||||
</script>
|
</script>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
</address>
|
</address>
|
||||||
</div>
|
</div>
|
||||||
<div class="span-12 last">
|
<div class="span-12 last">
|
||||||
<form:form id="confirm" modelAttribute="booking">
|
<form:form id="confirm" modelAttribute="booking" action="${flowExecutionUrl}">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Confirm Booking Details</legend>
|
<legend>Confirm Booking Details</legend>
|
||||||
<div>
|
<div>
|
||||||
@@ -49,9 +49,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<button type="submit" name="_eventId_confirm">Confirm</button>
|
<p>
|
||||||
<button type="submit" name="_eventId_revise">Revise</button>
|
<button type="submit" name="_eventId_confirm">Confirm</button>
|
||||||
<button type="submit" name="_eventId_cancel">Cancel</button>
|
<button type="submit" name="_eventId_revise" id="revise">Revise</button>
|
||||||
|
<button type="submit" name="_eventId_cancel">Cancel</button>
|
||||||
|
</p>
|
||||||
|
<script type="text/javascript">
|
||||||
|
Spring.addDecoration(new Spring.AjaxEventDecoration({elementId:'revise',event:'onclick',formId:'confirm'}));
|
||||||
|
</script>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</form:form>
|
</form:form>
|
||||||
|
|||||||
@@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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<Object> parseFlowExecutionAttributes(Element element) {
|
||||||
Element executionAttributesElement = DomUtils.getChildElementByTagName(element, "flow-execution-attributes");
|
Element executionAttributesElement = DomUtils.getChildElementByTagName(element, "flow-execution-attributes");
|
||||||
if (executionAttributesElement != null) {
|
if (executionAttributesElement != null) {
|
||||||
HashSet attributes = new HashSet();
|
HashSet<Object> attributes = new HashSet<Object>();
|
||||||
Element redirectElement = DomUtils.getChildElementByTagName(executionAttributesElement,
|
Element redirectElement = DomUtils.getChildElementByTagName(executionAttributesElement,
|
||||||
"always-redirect-on-pause");
|
"always-redirect-on-pause");
|
||||||
if (redirectElement != null) {
|
if (redirectElement != null) {
|
||||||
String value = redirectElement.getAttribute("value");
|
String value = redirectElement.getAttribute("value");
|
||||||
attributes.add(new FlowElementAttribute("alwaysRedirectOnPause", value, "boolean"));
|
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");
|
List attributeElements = DomUtils.getChildElementsByTagName(executionAttributesElement, "attribute");
|
||||||
for (Iterator it = attributeElements.iterator(); it.hasNext();) {
|
for (Iterator it = attributeElements.iterator(); it.hasNext();) {
|
||||||
Element attributeElement = (Element) it.next();
|
Element attributeElement = (Element) it.next();
|
||||||
|
|||||||
@@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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 ALWAYS_REDIRECT_ON_PAUSE = "alwaysRedirectOnPause";
|
||||||
|
|
||||||
|
private static final String REDIRECT_IN_SAME_STATE = "redirectInSameState";
|
||||||
|
|
||||||
private FlowDefinitionLocator flowDefinitionLocator;
|
private FlowDefinitionLocator flowDefinitionLocator;
|
||||||
|
|
||||||
private Integer maxFlowExecutions;
|
private Integer maxFlowExecutions;
|
||||||
@@ -185,11 +187,12 @@ class FlowExecutorFactoryBean implements FactoryBean, ApplicationContextAware, B
|
|||||||
|
|
||||||
private void putDefaultFlowExecutionAttributes(LocalAttributeMap executionAttributes) {
|
private void putDefaultFlowExecutionAttributes(LocalAttributeMap executionAttributes) {
|
||||||
if (!executionAttributes.contains(ALWAYS_REDIRECT_ON_PAUSE)) {
|
if (!executionAttributes.contains(ALWAYS_REDIRECT_ON_PAUSE)) {
|
||||||
if (environment == MvcEnvironment.PORTLET) {
|
boolean redirect = (environment == MvcEnvironment.PORTLET) ? Boolean.FALSE : Boolean.TRUE;
|
||||||
executionAttributes.put(ALWAYS_REDIRECT_ON_PAUSE, Boolean.FALSE);
|
executionAttributes.put(ALWAYS_REDIRECT_ON_PAUSE, redirect);
|
||||||
} else {
|
}
|
||||||
executionAttributes.put(ALWAYS_REDIRECT_ON_PAUSE, Boolean.TRUE);
|
if (!executionAttributes.contains(REDIRECT_IN_SAME_STATE)) {
|
||||||
}
|
boolean redirect = (environment == MvcEnvironment.PORTLET) ? Boolean.FALSE : Boolean.TRUE;
|
||||||
|
executionAttributes.put(REDIRECT_IN_SAME_STATE, redirect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -392,6 +392,16 @@ Example: 'flow1,flow2,flow3'.
|
|||||||
<xsd:documentation>
|
<xsd:documentation>
|
||||||
<![CDATA[
|
<![CDATA[
|
||||||
Determines if flow executions always redirect after they pause.
|
Determines if flow executions always redirect after they pause.
|
||||||
|
]]>
|
||||||
|
</xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="redirect-in-same-state" type="redirectInSameStateType" minOccurs="0" maxOccurs="1">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation>
|
||||||
|
<![CDATA[
|
||||||
|
Determines if flow executions redirect after they pause for transitions that remain in the same view state.
|
||||||
|
This attribute effectively overrides the value of the "always-redirect-on-pause" attribute in same state tranisitions.
|
||||||
]]>
|
]]>
|
||||||
</xsd:documentation>
|
</xsd:documentation>
|
||||||
</xsd:annotation>
|
</xsd:annotation>
|
||||||
@@ -420,6 +430,19 @@ true = always redirect on pause; false = do not, only redirect when explicitly i
|
|||||||
</xsd:attribute>
|
</xsd:attribute>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
|
|
||||||
|
<xsd:complexType name="redirectInSameStateType">
|
||||||
|
<xsd:attribute name="value" type="xsd:boolean" use="required">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation>
|
||||||
|
<![CDATA[
|
||||||
|
true = redirect on pause for transitions that remain in the same view state;
|
||||||
|
false = do not redirect for transitions that remain in the same view state.
|
||||||
|
]]>
|
||||||
|
</xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:attribute>
|
||||||
|
</xsd:complexType>
|
||||||
|
|
||||||
<xsd:complexType name="flowDefinitionAttributesType">
|
<xsd:complexType name="flowDefinitionAttributesType">
|
||||||
<xsd:sequence>
|
<xsd:sequence>
|
||||||
<xsd:element name="attribute" type="attributeType" minOccurs="0" maxOccurs="unbounded">
|
<xsd:element name="attribute" type="attributeType" minOccurs="0" maxOccurs="unbounded">
|
||||||
|
|||||||
@@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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();
|
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();
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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 {
|
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.
|
* The list of actions to be executed before the view is rendered.
|
||||||
*/
|
*/
|
||||||
@@ -61,12 +66,6 @@ public class ViewState extends TransitionableState {
|
|||||||
*/
|
*/
|
||||||
private Boolean redirect;
|
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.
|
* Whether or not the view should render as a popup.
|
||||||
*/
|
*/
|
||||||
@@ -139,22 +138,6 @@ public class ViewState extends TransitionableState {
|
|||||||
this.redirect = redirect;
|
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.
|
* Returns whether this view state should render as a popup.
|
||||||
*/
|
*/
|
||||||
@@ -289,17 +272,29 @@ public class ViewState extends TransitionableState {
|
|||||||
private boolean shouldRedirect(RequestControlContext context) {
|
private boolean shouldRedirect(RequestControlContext context) {
|
||||||
if (redirect != null) {
|
if (redirect != null) {
|
||||||
return redirect.booleanValue();
|
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) {
|
private boolean shouldRedirectInSameState(RequestControlContext context) {
|
||||||
if (redirectInSameState != null) {
|
if (redirect != null) {
|
||||||
return redirectInSameState.booleanValue();
|
return redirect.booleanValue();
|
||||||
} else {
|
|
||||||
return shouldRedirect(context);
|
|
||||||
}
|
}
|
||||||
|
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 {
|
private void render(RequestControlContext context, View view) throws ViewRenderingException {
|
||||||
@@ -367,8 +362,8 @@ public class ViewState extends TransitionableState {
|
|||||||
|
|
||||||
protected void appendToString(ToStringCreator creator) {
|
protected void appendToString(ToStringCreator creator) {
|
||||||
super.appendToString(creator);
|
super.appendToString(creator);
|
||||||
creator.append("viewFactory", viewFactory).append("variables", variables).append("redirect", redirect).append(
|
creator.append("viewFactory", viewFactory).append("variables", variables).append("redirect", redirect)
|
||||||
"popup", popup);
|
.append("popup", popup);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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 viewState = new ViewState(flow, id, viewFactory);
|
||||||
viewState.addVariables(variables);
|
viewState.addVariables(variables);
|
||||||
viewState.setRedirect(redirect);
|
viewState.setRedirect(redirect);
|
||||||
viewState.setRedirectInSameState(Boolean.FALSE);
|
|
||||||
viewState.setPopup(popup);
|
viewState.setPopup(popup);
|
||||||
viewState.getRenderActionList().addAll(renderActions);
|
viewState.getRenderActionList().addAll(renderActions);
|
||||||
configureCommonProperties(viewState, entryActions, transitions, exceptionHandlers, exitActions, attributes);
|
configureCommonProperties(viewState, entryActions, transitions, exceptionHandlers, exitActions, attributes);
|
||||||
|
|||||||
@@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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.AbstractMappingModel;
|
||||||
import org.springframework.webflow.engine.model.AbstractStateModel;
|
import org.springframework.webflow.engine.model.AbstractStateModel;
|
||||||
import org.springframework.webflow.engine.model.ActionStateModel;
|
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.AttributeModel;
|
||||||
import org.springframework.webflow.engine.model.BeanImportModel;
|
import org.springframework.webflow.engine.model.BeanImportModel;
|
||||||
import org.springframework.webflow.engine.model.BinderModel;
|
import org.springframework.webflow.engine.model.BinderModel;
|
||||||
@@ -382,6 +383,7 @@ public class FlowModelFlowBuilder extends AbstractFlowBuilder {
|
|||||||
MutableAttributeMap flowAttributes = parseMetaAttributes(flow.getAttributes());
|
MutableAttributeMap flowAttributes = parseMetaAttributes(flow.getAttributes());
|
||||||
parseAndPutPersistenceContext(flow.getPersistenceContext(), flowAttributes);
|
parseAndPutPersistenceContext(flow.getPersistenceContext(), flowAttributes);
|
||||||
parseAndPutSecured(flow.getSecured(), flowAttributes);
|
parseAndPutSecured(flow.getSecured(), flowAttributes);
|
||||||
|
parseAndPutAjaxDriven(flow.getAjaxDriven(), flowAttributes);
|
||||||
return 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) {
|
private void parseAndPutSecured(SecuredModel secured, MutableAttributeMap attributes) {
|
||||||
if (secured != null) {
|
if (secured != null) {
|
||||||
SecurityRule rule = new SecurityRule();
|
SecurityRule rule = new SecurityRule();
|
||||||
|
|||||||
@@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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;
|
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() {
|
public String toString() {
|
||||||
return new ToStringCreator(this).append("externalContext", externalContext)
|
return new ToStringCreator(this).append("externalContext", externalContext)
|
||||||
.append("currentEvent", currentEvent).append("requestScope", requestScope).append("attributes",
|
.append("currentEvent", currentEvent).append("requestScope", requestScope)
|
||||||
attributes).append("messageContext", messageContext).append("flowExecution", flowExecution)
|
.append("attributes", attributes).append("messageContext", messageContext)
|
||||||
.toString();
|
.append("flowExecution", flowExecution).toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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 PersistenceContextModel persistenceContext;
|
||||||
|
|
||||||
|
private AjaxDrivenModel ajaxDriven;
|
||||||
|
|
||||||
private LinkedList vars;
|
private LinkedList vars;
|
||||||
|
|
||||||
private LinkedList inputs;
|
private LinkedList inputs;
|
||||||
@@ -224,6 +226,20 @@ public class FlowModel extends AbstractModel {
|
|||||||
this.persistenceContext = persistenceContext;
|
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
|
* @return the vars
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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;
|
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.
|
||||||
* <p>
|
* <p>
|
||||||
* The xmlns of the XSD expected to be resolved:
|
* 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"
|
* <flow xmlns="http://www.springframework.org/schema/webflow"
|
||||||
* xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
* xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
* xsi:schemaLocation="http://www.springframework.org/schema/webflow
|
* 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">
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* @author Erwin Vervaet
|
* @author Erwin Vervaet
|
||||||
@@ -41,22 +41,32 @@ import org.xml.sax.SAXException;
|
|||||||
*/
|
*/
|
||||||
class WebFlowEntityResolver implements EntityResolver {
|
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 {
|
public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
|
||||||
if (systemId != null && systemId.indexOf(WEBFLOW_ELEMENT) > systemId.lastIndexOf("/")) {
|
if (systemId != null && systemId.indexOf("spring-webflow.xsd") > -1) {
|
||||||
String filename = systemId.substring(systemId.indexOf(WEBFLOW_ELEMENT));
|
return createInputSource(publicId, systemId, WEBFLOW_VERSIONS[0] + ".xsd");
|
||||||
try {
|
}
|
||||||
Resource resource = new ClassPathResource(filename, getClass());
|
for (int i = 0; i < WEBFLOW_VERSIONS.length; i++) {
|
||||||
InputSource source = new InputSource(resource.getInputStream());
|
if (systemId != null && systemId.indexOf(WEBFLOW_VERSIONS[i]) > systemId.lastIndexOf("/")) {
|
||||||
source.setPublicId(publicId);
|
String fileName = systemId.substring(systemId.indexOf(WEBFLOW_VERSIONS[i]));
|
||||||
source.setSystemId(systemId);
|
return createInputSource(publicId, systemId, fileName);
|
||||||
return source;
|
|
||||||
} catch (IOException ex) {
|
|
||||||
// fall through below
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// let the parser handle it
|
// let the parser handle it
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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.AbstractActionModel;
|
||||||
import org.springframework.webflow.engine.model.AbstractStateModel;
|
import org.springframework.webflow.engine.model.AbstractStateModel;
|
||||||
import org.springframework.webflow.engine.model.ActionStateModel;
|
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.AttributeModel;
|
||||||
import org.springframework.webflow.engine.model.BeanImportModel;
|
import org.springframework.webflow.engine.model.BeanImportModel;
|
||||||
import org.springframework.webflow.engine.model.BinderModel;
|
import org.springframework.webflow.engine.model.BinderModel;
|
||||||
@@ -201,6 +202,7 @@ public class XmlFlowModelBuilder implements FlowModelBuilder {
|
|||||||
flow.setAttributes(parseAttributes(element));
|
flow.setAttributes(parseAttributes(element));
|
||||||
flow.setSecured(parseSecured(element));
|
flow.setSecured(parseSecured(element));
|
||||||
flow.setPersistenceContext(parsePersistenceContext(element));
|
flow.setPersistenceContext(parsePersistenceContext(element));
|
||||||
|
flow.setAjaxDriven(parseAjaxDriven(element));
|
||||||
flow.setVars(parseVars(element));
|
flow.setVars(parseVars(element));
|
||||||
flow.setInputs(parseInputs(element));
|
flow.setInputs(parseInputs(element));
|
||||||
flow.setOnStartActions(parseOnStartActions(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) {
|
private VarModel parseVar(Element element) {
|
||||||
return new VarModel(element.getAttribute("name"), element.getAttribute("class"));
|
return new VarModel(element.getAttribute("name"), element.getAttribute("class"));
|
||||||
}
|
}
|
||||||
@@ -507,8 +518,8 @@ public class XmlFlowModelBuilder implements FlowModelBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private BindingModel parseBinding(Element element) {
|
private BindingModel parseBinding(Element element) {
|
||||||
return new BindingModel(element.getAttribute("property"), element.getAttribute("converter"), element
|
return new BindingModel(element.getAttribute("property"), element.getAttribute("converter"),
|
||||||
.getAttribute("required"));
|
element.getAttribute("required"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private LinkedList parseOnExitActions(Element element) {
|
private LinkedList parseOnExitActions(Element element) {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -125,6 +125,18 @@ public class MockRequestControlContext extends MockRequestContext implements Req
|
|||||||
return redirectOnPause != null ? redirectOnPause.booleanValue() : false;
|
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
|
// implementation specific accessors for testing
|
||||||
|
|
||||||
public void setAlwaysRedirectOnPause(boolean alwaysRedirectOnPause) {
|
public void setAlwaysRedirectOnPause(boolean alwaysRedirectOnPause) {
|
||||||
@@ -132,4 +144,9 @@ public class MockRequestControlContext extends MockRequestContext implements Req
|
|||||||
Boolean.valueOf(alwaysRedirectOnPause));
|
Boolean.valueOf(alwaysRedirectOnPause));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setRedirectInSameState(boolean redirectInSameState) {
|
||||||
|
getMockFlowExecutionContext().getAttributeMap()
|
||||||
|
.put("redirectInSameState", Boolean.valueOf(redirectInSameState));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -40,9 +40,11 @@ public class FlowExecutorBeanDefinitionParserTests extends TestCase {
|
|||||||
|
|
||||||
public static class ConfigurationListener extends FlowExecutionListenerAdapter {
|
public static class ConfigurationListener extends FlowExecutionListenerAdapter {
|
||||||
public void sessionCreating(RequestContext context, FlowDefinition definition) {
|
public void sessionCreating(RequestContext context, FlowDefinition definition) {
|
||||||
assertEquals(3, context.getFlowExecutionContext().getAttributes().size());
|
assertEquals(4, context.getFlowExecutionContext().getAttributes().size());
|
||||||
assertEquals(Boolean.FALSE,
|
assertEquals(Boolean.FALSE,
|
||||||
context.getFlowExecutionContext().getAttributes().getBoolean("alwaysRedirectOnPause"));
|
context.getFlowExecutionContext().getAttributes().getBoolean("alwaysRedirectOnPause"));
|
||||||
|
assertEquals(Boolean.TRUE,
|
||||||
|
context.getFlowExecutionContext().getAttributes().getBoolean("redirectInSameState"));
|
||||||
assertEquals("bar", context.getFlowExecutionContext().getAttributes().get("foo"));
|
assertEquals("bar", context.getFlowExecutionContext().getAttributes().get("foo"));
|
||||||
assertEquals(new Integer(2), context.getFlowExecutionContext().getAttributes().get("bar"));
|
assertEquals(new Integer(2), context.getFlowExecutionContext().getAttributes().get("bar"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
<webflow:flow-execution-repository max-executions="1" max-execution-snapshots="2" conversation-manager="conversationManager" />
|
<webflow:flow-execution-repository max-executions="1" max-execution-snapshots="2" conversation-manager="conversationManager" />
|
||||||
<webflow:flow-execution-attributes>
|
<webflow:flow-execution-attributes>
|
||||||
<webflow:always-redirect-on-pause value="false"/>
|
<webflow:always-redirect-on-pause value="false"/>
|
||||||
|
<webflow:redirect-in-same-state value="true"/>
|
||||||
<webflow:attribute name="foo" value="bar"/>
|
<webflow:attribute name="foo" value="bar"/>
|
||||||
<webflow:attribute name="bar" value="2" type="integer"/>
|
<webflow:attribute name="bar" value="2" type="integer"/>
|
||||||
</webflow:flow-execution-attributes>
|
</webflow:flow-execution-attributes>
|
||||||
|
|||||||
@@ -297,7 +297,6 @@ public class ViewStateTests extends TestCase {
|
|||||||
Flow flow = new Flow("myFlow");
|
Flow flow = new Flow("myFlow");
|
||||||
StubViewFactory viewFactory = new StubViewFactory();
|
StubViewFactory viewFactory = new StubViewFactory();
|
||||||
ViewState state = new ViewState(flow, "viewState", viewFactory);
|
ViewState state = new ViewState(flow, "viewState", viewFactory);
|
||||||
state.setRedirectInSameState(Boolean.TRUE);
|
|
||||||
Transition t = new Transition(on("submit"), null);
|
Transition t = new Transition(on("submit"), null);
|
||||||
state.getTransitionSet().add(t);
|
state.getTransitionSet().add(t);
|
||||||
MockRequestControlContext context = new MockRequestControlContext(flow);
|
MockRequestControlContext context = new MockRequestControlContext(flow);
|
||||||
@@ -320,7 +319,6 @@ public class ViewStateTests extends TestCase {
|
|||||||
Flow flow = new Flow("myFlow");
|
Flow flow = new Flow("myFlow");
|
||||||
StubViewFactory viewFactory = new StubViewFactory();
|
StubViewFactory viewFactory = new StubViewFactory();
|
||||||
ViewState state = new ViewState(flow, "viewState", viewFactory);
|
ViewState state = new ViewState(flow, "viewState", viewFactory);
|
||||||
state.setRedirectInSameState(Boolean.TRUE);
|
|
||||||
Transition t = new Transition(on("submit"), null);
|
Transition t = new Transition(on("submit"), null);
|
||||||
TestAction action = new TestAction();
|
TestAction action = new TestAction();
|
||||||
t.setExecutionCriteria(new ActionTransitionCriteria(action));
|
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));
|
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() {
|
public void testResumeViewStateForEventDestroyVariables() {
|
||||||
Flow flow = new Flow("myFlow");
|
Flow flow = new Flow("myFlow");
|
||||||
StubViewFactory viewFactory = new StubViewFactory();
|
StubViewFactory viewFactory = new StubViewFactory();
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import org.springframework.webflow.engine.ViewState;
|
|||||||
import org.springframework.webflow.engine.builder.FlowAssembler;
|
import org.springframework.webflow.engine.builder.FlowAssembler;
|
||||||
import org.springframework.webflow.engine.builder.FlowBuilderException;
|
import org.springframework.webflow.engine.builder.FlowBuilderException;
|
||||||
import org.springframework.webflow.engine.impl.FlowExecutionImplFactory;
|
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.AttributeModel;
|
||||||
import org.springframework.webflow.engine.model.EndStateModel;
|
import org.springframework.webflow.engine.model.EndStateModel;
|
||||||
import org.springframework.webflow.engine.model.EvaluateModel;
|
import org.springframework.webflow.engine.model.EvaluateModel;
|
||||||
@@ -129,6 +130,14 @@ public class FlowModelFlowBuilderTests extends TestCase {
|
|||||||
assertTrue(((Boolean) flow.getAttributes().get("persistenceContext")).booleanValue());
|
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() {
|
public void testFlowInputOutputMapping() {
|
||||||
InputModel input1 = new InputModel("foo", "flowScope.foo");
|
InputModel input1 = new InputModel("foo", "flowScope.foo");
|
||||||
InputModel input2 = new InputModel("foo", "flowScope.bar");
|
InputModel input2 = new InputModel("foo", "flowScope.bar");
|
||||||
@@ -190,8 +199,8 @@ public class FlowModelFlowBuilderTests extends TestCase {
|
|||||||
end.setSecured(new SecuredModel("ROLE_USER"));
|
end.setSecured(new SecuredModel("ROLE_USER"));
|
||||||
model.setStates(singleList(end));
|
model.setStates(singleList(end));
|
||||||
Flow flow = getFlow(model);
|
Flow flow = getFlow(model);
|
||||||
SecurityRule rule = (SecurityRule) flow.getState("end").getAttributes().get(
|
SecurityRule rule = (SecurityRule) flow.getState("end").getAttributes()
|
||||||
SecurityRule.SECURITY_ATTRIBUTE_NAME);
|
.get(SecurityRule.SECURITY_ATTRIBUTE_NAME);
|
||||||
assertNotNull(rule);
|
assertNotNull(rule);
|
||||||
assertEquals(SecurityRule.COMPARISON_ANY, rule.getComparisonType());
|
assertEquals(SecurityRule.COMPARISON_ANY, rule.getComparisonType());
|
||||||
assertEquals(1, rule.getAttributes().size());
|
assertEquals(1, rule.getAttributes().size());
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ import org.springframework.webflow.engine.builder.FlowAssembler;
|
|||||||
import org.springframework.webflow.engine.builder.model.FlowModelFlowBuilder;
|
import org.springframework.webflow.engine.builder.model.FlowModelFlowBuilder;
|
||||||
import org.springframework.webflow.engine.impl.FlowExecutionImplFactory;
|
import org.springframework.webflow.engine.impl.FlowExecutionImplFactory;
|
||||||
import org.springframework.webflow.engine.model.AbstractStateModel;
|
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.AttributeModel;
|
||||||
import org.springframework.webflow.engine.model.BindingModel;
|
import org.springframework.webflow.engine.model.BindingModel;
|
||||||
import org.springframework.webflow.engine.model.ExceptionHandlerModel;
|
import org.springframework.webflow.engine.model.ExceptionHandlerModel;
|
||||||
@@ -103,6 +104,16 @@ public class XmlFlowModelBuilderTests extends TestCase {
|
|||||||
assertEquals("ROLE_USER", secured.getAttributes());
|
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() {
|
public void testFlowSecuredState() {
|
||||||
ClassPathResource resource = new ClassPathResource("flow-secured-state.xml", getClass());
|
ClassPathResource resource = new ClassPathResource("flow-secured-state.xml", getClass());
|
||||||
FlowModelBuilder builder = new XmlFlowModelBuilder(resource, registry);
|
FlowModelBuilder builder = new XmlFlowModelBuilder(resource, registry);
|
||||||
@@ -290,16 +301,16 @@ public class XmlFlowModelBuilderTests extends TestCase {
|
|||||||
DefaultFlowModelHolder holder = new DefaultFlowModelHolder(builder);
|
DefaultFlowModelHolder holder = new DefaultFlowModelHolder(builder);
|
||||||
FlowModel model = holder.getFlowModel();
|
FlowModel model = holder.getFlowModel();
|
||||||
assertEquals("foo1", ((ExceptionHandlerModel) model.getExceptionHandlers().get(0)).getBean());
|
assertEquals("foo1", ((ExceptionHandlerModel) model.getExceptionHandlers().get(0)).getBean());
|
||||||
assertEquals("foo2", ((ExceptionHandlerModel) model.getStateById("state1").getExceptionHandlers().get(0))
|
assertEquals("foo2",
|
||||||
.getBean());
|
((ExceptionHandlerModel) model.getStateById("state1").getExceptionHandlers().get(0)).getBean());
|
||||||
assertEquals("foo3", ((ExceptionHandlerModel) model.getStateById("state2").getExceptionHandlers().get(0))
|
assertEquals("foo3",
|
||||||
.getBean());
|
((ExceptionHandlerModel) model.getStateById("state2").getExceptionHandlers().get(0)).getBean());
|
||||||
assertEquals("foo4", ((ExceptionHandlerModel) model.getStateById("state3").getExceptionHandlers().get(0))
|
assertEquals("foo4",
|
||||||
.getBean());
|
((ExceptionHandlerModel) model.getStateById("state3").getExceptionHandlers().get(0)).getBean());
|
||||||
assertEquals("foo5", ((ExceptionHandlerModel) model.getStateById("state4").getExceptionHandlers().get(0))
|
assertEquals("foo5",
|
||||||
.getBean());
|
((ExceptionHandlerModel) model.getStateById("state4").getExceptionHandlers().get(0)).getBean());
|
||||||
assertEquals("foo6", ((ExceptionHandlerModel) model.getStateById("state5").getExceptionHandlers().get(0))
|
assertEquals("foo6",
|
||||||
.getBean());
|
((ExceptionHandlerModel) model.getStateById("state5").getExceptionHandlers().get(0)).getBean());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testFormActionValidatorMethod() {
|
public void testFormActionValidatorMethod() {
|
||||||
|
|||||||
@@ -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.3.xsd">
|
||||||
|
|
||||||
|
<ajax-driven/>
|
||||||
|
|
||||||
|
<end-state id="end"/>
|
||||||
|
|
||||||
|
</flow>
|
||||||
Reference in New Issue
Block a user