From 5a1604b3c5fc40c66cc5aac430c5743c19c9096f Mon Sep 17 00:00:00 2001 From: Erwin Vervaet Date: Tue, 24 Jun 2008 10:29:58 +0000 Subject: [PATCH] Fixed fallback to JSF navigation when inside a restored flow execution. --- spring-webflow/changelog.txt | 3 ++- .../executor/jsf/AutowiringPhaseListener.java | 4 +++ .../executor/jsf/FlowExecutionHolder.java | 27 ++++++++++++++++++- .../jsf/FlowExecutionKeyStateHolder.java | 4 +-- .../executor/jsf/FlowNavigationHandler.java | 4 ++- .../executor/jsf/FlowPhaseListener.java | 8 +++++- 6 files changed, 44 insertions(+), 6 deletions(-) diff --git a/spring-webflow/changelog.txt b/spring-webflow/changelog.txt index b3f04d3e..2d439742 100644 --- a/spring-webflow/changelog.txt +++ b/spring-webflow/changelog.txt @@ -18,8 +18,9 @@ Package org.springframework.webflow.executor * In a JSF 1.2 environment, SWF will now correctly maintain the "java.faces.ViewState" parameter accross flow execution redirects (SWF-445). * FlowPhaseListener now maintains FacesMessages accross a flow execution redirect by temporarily storing them in - flash scope (SWF-745). + the flow execution (SWF-745). * FlowPhaseListener will now save the JSF component state before a flow execution redirect is issued (SWF-747). +* Fixed FlowPhaseListener to correctly deal with scenarios where we fallback to standard JSF navigation. Changes in version 1.0.5 (03.10.2007) ------------------------------------- diff --git a/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/AutowiringPhaseListener.java b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/AutowiringPhaseListener.java index 49613280..1cdbb3c6 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/AutowiringPhaseListener.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/AutowiringPhaseListener.java @@ -37,6 +37,10 @@ import org.springframework.webflow.executor.support.AutowiringSupport; * require special autowiring behaviour, consider overriding the {@link #autowire(ScopeType, String, Object)} method. *

* This phase listener will use the root web application context as {@link AutowireCapableBeanFactory}. + *

+ * Note about customization: since PhaseListeners managed directly by the JSF provider cannot benefit from dependency + * injection, See Spring's {@link org.springframework.web.jsf.DelegatingPhaseListenerMulticaster} when you need to + * customize an AutowiringPhaseListener instance. * * @see FlowPhaseListener * @see WebApplicationContextUtils#getWebApplicationContext(ServletContext) diff --git a/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowExecutionHolder.java b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowExecutionHolder.java index d5cfeba2..1018a44f 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowExecutionHolder.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowExecutionHolder.java @@ -54,10 +54,16 @@ public class FlowExecutionHolder implements Serializable { private FlowExecutionLock flowExecutionLock; /** - * The currently selected view selection for this request. + * The currently selected view for this request. */ private ViewSelection viewSelection; + /** + * Flag that indicates whether or not it was Web Flow that processed navigation, or if there was fallback to the + * standard JSF navigation handler. Defaults to false. + */ + private boolean navigationFallback = false; + /** * Creates a new flow execution holder for a flow execution that has not yet been placed in a repository. * @param flowExecution the flow execution to hold @@ -130,6 +136,24 @@ public class FlowExecutionHolder implements Serializable { this.viewSelection = viewSelection; } + /** + * Set whether or not fallback happened to the standard JSF navigation handler. Defaults to false. + * + * @since 1.0.6 + */ + public void setNavigationFallback(boolean b) { + this.navigationFallback = b; + } + + /** + * Returns whether or not fallback happened to the standard JSF navigation handler. + * + * @since 1.0.6 + */ + public boolean isNavigationFallback() { + return navigationFallback; + } + /** * Replace the current flow execution with the one provided. This method will clear out all state associated with * the original execution and unlock it if necessary. @@ -138,6 +162,7 @@ public class FlowExecutionHolder implements Serializable { public void replaceWith(FlowExecution flowExecution) { this.flowExecutionKey = null; this.viewSelection = null; + this.navigationFallback = false; unlockFlowExecutionIfNecessary(); this.flowExecution = flowExecution; } diff --git a/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowExecutionKeyStateHolder.java b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowExecutionKeyStateHolder.java index 00e80c56..fbd459f4 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowExecutionKeyStateHolder.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowExecutionKeyStateHolder.java @@ -54,7 +54,7 @@ public class FlowExecutionKeyStateHolder extends UIComponentBase { public static final String COMPONENT_ID = "FlowExecutionKeyStateHolder"; /** - * The key value + * The key value. */ private String flowExecutionKey; @@ -65,7 +65,7 @@ public class FlowExecutionKeyStateHolder extends UIComponentBase { } public void setId(String id) { - // Do nothing so as to ensure the id never gets overwritten. + // do nothing so as to ensure the id never gets overwritten. return; } diff --git a/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowNavigationHandler.java b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowNavigationHandler.java index 384f17c1..31a038de 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowNavigationHandler.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowNavigationHandler.java @@ -170,12 +170,13 @@ public class FlowNavigationHandler extends DecoratingNavigationHandler { } else { // not a launch request - see if this is a resume request to continue an existing execution if (FlowExecutionHolderUtils.isFlowExecutionRestored(facesContext)) { + FlowExecutionHolder holder = FlowExecutionHolderUtils.getFlowExecutionHolder(facesContext); + // a flow execution has been restored - see if we need to signal an event against it if (argumentExtractor.isEventIdPresent(context)) { // signal the event against the current flow execution String eventId = argumentExtractor.extractEventId(context); try { - FlowExecutionHolder holder = FlowExecutionHolderUtils.getFlowExecutionHolder(facesContext); ViewSelection selectedView = holder.getFlowExecution().signalEvent(eventId, context); // set the next view to render holder.setViewSelection(selectedView); @@ -185,6 +186,7 @@ public class FlowNavigationHandler extends DecoratingNavigationHandler { + "'; falling back to standard navigation handler."); } // not a valid event in the current state: proceed with standard navigation + holder.setNavigationFallback(true); originalNavigationHandler.handleNavigation(facesContext, fromAction, outcome); } } diff --git a/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowPhaseListener.java b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowPhaseListener.java index b7ff72f7..3533495b 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowPhaseListener.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/executor/jsf/FlowPhaseListener.java @@ -382,13 +382,19 @@ public class FlowPhaseListener implements PhaseListener { * @param holder the holder */ protected void prepareResponse(final JsfExternalContext context, final FlowExecutionHolder holder) { + if (holder.isNavigationFallback()) { + // navigation was not processed by Web Flow, but by the standard JSF navigation handler + // just leave response rendering up to JSF + return; + } + ViewSelection selectedView = holder.getViewSelection(); if (selectedView == null) { // no navigation event has been processed - simply refresh the execution with the same key selectedView = holder.getFlowExecution().refresh(context); holder.setViewSelection(selectedView); } else { - // an navigation event has been processed - generate a new flow execution key if necessary + // a navigation event has been processed - generate a new flow execution key if necessary generateKey(context, holder); } new ResponseInstructionHandler() {