Hi
Checking in deep the latest spec snapshot and pushing the concepts to its
utmost limits, I have found some clarifications that needs to be done and
that can change some parts in the current spec.
This is important feedback. I know there is plenty of things to do, but my
intention here is ensure the spec is consistent with the proposed API.
Really the spec in general looks very good, but are those details what
ensures compatibility and stability between different implementations of
the spec.
In summary:
1. The spec wording suggest only view nodes or vdl view identifiers can be
used in from-outcome of a switch case, but that could be insuficient.
2. It is necessary to provide a list of the current flows being traversed,
to resolve properly ReturnNode case. Also, ReturnNode should be resolved
taking into account the context, in which it is being resolved.
Now the discussion:
1.
Let's suppose this faces flow definition:
<faces-flow-definition id="flow1">
<switch id="switch1">
<navigation-case>
<if>#{flowBean.someCondition}</if>
<from-outcome>outcome2</from-outcome>
</navigation-case>
<default-outcome>
<from-outcome>exit</from-outcome>
</default-outcome>
</switch>
<flow-return id="exit">
<navigation-case>
<from-outcome>mainpage</from-outcome>
</navigation-case>
</flow-return>
</faces-flow-definition>
<navigation-rule>
<from-view-id>*</from-view-id>
<navigation-case>
<from-action>flow1</from-action>
<from-outcome>mainpage</from-outcome>
<to-view-id>/mainpageforflow1.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
The idea is inside the flow there is a switch and that switch defines as
default-outcome "exit", which is a return node that ends the flow and
outside there is a global navigation rule that resolve the final page.
The syntax is valid, and in my opinion the user expect that this
definition works.
But this is what it says in JSF 2.2 section 7.4.2.1 :
"... If the node is a SwitchNode, iterate over the NavigationCase
instances returned from its getCases() method. For each, one call
getCondition(). If the result is true, let vdl view identifier be the
value of its fromOutcome property. ..."
This description excludes in a explicit way that a SwitchCase
fromOutcome or SwitchNode defaultOutcome to point to a flow node, and
the example shows that this feature is very helpful. The same happens with
MethodCallNode "... If the result is null, let vdl view identifier be
the value of the MethodCallNode’s outcome property. ..."
The same thing is described in JSF 2.2 section 7.4.2.2 :
"... let switchCaseFromOutcome be the value of the fromOutcome property
of the NavigationCase returned from the defaultCase property of the SwitchNode,
if any. Synthesize a <navigation-case> as described below from the current
flow, the fromAction and switchCaseFromOutcome. ..."
and with MethodCallNode :
"... If the result is null, let invokeResult be the value of the
MethodCallNode’s outcome property. Synthesize a <navigation-case> as described
below from the current Flow, the fromAction, and invokeResult. ..."
In few words SwitchNode/SwitchCase, MethodCallNode, FlowCallNode and
ReturnNode are nodes that defines "transitions" between pages, but the
pages are the "states". In that sense, only a ViewNode can "synthesize" a
to a vdl view identifier through a NavigationCase, and the "transition
nodes" must be traversed until found a final "state".
It is also curious that the use of the flow id in the "from-action" after
a ReturnNode. There is a mention in JSF 2.2 section 7.4.2.2 related
to SwitchNode:
"... Treat fromAction as a flow id and ask the FlowHandler for a flow
under that flow id, passing null as the document id ..."
but the same applies to a ReturnNode, right?
2.
Consider the following example
<faces-flow-definition id="mainflow">
<flow-return id="exit2">
<navigation-case>
<from-outcome>exit1</from-outcome>
</navigation-case>
</flow-return>
</faces-flow-definition>
<faces-flow-definition id="subflow">
<flow-return id="exit">
<navigation-case>
<from-outcome>exit2</from-outcome>
</navigation-case>
</flow-return>
</faces-flow-definition>
And first you have entered in mainflow and then in subflow and now outcome is
"exit". JSF 2.2 section 7.4.2.1 says this:
"... If the node is a ReturnNode obtain its navigation case and start the
navigation algorithm over using it as the basis. ..."
The problem is the description of the behavior of ReturnNode in this case is
insuficient. It is expected ReturnNode to start over again but only if the
flow was not invoked from another flow. In this case, the context takes
precedence, and the outcome should be resolved first in "mainflow context"
and then in the "global context".
This example also shows that getCurrentFlow() is insuficient, because to
resolve this properly we need the list of flows actually active. The navigation
algorithm should be smart enough to apply the outcome according to the context.
For example, if a navigation case is defined inside "mainflow" instead the
return node, the algorithm should take into account the navigation rules first
before try to resolve it globally.
This example also suggest that the conditions for call FlowHandler.transition()
are a little bit more complex that it seems:
"... Call transition() on the FlowHandler, passing the current FacesContext,
the current UIViewRoot, the new UIViewRoot and the facesFlowCallNode
corresponding to this faces flow call, if any ..."
Note this does not match with the current API:
public abstract void transition(FacesContext context,
Flow sourceFlow,
Flow targetFlow,
FlowCallNode outboundCallNode)
Which in my opinion is correct. By definition it is not possible to call the
same flow recursively, so the method is ok.
regards,
Leonardo Uribe