You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by lu...@apache.org on 2013/09/09 01:52:33 UTC

svn commit: r1520941 - in /myfaces/core/trunk/impl/src: main/java/org/apache/myfaces/application/ main/java/org/apache/myfaces/flow/ test/java/org/apache/myfaces/application/flow/ test/resources/ test/resources/org/apache/myfaces/application/flow/ test...

Author: lu4242
Date: Sun Sep  8 23:52:33 2013
New Revision: 1520941

URL: http://svn.apache.org/r1520941
Log:
MYFACES-3691 Implement Faces Flows (consider enter/exit from multiple flows and make the different when a flow is open "globally" or it is open using a call node).

Added:
    myfaces/core/trunk/impl/src/test/resources/begin.xhtml
      - copied, changed from r1518877, myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow1/begin.xhtml
    myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow4/   (with props)
    myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow4/content.xhtml   (with props)
    myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow4/flow4.xhtml   (with props)
    myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow_base.xhtml
      - copied, changed from r1509781, myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow1_end.xhtml
Modified:
    myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/application/NavigationHandlerImpl.java
    myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/flow/FlowHandlerImpl.java
    myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/flow/_FlowContextualInfo.java
    myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/application/flow/FlowMyFacesRequestTestCase.java
    myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/WEB-INF/flow1-flow.xml
    myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow1/begin.xhtml
    myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow2/begin.xhtml

Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/application/NavigationHandlerImpl.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/application/NavigationHandlerImpl.java?rev=1520941&r1=1520940&r2=1520941&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/application/NavigationHandlerImpl.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/application/NavigationHandlerImpl.java Sun Sep  8 23:52:33 2013
@@ -308,14 +308,14 @@ public class NavigationHandlerImpl
         if (navigationContext != null)
         {
             // Is any flow transition on the way?
-            if (navigationContext.getSourceFlow() != null ||
+            if (navigationContext.getSourceFlows() != null ||
                 (navigationContext.getTargetFlows() != null &&
                  !navigationContext.getTargetFlows().isEmpty()))
             {
                 FlowHandler flowHandler = facesContext.getApplication().getFlowHandler();
-                Flow sourceFlow = navigationContext.getSourceFlow();
                 for (int i = 0; i < navigationContext.getTargetFlows().size(); i++)
                 {
+                    Flow sourceFlow = navigationContext.getSourceFlows().get(i);
                     Flow targetFlow = navigationContext.getTargetFlows().get(i);
 
                     flowHandler.transition(facesContext, sourceFlow, targetFlow, 
@@ -524,10 +524,7 @@ public class NavigationHandlerImpl
                         {
                             // Add the transition to exit from the flow
                             Flow baseReturnFlow = navigationContext.getCurrentFlow(facesContext);
-                            if (navigationContext.getSourceFlow() == null)
-                            {
-                                navigationContext.setSourceFlow(baseReturnFlow);
-                            }
+                            Flow sourceFlow = baseReturnFlow;
                             // This is the part when the pseudo "recursive call" is done. 
                             while (baseReturnFlow != null && !(baseReturnFlow.getDefiningDocumentId().equals(
                                     targetFlow.getDefiningDocumentId()) &&
@@ -535,19 +532,17 @@ public class NavigationHandlerImpl
                             {
                                 navigationContext.popFlow(facesContext);
                                 baseReturnFlow = navigationContext.getCurrentFlow(facesContext);
-                                navigationContext.addTargetFlow(baseReturnFlow, null);
                             }
                             navigationContext.popFlow(facesContext);
                             currentFlow = navigationContext.getCurrentFlow(facesContext);
-                            navigationContext.addTargetFlow(currentFlow, null);                            
+                            navigationContext.addTargetFlow(baseReturnFlow, currentFlow, null);
                         }
                         if (startFlowId == null)
                         {
                             startFlowDocumentId = targetFlow.getDefiningDocumentId();
                             startFlowId = targetFlowCallNode == null ? targetFlow.getId() : targetFlowCallNode.getId();
                         }
-                        navigationContext.setSourceFlow(currentFlow);
-                        navigationContext.addTargetFlow(targetFlow, targetFlowCallNode);
+                        navigationContext.addTargetFlow(currentFlow, targetFlow, targetFlowCallNode);
                         targetFlowCallNode = null;
                         // Since we start a new flow, the current flow is now the
                         // target flow.
@@ -613,13 +608,9 @@ public class NavigationHandlerImpl
                             {
                                 ReturnNode returnNode = (ReturnNode) flowNode;
                                 String fromOutcome = returnNode.getFromOutcome(facesContext);
-                                
                                 actionToGo = currentFlow.getId();
+                                Flow sourceFlow = currentFlow;
                                 Flow baseReturnFlow = navigationContext.getCurrentFlow(facesContext);
-                                if (navigationContext.getSourceFlow() == null)
-                                {
-                                    navigationContext.setSourceFlow(baseReturnFlow);
-                                }
                                 // This is the part when the pseudo "recursive call" is done. 
                                 while (baseReturnFlow != null && !(baseReturnFlow.getDefiningDocumentId().equals(
                                         currentFlow.getDefiningDocumentId()) &&
@@ -627,11 +618,10 @@ public class NavigationHandlerImpl
                                 {
                                     navigationContext.popFlow(facesContext);
                                     baseReturnFlow = navigationContext.getCurrentFlow(facesContext);
-                                    navigationContext.addTargetFlow(baseReturnFlow, null);
                                 }
                                 navigationContext.popFlow(facesContext);
                                 currentFlow = navigationContext.getCurrentFlow(facesContext);
-                                navigationContext.addTargetFlow(currentFlow, null);
+                                navigationContext.addTargetFlow(sourceFlow, currentFlow, null);
                                 outcomeToGo = fromOutcome;
                                 String lastDisplayedViewId = navigationContext.getLastDisplayedViewId(facesContext, 
                                             currentFlow);
@@ -664,7 +654,6 @@ public class NavigationHandlerImpl
                                     complete = true;
                                 }
                                 continue;
-                                //complete = true;
                             }
                             if (!complete && flowNode instanceof ViewNode)
                             {
@@ -1503,7 +1492,7 @@ public class NavigationHandlerImpl
     protected static class NavigationContext
     {
         private NavigationCase navigationCase;
-        private Flow sourceFlow;
+        private List<Flow> sourceFlows;
         private List<Flow> targetFlows;
         private List<FlowCallNode> targetFlowCallNodes;
         private List<Flow> currentFlows;
@@ -1528,14 +1517,9 @@ public class NavigationHandlerImpl
             this.navigationCase = navigationCase;
         }
 
-        public Flow getSourceFlow()
-        {
-            return sourceFlow;
-        }
-
-        public void setSourceFlow(Flow sourceFlow)
+        public List<Flow> getSourceFlows()
         {
-            this.sourceFlow = sourceFlow;
+            return sourceFlows;
         }
 
         public List<Flow> getTargetFlows()
@@ -1548,13 +1532,15 @@ public class NavigationHandlerImpl
             return targetFlowCallNodes;
         }
 
-        public void addTargetFlow(Flow targetFlow, FlowCallNode flowCallNode)
+        public void addTargetFlow(Flow sourceFlow, Flow targetFlow, FlowCallNode flowCallNode)
         {
             if (targetFlows == null)
             {
+                sourceFlows = new ArrayList<Flow>(4);
                 targetFlows = new ArrayList<Flow>(4);
                 targetFlowCallNodes = new ArrayList<FlowCallNode>(4);
             }
+            this.sourceFlows.add(sourceFlow);
             this.targetFlows.add(targetFlow);
             this.targetFlowCallNodes.add(flowCallNode);
         }

Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/flow/FlowHandlerImpl.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/flow/FlowHandlerImpl.java?rev=1520941&r1=1520940&r2=1520941&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/flow/FlowHandlerImpl.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/flow/FlowHandlerImpl.java Sun Sep  8 23:52:33 2013
@@ -191,14 +191,15 @@ public class FlowHandlerImpl extends Flo
             return;
         }
         
-        // TODO: Implement me! In theory here lies the push/pop logic, but to know how it works, it is necessary
-        // to add the code inside NavigationHandlerImpl first. For now, the logic only allows 1 flow at the time
-        // but this should work with multiple nested flows.
         if (sourceFlow == null && targetFlow == null)
         {
             return;
         }
 
+        // Calculate the parentFlowReference, since it will be used later.
+        FlowReference parentFlowReference = (outboundCallNode != null && sourceFlow != null) ?
+            new FlowReference(sourceFlow.getDefiningDocumentId(), sourceFlow.getId()) : null;
+        
         if (sourceFlow == null)
         {
             // Entering a flow
@@ -206,24 +207,19 @@ public class FlowHandlerImpl extends Flo
                 targetFlow, !outboundCallNodeProcessed ? outboundCallNode : null);
             outboundCallNodeProcessed = true;
             pushFlowReference(context, clientWindow, 
-                    new FlowReference(targetFlow.getDefiningDocumentId(), targetFlow.getId()), toViewId);
+                    new FlowReference(targetFlow.getDefiningDocumentId(), targetFlow.getId()), 
+                    toViewId, parentFlowReference);
             doAfterEnterFlow(context, targetFlow, outboundParameters);
         }
         else if (targetFlow == null)
         {
-            // Getting out of the flow, since targetFlow is null, just clear the stack
+            // Getting out of the flow, since targetFlow is null,
+            // we need to take sourceFlow and take it out and all the chain
             List<_FlowContextualInfo> currentFlowStack = getCurrentFlowStack(context, clientWindow);
             if (currentFlowStack != null)
             {
-                //currentFlowStack.clear();
-                for (int i = currentFlowStack.size()-1; i >= 0; i--)
-                {
-                    _FlowContextualInfo fci = currentFlowStack.get(i);
-                    FlowReference fr = fci.getFlowReference();
-                    doBeforeExitFlow(context, getFlow(context, fr.getDocumentId(), fr.getId()));
-                    popFlowReference(context, clientWindow, currentFlowStack, i);
-                }
-            }            
+                removeFlowFromStack(context, currentFlowStack, sourceFlow);
+            }
         }
         else
         {
@@ -246,61 +242,103 @@ public class FlowHandlerImpl extends Flo
                 }
                 if (targetFlowIndex >= 0)
                 {
-                    for (int i = currentFlowStack.size()-1; i > targetFlowIndex; i--)
-                    {
-                        _FlowContextualInfo fci = currentFlowStack.get(i);
-                        FlowReference fr = fci.getFlowReference();
-                        doBeforeExitFlow(context, getFlow(context, fr.getDocumentId(), fr.getId()));
-                        popFlowReference(context, clientWindow, currentFlowStack, i);
-                    }
+                    // targetFlow is on the stack, so it is a return.
+                    removeFlowFromStack(context, currentFlowStack, sourceFlow);
                 }
                 else
                 {
-                    // sourceFlow should match.
-                    FlowReference sourceFlowReference = new FlowReference(
-                            sourceFlow.getDefiningDocumentId(), sourceFlow.getId());
-                    if ( sourceFlowReference.equals(
-                        currentFlowStack.get(currentFlowStack.size()-1).getFlowReference()) )
-                    {
-                        Map<String, Object> outboundParameters = doBeforeEnterFlow(context,
-                            targetFlow, !outboundCallNodeProcessed ? outboundCallNode : null);
-                        outboundCallNodeProcessed = true;
-                        pushFlowReference(context, clientWindow, 
-                                new FlowReference(targetFlow.getDefiningDocumentId(), targetFlow.getId()), toViewId);
-                        doAfterEnterFlow(context, targetFlow, outboundParameters);
-                    }
-                    else
-                    {
-                        // Chain gets broken. Clear stack and start again.
-                        for (int i = currentFlowStack.size()-1; i >= 0; i--)
-                        {
-                            _FlowContextualInfo fci = currentFlowStack.get(i);
-                            FlowReference fr = fci.getFlowReference();
-                            doBeforeExitFlow(context, getFlow(context, fr.getDocumentId(), fr.getId()));
-                            popFlowReference(context, clientWindow, currentFlowStack, i);
-                        }
-                        
-                        Map<String, Object> outboundParameters = doBeforeEnterFlow(context,
-                            targetFlow, !outboundCallNodeProcessed ? outboundCallNode : null);
-                        outboundCallNodeProcessed = true;
-                        pushFlowReference(context, clientWindow, 
-                                new FlowReference(targetFlow.getDefiningDocumentId(), targetFlow.getId()), toViewId);
-                        doAfterEnterFlow(context, targetFlow, outboundParameters);
-                    }
+                    // targetFlow is not on the stack, so it is flow call.
+                    Map<String, Object> outboundParameters = doBeforeEnterFlow(context,
+                        targetFlow, !outboundCallNodeProcessed ? outboundCallNode : null);
+                    outboundCallNodeProcessed = true;
+                    pushFlowReference(context, clientWindow, 
+                            new FlowReference(targetFlow.getDefiningDocumentId(), targetFlow.getId()), toViewId,
+                            parentFlowReference);
+                    doAfterEnterFlow(context, targetFlow, outboundParameters);
                 }
             }
             else
             {
+                // sourceFlow and targetFlow are not null, but there is no currentFlowStack. It that
+                // case just enter into targetFlow
                 Map<String, Object> outboundParameters = doBeforeEnterFlow(context, 
                     targetFlow, !outboundCallNodeProcessed ? outboundCallNode : null);
                 outboundCallNodeProcessed = true;
                 pushFlowReference(context, clientWindow, 
-                        new FlowReference(targetFlow.getDefiningDocumentId(), targetFlow.getId()), toViewId);
+                        new FlowReference(targetFlow.getDefiningDocumentId(), targetFlow.getId()), toViewId,
+                        parentFlowReference);
                 doAfterEnterFlow(context, targetFlow, outboundParameters);
             }
         }
     }
     
+    private void removeFlowFromStack(FacesContext context, List<_FlowContextualInfo> currentFlowStack, Flow sourceFlow)
+    {
+        // Steps to remove a flow:
+        // 1. locate where is the flow in the chain
+        int sourceFlowIndex = -1;
+        FlowReference sourceFlowReference = new FlowReference(sourceFlow.getDefiningDocumentId(),
+            sourceFlow.getId());
+        List<_FlowContextualInfo> flowsToRemove = new ArrayList<_FlowContextualInfo>();
+        for (int i = currentFlowStack.size()-1; i >= 0; i--)
+        {
+            _FlowContextualInfo fci = currentFlowStack.get(i);
+            if (fci.getFlowReference().equals(sourceFlowReference))
+            {
+                sourceFlowIndex = i;
+                flowsToRemove.add(fci);
+                break;
+            }
+        }
+
+        if (sourceFlowIndex != -1)
+        {
+            // From sourceFlowIndex, add all flows 
+            traverseDependantFlows(sourceFlowReference, sourceFlowIndex+1, currentFlowStack, flowsToRemove);
+
+            // Remove all marked elements
+            if (!flowsToRemove.isEmpty())
+            {
+                for (int i = flowsToRemove.size()-1; i >= 0; i--)
+                {
+                    _FlowContextualInfo fci = flowsToRemove.get(i);
+                    FlowReference fr = fci.getFlowReference();
+                    doBeforeExitFlow(context, getFlow(context, fr.getDocumentId(), fr.getId()));
+                    //popFlowReference(context, clientWindow, currentFlowStack, i);
+                    currentFlowStack.remove(fci);
+                }
+            }
+
+            if (currentFlowStack.isEmpty())
+            {
+                // Remove it from session but keep it in request scope.
+                context.getAttributes().put(ROOT_LAST_VIEW_ID, 
+                    context.getExternalContext().getSessionMap().remove(ROOT_LAST_VIEW_ID + 
+                    context.getExternalContext().getClientWindow().getId()));
+            }
+        }
+    }
+    
+    private void traverseDependantFlows(FlowReference sourceFlowReference, 
+        int index, List<_FlowContextualInfo> currentFlowStack, List<_FlowContextualInfo> flowsToRemove)
+    {
+        if (index < currentFlowStack.size())
+        {
+            for (int i = index; i < currentFlowStack.size(); i++)
+            {
+                _FlowContextualInfo info = currentFlowStack.get(i);
+                if (sourceFlowReference.equals(info.getSourceFlowReference()))
+                {
+                    if (!flowsToRemove.contains(info))
+                    {
+                        flowsToRemove.add(info);
+                        traverseDependantFlows(info.getFlowReference(), i+1, currentFlowStack, flowsToRemove);
+                    }
+                }
+            }
+        }
+    }
+    
     private Map<String, Object> doBeforeEnterFlow(FacesContext context, Flow flow, FlowCallNode outboundCallNode)
     {
         Map<String, Object> outboundParameters = null;
@@ -466,8 +504,10 @@ public class FlowHandlerImpl extends Flo
                 // where the flow should return, because that information was not passed
                 // in the parameters of the link. 
                 String toFlowDocumentId = FlowHandler.NULL_FLOW;
-                String targetFlowId = null;
                 String fromOutcome = flowIdRequestParam;
+                //Flow sourceFlow = null;
+                List<Flow> sourceFlows = null;
+                List<Flow> targetFlows = null;
                 
                 boolean failed = false;
                 int i = 0;
@@ -483,7 +523,13 @@ public class FlowHandlerImpl extends Flo
                     FlowNode node = currentFlow.getNode(fromOutcome);
                     if (node instanceof ReturnNode)
                     {
+                        if (targetFlows == null)
+                        {
+                            sourceFlows = new ArrayList<Flow>(4);
+                            targetFlows = new ArrayList<Flow>(4);
+                        }
                         // Get the navigation case using the outcome
+                        Flow sourceFlow = currentFlow;
                         flowHandler.pushReturnMode(context);
                         currentFlow = flowHandler.getCurrentFlow(context);
                         i++;
@@ -495,15 +541,17 @@ public class FlowHandlerImpl extends Flo
                         {
                             if (currentLastDisplayedViewId != null)
                             {
+                                sourceFlows.add(sourceFlow);
                                 if (currentFlow != null)
                                 {
                                     toFlowDocumentId = currentFlow.getDefiningDocumentId();
-                                    targetFlowId = currentFlow.getId();
+                                    targetFlows.add(currentFlow);
                                 }
                                 else
                                 {
                                     // No active flow
                                     toFlowDocumentId = null;
+                                    targetFlows.add(null);
                                 }
                             }
                             else
@@ -521,23 +569,33 @@ public class FlowHandlerImpl extends Flo
                             }
                             else
                             {
+                                sourceFlows.add(sourceFlow);
                                 // The absence of FlowHandler.NULL_FLOW means the return went somewhere else.
                                 if (currentFlow != null)
                                 {
                                     toFlowDocumentId = currentFlow.getDefiningDocumentId();
-                                    targetFlowId = currentFlow.getId();
+                                    targetFlows.add(currentFlow);
                                 }
                                 else
                                 {
                                     // No active flow
                                     toFlowDocumentId = null;
+                                    targetFlows.add(null);
                                 }
                             }
                         }
                     }
                     else
                     {
-                        failed = true;
+                        // No return node found in current flow, push it and check 
+                        // the next flow
+                        flowHandler.pushReturnMode(context);
+                        currentFlow = flowHandler.getCurrentFlow(context);
+                        i++;
+                        if (currentFlow == null)
+                        {
+                            failed = true;
+                        }
                     }
                 }
                 for (int j = 0; j<i; j++)
@@ -550,12 +608,16 @@ public class FlowHandlerImpl extends Flo
                 }
                 else 
                 {
-                    Flow targetFlow = targetFlowId == null ? null : 
-                        getFlow(context, toFlowDocumentId, targetFlowId);
                     //Call transitions.
-                    flowHandler.transition(context, 
-                        flowHandler.getCurrentFlow(context),
-                        targetFlow, null, context.getViewRoot().getViewId());
+                    for (int j = 0; j < targetFlows.size(); j++)
+                    {
+                        Flow sourceFlow = sourceFlows.get(j);
+                        Flow targetFlow = targetFlows.get(j);
+                        flowHandler.transition(context, 
+                            sourceFlow,
+                            targetFlow, null, context.getViewRoot().getViewId());
+                        
+                    }
                 }
             }
             else
@@ -740,7 +802,7 @@ public class FlowHandlerImpl extends Flo
     }
     
     private void pushFlowReference(FacesContext context, ClientWindow clientWindow, FlowReference flowReference,
-        String toViewId)
+        String toViewId, FlowReference sourceFlowReference)
     {
         Map<String, Object> sessionMap = context.getExternalContext().getSessionMap();
         String currentFlowMapKey = CURRENT_FLOW_STACK + clientWindow.getId();
@@ -760,7 +822,7 @@ public class FlowHandlerImpl extends Flo
             context.getExternalContext().getSessionMap().put(ROOT_LAST_VIEW_ID + clientWindow.getId(), 
                 context.getViewRoot().getViewId());
         }
-        currentFlowStack.add(new _FlowContextualInfo(flowReference, toViewId));
+        currentFlowStack.add(new _FlowContextualInfo(flowReference, toViewId, sourceFlowReference));
     }
     
     private void popFlowReference(FacesContext context, ClientWindow clientWindow,

Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/flow/_FlowContextualInfo.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/flow/_FlowContextualInfo.java?rev=1520941&r1=1520940&r2=1520941&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/flow/_FlowContextualInfo.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/flow/_FlowContextualInfo.java Sun Sep  8 23:52:33 2013
@@ -30,15 +30,18 @@ class _FlowContextualInfo implements Ser
     
     private FlowReference flowReference;
     private String lastDisplayedViewId;
+    private FlowReference sourceFlowReference;
 
     public _FlowContextualInfo()
     {
     }
     
-    public _FlowContextualInfo(FlowReference flowReference, String lastDisplayedViewId)
+    public _FlowContextualInfo(FlowReference flowReference, String lastDisplayedViewId, 
+        FlowReference souFlowReference)
     {
         this.flowReference = flowReference;
         this.lastDisplayedViewId = lastDisplayedViewId;
+        this.sourceFlowReference = souFlowReference;
     }
 
     @Override
@@ -107,4 +110,20 @@ class _FlowContextualInfo implements Ser
     {
         this.lastDisplayedViewId = lastDisplayedViewId;
     }
+
+    /**
+     * @return the sourceFlowReference
+     */
+    public FlowReference getSourceFlowReference()
+    {
+        return sourceFlowReference;
+    }
+
+    /**
+     * @param sourceFlowReference the sourceFlowReference to set
+     */
+    public void setSourceFlowReference(FlowReference sourceFlowReference)
+    {
+        this.sourceFlowReference = sourceFlowReference;
+    }
 }

Modified: myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/application/flow/FlowMyFacesRequestTestCase.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/application/flow/FlowMyFacesRequestTestCase.java?rev=1520941&r1=1520940&r2=1520941&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/application/flow/FlowMyFacesRequestTestCase.java (original)
+++ myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/application/flow/FlowMyFacesRequestTestCase.java Sun Sep  8 23:52:33 2013
@@ -480,4 +480,949 @@ public class FlowMyFacesRequestTestCase 
     {
         return new org.apache.el.ExpressionFactoryImpl();
     }
+    
+    /**
+     * This tests do the following:
+     * 
+     * - Start flow 1
+     * - Start flow 2
+     * - End flow 1
+     * - End flow 2
+     * 
+     * Since flow2 was called using the flow name, end flow 1 doesn't end flow 2
+     * 
+     * @throws Exception 
+     */
+    @Test
+    public void testFlow1_6() throws Exception
+    {
+        setupRequest("/flow_base.xhtml");
+        processLifecycleExecute();
+        
+        ConfigurableNavigationHandler handler = (ConfigurableNavigationHandler) facesContext.getApplication().getNavigationHandler();
+        
+        NavigationCase navCase = handler.getNavigationCase(facesContext, null, "flow1");
+        
+        Assert.assertNotNull(navCase);
+        
+        // Check begin view node
+        Assert.assertEquals("/flow1/begin.xhtml", navCase.getToViewId(facesContext));
+        
+        processRender();
+       
+        //Enter flow 1
+        UICommand button = (UICommand) facesContext.getViewRoot().findComponent("mainForm:startFlow1");
+        submit(button);
+        
+        processLifecycleExecute();
+        
+        Flow currentFlow = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNotNull(currentFlow);
+        Assert.assertEquals(currentFlow.getId(), "flow1");
+        
+        processRender();
+        
+        NavigationCase goFlowBase = handler.getNavigationCase(facesContext, null, "flow_base");
+        Assert.assertNotNull(goFlowBase);
+        
+        UICommand button2 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:go_flow_base");
+        submit(button2);
+        
+        processLifecycleExecute();
+        
+        Assert.assertEquals("/flow_base.xhtml", facesContext.getViewRoot().getViewId());
+
+        processRender();
+        
+        UICommand button3 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:startFlow2");
+        submit(button3);
+        
+        processLifecycleExecute();
+        
+        Flow currentFlow2 = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNotNull(currentFlow2);
+        Assert.assertEquals(currentFlow2.getId(), "flow2");
+        
+        processRender();
+        
+        UICommand button4 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:go_flow_base");
+        submit(button4);
+        
+        processLifecycleExecute();
+        
+        Assert.assertEquals("/flow_base.xhtml", facesContext.getViewRoot().getViewId());
+        
+        processRender();
+        
+        UICommand button5 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:returnFlow1");
+        submit(button5);
+        
+        processLifecycleExecute();
+        
+        Assert.assertEquals("/flow_base.xhtml", facesContext.getViewRoot().getViewId());
+
+        Flow currentFlow3 = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNotNull(currentFlow3);
+        Assert.assertEquals(currentFlow3.getId(), "flow2");
+        
+        processRender();
+        
+        UICommand button6 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:returnFlow2");
+        submit(button6);
+        
+        processLifecycleExecute();
+
+        Flow currentFlow4 = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNull(currentFlow4);
+        
+        processRender();
+    }
+    
+    /**
+     * This tests do the following:
+     * 
+     * - Start flow 1
+     * - Start flow 2 (using call node)
+     * - End flow 1
+     * 
+     * Since flow2 was called using a call node, end flow 1 also end flow 2
+     * 
+     * @throws Exception 
+     */
+    @Test
+    public void testFlow1_7() throws Exception
+    {
+        setupRequest("/flow_base.xhtml");
+        processLifecycleExecute();
+        
+        ConfigurableNavigationHandler handler = (ConfigurableNavigationHandler) facesContext.getApplication().getNavigationHandler();
+        
+        NavigationCase navCase = handler.getNavigationCase(facesContext, null, "flow1");
+        
+        Assert.assertNotNull(navCase);
+        
+        // Check begin view node
+        Assert.assertEquals("/flow1/begin.xhtml", navCase.getToViewId(facesContext));
+        
+        processRender();
+       
+        //Enter flow 1
+        UICommand button = (UICommand) facesContext.getViewRoot().findComponent("mainForm:startFlow1");
+        submit(button);
+        
+        processLifecycleExecute();
+        
+        Flow currentFlow = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNotNull(currentFlow);
+        Assert.assertEquals(currentFlow.getId(), "flow1");
+        
+        processRender();
+        
+        UICommand button3 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:call_flow2");
+        submit(button3);
+        
+        processLifecycleExecute();
+        
+        Flow currentFlow2 = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNotNull(currentFlow2);
+        Assert.assertEquals(currentFlow2.getId(), "flow2");
+        
+        processRender();
+        
+        UICommand button4 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:go_flow_base");
+        submit(button4);
+        
+        processLifecycleExecute();
+        
+        Assert.assertEquals("/flow_base.xhtml", facesContext.getViewRoot().getViewId());
+        
+        processRender();
+        
+        UICommand button5 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:returnFlow1");
+        submit(button5);
+        
+        processLifecycleExecute();
+        
+        Assert.assertEquals("/flow_base.xhtml", facesContext.getViewRoot().getViewId());
+
+        Flow currentFlow3 = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNull(currentFlow3);
+    }
+
+    /**
+     * This tests do the following:
+     * 
+     * - Start flow 3 (start flow 1)
+     * - Start flow 2
+     * - End flow 1
+     * - End flow 2
+     * 
+     * Since flow2 was called using the flow name, end flow 1 doesn't end flow 2
+     * At the end flow 3 should still be active
+     * 
+     * @throws Exception 
+     */
+    @Test
+    public void testFlow1_8() throws Exception
+    {
+        setupRequest("/flow_base.xhtml");
+        processLifecycleExecute();
+        
+        ConfigurableNavigationHandler handler = (ConfigurableNavigationHandler) facesContext.getApplication().getNavigationHandler();
+        
+        processRender();
+       
+        //Enter flow 1
+        UICommand button = (UICommand) facesContext.getViewRoot().findComponent("mainForm:startFlow3");
+        submit(button);
+        
+        processLifecycleExecute();
+        
+        Flow currentFlow = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNotNull(currentFlow);
+        Assert.assertEquals(currentFlow.getId(), "flow1");
+        
+        processRender();
+        
+        UICommand button3 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:call_flow2");
+        submit(button3);
+        
+        processLifecycleExecute();
+        
+        Flow currentFlow2 = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNotNull(currentFlow2);
+        Assert.assertEquals(currentFlow2.getId(), "flow2");
+        
+        processRender();
+        
+        UICommand button4 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:go_flow_base");
+        submit(button4);
+        
+        processLifecycleExecute();
+        
+        Assert.assertEquals("/flow_base.xhtml", facesContext.getViewRoot().getViewId());
+        
+        processRender();
+        
+        UICommand button5 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:returnFlow1");
+        submit(button5);
+        
+        processLifecycleExecute();
+        
+        Assert.assertEquals("/flow_base.xhtml", facesContext.getViewRoot().getViewId());
+
+        Flow currentFlow3 = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNotNull(currentFlow3);
+        Assert.assertEquals(currentFlow3.getId(), "flow3");
+    }
+
+    /**
+     * This tests do the following:
+     * 
+     * - Start flow 2 
+     * - Start flow 3 (start flow 1)
+     * - Return flow 1 and 3
+     * - End flow 2
+     * 
+     * @throws Exception 
+     */    
+    @Test
+    public void testFlow1_9() throws Exception
+    {
+        setupRequest("/flow_base.xhtml");
+        processLifecycleExecute();
+        
+        ConfigurableNavigationHandler handler = (ConfigurableNavigationHandler) facesContext.getApplication().getNavigationHandler();
+        
+        processRender();
+       
+        //Enter flow 1
+        UICommand button = (UICommand) facesContext.getViewRoot().findComponent("mainForm:startFlow2");
+        submit(button);
+        
+        processLifecycleExecute();
+        
+        Flow currentFlow = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNotNull(currentFlow);
+        Assert.assertEquals(currentFlow.getId(), "flow2");
+        
+        processRender();
+        
+        NavigationCase goFlowBase = handler.getNavigationCase(facesContext, null, "flow_base");
+        Assert.assertNotNull(goFlowBase);
+        
+        UICommand button2 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:go_flow_base");
+        submit(button2);
+        
+        processLifecycleExecute();
+        
+        Assert.assertEquals("/flow_base.xhtml", facesContext.getViewRoot().getViewId());
+
+        processRender();
+        
+        UICommand button3 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:startFlow3");
+        submit(button3);
+        
+        processLifecycleExecute();
+        
+        Flow currentFlow2 = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNotNull(currentFlow2);
+        Assert.assertEquals(currentFlow2.getId(), "flow1");
+        
+        processRender();
+        
+        UICommand button4 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:go_flow_base");
+        submit(button4);
+        
+        processLifecycleExecute();
+        
+        Assert.assertEquals("/flow_base.xhtml", facesContext.getViewRoot().getViewId());
+        
+        processRender();
+        
+        UICommand button5 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:returnFlow1_3");
+        submit(button5);
+        
+        processLifecycleExecute();
+        
+        Assert.assertEquals("/flow_base.xhtml", facesContext.getViewRoot().getViewId());
+
+        Flow currentFlow3 = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNotNull(currentFlow3);
+        Assert.assertEquals(currentFlow3.getId(), "flow2");
+        
+        processRender();
+        
+        UICommand button6 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:returnFlow2");
+        submit(button6);
+        
+        processLifecycleExecute();
+
+        Flow currentFlow4 = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNull(currentFlow4);
+        
+        processRender();
+    }
+    
+    /**
+     * This tests do the following:
+     * 
+     * - Start flow 2 
+     * - Start flow 3 (start flow 1)
+     * - Return flow 1 and 3 (GET)
+     * - End flow 2
+     * 
+     * @throws Exception 
+     */   
+    @Test
+    public void testFlow1_9_1() throws Exception
+    {
+        setupRequest("/flow_base.xhtml");
+        processLifecycleExecute();
+        
+        ConfigurableNavigationHandler handler = (ConfigurableNavigationHandler) facesContext.getApplication().getNavigationHandler();
+        
+        processRender();
+       
+        //Enter flow 1
+        UICommand button = (UICommand) facesContext.getViewRoot().findComponent("mainForm:startFlow2");
+        submit(button);
+        
+        processLifecycleExecute();
+        
+        Flow currentFlow = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNotNull(currentFlow);
+        Assert.assertEquals(currentFlow.getId(), "flow2");
+        
+        processRender();
+        
+        NavigationCase goFlowBase = handler.getNavigationCase(facesContext, null, "flow_base");
+        Assert.assertNotNull(goFlowBase);
+        
+        UICommand button2 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:go_flow_base");
+        submit(button2);
+        
+        processLifecycleExecute();
+        
+        Assert.assertEquals("/flow_base.xhtml", facesContext.getViewRoot().getViewId());
+
+        processRender();
+        
+        UICommand button3 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:startFlow3");
+        submit(button3);
+        
+        processLifecycleExecute();
+        
+        Flow currentFlow2 = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNotNull(currentFlow2);
+        Assert.assertEquals(currentFlow2.getId(), "flow1");
+        
+        processRender();
+        
+        UICommand button4 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:go_flow_base");
+        submit(button4);
+        
+        processLifecycleExecute();
+        
+        Assert.assertEquals("/flow_base.xhtml", facesContext.getViewRoot().getViewId());
+        
+        processRender();
+        
+        //UICommand button5 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:returnFlow1_3");
+        //submit(button5);
+        
+        NavigationCase endCase = handler.getNavigationCase(facesContext, null, "back_flow_1_3");
+        Assert.assertNotNull(endCase);
+
+        String toViewId = endCase.getToViewId(facesContext);
+        // Check if the dynamic outcome return hack has been correctly resolved. 
+        Assert.assertEquals(toViewId, "/flow_base.xhtml");
+        String fromOutcome = endCase.getFromOutcome();
+        String clientWindowId = facesContext.getExternalContext().getClientWindow().getId();
+        
+        tearDownRequest();
+        setupRequest(toViewId);
+        request.addParameter(FlowHandler.TO_FLOW_DOCUMENT_ID_REQUEST_PARAM_NAME, FlowHandler.NULL_FLOW);
+        request.addParameter(FlowHandler.FLOW_ID_REQUEST_PARAM_NAME, fromOutcome);
+        request.addParameter(ResponseStateManager.CLIENT_WINDOW_URL_PARAM, clientWindowId);
+        
+        processLifecycleExecute();
+        
+        Assert.assertEquals("/flow_base.xhtml", facesContext.getViewRoot().getViewId());
+
+        Flow currentFlow3 = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNotNull(currentFlow3);
+        Assert.assertEquals(currentFlow3.getId(), "flow2");
+        
+        processRender();
+        
+        UICommand button6 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:returnFlow2");
+        submit(button6);
+        
+        processLifecycleExecute();
+
+        Flow currentFlow4 = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNull(currentFlow4);
+        
+        processRender();
+    }
+    
+
+    /**
+     * This tests do the following:
+     * 
+     * - Start flow 3 (start flow 1)
+     * - Start flow 2 
+     * - End flow 2
+     * - Return flow 1 and 3
+     * 
+     * @throws Exception 
+     */   
+    @Test
+    public void testFlow1_10() throws Exception
+    {
+        setupRequest("/flow_base.xhtml");
+        processLifecycleExecute();
+        
+        ConfigurableNavigationHandler handler = (ConfigurableNavigationHandler) facesContext.getApplication().getNavigationHandler();
+        
+        processRender();
+       
+        //Enter flow 1
+        UICommand button = (UICommand) facesContext.getViewRoot().findComponent("mainForm:startFlow3");
+        submit(button);
+        
+        processLifecycleExecute();
+        
+        Flow currentFlow = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNotNull(currentFlow);
+        Assert.assertEquals(currentFlow.getId(), "flow1");
+        
+        processRender();
+        
+        NavigationCase goFlowBase = handler.getNavigationCase(facesContext, null, "flow_base");
+        Assert.assertNotNull(goFlowBase);
+        
+        UICommand button2 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:go_flow_base");
+        submit(button2);
+        
+        processLifecycleExecute();
+        
+        Assert.assertEquals("/flow_base.xhtml", facesContext.getViewRoot().getViewId());
+
+        processRender();
+        
+        UICommand button3 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:startFlow2");
+        submit(button3);
+        
+        processLifecycleExecute();
+        
+        Flow currentFlow2 = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNotNull(currentFlow2);
+        Assert.assertEquals(currentFlow2.getId(), "flow2");
+        
+        processRender();
+        
+        UICommand button4 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:go_flow_base");
+        submit(button4);
+        
+        processLifecycleExecute();
+        
+        Assert.assertEquals("/flow_base.xhtml", facesContext.getViewRoot().getViewId());
+        
+        processRender();
+        
+        UICommand button5 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:returnFlow2");
+        submit(button5);
+        
+        processLifecycleExecute();
+        
+        Assert.assertEquals("/flow_base.xhtml", facesContext.getViewRoot().getViewId());
+
+        Flow currentFlow3 = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNotNull(currentFlow3);
+        Assert.assertEquals(currentFlow3.getId(), "flow1");
+        
+        processRender();
+        
+        UICommand button6 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:returnFlow1_3");
+        submit(button6);
+        
+        processLifecycleExecute();
+
+        Flow currentFlow4 = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNull(currentFlow4);
+        
+        processRender();
+    }
+    
+    /**
+     * This tests do the following:
+     * 
+     * - Start flow 3 (start flow 1)
+     * - Start flow 2 
+     * - Return flow 1 and 3
+     * - End flow 2
+     * 
+     * @throws Exception 
+     */ 
+    @Test
+    public void testFlow1_10_1() throws Exception
+    {
+        setupRequest("/flow_base.xhtml");
+        processLifecycleExecute();
+        
+        ConfigurableNavigationHandler handler = (ConfigurableNavigationHandler) facesContext.getApplication().getNavigationHandler();
+        
+        processRender();
+       
+        //Enter flow 1
+        UICommand button = (UICommand) facesContext.getViewRoot().findComponent("mainForm:startFlow3");
+        submit(button);
+        
+        processLifecycleExecute();
+        
+        Flow currentFlow = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNotNull(currentFlow);
+        Assert.assertEquals(currentFlow.getId(), "flow1");
+        
+        processRender();
+        
+        NavigationCase goFlowBase = handler.getNavigationCase(facesContext, null, "flow_base");
+        Assert.assertNotNull(goFlowBase);
+        
+        UICommand button2 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:go_flow_base");
+        submit(button2);
+        
+        processLifecycleExecute();
+        
+        Assert.assertEquals("/flow_base.xhtml", facesContext.getViewRoot().getViewId());
+
+        processRender();
+        
+        UICommand button3 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:startFlow2");
+        submit(button3);
+        
+        processLifecycleExecute();
+        
+        Flow currentFlow2 = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNotNull(currentFlow2);
+        Assert.assertEquals(currentFlow2.getId(), "flow2");
+        
+        processRender();
+        
+        UICommand button4 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:go_flow_base");
+        submit(button4);
+        
+        processLifecycleExecute();
+        
+        Assert.assertEquals("/flow_base.xhtml", facesContext.getViewRoot().getViewId());
+        
+        processRender();
+        
+        UICommand button5 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:returnFlow1_3");
+        submit(button5);
+        
+        processLifecycleExecute();
+        
+        Assert.assertEquals("/flow_base.xhtml", facesContext.getViewRoot().getViewId());
+
+        Flow currentFlow3 = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNotNull(currentFlow3);
+        Assert.assertEquals(currentFlow3.getId(), "flow2");
+        
+        processRender();
+        
+        UICommand button6 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:returnFlow2");
+        submit(button6);
+        
+        processLifecycleExecute();
+
+        Flow currentFlow4 = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNull(currentFlow4);
+        
+        processRender();
+    }
+
+    /**
+     * This tests do the following:
+     * 
+     * - Start flow 3 (start flow 1)
+     * - Start flow 2 
+     * - Return flow 1 and 3 (GET)
+     * - End flow 2
+     * 
+     * @throws Exception 
+     */ 
+    @Test
+    public void testFlow1_10_2() throws Exception
+    {
+        setupRequest("/flow_base.xhtml");
+        processLifecycleExecute();
+        
+        ConfigurableNavigationHandler handler = (ConfigurableNavigationHandler) facesContext.getApplication().getNavigationHandler();
+        
+        processRender();
+       
+        //Enter flow 1
+        UICommand button = (UICommand) facesContext.getViewRoot().findComponent("mainForm:startFlow3");
+        submit(button);
+        
+        processLifecycleExecute();
+        
+        Flow currentFlow = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNotNull(currentFlow);
+        Assert.assertEquals(currentFlow.getId(), "flow1");
+        
+        processRender();
+        
+        NavigationCase goFlowBase = handler.getNavigationCase(facesContext, null, "flow_base");
+        Assert.assertNotNull(goFlowBase);
+        
+        UICommand button2 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:go_flow_base");
+        submit(button2);
+        
+        processLifecycleExecute();
+        
+        Assert.assertEquals("/flow_base.xhtml", facesContext.getViewRoot().getViewId());
+
+        processRender();
+        
+        UICommand button3 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:startFlow2");
+        submit(button3);
+        
+        processLifecycleExecute();
+        
+        Flow currentFlow2 = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNotNull(currentFlow2);
+        Assert.assertEquals(currentFlow2.getId(), "flow2");
+        
+        processRender();
+        
+        UICommand button4 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:go_flow_base");
+        submit(button4);
+        
+        processLifecycleExecute();
+        
+        Assert.assertEquals("/flow_base.xhtml", facesContext.getViewRoot().getViewId());
+        
+        processRender();
+        
+        //UICommand button5 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:returnFlow1_3");
+        //submit(button5);
+        NavigationCase endCase = handler.getNavigationCase(facesContext, null, "back_flow_1_3");
+        Assert.assertNotNull(endCase);
+
+        String toViewId = endCase.getToViewId(facesContext);
+        // Check if the dynamic outcome return hack has been correctly resolved. 
+        Assert.assertEquals(toViewId, "/flow_base.xhtml");
+        String fromOutcome = endCase.getFromOutcome();
+        String clientWindowId = facesContext.getExternalContext().getClientWindow().getId();
+        
+        tearDownRequest();
+        setupRequest(toViewId);
+        request.addParameter(FlowHandler.TO_FLOW_DOCUMENT_ID_REQUEST_PARAM_NAME, FlowHandler.NULL_FLOW);
+        request.addParameter(FlowHandler.FLOW_ID_REQUEST_PARAM_NAME, fromOutcome);
+        request.addParameter(ResponseStateManager.CLIENT_WINDOW_URL_PARAM, clientWindowId);
+        
+        processLifecycleExecute();
+        
+        Assert.assertEquals("/flow_base.xhtml", facesContext.getViewRoot().getViewId());
+
+        Flow currentFlow3 = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNotNull(currentFlow3);
+        Assert.assertEquals(currentFlow3.getId(), "flow2");
+        
+        processRender();
+        
+        UICommand button6 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:returnFlow2");
+        submit(button6);
+        
+        processLifecycleExecute();
+
+        Flow currentFlow4 = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNull(currentFlow4);
+        
+        processRender();
+    }
+    
+    /**
+     * This tests do the following:
+     * 
+     * - Start flow 4
+     * - Start flow 2
+     * - Start flow 1 (use call node from flow 4)
+     * - Return flow 1 and 4
+     * - Return flow 2
+     * 
+     * @throws Exception 
+     */ 
+    @Test
+    public void testFlow1_11() throws Exception
+    {
+        setupRequest("/flow_base.xhtml");
+        processLifecycleExecute();
+        
+        ConfigurableNavigationHandler handler = (ConfigurableNavigationHandler) facesContext.getApplication().getNavigationHandler();
+        
+        processRender();
+       
+        //Enter flow 1
+        UICommand button = (UICommand) facesContext.getViewRoot().findComponent("mainForm:startFlow4");
+        submit(button);
+        
+        processLifecycleExecute();
+        
+        Assert.assertEquals("/flow4/flow4.xhtml", facesContext.getViewRoot().getViewId());
+        
+        Flow currentFlow = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNotNull(currentFlow);
+        Assert.assertEquals(currentFlow.getId(), "flow4");
+        
+        processRender();
+        
+        NavigationCase goFlowBase = handler.getNavigationCase(facesContext, null, "flow_base");
+        Assert.assertNotNull(goFlowBase);
+        
+        UICommand button2 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:go_flow_base");
+        submit(button2);
+        
+        processLifecycleExecute();
+        
+        Assert.assertEquals("/flow_base.xhtml", facesContext.getViewRoot().getViewId());
+
+        processRender();
+        
+        UICommand button3 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:startFlow2");
+        submit(button3);
+        
+        processLifecycleExecute();
+        
+        Flow currentFlow2 = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNotNull(currentFlow2);
+        Assert.assertEquals(currentFlow2.getId(), "flow2");
+        
+        processRender();
+        
+        UICommand button4 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:go_flow_base");
+        submit(button4);
+        
+        processLifecycleExecute();
+        
+        Assert.assertEquals("/flow_base.xhtml", facesContext.getViewRoot().getViewId());
+        
+        processRender();
+        
+        UICommand button5 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:startFlow1_4");
+        submit(button5);
+        
+        processLifecycleExecute();
+        
+        Flow currentFlow3 = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNotNull(currentFlow3);
+        Assert.assertEquals(currentFlow3.getId(), "flow1");
+        
+        processRender();
+        
+        UICommand button6 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:go_flow_base");
+        submit(button6);
+        
+        processLifecycleExecute();
+        
+        Assert.assertEquals("/flow_base.xhtml", facesContext.getViewRoot().getViewId());
+        
+        processRender();
+        
+        
+        UICommand button7 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:returnFlow1_4");
+        submit(button7);
+        
+        processLifecycleExecute();
+        
+        Assert.assertEquals("/flow_base.xhtml", facesContext.getViewRoot().getViewId());
+
+        Flow currentFlow4 = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNotNull(currentFlow4);
+        Assert.assertEquals(currentFlow4.getId(), "flow2");
+        
+        processRender();
+        
+        UICommand button8 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:returnFlow2");
+        submit(button8);
+        
+        processLifecycleExecute();
+
+        Flow currentFlow5 = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNull(currentFlow5);
+        
+        processRender();
+
+    }
+    
+    /**
+     * This tests do the following:
+     * 
+     * - Start flow 4
+     * - Start flow 2
+     * - Start flow 1 (use call node from flow 4)
+     * - Return flow 1 and 4 (GET)
+     * - Return flow 2
+     * 
+     * @throws Exception 
+     */
+    @Test
+    public void testFlow1_11_1() throws Exception
+    {
+        setupRequest("/flow_base.xhtml");
+        processLifecycleExecute();
+        
+        ConfigurableNavigationHandler handler = (ConfigurableNavigationHandler) facesContext.getApplication().getNavigationHandler();
+        
+        processRender();
+       
+        //Enter flow 1
+        UICommand button = (UICommand) facesContext.getViewRoot().findComponent("mainForm:startFlow4");
+        submit(button);
+        
+        processLifecycleExecute();
+        
+        Assert.assertEquals("/flow4/flow4.xhtml", facesContext.getViewRoot().getViewId());
+        
+        Flow currentFlow = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNotNull(currentFlow);
+        Assert.assertEquals(currentFlow.getId(), "flow4");
+        
+        processRender();
+        
+        NavigationCase goFlowBase = handler.getNavigationCase(facesContext, null, "flow_base");
+        Assert.assertNotNull(goFlowBase);
+        
+        UICommand button2 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:go_flow_base");
+        submit(button2);
+        
+        processLifecycleExecute();
+        
+        Assert.assertEquals("/flow_base.xhtml", facesContext.getViewRoot().getViewId());
+
+        processRender();
+        
+        UICommand button3 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:startFlow2");
+        submit(button3);
+        
+        processLifecycleExecute();
+        
+        Flow currentFlow2 = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNotNull(currentFlow2);
+        Assert.assertEquals(currentFlow2.getId(), "flow2");
+        
+        processRender();
+        
+        UICommand button4 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:go_flow_base");
+        submit(button4);
+        
+        processLifecycleExecute();
+        
+        Assert.assertEquals("/flow_base.xhtml", facesContext.getViewRoot().getViewId());
+        
+        processRender();
+        
+        UICommand button5 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:startFlow1_4");
+        submit(button5);
+        
+        processLifecycleExecute();
+        
+        Flow currentFlow3 = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNotNull(currentFlow3);
+        Assert.assertEquals(currentFlow3.getId(), "flow1");
+        
+        processRender();
+        
+        UICommand button6 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:go_flow_base");
+        submit(button6);
+        
+        processLifecycleExecute();
+        
+        Assert.assertEquals("/flow_base.xhtml", facesContext.getViewRoot().getViewId());
+        
+        processRender();
+        
+        //UICommand button7 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:returnFlow1_4");
+        //submit(button7);
+        
+        NavigationCase endCase = handler.getNavigationCase(facesContext, null, "back_flow_1_4");
+        Assert.assertNotNull(endCase);
+
+        String toViewId = endCase.getToViewId(facesContext);
+        // Check if the dynamic outcome return hack has been correctly resolved. 
+        Assert.assertEquals(toViewId, "/flow_base.xhtml");
+        String fromOutcome = endCase.getFromOutcome();
+        String clientWindowId = facesContext.getExternalContext().getClientWindow().getId();
+        
+        tearDownRequest();
+        setupRequest(toViewId);
+        request.addParameter(FlowHandler.TO_FLOW_DOCUMENT_ID_REQUEST_PARAM_NAME, FlowHandler.NULL_FLOW);
+        request.addParameter(FlowHandler.FLOW_ID_REQUEST_PARAM_NAME, fromOutcome);
+        request.addParameter(ResponseStateManager.CLIENT_WINDOW_URL_PARAM, clientWindowId);
+        
+        processLifecycleExecute();
+        
+        Assert.assertEquals("/flow_base.xhtml", facesContext.getViewRoot().getViewId());
+
+        Flow currentFlow4 = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNotNull(currentFlow4);
+        Assert.assertEquals(currentFlow4.getId(), "flow2");
+        
+        processRender();
+        
+        UICommand button8 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:returnFlow2");
+        submit(button8);
+        
+        processLifecycleExecute();
+
+        Flow currentFlow5 = facesContext.getApplication().getFlowHandler().getCurrentFlow(facesContext);
+        Assert.assertNull(currentFlow5);
+        
+        processRender();
+
+    }
 }

Copied: myfaces/core/trunk/impl/src/test/resources/begin.xhtml (from r1518877, myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow1/begin.xhtml)
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/test/resources/begin.xhtml?p2=myfaces/core/trunk/impl/src/test/resources/begin.xhtml&p1=myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow1/begin.xhtml&r1=1518877&r2=1520941&rev=1520941&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow1/begin.xhtml (original)
+++ myfaces/core/trunk/impl/src/test/resources/begin.xhtml Sun Sep  8 23:52:33 2013
@@ -35,6 +35,7 @@
         <h:commandButton id="call_flow2" value="Start Flow 2" action="call_flow2"/>
         <h:commandButton id="end_flow" value="End" action="end"/>
         <h:commandButton id="back_flow" value="Back" action="back"/>
+        <h:commandButton id="go_flow_base" value="Go to base" action="flow_base"/>
     </h:form>
     <!-- resolve outcome is quite useful to check how the flow target is resolved -->
     <h:link outcome="end" value="End Outcome Link"/>

Modified: myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/WEB-INF/flow1-flow.xml
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/WEB-INF/flow1-flow.xml?rev=1520941&r1=1520940&r2=1520941&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/WEB-INF/flow1-flow.xml (original)
+++ myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/WEB-INF/flow1-flow.xml Sun Sep  8 23:52:33 2013
@@ -46,6 +46,16 @@
             <from-outcome>back</from-outcome>
         </flow-return>
         
+        <flow-return id="back_flow_1"/>
+        
+        <flow-return id="back_flow_1_3">
+            <from-outcome>back_flow_3</from-outcome>
+        </flow-return>
+        
+        <flow-return id="back_flow_1_4">
+            <from-outcome>back_flow_4</from-outcome>
+        </flow-return>
+        
         <!-- The idea is use a switch combo to get back, since
              the from-outcome of the switch cannot receive
              EL expressions -->
@@ -106,6 +116,8 @@
              default-->
         <flow-return id="back"/>
         
+        <flow-return id="back_flow_2"/>
+        
         <navigation-rule>
             <from-view-id>*</from-view-id>
             <navigation-case>
@@ -151,12 +163,48 @@
             <from-outcome>exit</from-outcome>
         </flow-return>
         
+        <flow-return id="back_flow_3"/>
+        
         <view id="content">
             <vdl-document>/flow3/content.xhtml</vdl-document>
         </view>
         
     </flow-definition>
     
+    <flow-definition id="flow4">
+        
+        <flow-call id="call_flow1_4">
+            <flow-reference>
+                <flow-document-id>flow_def_1</flow-document-id>
+                <flow-id>flow1</flow-id>
+            </flow-reference>
+            <outbound-parameter>
+                <name>backOutcome</name>
+                <value>content</value>
+            </outbound-parameter>
+        </flow-call>
+        
+        <navigation-rule>
+            <from-view-id>*</from-view-id>
+            <navigation-case>
+                <from-action>flow1</from-action>
+                <from-outcome>exit</from-outcome>
+                <to-view-id>content</to-view-id>
+            </navigation-case>
+        </navigation-rule>
+        
+        <flow-return id="end">
+            <from-outcome>exit</from-outcome>
+        </flow-return>
+        
+        <flow-return id="back_flow_4"/>
+        
+        <view id="content">
+            <vdl-document>/flow4/content.xhtml</vdl-document>
+        </view>
+        
+    </flow-definition>
+
     <navigation-rule>
         <from-view-id>*</from-view-id>
         <!-- End Flow 1 -->
@@ -165,7 +213,10 @@
             <from-outcome>exit</from-outcome>
             <to-view-id>/flow1_end.xhtml</to-view-id>
         </navigation-case>
-        
+        <navigation-case>
+            <from-outcome>flow_base</from-outcome>
+            <to-view-id>/flow_base.xhtml</to-view-id>
+        </navigation-case>
     </navigation-rule>
     
 </faces-config>

Modified: myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow1/begin.xhtml
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow1/begin.xhtml?rev=1520941&r1=1520940&r2=1520941&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow1/begin.xhtml (original)
+++ myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow1/begin.xhtml Sun Sep  8 23:52:33 2013
@@ -35,6 +35,7 @@
         <h:commandButton id="call_flow2" value="Start Flow 2" action="call_flow2"/>
         <h:commandButton id="end_flow" value="End" action="end"/>
         <h:commandButton id="back_flow" value="Back" action="back"/>
+        <h:commandButton id="go_flow_base" value="Go to base" action="flow_base"/>
     </h:form>
     <!-- resolve outcome is quite useful to check how the flow target is resolved -->
     <h:link outcome="end" value="End Outcome Link"/>

Modified: myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow2/begin.xhtml
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow2/begin.xhtml?rev=1520941&r1=1520940&r2=1520941&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow2/begin.xhtml (original)
+++ myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow2/begin.xhtml Sun Sep  8 23:52:33 2013
@@ -33,6 +33,7 @@
         <h:commandButton id="content" value="Content" action="content"/>
         <h:commandButton id="end_flow" value="End" action="end"/>
         <h:commandButton id="back_flow" value="Back" action="back"/>
+        <h:commandButton id="go_flow_base" value="Go to base" action="flow_base"/>
     </h:form>
     <!-- resolve outcome is quite useful to check how the flow target is resolved -->
     <h:link outcome="end" value="End Outcome Link"/>

Propchange: myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow4/
------------------------------------------------------------------------------
    bugtraq:number = true

Added: myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow4/content.xhtml
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow4/content.xhtml?rev=1520941&view=auto
==============================================================================
--- myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow4/content.xhtml (added)
+++ myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow4/content.xhtml Sun Sep  8 23:52:33 2013
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:h="http://java.sun.com/jsf/html"
+ xmlns:f="http://java.sun.com/jsf/core"
+ xmlns:c="http://java.sun.com/jsp/jstl/core"
+ xmlns:ui="http://java.sun.com/jsf/facelets"
+ >
+<body>
+<ui:composition>
+    <h1>Myfaces Flow Examples</h1>
+    <h2>begin.xhtml</h2>
+    <h:form id="mainForm">
+        <h:commandButton value="End" action="end"/>
+    </h:form>
+    <!-- resolve outcome is quite useful to check how the flow target is resolved -->
+    <h:link outcome="end" value="End Outcome Link"/>
+</ui:composition>
+</body>
+</html>

Propchange: myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow4/content.xhtml
------------------------------------------------------------------------------
    svn:eol-style = native

Added: myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow4/flow4.xhtml
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow4/flow4.xhtml?rev=1520941&view=auto
==============================================================================
--- myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow4/flow4.xhtml (added)
+++ myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow4/flow4.xhtml Sun Sep  8 23:52:33 2013
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:h="http://java.sun.com/jsf/html"
+ xmlns:f="http://java.sun.com/jsf/core"
+ xmlns:c="http://java.sun.com/jsp/jstl/core"
+ xmlns:ui="http://java.sun.com/jsf/facelets"
+ >
+<body>
+<ui:composition>
+    <h1>Myfaces Flow Examples</h1>
+    <h2>begin.xhtml</h2>
+    <h:form id="mainForm">
+        <h:commandButton value="End" action="content"/>
+        <h:commandButton id="end_flow" value="End" action="end"/>
+        <h:commandButton id="back_flow" value="Back" action="back"/>
+        <h:commandButton id="go_flow_base" value="Go to base" action="flow_base"/>
+    </h:form>
+    <!-- resolve outcome is quite useful to check how the flow target is resolved -->
+    <h:link outcome="end" value="End Outcome Link"/>
+</ui:composition>
+</body>
+</html>

Propchange: myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow4/flow4.xhtml
------------------------------------------------------------------------------
    svn:eol-style = native

Copied: myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow_base.xhtml (from r1509781, myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow1_end.xhtml)
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow_base.xhtml?p2=myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow_base.xhtml&p1=myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow1_end.xhtml&r1=1509781&r2=1520941&rev=1520941&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow1_end.xhtml (original)
+++ myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/application/flow/flow_base.xhtml Sun Sep  8 23:52:33 2013
@@ -28,6 +28,16 @@
     <h:panelGrid columns="1">
         <h:form id="mainForm">
             <h:commandButton id="startFlow1" action="flow1" value="Start Flow 1"/>
+            <h:commandButton id="startFlow2" action="flow2" value="Start Flow 2"/>
+            <h:commandButton id="startFlow3" action="flow3" value="Start Flow 3"/>
+            <h:commandButton id="startFlow4" action="flow4" value="Start Flow 4"/>
+            <h:commandButton id="startFlow1_4" action="call_flow1_4" value="Start Flow 1 from 4"/>
+            <h:commandButton id="returnFlow1" action="back_flow_1" value="Return Flow 1"/>
+            <h:commandButton id="returnFlow2" action="back_flow_2" value="Return Flow 2"/>
+            <h:commandButton id="returnFlow3" action="back_flow_3" value="Return Flow 3"/>
+            <h:commandButton id="returnFlow4" action="back_flow_4" value="Return Flow 4"/>
+            <h:commandButton id="returnFlow1_3" action="back_flow_1_3" value="Return from Flow 1 and 3"/>
+            <h:commandButton id="returnFlow1_4" action="back_flow_1_4" value="Return from Flow 1 and 4"/>
         </h:form>
     </h:panelGrid>
 </h:body>