You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by sk...@apache.org on 2008/07/11 10:21:32 UTC
svn commit: r675860 - in
/myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow:
FlowHandler.java FlowHandler.java.sk FlowInfo.java
FlowNavigationHandler.java FlowNavigationHandler.java.sk FlowNavigator.java
Navigator.java.sk
Author: skitching
Date: Fri Jul 11 01:21:30 2008
New Revision: 675860
URL: http://svn.apache.org/viewvc?rev=675860&view=rev
Log:
Clean up code in NavigationHandler (get rid of ugly Selection class).
Add comments.
Added:
myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/FlowHandler.java.sk (with props)
myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/FlowNavigationHandler.java.sk (with props)
myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/FlowNavigator.java (with props)
myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/Navigator.java.sk (with props)
Modified:
myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/FlowHandler.java
myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/FlowInfo.java
myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/FlowNavigationHandler.java
Modified: myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/FlowHandler.java
URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/FlowHandler.java?rev=675860&r1=675859&r2=675860&view=diff
==============================================================================
--- myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/FlowHandler.java (original)
+++ myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/FlowHandler.java Fri Jul 11 01:21:30 2008
@@ -54,25 +54,6 @@
private static final Log log = LogFactory.getLog(FlowHandler.class);
/**
- * A simple object that allows methods to return multiple values.
- * <p>
- * TODO: consider making this an "active" object rather than a
- * passive one, ie for it to decide which view to return based
- * on its internal state rather than just being a data structure.
- * Maybe this entire class should be used as the "selection" object,
- * ie a FlowHandler is a handler for a specific request and is either
- * created by navhandler and passed to viewhandler, or created by
- * viewhandler?
- */
- static class Selection
- {
- public String viewId;
- public UIViewRoot viewRoot;
- public String outcome;
- public FlowInfo flowInfo; // null unless a flowcall is being started
- }
-
- /**
* Return the FlowCall object associated with the current conversation
* context (if any).
* <p>
@@ -144,7 +125,7 @@
* postback has caused a navigation to occur. Note that in that case we do
* not yet know what viewId we are going <i>to</i>.
*/
- static void processCall(Selection sel, FacesContext facesContext, String oldViewId, String outcome)
+ static void processCall(FlowNavigator nav, FacesContext facesContext, String oldViewId, String outcome)
{
log.debug("processCall: [" + String.valueOf(oldViewId) + "] outcome: " + outcome);
@@ -158,22 +139,24 @@
if (outcome.equals(fa.getCancelWhen()))
{
// cancel current flow
- sel.viewId = flowInfo.getCallerViewId();
- sel.viewRoot = flowInfo.getCallerViewRoot();
+ String callerViewId = flowInfo.getCallerViewId();
+ UIViewRoot callerViewRoot = flowInfo.getCallerViewRoot();
ConversationManager cm = ConversationManager.getInstance(true);
ConversationContext ctx = cm.getCurrentConversationContext();
ConversationContext parent = ctx.getParent();
cm.activateConversationContext(parent);
cm.removeAndInvalidateConversationContext(ctx);
+
+ nav.goToView(callerViewRoot, callerViewId);
return;
}
if (outcome.equals(fa.getCommitWhen()))
{
// commit current flow
- sel.viewId = flowInfo.getCallerViewId();
- sel.viewRoot = flowInfo.getCallerViewRoot();
+ String callerViewId = flowInfo.getCallerViewId();
+ UIViewRoot callerViewRoot = flowInfo.getCallerViewRoot();
ConversationManager cm = ConversationManager.getInstance(true);
ConversationContext ctx = cm.getCurrentConversationContext();
@@ -204,14 +187,12 @@
Object actionOutcome = onCommit.execute(facesContext);
if (actionOutcome != null)
{
- // Note: sel.viewId is already set; that is important as the
- // navigation handler will use that when choosing the nav-case.
- sel.outcome = actionOutcome.toString();
+ nav.goToOutcome(callerViewRoot, callerViewId, actionOutcome.toString());
+ return;
}
}
- // Return with sel holding the restored view and the parent context
- // selected as the active one.
+ nav.goToView(callerViewRoot, callerViewId);
return;
}
}
@@ -234,9 +215,6 @@
child.setAttribute("flowInfo", flowInfo);
cm.activateConversationContext(child);
-
- sel.flowInfo = flowInfo;
- return;
}
}
@@ -370,6 +348,13 @@
}
}
+ /**
+ * Return the FlowConfig object associated with the specified viewId, or null if
+ * none exists.
+ * <p>
+ * The current implementation just looks for a "-flow.xml" file relative to the
+ * specified view.
+ */
private static FlowConfig getFlowConfig(String viewId)
{
log.debug("getflowConfig for [" + viewId + "]");
@@ -383,20 +368,18 @@
}
String path = viewId.substring(0, lastDot) + "-flow.xml";
- log.debug("getFlowConfig: path=" + path);
InputStream is = ec.getResourceAsStream(path);
if (is == null)
{
- log.debug("getFlowConfig: not found");
+ log.debug("getFlowConfig: not found at " + path);
return null;
}
try
{
- log.debug("getFlowConfig: parsing");
InputSource source = new InputSource(is);
source.setSystemId(path);
FlowConfig cfg = FlowDigester.digest(source);
- log.debug("getFlowConfig: parsed. " + cfg.toString());
+ log.debug("getFlowConfig: found at " + path);
return cfg;
}
finally
@@ -412,6 +395,10 @@
}
}
+ /**
+ * Return the FlowInfo object stored in the current conversation
+ * context (if any).
+ */
private static FlowInfo getFlowInfo()
{
ConversationManager cm = ConversationManager.getInstance(false);
@@ -429,11 +416,21 @@
return getFlowInfo(context);
}
+ /**
+ * Return the FlowInfo object associated with the specified conversation
+ * context (if any).
+ */
private static FlowInfo getFlowInfo(ConversationContext context)
{
return (FlowInfo) context.getAttribute("flowInfo");
}
+ /**
+ * Assuming that the specified viewId is the entry page for a flow, return a
+ * viewId prefix that will match all views in the same flow.
+ * <p>
+ * This implementation assumes that a flow always lives in its own directory.
+ */
private static String getFlowPath(String viewId)
{
// just return everything up to (and including) the final slash
Added: myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/FlowHandler.java.sk
URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/FlowHandler.java.sk?rev=675860&view=auto
==============================================================================
--- myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/FlowHandler.java.sk (added)
+++ myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/FlowHandler.java.sk Fri Jul 11 01:21:30 2008
@@ -0,0 +1,536 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.myfaces.orchestra.flow;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.faces.component.UIViewRoot;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.myfaces.orchestra.conversation.ConversationContext;
+import org.apache.myfaces.orchestra.conversation.ConversationManager;
+import org.apache.myfaces.orchestra.flow.config.FlowAccept;
+import org.apache.myfaces.orchestra.flow.config.FlowCall;
+import org.apache.myfaces.orchestra.flow.config.FlowConfig;
+import org.apache.myfaces.orchestra.flow.config.FlowOnCommit;
+import org.apache.myfaces.orchestra.flow.config.FlowParamAccept;
+import org.apache.myfaces.orchestra.flow.config.FlowParamSend;
+import org.apache.myfaces.orchestra.flow.config.FlowReturnAccept;
+import org.apache.myfaces.orchestra.flow.config.FlowReturnSend;
+import org.apache.myfaces.orchestra.flow.digest.FlowDigester;
+import org.apache.myfaces.orchestra.lib.OrchestraException;
+import org.xml.sax.InputSource;
+
+/**
+ * Common logic for managing orchestra flows, called from both FlowNavigationHandler
+ * and FlowViewHandler.
+ */
+public class FlowHandler
+{
+ private static final Log log = LogFactory.getLog(FlowHandler.class);
+
+ /**
+ * A simple object that allows methods to return multiple values.
+ * <p>
+ * TODO: consider making this an "active" object rather than a
+ * passive one, ie for it to decide which view to return based
+ * on its internal state rather than just being a data structure.
+ * Maybe this entire class should be used as the "selection" object,
+ * ie a FlowHandler is a handler for a specific request and is either
+ * created by navhandler and passed to viewhandler, or created by
+ * viewhandler?
+ */
+ static class Selection
+ {
+ public String viewId;
+ public UIViewRoot viewRoot;
+ public String outcome;
+ public FlowInfo flowInfo; // null unless a flowcall is being started
+ }
+
+ /**
+ * Return the FlowCall object associated with the current conversation
+ * context (if any).
+ * <p>
+ * There will be one if the current context was created by some page
+ * doing a "call" to a flow.
+ * <p>
+ * Returns null if there is no FlowCall object in the current conversation
+ * context.
+ */
+ static FlowCall getFlowCall(String viewId, String outcome)
+ {
+ if (viewId == null)
+ {
+ return null;
+ }
+
+ FlowConfig config = getFlowConfig(viewId);
+ if (config == null)
+ {
+ log.debug("No flowcall for " + viewId);
+ return null;
+ }
+
+ FlowCall fc = config.getFlowCall(outcome);
+ log.debug("Flowcall for " + viewId + " outcome: " + outcome + " is " + String.valueOf(fc));
+ return fc;
+ }
+
+ /**
+ * Return the FlowAccept object associated with the current conversation
+ * context (if any).
+ * <p>
+ * There will be one if the current context was created by some page
+ * doing a "call" to a flow, AND the flow call initialisation has
+ * completed.
+ */
+ static FlowAccept getFlowAccept(FlowInfo flowInfo, String viewId)
+ {
+ if ((flowInfo != null) && (flowInfo.getFlowAccept() != null))
+ {
+ // ok, we are already within a configured flow.
+ return flowInfo.getFlowAccept();
+ }
+
+ FlowConfig config = getFlowConfig(viewId);
+ if (config == null)
+ {
+ log.debug("No flowaccept for " + viewId);
+ return null;
+ }
+
+ FlowAccept fa = config.getFlowAccept();
+ log.debug("FlowAccept for " + viewId + " is " + String.valueOf(fa));
+ return fa;
+ }
+
+ /**
+ * Do actions that do not depend on properties of the called flow (if any).
+ * <p>
+ * Without knowing anything about the target of the navigation (other than
+ * its viewId), the following can still be done:
+ * <ul>
+ * <li>commit or cancel the current flow if the nav outcome matches</li>
+ * <li>determine whether this navigation triggers a new flow. If so, create
+ * a child context and place a partially-initialised FlowInfo object in it.</li>
+ * </ul>
+ * <p>
+ * This is expected to be called from the FlowNavigationHandler after some
+ * postback has caused a navigation to occur. Note that in that case we do
+ * not yet know what viewId we are going <i>to</i>.
+ */
+ static void processCall(Navigator nav, FacesContext facesContext, String oldViewId, String outcome)
+ {
+ log.debug("processCall: [" + String.valueOf(oldViewId) + "] outcome: " + outcome);
+
+ // Handle COMMIT and CANCEL
+ FlowInfo flowInfo = getFlowInfo();
+ if (flowInfo != null)
+ {
+ // we are in a flow - are we leaving it?
+ FlowAccept fa = flowInfo.getFlowAccept();
+
+ if (outcome.equals(fa.getCancelWhen()))
+ {
+ // cancel current flow
+ String callerViewId = flowInfo.getCallerViewId();
+ UIViewRoot callerViewRoot = flowInfo.getCallerViewRoot();
+
+ ConversationManager cm = ConversationManager.getInstance(true);
+ ConversationContext ctx = cm.getCurrentConversationContext();
+ ConversationContext parent = ctx.getParent();
+ cm.activateConversationContext(parent);
+ cm.removeAndInvalidateConversationContext(ctx);
+
+ nav.goToView(callerViewId, callerViewRoot);
+ return;
+ }
+
+ if (outcome.equals(fa.getCommitWhen()))
+ {
+ // commit current flow
+ String callerViewId = flowInfo.getCallerViewId();
+ UIViewRoot callerViewRoot = flowInfo.getCallerViewRoot();
+
+ ConversationManager cm = ConversationManager.getInstance(true);
+ ConversationContext ctx = cm.getCurrentConversationContext();
+ ConversationContext parent = ctx.getParent();
+
+ // With the child conversationContext currently active, read
+ // all the output parameters
+ Map map = flowInfo.getFlowAccept().readReturnParams(facesContext);
+
+ // Activate the parent and discard the child context
+ cm.activateConversationContext(parent);
+ cm.removeAndInvalidateConversationContext(ctx);
+
+ // Now with the original context active, write parameters back.
+ flowInfo.getFlowCall().writeAcceptParams(facesContext, map);
+
+ // And execute any actions the caller wants to run.
+ // Hmm..but these cannot do anything to set the viewroot, because
+ // we stomp over that after returning from this method. Maybe this
+ // method should be responsible for setting up viewroot itself, and
+ // return a boolean to say it did it?
+ //
+ // And should we restore the saved view before running these callbacks,
+ // in case the invoked code tries to access it?
+ FlowOnCommit onCommit = flowInfo.getFlowCall().getOnCommit();
+ if (onCommit != null)
+ {
+ Object actionOutcome = onCommit.execute(facesContext);
+ if (actionOutcome != null)
+ {
+ nav.goToOutcome(callerViewId, callerViewRoot, outcome);
+ return;
+ }
+ }
+
+ nav.goToView(callerViewId, callerViewRoot);
+ return;
+ }
+ }
+
+ // OK, we know we are not committing or canceling a flow. Are we trying to
+ // enter one?
+ FlowCall flowCall = getFlowCall(oldViewId, outcome);
+ if (flowCall != null)
+ {
+ // fetch parameters from caller into a map
+ Map data = flowCall.readSendParams(facesContext);
+
+ // Build a FlowInfo object for the called flow to use.
+ flowInfo = new FlowInfo(oldViewId, facesContext.getViewRoot(), flowCall, data);
+
+ // create child context
+ ConversationManager cm = ConversationManager.getInstance(true);
+ ConversationContext parent = cm.getCurrentConversationContext();
+ ConversationContext child = cm.createConversationContext(parent);
+
+ child.setAttribute("flowInfo", flowInfo);
+ cm.activateConversationContext(child);
+ }
+
+ // do normal nav
+ nav.goToOutcome(outcome);
+ }
+
+ /**
+ * Handle case where the user is in a flow, then navigates somehow to a page that is not
+ * within the flow.
+ */
+ private static boolean isAbnormalFlowExit(FlowInfo flowInfo, String newViewId)
+ {
+ // Are we leaving a flow in an abnormal manner?
+ if (flowInfo != null)
+ {
+ String flowPath = getFlowPath(newViewId);
+ if (!flowPath.startsWith(flowInfo.getFlowPath()))
+ {
+ // User must have navigated away from the current flow. Walk up the tree invalidating
+ // each context until we find a flow that does match the new path, or we find the root
+ // context (which never has a FlowInfo in it).
+ ConversationManager cm = ConversationManager.getInstance(true);
+ ConversationContext ctx = cm.getCurrentConversationContext();
+ ConversationContext parent = ctx.getParent();
+ for(;;)
+ {
+ if (parent == null)
+ {
+ // After discarding all invalid flows, let normal navigation occur
+ // to the new viewId. There is no view to "restore"..
+ break;
+ }
+
+ cm.activateConversationContext(parent);
+ cm.removeAndInvalidateConversationContext(ctx);
+ ctx = parent;
+ parent = ctx.getParent();
+
+ FlowInfo fi = getFlowInfo(ctx);
+ if ((fi != null) && (flowPath.startsWith(fi.getFlowPath())))
+ {
+ // ok, the view the user wants to navigate to is within
+ // the context just activated, so we now have things set
+ // up as required; break out of loop.
+ break;
+ }
+ }
+
+ return true;
+ }
+ }
+
+ // nope, this is just a normal navigation
+ return false;
+ }
+
+ /**
+ * Handle case where we have just navigated to a new page, and the page that caused the
+ * navigation did a flow-call.
+ * <p>
+ * When this is true, a child context will already have been created, and a half-initialised
+ * FlowInfo object will be in the context. We need to now fetch the called flow's specs,
+ * check that the caller and called flows match in type and parameters, then finish
+ * initialising the FlowInfo and import the passed params into the child context.
+ * <p>
+ * This is expected to be called from FlowViewHandler when a new view is being created
+ * (due either to a GET or POST from the user, or an internal forward due to navigation.
+ */
+ static boolean isNewFlowEntry(FacesContext facesContext, FlowInfo flowInfo, String newViewId)
+ {
+ if (flowInfo == null)
+ {
+ // No, the processCall method has not created a new partially-initialised
+ // FlowInfo object.
+ return false;
+ }
+
+ if (flowInfo.getFlowAccept() != null)
+ {
+ // No, the processCall method has not created a new partially-initialised
+ // FlowInfo object.
+ return false;
+ }
+
+ FlowAccept flowAccept = getFlowAccept(flowInfo, newViewId);
+ if (flowAccept == null)
+ {
+ // TODO: discard all flow stacks here for safety?
+ log.debug("isNewFlowEntry: Error: invocation of flow without callee declaration");
+ throw new OrchestraException("Invocation of flow without callee declaration");
+ }
+
+ log.debug("isNewFlowEntry: new flow detected.");
+ // validate flowCall against flowAccept. TODO: discard flow stacks on error?
+ validateCall(flowInfo.getFlowCall(), flowAccept);
+
+ // finish initialising the new flowInfo
+ String flowPath = getFlowPath(newViewId);
+ flowInfo.setAcceptInfo(newViewId, flowPath, flowAccept);
+
+ // push parameters from caller to callee
+ Map data = flowInfo.getData();
+ flowAccept.writeAcceptParams(facesContext, data);
+
+ return true;
+ }
+
+ /**
+ * Invoked to handle actions that require info about the called page, but not the caller.
+ */
+ static void processAccept(FacesContext facesContext, String newViewId)
+ {
+ log.debug("processAccept: [" + newViewId + "]");
+ FlowInfo flowInfo = getFlowInfo();
+
+ if (isNewFlowEntry(facesContext, flowInfo, newViewId))
+ {
+ return;
+ }
+
+ if (isAbnormalFlowExit(flowInfo, newViewId))
+ {
+ return;
+ }
+
+ FlowAccept flowAccept = getFlowAccept(flowInfo, newViewId);
+ boolean isFlowEntryPoint = (flowAccept != null);
+ boolean isInFlow = (flowInfo != null);
+
+ if (isFlowEntryPoint && !isInFlow)
+ {
+ log.debug("processAccept: Error: invocation of flow without caller declaration");
+ throw new OrchestraException("Invocation of flow without caller declaration");
+ }
+ }
+
+ private static FlowConfig getFlowConfig(String viewId)
+ {
+ log.debug("getflowConfig for [" + viewId + "]");
+ FacesContext fc = FacesContext.getCurrentInstance();
+ ExternalContext ec = fc.getExternalContext();
+
+ int lastDot = viewId.lastIndexOf(".");
+ if (lastDot == -1)
+ {
+ return null;
+ }
+
+ String path = viewId.substring(0, lastDot) + "-flow.xml";
+ log.debug("getFlowConfig: path=" + path);
+ InputStream is = ec.getResourceAsStream(path);
+ if (is == null)
+ {
+ log.debug("getFlowConfig: not found");
+ return null;
+ }
+ try
+ {
+ log.debug("getFlowConfig: parsing");
+ InputSource source = new InputSource(is);
+ source.setSystemId(path);
+ FlowConfig cfg = FlowDigester.digest(source);
+ log.debug("getFlowConfig: parsed. " + cfg.toString());
+ return cfg;
+ }
+ finally
+ {
+ try
+ {
+ is.close();
+ }
+ catch(Exception e)
+ {
+ // ignore
+ }
+ }
+ }
+
+ private static FlowInfo getFlowInfo()
+ {
+ ConversationManager cm = ConversationManager.getInstance(false);
+ if (cm == null)
+ {
+ return null;
+ }
+
+ ConversationContext context = cm.getCurrentConversationContext();
+ if (context == null)
+ {
+ return null;
+ }
+
+ return getFlowInfo(context);
+ }
+
+ private static FlowInfo getFlowInfo(ConversationContext context)
+ {
+ return (FlowInfo) context.getAttribute("flowInfo");
+ }
+
+ private static String getFlowPath(String viewId)
+ {
+ // just return everything up to (and including) the final slash
+ int idx = viewId.lastIndexOf('/');
+ if (idx <= 0)
+ {
+ // this cannot possibly be a flow
+ return null;
+ }
+ return viewId.substring(0, idx +1);
+ }
+
+ /**
+ * Ensure that the specified FlowCall object is compatible with this FlowAccept.
+ * <p>
+ * The parameters sent by the caller should match the params accepted by the called
+ * flow. On return, the parameters sent by the called flow should match the params
+ * accepted by the caller.
+ * <p>
+ * This method needs to be consistent with FlowAccept.writeAcceptParams method, but
+ * should report better error messages. Possibly the writeAcceptParams method's error
+ * messages could just be improved. However it is useful to be able to perform this
+ * check earlier than before actually processing the return result.
+ * <p>
+ * This method also needs to be consistent with FlowCall.writeAcceptParams method.
+ */
+ static private void validateCall(FlowCall flowCall, FlowAccept flowAccept)
+ {
+ // check type matches
+ if (!flowCall.getService().equals(flowAccept.getService()))
+ {
+ throw new OrchestraException(
+ "FlowCall service [" + flowCall.getService()
+ + "] does not match FlowAccept service [" + flowAccept.getService() + "]");
+ }
+
+ List errors = new ArrayList();
+
+ // check input params
+ List callParamNames = new ArrayList();
+ for(Iterator i = flowCall.getParams().iterator(); i.hasNext(); )
+ {
+ FlowParamSend p = (FlowParamSend) i.next();
+ callParamNames.add(p.getName());
+ }
+
+ for(Iterator i = flowAccept.getParams().iterator(); i.hasNext(); )
+ {
+ FlowParamAccept p = (FlowParamAccept) i.next();
+ String name = p.getName();
+ if (!callParamNames.remove(name) && (p.getDflt() == null))
+ {
+ // accept param does not have call param equivalent, and
+ // there is no default parameter value defined.
+ errors.add(name);
+ }
+ }
+ // add any leftover call params that do not have accept param equivalents
+ errors.addAll(callParamNames);
+
+ if (!errors.isEmpty())
+ {
+ throw new OrchestraException("Parameter names mismatch:" + StringUtils.join(errors.iterator(), ","));
+ }
+
+ // check return params
+ List returnParamNames = new ArrayList();
+ for(Iterator i = flowAccept.getReturns().iterator(); i.hasNext(); )
+ {
+ FlowReturnSend p = (FlowReturnSend) i.next();
+ returnParamNames.add(p.getName());
+ }
+
+ for(Iterator i = flowCall.getReturns().iterator(); i.hasNext(); )
+ {
+ FlowReturnAccept p = (FlowReturnAccept) i.next();
+ String name = p.getName();
+ if (!returnParamNames.remove(name))
+ {
+ // accept param does not have call param equivalent
+ errors.add(name);
+ }
+ }
+ // add any leftover return params that do not have accept return param equivalents
+ errors.addAll(returnParamNames);
+
+ if (!errors.isEmpty())
+ {
+ throw new OrchestraException("Return parameter names mismatch:" + StringUtils.join(errors.iterator(), ","));
+ }
+
+ // all ok
+ return;
+ }
+
+ // no instances of this class expected
+ private FlowHandler()
+ {
+ }
+}
Propchange: myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/FlowHandler.java.sk
------------------------------------------------------------------------------
svn:executable = *
Modified: myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/FlowInfo.java
URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/FlowInfo.java?rev=675860&r1=675859&r2=675860&view=diff
==============================================================================
--- myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/FlowInfo.java (original)
+++ myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/FlowInfo.java Fri Jul 11 01:21:30 2008
@@ -25,6 +25,12 @@
import org.apache.myfaces.orchestra.flow.config.FlowAccept;
import org.apache.myfaces.orchestra.flow.config.FlowCall;
+/**
+ * Holds information about a specific call to a specific flow.
+ * <p>
+ * A new instance of this type is created for each call to a flow,
+ * and is stored in the ConversationContext created for that flow.
+ */
public class FlowInfo
{
// caller info
@@ -48,10 +54,20 @@
* created a call to setAcceptInfo will be made to add information about the
* called flow.
*
- * @param callerViewId
- * @param callerViewRoot
- * @param flowCall
- * @param data
+ * @param callerViewId contains the viewId of the view that started the call. This
+ * value must not be null.
+ *
+ * @param callerViewRoot contains the view tree for the view that started the call.
+ * This is optional; when not null then this view state will be restored on return
+ * from the flow. In particular, this allows data entered by the user into input
+ * components to be restored when the flow returns (assumes that the flow call is
+ * triggered by an immediate command component). When this is null, then on return
+ * to the caller a new view tree will be created.
+ *
+ * @param flowCall is the configuration object that describes the caller of a flow.
+ *
+ * @param data is the set of parameter values being passed to the called flow. See
+ * FlowCall.readSendParams().
*/
public FlowInfo(String callerViewId, UIViewRoot callerViewRoot, FlowCall flowCall, Map data)
{
@@ -62,12 +78,19 @@
// todo: save the serialized view tree, not just a ref to the viewroot
}
-
+
+ /**
+ * Return the viewId of the page that initiated the call to a flow (never null).
+ */
public String getCallerViewId()
{
return callerViewId;
}
+ /**
+ * Return the view root of the page that initiated the call to a flow (optional,
+ * may be null).
+ */
public UIViewRoot getCallerViewRoot()
{
return callerViewRoot;
Modified: myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/FlowNavigationHandler.java
URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/FlowNavigationHandler.java?rev=675860&r1=675859&r2=675860&view=diff
==============================================================================
--- myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/FlowNavigationHandler.java (original)
+++ myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/FlowNavigationHandler.java Fri Jul 11 01:21:30 2008
@@ -18,13 +18,8 @@
*/
package org.apache.myfaces.orchestra.flow;
-import java.io.IOException;
-
-import javax.faces.FacesException;
import javax.faces.application.NavigationHandler;
-import javax.faces.application.ViewHandler;
import javax.faces.component.UIViewRoot;
-import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import org.apache.commons.logging.Log;
@@ -36,88 +31,50 @@
private NavigationHandler delegate;
+ /**
+ * Constructor.
+ */
public FlowNavigationHandler(NavigationHandler delegate)
{
this.delegate = delegate;
}
- // WARNING: when doing a "commit" or "cancel" of a flow, the underlying navigation handler
- // is never called; we leap directly back to the flow that called the one that just ended.
- //
- // TODO: what about restoring state that is not in the view tree, eg FacesMessage objects
- // (which are attached to a FacesContext)? I guess if this is important then it can also
- // be attached to the FlowInfo object...
- //
- // TODO: what about RedirectTracker's functionality to save and restore messages,
- // request-scoped beans etc? Presumably that will not run. So are there any cases where
- // redirectTracker and "commit/cancel" have unexpected behaviour? Probably yes if a
- // page with request-scoped beans does a redirect to a flow start. The RedirectTracker
- // will save request-scoped beans and messages when leaving the caller page. Then it
- // will try to restore them into the child context (yecch). On return to the caller,
- // if it runs *earlier* then it will try to copy stuff from the flow exit page back to
- // the caller; if it runs later then it never runs at all.
- //
- // TODO: what about the orchestra custom NavigationHandler? --> that is no longer
- // used...
+ /**
+ * Special handleNavigation processing for Orchestra Flows.
+ * <p>
+ * In most cases this method simply delegates to the wrapped instance to perform a
+ * normal navigation. However there are the following exceptions:
+ * <ul>
+ * <li>When a flow is active, and the outcome matches the "commit" outcome for the flow
+ * <li>When a flow is active, and the outcome matches the "cancel" outcome for the flow
+ * </ul>
+ * Note that in these special cases, the underlying navigation handler is never called;
+ * we leap directly back to the clow that called the one that just ended.
+ * <p>
+ * There are also some special logic that needs to be triggered when the navigation
+ * outcome matches a "flowCall" value for the current view.
+ */
public void handleNavigation(FacesContext facesContext, String fromAction, String outcome)
{
+ // TODO: what about restoring state that is not in the view tree, eg FacesMessage objects
+ // (which are attached to a FacesContext)? I guess if this is important then it can also
+ // be attached to the FlowInfo object...
+ //
+ // TODO: what about RedirectTracker's functionality to save and restore messages,
+ // request-scoped beans etc? Presumably that will not run. So are there any cases where
+ // redirectTracker and "commit/cancel" have unexpected behaviour? Probably yes if a
+ // page with request-scoped beans does a redirect to a flow start. The RedirectTracker
+ // will save request-scoped beans and messages when leaving the caller page. Then it
+ // will try to restore them into the child context (yecch). On return to the caller,
+ // if it runs *earlier* then it will try to copy stuff from the flow exit page back to
+ // the caller; if it runs later then it never runs at all.
+
UIViewRoot currViewRoot = facesContext.getViewRoot();
String oldViewId = currViewRoot.getViewId();
log.debug("handleNavigation: view=" + oldViewId + ",outcome=" + outcome);
- FlowHandler.Selection sel = new FlowHandler.Selection();
- FlowHandler.processCall(sel, facesContext, oldViewId, outcome);
-
- if (sel.outcome != null)
- {
- // Doing custom navigation. We need to have the current view root set
- // correctly so that the navigation handler selects the right nav-case.
- //
- // The UIViewRoot installed also needs a locale and renderKitId, as the
- // NavHandler will call ViewHandler.createView, which will try to copy
- // those values onto the new UIViewRoot it creates.
- UIViewRoot dummyViewRoot = new UIViewRoot();
- dummyViewRoot.setViewId(sel.viewId);
- dummyViewRoot.setLocale(currViewRoot.getLocale());
- dummyViewRoot.setRenderKitId(currViewRoot.getRenderKitId());
- facesContext.setViewRoot(dummyViewRoot);
- delegate.handleNavigation(facesContext, fromAction, sel.outcome);
- return;
- }
-
- if (sel.viewRoot != null)
- {
- // We are leaping back to a previous view, and have a saved version of
- // the view tree. The processCall method will already have destroyed the
- // child context, so just render the specified view.
- facesContext.setViewRoot(sel.viewRoot);
- facesContext.renderResponse();
- return;
- }
-
- if (sel.viewId != null)
- {
- // We are leaping back to a previous view, but have no saved version of
- // the view tree. The processCall method will already have destroyed the
- // child context, so just send a redirect to the specified view. Using
- // a redirect here nicely updates the browser URL bar. Sometimes an
- // internal forward might be better though; how can we tell I wonder..
- ExternalContext externalContext = facesContext.getExternalContext();
- ViewHandler viewHandler = facesContext.getApplication().getViewHandler();
- String redirectPath = viewHandler.getActionURL(facesContext, sel.viewId);
-
- try
- {
- externalContext.redirect(externalContext.encodeActionURL(redirectPath));
- }
- catch (IOException e)
- {
- throw new FacesException(e.getMessage(), e);
- }
- return;
- }
-
- // Navigation is not being overruled, so do normal operation
- delegate.handleNavigation(facesContext, fromAction, outcome);
+ FlowNavigator nav = new FlowNavigator(facesContext, delegate, fromAction, outcome);
+ FlowHandler.processCall(nav, facesContext, oldViewId, outcome);
+ nav.doNavigation();
}
}
Added: myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/FlowNavigationHandler.java.sk
URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/FlowNavigationHandler.java.sk?rev=675860&view=auto
==============================================================================
--- myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/FlowNavigationHandler.java.sk (added)
+++ myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/FlowNavigationHandler.java.sk Fri Jul 11 01:21:30 2008
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.myfaces.orchestra.flow;
+
+import javax.faces.application.NavigationHandler;
+import javax.faces.component.UIViewRoot;
+import javax.faces.context.FacesContext;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+public class FlowNavigationHandler extends NavigationHandler
+{
+ private static final Log log = LogFactory.getLog(FlowNavigationHandler.class);
+
+ private NavigationHandler delegate;
+
+ public FlowNavigationHandler(NavigationHandler delegate)
+ {
+ this.delegate = delegate;
+ }
+
+ // WARNING: when doing a "commit" or "cancel" of a flow, the underlying navigation handler
+ // is never called; we leap directly back to the flow that called the one that just ended.
+ //
+ // TODO: what about restoring state that is not in the view tree, eg FacesMessage objects
+ // (which are attached to a FacesContext)? I guess if this is important then it can also
+ // be attached to the FlowInfo object...
+ //
+ // TODO: what about RedirectTracker's functionality to save and restore messages,
+ // request-scoped beans etc? Presumably that will not run. So are there any cases where
+ // redirectTracker and "commit/cancel" have unexpected behaviour? Probably yes if a
+ // page with request-scoped beans does a redirect to a flow start. The RedirectTracker
+ // will save request-scoped beans and messages when leaving the caller page. Then it
+ // will try to restore them into the child context (yecch). On return to the caller,
+ // if it runs *earlier* then it will try to copy stuff from the flow exit page back to
+ // the caller; if it runs later then it never runs at all.
+ //
+ // TODO: what about the orchestra custom NavigationHandler? --> that is no longer
+ // used...
+ public void handleNavigation(FacesContext facesContext, String fromAction, String outcome)
+ {
+ UIViewRoot currViewRoot = facesContext.getViewRoot();
+ String oldViewId = currViewRoot.getViewId();
+ log.debug("handleNavigation: view=" + oldViewId + ",outcome=" + outcome);
+
+ Navigator nav = new Navigator(facesContext, delegate, fromAction);
+ FlowHandler.processCall(nav, facesContext, oldViewId, outcome);
+ }
+}
Propchange: myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/FlowNavigationHandler.java.sk
------------------------------------------------------------------------------
svn:executable = *
Added: myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/FlowNavigator.java
URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/FlowNavigator.java?rev=675860&view=auto
==============================================================================
--- myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/FlowNavigator.java (added)
+++ myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/FlowNavigator.java Fri Jul 11 01:21:30 2008
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.myfaces.orchestra.flow;
+
+import java.io.IOException;
+import java.util.Locale;
+
+import javax.faces.FacesException;
+import javax.faces.application.NavigationHandler;
+import javax.faces.application.ViewHandler;
+import javax.faces.component.UIViewRoot;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+
+/**
+ * Provides methods that can be invoked to do various types of JSF
+ * page navigation.
+ * <p>
+ * Internally this wraps a standard JSF NavigationHandler object.
+ */
+public class FlowNavigator
+{
+ private FacesContext facesContext;
+ private NavigationHandler delegate;
+ private String fromAction;
+ private String outcome;
+ private boolean navigationDone = false;
+
+ /**
+ * Constructor that initialises this instance with all the info it needs to
+ * do a normal navigation operation.
+ * <p>
+ * If some kind of special navigation is desired (eg due to flow return) then the
+ * extra data is provided in the call to the relevant method.
+ */
+ public FlowNavigator(FacesContext facesContext, NavigationHandler delegate, String fromAction, String outcome)
+ {
+ this.facesContext = facesContext;
+ this.delegate = delegate;
+ this.fromAction = fromAction;
+ this.outcome = outcome;
+ }
+
+ /**
+ * Forces a navigation to the specified viewRoot (if not null), or to the
+ * specified viewId if the viewRoot is null.
+ * <p>
+ * This is expected to be called when a flow is cancelled or committed,
+ * in order to return to the calling view (whose view state might or
+ * might not have been saved when the flow was originally called).
+ */
+ public void goToView(UIViewRoot viewRoot, String viewId)
+ {
+ if (viewRoot != null)
+ {
+ // We are leaping back to a previous view, and have a saved version of
+ // the view tree. The processCall method will already have destroyed the
+ // child context, so just render the specified view.
+ facesContext.setViewRoot(viewRoot);
+ facesContext.renderResponse();
+ }
+ else
+ {
+ // We are leaping back to a previous view, but have no saved version of
+ // the view tree. Using a redirect here nicely updates the browser URL
+ // bar. Sometimes an internal forward might be preferred though; how
+ // can we tell I wonder..
+ ExternalContext externalContext = facesContext.getExternalContext();
+ ViewHandler viewHandler = facesContext.getApplication().getViewHandler();
+ String redirectPath = viewHandler.getActionURL(facesContext, viewId);
+
+ try
+ {
+ externalContext.redirect(externalContext.encodeActionURL(redirectPath));
+ }
+ catch (IOException e)
+ {
+ throw new FacesException(e.getMessage(), e);
+ }
+ }
+ navigationDone = true;
+ }
+
+ /**
+ * Forces a navigation by outcome to occur relative to the specified
+ * view.
+ * <p>
+ * When viewRoot is non-null, then that is used as the base view for
+ * looking up the outcome. When null, then viewId is used.
+ * <p>
+ * This is expected to be called when a flow is committed and a custom
+ * onCommit method returns a navigation outcome.
+ */
+ public void goToOutcome(UIViewRoot viewRoot, String viewId, String outcome)
+ {
+ // We *must* set a view root in order for the navigation case lookup
+ // to work correctly; the current view root is used as the "from-view-id"
+ // in the lookup. When we have the original view state to restore, that
+ // is no problem. But when we only have an original viewId, then we must
+ // create a dummy viewRoot with that viewId before invoking the navigation
+ // handler.
+
+ if (viewRoot != null)
+ {
+ facesContext.setViewRoot(viewRoot);
+ }
+ else
+ {
+ // Create dummy viewRoot with the right viewId so that navigation
+ // uses the desired from-view-id during navigation case lookup.
+ //
+ // Unfortunately when the navigation rule matched is not a forward,
+ // then ViewHandler.createView is executed and that expects to be able to
+ // copy properties locale and renderKitId from the current view root into
+ // the new view root. So we need to ensure that those properties are also
+ // correctly set up on our dummy view root.
+
+ Locale locale;
+ String renderKitId;
+
+ if (facesContext.getViewRoot() != null)
+ {
+ UIViewRoot currViewRoot = facesContext.getViewRoot();
+ locale = currViewRoot.getLocale();
+ renderKitId = currViewRoot.getRenderKitId();
+ }
+ else
+ {
+ ViewHandler vh = facesContext.getApplication().getViewHandler();
+ locale = vh.calculateLocale(facesContext);
+ renderKitId = vh.calculateRenderKitId(facesContext);
+ }
+
+ UIViewRoot dummyViewRoot = new UIViewRoot();
+ dummyViewRoot.setViewId(viewId);
+ dummyViewRoot.setLocale(locale);
+ dummyViewRoot.setRenderKitId(renderKitId);
+ facesContext.setViewRoot(dummyViewRoot);
+ }
+
+ delegate.handleNavigation(facesContext, "flowCommit", outcome);
+ navigationDone = true;
+ }
+
+ /**
+ * If special navigation has not yet been caused by calls to other methods on this class,
+ * then do a normal navigation operation.
+ */
+ public void doNavigation()
+ {
+ if (!navigationDone)
+ {
+ // Navigation is not being overruled, so do normal operation
+ delegate.handleNavigation(facesContext, fromAction, outcome);
+ navigationDone = true;
+ }
+ }
+}
Propchange: myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/FlowNavigator.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/FlowNavigator.java
------------------------------------------------------------------------------
svn:executable = *
Added: myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/Navigator.java.sk
URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/Navigator.java.sk?rev=675860&view=auto
==============================================================================
--- myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/Navigator.java.sk (added)
+++ myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/Navigator.java.sk Fri Jul 11 01:21:30 2008
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.myfaces.orchestra.flow;
+
+import java.io.IOException;
+import java.util.Locale;
+
+import javax.faces.FacesException;
+import javax.faces.application.NavigationHandler;
+import javax.faces.application.ViewHandler;
+import javax.faces.component.UIViewRoot;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+
+public class Navigator
+{
+ private FacesContext facesContext;
+ private NavigationHandler delegate;
+ private String fromAction;
+
+ public Navigator(FacesContext facesContext, NavigationHandler delegate, String fromAction)
+ {
+ this.facesContext = facesContext;
+ this.delegate = delegate;
+ this.fromAction = fromAction;
+ }
+
+ public void goToView(String viewId, UIViewRoot viewRoot)
+ {
+ if (viewRoot != null)
+ {
+ // We are leaping back to a previous view, and have a saved version of
+ // the view tree. The processCall method will already have destroyed the
+ // child context, so just render the specified view.
+ facesContext.setViewRoot(viewRoot);
+ facesContext.renderResponse();
+ }
+ else
+ {
+ // We are leaping back to a previous view, but have no saved version of
+ // the view tree. The processCall method will already have destroyed the
+ // child context, so just send a redirect to the specified view. Using
+ // a redirect here nicely updates the browser URL bar. Sometimes an
+ // internal forward might be better though; how can we tell I wonder..
+ ExternalContext externalContext = facesContext.getExternalContext();
+ ViewHandler viewHandler = facesContext.getApplication().getViewHandler();
+ String redirectPath = viewHandler.getActionURL(facesContext, viewId);
+
+ try
+ {
+ externalContext.redirect(externalContext.encodeActionURL(redirectPath));
+ }
+ catch (IOException e)
+ {
+ throw new FacesException(e.getMessage(), e);
+ }
+ }
+ }
+
+ // Doing custom navigation. We need to have the current view root set
+ // correctly so that the navigation handler selects the right nav-case.
+ //
+ // The UIViewRoot installed also needs a locale and renderKitId, as the
+ // NavHandler will call ViewHandler.createView, which will try to copy
+ // those values onto the new UIViewRoot it creates.
+ public void goToOutcome(String viewId, UIViewRoot viewRoot, String outcome)
+ {
+ Locale locale;
+ String renderKitId;
+
+ if (viewRoot != null)
+ {
+ locale = viewRoot.getLocale();
+ renderKitId = viewRoot.getRenderKitId();
+ }
+ else if (facesContext.getViewRoot() != null)
+ {
+ UIViewRoot currViewRoot = facesContext.getViewRoot();
+ locale = currViewRoot.getLocale();
+ renderKitId = currViewRoot.getRenderKitId();
+ }
+ else
+ {
+ ViewHandler vh = facesContext.getApplication().getViewHandler();
+ locale = vh.calculateLocale(facesContext);
+ renderKitId = vh.calculateRenderKitId(facesContext);
+ }
+
+ UIViewRoot dummyViewRoot = new UIViewRoot();
+ dummyViewRoot.setViewId(viewId);
+ dummyViewRoot.setLocale(locale);
+ dummyViewRoot.setRenderKitId(renderKitId);
+ facesContext.setViewRoot(dummyViewRoot);
+
+ delegate.handleNavigation(facesContext, "flowCommit", outcome);
+ }
+
+ public void goToOutcome(String outcome)
+ {
+ // Navigation is not being overruled, so do normal operation
+ delegate.handleNavigation(facesContext, fromAction, outcome);
+ }
+}
Propchange: myfaces/orchestra/trunk/flow/src/main/java/org/apache/myfaces/orchestra/flow/Navigator.java.sk
------------------------------------------------------------------------------
svn:executable = *