Author: craigmcc Date: Sun Jul 24 21:25:43 2005 New Revision: 224687 URL: http://svn.apache.org/viewcvs?rev=224687&view=rev Log: Initial compilable integration between Spring WebFlow and JSF.
NOTE: THIS CODE ULTIMATELY BELONGS INSIDE SPRING WEBFLOW, BECAUSE IT IS NOT DEPENDENT ON SHALE. IT WILL BE OFFERED TO SPRING WHEN IT IS COMPLETED. There are a bunch of FIXME issues that need to be addressed before this code will actually function, but this gives an indication for how Spring WebFlow (I'm testing against Spring 1.2.2 and Spring WebFlow PR4) should be able to integrate with JavaServer Faces. Added: struts/shale/trunk/core-library/src/java/org/apache/shale/spring/webflow/ struts/shale/trunk/core-library/src/java/org/apache/shale/spring/webflow/WebFlowNavigationHandler.java struts/shale/trunk/core-library/src/java/org/apache/shale/spring/webflow/WebFlowNavigationStrategy.java Modified: struts/shale/trunk/core-library/build.xml Modified: struts/shale/trunk/core-library/build.xml URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/build.xml?rev=224687&r1=224686&r2=224687&view=diff ============================================================================== --- struts/shale/trunk/core-library/build.xml (original) +++ struts/shale/trunk/core-library/build.xml Sun Jul 24 21:25:43 2005 @@ -111,6 +111,7 @@ <pathelement location="${spring-context.jar}"/> <pathelement location="${spring-core.jar}"/> <pathelement location="${spring-web.jar}"/> + <pathelement location="${spring-webflow.jar}"/> <pathelement location="${build.home}/classes"/> <pathelement location="${tiles.jar}"/> </path> @@ -131,6 +132,7 @@ <pathelement location="${spring-context.jar}"/> <pathelement location="${spring-core.jar}"/> <pathelement location="${spring-web.jar}"/> + <pathelement location="${spring-webflow.jar}"/> </path> @@ -156,6 +158,7 @@ <pathelement location="${spring-context.jar}"/> <pathelement location="${spring-core.jar}"/> <pathelement location="${spring-web.jar}"/> + <pathelement location="${spring-webflow.jar}"/> <pathelement location="${build.home}/classes"/> <pathelement location="${build.home}/test-classes"/> <pathelement location="${tiles.jar}"/> Added: struts/shale/trunk/core-library/src/java/org/apache/shale/spring/webflow/WebFlowNavigationHandler.java URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/spring/webflow/WebFlowNavigationHandler.java?rev=224687&view=auto ============================================================================== --- struts/shale/trunk/core-library/src/java/org/apache/shale/spring/webflow/WebFlowNavigationHandler.java (added) +++ struts/shale/trunk/core-library/src/java/org/apache/shale/spring/webflow/WebFlowNavigationHandler.java Sun Jul 24 21:25:43 2005 @@ -0,0 +1,246 @@ +/* + * Copyright 2004-2005 The Apache Software Foundation. + * + * 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.apache.shale.spring.webflow; + +import javax.faces.application.NavigationHandler; +import javax.faces.context.FacesContext; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.jsf.FacesContextUtils; +import org.springframework.webflow.ViewDescriptor; +import org.springframework.webflow.execution.FlowExecutionManager; +import org.springframework.webflow.execution.servlet.ServletFlowExecutionManager; + + +/** + * <p>An implementation of <code>NavigationHandler</code> that provides + * integration with Spring WebFlow (tested with version PR4). + * It delegates handling to the standard implementation when there is no + * current flow.</p> + * <ul> + * <li>If a flow is <strong>not</strong> currently in progress: + * <ul> + * <li>If the specified logical outcome is <strong>not</strong> + * of the form <em>webflow:xxx</em>, delegate to the standard + * <code>NavigationHandler</code> implementation and return.</li> + * <li>If the specified logical outcome <strong>is</strong> of the + * form <em>webflow:xxx</em>, look up the corresponding + * <code>Flow</code>, and begin execution at its starting state. + * Record state information to indicate that this flow is + * in progress.</li> + * </ul></li> + * <li>If a flow <strong>is</strong> currently in progress: + * <ul> + * <li>Acquire references to the current state information for the + * in progress flow.</li> + * <li>Continue execution of the flow until it returns a + * <code>ViewDescriptor</code> describing the next view to be + * rendered.</li> + * <li>Cause navigation to the requested view (FIXME - perhaps exposing + * any specified model state as a Map under some reasonable request + * attribute name.</li> + * </ul></li> + * </ul> + * + * $Id$ + */ + +public class WebFlowNavigationHandler extends NavigationHandler { + + + // ------------------------------------------------------------ Constructors + + + /** + * <p>Create a new [EMAIL PROTECTED] WebFlowNavigationHandler}, wrapping the + * specified standard navigation handler implementation.</p> + * + * @param handler Standard <code>NavigationHandler</code> we are wrapping + */ + public WebFlowNavigationHandler(NavigationHandler handler) { + + this.handler = handler; + + } + + + // -------------------------------------------------------- Static Variables + + + /** + * <p>Bean name under which we will find the configured instance of the + * <code>FlowExecutionManager</code> to use for executing web flows.</p> + */ + private static final String MANAGER = "flowExecutionManager"; + + + /** + * <p>Bean name under which we will find the configured instance of the + * [EMAIL PROTECTED] WebFlowNavigationStrategy} to be used for determining what + * logical actions to undertake.</p. + */ + private static final String STRATEGY = "webFlowNavigationStrategy"; + + + // ------------------------------------------------------ Instance Variables + + + /** + * <p>The standard <code>NavigationHandler</code> implementation that + * we are wrapping.</p> + */ + private NavigationHandler handler = null; + + + /** + * <p>The <code>Log</code> instance for this class.</p> + */ + private final Log log = LogFactory.getLog(getClass()); + + + /** + * <p>The <code>WebFlowExecutionManager</code> instance to use, lazily + * instantiated upon first use.</p> + */ + private FlowExecutionManager manager = null; + + + /** + * <p>The [EMAIL PROTECTED] WebFlowNavigationStrategy} instance to use, lazily + * instantiated upon first use.</p> + */ + private WebFlowNavigationStrategy strategy = null; + + + // ----------------------------------------------- NavigationHandler Methods + + + /** + * <p>Handle the navigation request implied by the specified parameters.</p> + * + * @param context <code>FacesContext</code> for the current request + * @param fromAction The action binding expression that was evaluated + * to retrieve the specified outcome (if any) + * @param outcome The logical outcome returned by the specified action + */ + public void handleNavigation(FacesContext context, String fromAction, + String outcome) { + + if (log.isDebugEnabled()) { + log.debug("handleNavigation(viewId=" + + context.getViewRoot().getViewId() + + ",fromAction=" + fromAction + + ",outcome=" + outcome + ")"); + } + + ViewDescriptor descriptor = null; + + // Is there an active flow for the current request? + if (getStrategy(context).active(context, fromAction, outcome)) { + + // Resume the current flow until it returns a ViewDescriptor + try { + descriptor = + getStrategy(context).resume(context, fromAction, outcome, + getManager(context)); + } catch (Exception e) { + ; // FIXME - handle resume exception + } + + } else { + + // Does the specified outcome request starting a new flow? + if (getStrategy(context).creating(context, fromAction, outcome)) { + // Yes -- create a new flow and proceed until a view is ready + try { + descriptor = + getStrategy(context).create(context, fromAction, + outcome, getManager(context)); + } catch (Exception e) { + ; // FIXME - handle create exeception + } + } else { + // No -- delegate to standard NavigationHandler implementation + handler.handleNavigation(context, fromAction, outcome); + return; + } + + } + + // Cause the view requested by this ViewDescriptor to be rendered + getStrategy(context).render(context, fromAction, outcome, + getManager(context), descriptor); + + } + + + // --------------------------------------------------------- Private Methods + + + /** + * <p>Return the <code>FlowExecutionManager</code> instance we will use to + * manage web flow execution. The instance to use is discovered by looking + * for a bean named by <code>WebFlowNavigationHandler.MANAGER</code>, or + * defaulting to an instance of <code>ServletFlowExecutionManager</code>. + * FIXME - ultimately this needs to be portable to portlets also.</p> + * + * @param context <code>FacesContext</code> for the current request + */ + private FlowExecutionManager getManager(FacesContext context) { + + if (manager == null) { + WebApplicationContext wac = + FacesContextUtils.getWebApplicationContext(context); + if (wac != null) { + manager = (FlowExecutionManager) wac.getBean(MANAGER); + } + if (manager == null) { + manager = new ServletFlowExecutionManager(); + } + } + return manager; + + } + + + /** + * <p>Return the [EMAIL PROTECTED] WebFlowNavigationStrategy} instance we will use to + * make navigation handler decisions. The instance to use is discovered by + * looking for a bean named by <code>WebFlowNavigationHandler.STRATEGY</code>, + * or defaulting to an instance of [EMAIL PROTECTED] WebFlowNavigationStrategy}.</p> + * + * @param context <code>FacesContext</code> for the current request + */ + private WebFlowNavigationStrategy getStrategy(FacesContext context) { + + if (strategy == null) { + WebApplicationContext wac = + FacesContextUtils.getWebApplicationContext(context); + if (wac != null) { + strategy = (WebFlowNavigationStrategy) wac.getBean(STRATEGY); + } + if (strategy == null) { + strategy = new WebFlowNavigationStrategy(); + } + } + return strategy; + + } + + +} Added: struts/shale/trunk/core-library/src/java/org/apache/shale/spring/webflow/WebFlowNavigationStrategy.java URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/spring/webflow/WebFlowNavigationStrategy.java?rev=224687&view=auto ============================================================================== --- struts/shale/trunk/core-library/src/java/org/apache/shale/spring/webflow/WebFlowNavigationStrategy.java (added) +++ struts/shale/trunk/core-library/src/java/org/apache/shale/spring/webflow/WebFlowNavigationStrategy.java Sun Jul 24 21:25:43 2005 @@ -0,0 +1,211 @@ +/* + * Copyright 2004-2005 The Apache Software Foundation. + * + * 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.apache.shale.spring.webflow; + +import javax.faces.application.ViewHandler; +import javax.faces.component.UIViewRoot; +import javax.faces.context.FacesContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.webflow.Event; +import org.springframework.webflow.ViewDescriptor; +import org.springframework.webflow.execution.FlowExecutionManager; +import org.springframework.webflow.execution.servlet.ServletEvent; + +/** + * <p>Strategy methods for [EMAIL PROTECTED] WebFlowNavigationHandler}. Because a JSF + * <code>NavigationHandler</code> must be registered directly with the JSF + * runtime, the implementation class cannot be customized in the typical + * fashion for a Spring-based application. Therefore, decisions that would + * typically be placed in hook methods for a specialzed subclass have been + * isolated into an instance of this class. You can either use this class + * (which contains the default implementation), or you can subclass it and + * register a bean under the name specified by manifest constant + * <code>WebFlowNavigationHandler.STRATEGY_BEAN</code>.</p> + */ +public class WebFlowNavigationStrategy { + + + // -------------------------------------------------------- Statoc Variables + + + /** + * <p>The request parameter used to represent a flow execution id for an + * existing in-progress flow.</p> + */ + protected static final String FLOW_EXECUTION_ID_PARAMETER = "_flowExecutionId"; + + + /** + * <p>Prefix on a logical outcome value that identifies a logical outcome + * as the identifier for a web flow that should be entered.</p> + */ + protected static final String PREFIX = "webflow:"; + + + // -------------------------------------------------------------- Properties + + + // ---------------------------------------------------------- Public Methods + + + /** + * <p>Return <code>true</code> if there is an existing flow execution in + * progress for the current request. Implementors must ensure that the + * algorithm used to makes this determination matches the determination + * that will be made by the <code>FlowExecutionManager</code> that is + * being used. The default implementation looks for a request parameter + * named by <code>WebFlowNavigationStrategy.FLOW_EXECUTION_ID</code>.</p> + * + * @param context <code>FacesContext</code> for the current request + * @param fromAction The action binding expression that was evaluated + * to retrieve the specified outcome (if any) + * @param outcome The logical outcome returned by the specified action + */ + public boolean active(FacesContext context, String fromAction, + String outcome) { + + return context.getExternalContext().getRequestParameterMap(). + containsKey(FLOW_EXECUTION_ID_PARAMETER); + + } + + + /** + * <p>Create a new flow execution for the current request. Proceed + * until a <code>ViewDescriptor</code> is returned describing the next view + * that should be rendered.</p> + * + * @param context <code>FacesContext</code> for the current request + * @param fromAction The action binding expression that was evaluated + * to retrieve the specified outcome (if any) + * @param outcome The logical outcome returned by the specified action + * @param manager <code>FlowExecutionManager</code> used to manage this flow + */ + public ViewDescriptor create(FacesContext context, String fromAction, + String outcome, FlowExecutionManager manager) + throws Exception { + + String flowId = outcome.substring(PREFIX.length()); + // FIXME - need to pass the requested flow id on to the FlowExecutionManager + return manager.onEvent(event(context, fromAction, outcome)); + + } + + + /** + * <p>Return <code>true</code> if the current request is asking for + * the creation of a new flow. The default implementation examines + * the logical outcome to see if it starts with the prefix specified by + * <code>WebFlowNavigationStrategy.PREFIX</code>.</p> + * + * @param context <code>FacesContext</code> for the current request + * @param fromAction The action binding expression that was evaluated + * to retrieve the specified outcome (if any) + * @param outcome The logical outcome returned by the specified action + */ + public boolean creating(FacesContext context, String fromAction, + String outcome) { + + if (outcome == null) { + return false; + } + return outcome.startsWith(PREFIX); + + } + + + /** + * <p>Construct and return an <code>Event</code> reflecting the flow + * execution event that represents this request. The default implementation + * constructs and returns a <code>ServletEvent</code> reflecting the + * current request. FIXME - this will need to be refactored to support + * portlet events as well as servlet events -- or perhaps a FacesEvent + * can be constructed that hides the environmental differences.</p> + * + * @param context <code>FacesContext</code> for the current request + * @param fromAction The action binding expression that was evaluated + * to retrieve the specified outcome (if any) + * @param outcome The logical outcome returned by the specified action + */ + public Event event(FacesContext context, String fromAction, + String outcome) { + + HttpServletRequest request = (HttpServletRequest) + context.getExternalContext().getRequest(); + HttpServletResponse response = (HttpServletResponse) + context.getExternalContext().getResponse(); + return new ServletEvent(request, response); + + } + + + /** + * <p>Render the view specified by this <code>ViewDescriptor</code>, + * after exposing any model data it includes.</p> + * + * @param context <code>FacesContext</code> for the current request + * @param fromAction The action binding expression that was evaluated + * to retrieve the specified outcome (if any) + * @param outcome The logical outcome returned by the specified action + * @param manager <code>FlowExecutionManager</code> used to manage this flow + * @param descriptor <code>ViewDescriptor</code> for the view to render + */ + public void render(FacesContext context, String fromAction, String outcome, + FlowExecutionManager manager, ViewDescriptor descriptor) { + + // Assume that the view name in the descriptor corresponds to + // a JSF view identifier + String viewId = descriptor.getViewName() + + // Expose model data specified in the descriptor as needed + ; // FIXME + + // Stay on the same view if requested + if (viewId == null) { + return; + } + + // Create the specified view so that it can be rendered + ViewHandler vh = context.getApplication().getViewHandler(); + UIViewRoot view = vh.createView(context, viewId); + context.setViewRoot(view); + + } + + + /** + * <p>Resume an active flow execution for the current request. Proceed + * until a <code>ViewDescriptor</code> is returned describing the next view + * that should be rendered.</p> + * + * @param context <code>FacesContext</code> for the current request + * @param fromAction The action binding expression that was evaluated + * to retrieve the specified outcome (if any) + * @param outcome The logical outcome returned by the specified action + * @param manager <code>FlowExecutionManager</code> used to manage this flow + */ + public ViewDescriptor resume(FacesContext context, String fromAction, + String outcome, FlowExecutionManager manager) + throws Exception { + + return manager.onEvent(event(context, fromAction, outcome)); + + } + + +} --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]