Hi
LU> In few words, after reading the answer, I can see there is a
LU> difference between what the API was designed for and what should the
LU> API be capable of. Let's see if we can clarify the different points
LU> of view.
LU> The first problem we have and the reason why @FlowScoped does not
LU> look like a full conversation scope (Hantsy's item 4) is there is no
LU> clarification about the relationship between flows.
LU> In few words, there are two cases:
LU> - Two or more independent flows are running at the same time.
LU> - Flow A call flow B, but when the flow A ends, since A was called
LU> from B, B should end too.
EB> But from the perspective that flows should be analogous to method
EB> invocations this does not make sense.
EB> public void foo() { bar(); }
EB> public void bar() { println("bar"); }
EB> It's not possible for foo() to return without bar() returning first.
LU> The fact is this: global (explicit or implicit) navigation still
LU> works while you are inside a flow, which means even if we model a
LU> flow as something analogous to a method, the user can take different
LU> routes that will lead to the mentioned situation. In other words,
LU> enter into a flow does not disable other forms of navigation in an
LU> application.
EB> The spec has the following intents.
EB> 1. The only way to get into a flow is through its start node. Any
EB> attempt to enter a flow will cause its start node to be first traversed.
EB> 2. Once you are in a flow, there are exactly three ways to leave it.
EB> a. traverse one of the flow's return nodes.
EB> b. traverse one of the flows flow-call nodes.
EB> c. abandon the flow. This has the side effect of also abandoning all
EB> flows higher up on the call stack.
I think the problem is there is no definition about the boundaries of the
flow. A flow defines entry and exit points, but there is no way to know if
a view belongs to a flow or not, and in that way there is no way to know
if the navigation to a view is going outside the flow, or to know if a
navigation to a view inside a flow is valid or not.
The intention of the spec is still valid. It is also clear that some flows
should restrict the valid navigation cases once inside, but the point is
there are other flows where that is not necessary true, and you really want
the opposite. In other words, there are flow definitions that only want
to define entry and exit points, so the context is activated and deactivated
properly, but once inside do things like navigate to views not directly
associated with the flow but still preserve the context.
LU> I think it is up to the user to allow or deny jump outside a flow and then
LU> go back. The fact that a flow can be modeled with defined entry and exit
LU> points, and that some flows does not allow to jump outside, does not means
LU> that all flows should be bound by that rule.
EB> While the 2.2 spec does not prohibit this sort of thing, Rossen observed
EB> the following:
RS> One observation is with regards to access to flow scoped data. When
RS> using the provided buttons, everything works as it should, i.e. flow
RS> scoped data is created at the point of entering the flow and
RS> destroyed when the flow is exited. However, it is easy to bypass the
RS> entry and exit points. For example I can go directly to a page
RS> associated with a flow without entering the flow, and if the page
RS> tries to access flow data, the result would be
RS> ContextNotActiveException.
EB> I replied:
EB> One could argue that such an exception is the right response. The
EB> context is indeed not active. However, if you are not using any
EB> flow scoped data, such an exception would not be thrown, and it
EB> would give the impression that the navigation is supported.
EB> I think we can add some language to the spec to detect these "jump
EB> in" cases.
EB> I continue to believe we should prohibit the jump in and jump out cases.
In my opinion, this is something that should be defined as a config parameter
of the flow. In other words, it should be a parameter or something that says:
"... once the user enter into this flow, it is not valid to visit views
outside the flow or all views belonging to this flow cannot be accessed
directly from outside ...". Maybe something to define that the flow navigation
is "strict" or "lax" (by default lax, because the current spec does not define
how to identify if a view belongs to a flow or not). If a flow is strict it
should be some way to define when a view belongs to a flow, and by default
identify that all views starting with the flow name belongs to that view
(/<flow-name>/*). Something like flow-view-mapping?.
LU> In conclusion we have found two important points that needs to be handled
LU> in a flow:
LU> - It could be good to inhibit a part of the navigation after enter into
LU> a flow.
EB> Yes, I agree.
LU> - Some flows should not support back button, or if back button is pressed
LU> it should throw ViewExpiredException or something like that.
EB> How about generalizing this? We could imagine some kind of
EB> configuration syntax that allows us to say, throw an exception when page
EB> X is reached via the back button.
EB> [...]
I think we can do it restricting the views that are valid after enter into
a flow. But it could be great to have an "abort" finalizer method or something
that allow us to recognize when the current flow is being terminated without
use a return node, and the finalization is caused by a navigation outside the
flow or by the finalization of a parent flow.
LU> Take a look at the example named jsf22-flows. It is quite easy to modify
LU> it and do some tests. Try enter into a flow, then to another without exit
LU> from the first flow and if you use Mojarra you'll see how some links
LU> are disabled.
EB> I'm not sure if that's a bug or not, but I want to make it so when
EB> you're in the inner flow, you can not access data from the calling
EB> flow unless it is specifically passed in via parameters.
LU> I think do that goes against simplicity. Suppose this: Flow A has a
LU> bean called FlowABean. Flow B has a bean called FlowBBean. Flow A
LU> call Flow B, so if the user wants to get some information from A,
LU> he/she writes something like this:
LU> @Named
LU> FlowScoped("B")
LU> public class FlowBBean
LU> {
LU> @Inject
LU> private FlowABean flowABean;
LU> /* .... */
LU> }
LU> Shoud that be valid? Yes, why not? If Flow A has an explicit call
LU> node that calls Flow B, it means Flow B is a subflow of Flow A, so
LU> the context from Flow A should be accesible from all beans in Flow
LU> B. Is it useful? Yes, of course. Too useful to ignore it. There are
LU> situations where pass parameters can be important or necessary, but
LU> there are others where flow reusal is not that important and a
LU> simple injection will do it just fine. The important thing here is
LU> use Faces Flow to define boundaries and contexts around those
LU> boundaries. Note I'm not using Faces Flow here to pack all views
LU> related to a flow in a single location.
LU> Other typical use case is if the user wants to create a link to enter into
LU> a flow, but the flow is already active. In theory, the flow must be
LU> restarted, but sometimes you want to encode the link to just move
LU> to the start page.
EB> If you want to enter a flow that is already active, from where are you
EB> entering the flow? If you are in another flow, then you must return
EB> from that flow to get back to the previous flow. If you are not in a
EB> flow, then the previous flow can't be active. I don't understand this one.
LU> The idea is:
LU> 1. Enter into Flow A
LU> 2. Navigate to another page using normal/implicit navigation.
LU> 3. Go back to the point the user enter into Flow A
EB> I assume by "the point the user enters into Flow A" you mean, "the page
EB> that has the button that, when clicked, causes entry to Flow A". Let's
EB> call that page, the "doormat page" because you have to stand on the door
EB> mat in front of the door if you want to enter it. If I'm understanding
EB> you correctly, the current spec intent is to treat the act of navigating
EB> from inside Flow A to the doormat page as abandoning the flow. If
EB> navigating from within a flow to the doormat page does not cause
EB> abandoning the flow, it is a bug.
The problem is according to the spec the viewId is not checked according to
the context, and in that sense a navigation from a page in the flow to the
doormat page cannot be detected. How can you detect that if there is no way
to recognize if a view belongs to a flow or not?. To fix this, it should be
added a method in FlowHandler to deal with that logic and the navigation
algorithm and section 2.2.1 (restore view) should be updated to take it
into account.
What I did in this part for MyFaces was to detect on navigation handling
if the flow is active and if that so, trigger an exit from the flow and
then re-enter to the same flow, so the user will not notice anything, only
that things works as it should instead of a dirty exception.
LU> 4. Click again on the button or link.
LU> It can be done with the current spec, and if it can be done it
LU> should work.
EB> If I'm understanding you correctly, this is a bug.
Yes, it is a bug. If the flow is in "strict" mode, it should never
happen because the exit condition is activated before display the doormat
page. If the flow is in "lax" mode, it should reenter into the flow. But
in some cases you don't want to destroy the context, so it should be
possible to enter into the starting page of the flow without destroy the
context. With the current spec I have to add a method like this:
public String goToCustomerFlow()
{
FacesContext facesContext = FacesContext.getCurrentInstance();
if (facesContext.getApplication().getFlowHandler().isActive(
facesContext, "", "customer"))
{
// go to customer flow start page without create it.
return "/customer/customer";
}
else
{
//Invoke through call node
return "callCustomer";
}
}
It can be done with EL, but a shorter syntax is welcomed.
regards,
Leonardo
2014/1/7 Edward Burns <edward.burns_at_oracle.com>:
>>>>>> On Mon, 6 Jan 2014 16:06:45 -0500, Leonardo Uribe <lu4242_at_gmail.com> said:
>
> LU> In few words, after reading the answer, I can see there is a
> LU> difference between what the API was designed for and what should the
> LU> API be capable of. Let's see if we can clarify the different points
> LU> of view.
>
> LU> The first problem we have and the reason why @FlowScoped does not
> LU> look like a full conversation scope (Hantsy's item 4) is there is no
> LU> clarification about the relationship between flows.
>
> LU> In few words, there are two cases:
>
> LU> - Two or more independent flows are running at the same time.
>
> LU> - Flow A call flow B, but when the flow A ends, since A was called
> LU> from B, B should end too.
>
> EB> But from the perspective that flows should be analogous to method
> EB> invocations this does not make sense.
>
> EB> public void foo() { bar(); }
>
> EB> public void bar() { println("bar"); }
>
> EB> It's not possible for foo() to return without bar() returning first.
>
> LU> The fact is this: global (explicit or implicit) navigation still
> LU> works while you are inside a flow, which means even if we model a
> LU> flow as something analogous to a method, the user can take different
> LU> routes that will lead to the mentioned situation. In other words,
> LU> enter into a flow does not disable other forms of navigation in an
> LU> application.
>
> The spec has the following intents.
>
> 1. The only way to get into a flow is through its start node. Any
> attempt to enter a flow will cause its start node to be first traversed.
>
> 2. Once you are in a flow, there are exactly three ways to leave it.
>
> a. traverse one of the flow's return nodes.
>
> b. traverse one of the flows flow-call nodes.
>
> c. abandon the flow. This has the side effect of also abandoning all
> flows higher up on the call stack.
>
> LU> I think it is up to the user to allow or deny jump outside a flow and then
> LU> go back. The fact that a flow can be modeled with defined entry and exit
> LU> points, and that some flows does not allow to jump outside, does not means
> LU> that all flows should be bound by that rule.
>
> While the 2.2 spec does not prohibit this sort of thing, Rossen observed
> the following:
>
> RS> One observation is with regards to access to flow scoped data. When
> RS> using the provided buttons, everything works as it should, i.e. flow
> RS> scoped data is created at the point of entering the flow and
> RS> destroyed when the flow is exited. However, it is easy to bypass the
> RS> entry and exit points. For example I can go directly to a page
> RS> associated with a flow without entering the flow, and if the page
> RS> tries to access flow data, the result would be
> RS> ContextNotActiveException.
>
> I replied:
>
> EB> One could argue that such an exception is the right response. The
> EB> context is indeed not active. However, if you are not using any
> EB> flow scoped data, such an exception would not be thrown, and it
> EB> would give the impression that the navigation is supported.
>
> EB> I think we can add some language to the spec to detect these "jump
> EB> in" cases.
>
> I continue to believe we should prohibit the jump in and jump out cases.
>
> LU> In conclusion we have found two important points that needs to be handled
> LU> in a flow:
>
> LU> - It could be good to inhibit a part of the navigation after enter into
> LU> a flow.
>
> Yes, I agree.
>
> LU> - Some flows should not support back button, or if back button is pressed
> LU> it should throw ViewExpiredException or something like that.
>
> How about generalizing this? We could imagine some kind of
> configuration syntax that allows us to say, throw an exception when page
> X is reached via the back button.
>
> [...]
>
> LU> Take a look at the example named jsf22-flows. It is quite easy to modify
> LU> it and do some tests. Try enter into a flow, then to another without exit
> LU> from the first flow and if you use Mojarra you'll see how some links
> LU> are disabled.
>
> EB> I'm not sure if that's a bug or not, but I want to make it so when
> EB> you're in the inner flow, you can not access data from the calling
> EB> flow unless it is specifically passed in via parameters.
>
> LU> I think do that goes against simplicity. Suppose this: Flow A has a
> LU> bean called FlowABean. Flow B has a bean called FlowBBean. Flow A
> LU> call Flow B, so if the user wants to get some information from A,
> LU> he/she writes something like this:
>
> LU> @Named
> LU> FlowScoped("B")
> LU> public class FlowBBean
> LU> {
>
> LU> @Inject
> LU> private FlowABean flowABean;
>
> LU> /* .... */
> LU> }
>
> LU> Shoud that be valid? Yes, why not? If Flow A has an explicit call
> LU> node that calls Flow B, it means Flow B is a subflow of Flow A, so
> LU> the context from Flow A should be accesible from all beans in Flow
> LU> B. Is it useful? Yes, of course. Too useful to ignore it. There are
> LU> situations where pass parameters can be important or necessary, but
> LU> there are others where flow reusal is not that important and a
> LU> simple injection will do it just fine. The important thing here is
> LU> use Faces Flow to define boundaries and contexts around those
> LU> boundaries. Note I'm not using Faces Flow here to pack all views
> LU> related to a flow in a single location.
>
> LU> Other typical use case is if the user wants to create a link to enter into
> LU> a flow, but the flow is already active. In theory, the flow must be
> LU> restarted, but sometimes you want to encode the link to just move
> LU> to the start page.
>
> EB> If you want to enter a flow that is already active, from where are you
> EB> entering the flow? If you are in another flow, then you must return
> EB> from that flow to get back to the previous flow. If you are not in a
> EB> flow, then the previous flow can't be active. I don't understand this one.
>
> LU> The idea is:
>
> LU> 1. Enter into Flow A
> LU> 2. Navigate to another page using normal/implicit navigation.
> LU> 3. Go back to the point the user enter into Flow A
>
> I assume by "the point the user enters into Flow A" you mean, "the page
> that has the button that, when clicked, causes entry to Flow A". Let's
> call that page, the "doormat page" because you have to stand on the door
> mat in front of the door if you want to enter it. If I'm understanding
> you correctly, the current spec intent is to treat the act of navigating
> from inside Flow A to the doormat page as abandoning the flow. If
> navigating from within a flow to the doormat page does not cause
> abandoning the flow, it is a bug.
>
> LU> 4. Click again on the button or link.
>
> LU> It can be done with the current spec, and if it can be done it
> LU> should work.
>
> If I'm understanding you correctly, this is a bug.
>
> Ed