SWF-991 Replace ajax-driven flow attribute with mode=embedded flow input attribute.

This commit is contained in:
Rossen Stoyanchev
2011-02-04 16:57:21 +00:00
parent 5cb3ef0067
commit 780ff8f225
18 changed files with 51 additions and 1750 deletions

View File

@@ -5,8 +5,6 @@
<secured attributes="ROLE_USER" />
<ajax-driven />
<input name="hotelId" required="true" />
<on-start>

View File

@@ -10,7 +10,7 @@
<br />
${hotel.country}
</address>
<form action="booking" method="get">
<form action="booking?mode=embedded" method="get">
<p>
Nightly Rate:
<spring:bind path="hotel.price">${status.value}</spring:bind>

View File

@@ -157,4 +157,10 @@ public interface RequestControlContext extends RequestContext {
*/
public boolean getRedirectInSameState();
/**
* Returns true if the flow current flow execution was launched in embedded page mode. When a flow is embedded on a
* page it can make different assumptions with regards to whether redirect after post is necessary.
*/
public boolean getEmbeddedMode();
}

View File

@@ -41,11 +41,6 @@ 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.
*/
@@ -273,10 +268,8 @@ public class ViewState extends TransitionableState {
if (redirect != null) {
return redirect.booleanValue();
}
if (getAjaxDriven(context) != null) {
if (context.getExternalContext().isAjaxRequest()) {
return false;
}
if (context.getExternalContext().isAjaxRequest() && context.getEmbeddedMode()) {
return false;
}
return context.getRedirectOnPause();
}
@@ -285,18 +278,12 @@ public class ViewState extends TransitionableState {
if (redirect != null) {
return redirect.booleanValue();
}
if (getAjaxDriven(context) != null) {
if (context.getExternalContext().isAjaxRequest()) {
return false;
}
if (context.getExternalContext().isAjaxRequest() && context.getEmbeddedMode()) {
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 {
if (logger.isDebugEnabled()) {
logger.debug("Rendering + " + view);

View File

@@ -76,7 +76,6 @@ 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;
@@ -383,7 +382,6 @@ public class FlowModelFlowBuilder extends AbstractFlowBuilder {
MutableAttributeMap flowAttributes = parseMetaAttributes(flow.getAttributes());
parseAndPutPersistenceContext(flow.getPersistenceContext(), flowAttributes);
parseAndPutSecured(flow.getSecured(), flowAttributes);
parseAndPutAjaxDriven(flow.getAjaxDriven(), flowAttributes);
return flowAttributes;
}
@@ -929,12 +927,6 @@ 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();

View File

@@ -214,6 +214,9 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable {
if (logger.isDebugEnabled()) {
logger.debug("Starting in " + externalContext + " with input " + input);
}
if (hasEmbeddedModeAttribute(input)) {
attributes.asMap().put("embeddedMode", Boolean.TRUE);
}
MessageContext messageContext = createMessageContext(null);
RequestControlContext requestContext = createRequestContext(externalContext, messageContext);
RequestContextHolder.setRequestContext(requestContext);
@@ -243,9 +246,8 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable {
}
public void resume(ExternalContext externalContext) throws FlowExecutionException, IllegalStateException {
Assert
.state(status == FlowExecutionStatus.ACTIVE,
"This FlowExecution cannot be resumed because it is not active; it has either not been started or has ended");
Assert.state(status == FlowExecutionStatus.ACTIVE,
"This FlowExecution cannot be resumed because it is not active; it has either not been started or has ended");
if (logger.isDebugEnabled()) {
logger.debug("Resuming in " + externalContext);
}
@@ -643,4 +645,14 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable {
return getActiveSessionInternal().getFlow().handleException(exception, context);
}
private boolean hasEmbeddedModeAttribute(AttributeMap input) {
if (input != null) {
String mode = (String) input.get("mode");
if (mode != null && mode.trim().toLowerCase().equals("embedded")) {
return true;
}
}
return false;
}
}

View File

@@ -251,11 +251,12 @@ class RequestControlContextImpl implements RequestControlContext {
return true;
}
Boolean redirectInSameState = flowExecution.getAttributes().getBoolean("redirectInSameState");
if (redirectInSameState != null) {
return redirectInSameState.booleanValue();
} else {
return getRedirectOnPause();
}
return (redirectInSameState != null) ? redirectInSameState.booleanValue() : getRedirectOnPause();
}
public boolean getEmbeddedMode() {
Boolean embedded = flowExecution.getAttributes().getBoolean("embeddedMode");
return (embedded != null) ? embedded.booleanValue() : false;
}
public String toString() {
@@ -264,4 +265,5 @@ class RequestControlContextImpl implements RequestControlContext {
.append("attributes", attributes).append("messageContext", messageContext)
.append("flowExecution", flowExecution).toString();
}
}

View File

@@ -1,41 +0,0 @@
/*
* 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();
}
}

View File

@@ -58,8 +58,6 @@ public class FlowModel extends AbstractModel {
private PersistenceContextModel persistenceContext;
private AjaxDrivenModel ajaxDriven;
private LinkedList vars;
private LinkedList inputs;
@@ -226,20 +224,6 @@ 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
*/

View File

@@ -41,7 +41,7 @@ import org.xml.sax.SAXException;
*/
class WebFlowEntityResolver implements EntityResolver {
private static final String[] WEBFLOW_VERSIONS = new String[] { "spring-webflow-2.3", "spring-webflow-2.0" };
private static final String[] WEBFLOW_VERSIONS = new String[] { "spring-webflow-2.0" };
public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
if (systemId != null && systemId.indexOf("spring-webflow.xsd") > -1) {

View File

@@ -31,7 +31,6 @@ 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;
@@ -202,7 +201,6 @@ 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));
@@ -412,15 +410,6 @@ 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"));
}

View File

@@ -137,6 +137,14 @@ public class MockRequestControlContext extends MockRequestContext implements Req
}
}
public boolean getEmbeddedMode() {
Boolean embedded = getMockFlowExecutionContext().getAttributes().getBoolean("embeddedMode");
if (embedded != null) {
return embedded;
}
return false;
}
// implementation specific accessors for testing
public void setAlwaysRedirectOnPause(boolean alwaysRedirectOnPause) {
@@ -149,4 +157,8 @@ public class MockRequestControlContext extends MockRequestContext implements Req
.put("redirectInSameState", Boolean.valueOf(redirectInSameState));
}
public void setEmbeddedMode(boolean embedded) {
getMockFlowExecutionContext().getAttributeMap().put("embeddedMode", Boolean.valueOf(embedded));
}
}

View File

@@ -443,28 +443,23 @@ public class ViewStateTests extends TestCase {
assertTrue(context.getMockExternalContext().getFlowExecutionRedirectRequested());
}
public void testAjaxDrivenAttributeOverridesRedirectInSameState() {
public void testEmbeddedModeOverridesRedirectInSameState() {
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.setEmbeddedMode(true);
context.setAlwaysRedirectOnPause(true);
context.setRedirectInSameState(true);
context.getFlowScope().remove("renderCalled");
context.putRequestParameter("_eventId", "submit");
state.resume(context);
state.enter(context);
assertFalse(context.getMockExternalContext().getFlowExecutionRedirectRequested());
}
public void testViewStateRedirectOverridesAjaxDrivenAttribute() {
public void testViewStateRedirectOverridesEmbeddedMode() {
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);
@@ -472,13 +467,10 @@ public class ViewStateTests extends TestCase {
state.getTransitionSet().add(t);
MockRequestControlContext context = new MockRequestControlContext(flow);
state.enter(context);
context = new MockRequestControlContext(context.getFlowExecutionContext());
context.getMockExternalContext().setAjaxRequest(true);
context.setEmbeddedMode(true);
context.setAlwaysRedirectOnPause(true);
context.setRedirectInSameState(true);
context.getFlowScope().remove("renderCalled");
context.putRequestParameter("_eventId", "submit");
state.resume(context);
assertFalse(context.getMockExternalContext().getFlowExecutionRedirectRequested());
}

View File

@@ -18,7 +18,6 @@ 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;
@@ -130,14 +129,6 @@ 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");

View File

@@ -8,13 +8,6 @@ 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,

View File

@@ -13,7 +13,6 @@ 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;
@@ -104,16 +103,6 @@ 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);

View File

@@ -1,9 +0,0 @@
<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>