jsr344-experts@javaserverfaces-spec-public.java.net

[jsr344-experts] Re: Attention Pivotal/SpringSource folks: Faces Flows and SWF

From: Leonardo Uribe <lu4242_at_gmail.com>
Date: Mon, 6 Jan 2014 17:28:18 -0500

Hi

In few words, after reading the answer, I can see there is a difference
between what the API was designed for and what should the API be capable of.
Let's see if we can clarify the different points 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.

The fact is this: global (explicit or implicit) navigation still works while
you are inside a flow, which means even if we model a flow as something
analogous to a method, the user can take different routes that will lead
to the mentioned situation. In other words, enter into a flow does not
disable other forms of navigation in an application.

I think it is up to the user to allow or deny jump outside a flow and then
go back. The fact that a flow can be modeled with defined entry and exit
points, and that some flows does not allow to jump outside, does not means
that all flows should be bound by that rule.

The spec "as is" does not inhibit global navigation after enter into a flow.
So, in MyFaces we have taken the broader interpretation, which is "everything
must work as expected". If it doesn't, it is a bug that should be fixed.

Suppose this interaction. Flow A call flow B and flow A pass a view name to
flow B to fill some detail in the flow. In some point of the time, the view
that has been passed as parameter is displayed, and that view has a link
to exit flow A. The user click on that button, ending both flow B and A. Is
it something probable or useful? Yes, of course it is something technically
feasible and it can be useful. It is more easier to reproduce the problem
if the user is in flow A and call flow B and then click on the back button.

In conclusion we have found two important points that needs to be handled
in a flow:

- It could be good to inhibit a part of the navigation after enter into
a flow.
- Some flows should not support back button, or if back button is pressed
it should throw ViewExpiredException or something like that.

The point is with things "as is", everything should work without problem.
Take a look at the definition of "flow" from spring webflow reference:

"... A flow encapsulates a reusable sequence of steps that can execute in
different contexts. ..."

Is a login/logout use case a flow? Yes, it is. But in some point after
he/she has login, the control moves to somewhere else. Where? it doesn't
matter but the important thing is the "context" is still active and valid
until he/she logout or the session is invalidated or whatever.

In conclusion is, it is not hard to find reusable sequence of steps that
requires some flexibility at some point.

LU> Support these two concepts are very important because users want
LU> to model everything as a flow, and this is esential to allow better flow
LU> reusal. In MyFaces I did this:

LU> - Take into account an ordered list of the active flows instead the
LU> current flow only in the navigation step.
LU> - If the flow is called using its flow name, it is considered and
LU> independent flow.
LU> - If the flow is called using a call flow node, it is considered a
subflow, so
LU> if the parent flow ends, the children ends too.

LU> I don't know if you have seen these examples done by a colleague
LU> at Irian, Michael Kurz:

LU> https://github.com/jsflive/jsf22-examples

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.

I think do that goes against simplicity. Suppose this: Flow A has a bean
called FlowABean. Flow B has a bean called FlowBBean. Flow A call Flow B, so
if the user wants to get some information from A, he/she writes something
like this:

@Named
FlowScoped("B")
public class FlowBBean
{

    @Inject
    private FlowABean flowABean;

    /* .... */
}

Shoud that be valid? Yes, why not? If Flow A has an explicit call node that
calls Flow B, it means Flow B is a subflow of Flow A, so the context from
Flow A should be accesible from all beans in Flow B. Is it useful? Yes, of
course. Too useful to ignore it. There are situations where pass parameters
can be important or necessary, but there are others where flow reusal is
not that important and a simple injection will do it just fine. The important
thing here is use Faces Flow to define boundaries and contexts around
those boundaries. Note I'm not using Faces Flow here to pack all views 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.

The idea is:

1. Enter into Flow A
2. Navigate to another page using normal/implicit navigation.
3. Go back to the point the user enter into Flow A
4. Click again on the button or link.

It can be done with the current spec, and if it can be done it should work.

LU> The other problem is about how FlowHandler.clientWindowTransition(...).
LU> The way how flow navigation using GET is different between MyFaces
LU> and Mojarra, even if both are compatible with the spec. In MyFaces, we
LU> are using a NavigationCase wrapper to override fromOutcome and
LU> toFlowDocumentId:

LU> * Wrapper that helps overriding toFlowDocumentId and fromOutcome, to build
LU> * correctly a navigation case that cause a flow action (enter into a flow
LU> * or return from a flow).
LU> *
LU> * The idea is if is necessary to enter into a flow set fromOutcome as the
LU> * flow id and toFlowDocumentId as the flow document id. If it is a return,
LU> * set fromOutcome as the return node and toFlowDocumentId as
LU> FlowHandler.NULL_FLOW

LU> Later, this will be useful to keep track of the navigation and start/end the
LU> flows properly in FlowHandler.clientWindowTransition(...) This is how I
LU> understood this part:

LU> /**
LU> * There are two basic cases: Enter into a flow and return from a flow.
LU> *
LU> * - FlowHandler.TO_FLOW_DOCUMENT_ID_REQUEST_PARAM_NAME : value of
LU> the toFlowDocumentId property
LU> * of the navigation case when enter into a flow OR
LU> FlowHandler.NULL_FLOW when return from a flow.
LU> *
LU> * - FlowHandler.FLOW_ID_REQUEST_PARAM_NAME : value of the
LU> fromOutcome property of the navigation case.
LU> * According to the intention it has multiple options:
LU> *
LU> * 1. It can be a flowId, which means enter into a flow.
LU> * 2. It can be a flow call id, which means enter into a flow.
LU> * 3. It can be a flow return id, which means return from a flow.

LU> * - The javadoc of NavigationCase.getToFlowDocumentId() says this:
LU> * "... If this navigation case represents a flow invocation, this
LU> property is the documentId in
LU> * which the flow whose id is given by the return from
LU> getFromOutcome() is defined. Implementations
LU> * must override this method to return the value defined in the
LU> corresponding application
LU> * configuration resources element. The base implementation
LU> returns the empty string. ..."
LU> *
LU> * This is consistent with the previous interpretation, but we
LU> need to include the case where
LU> * toFlowDocumentId is FlowHandler.NULL_FLOW too, which is derived
LU> implicitly. The key of the trick
LU> * is override fromOutcome / toFlowDocumentId in the navigation
LU> algorithm to indicate when the
LU> * navigation case is entering into a flow or return from a flow.
LU> In that way, it is possible
LU> * to use ConfigurableNavigationHandler.getNavigationCase(...) to
LU> know the "route" using the
LU> * initial fromOutcome given in FLOW_ID_REQUEST_PARAM_NAME.
LU> *
LU> **/

LU> In my opinion, the specification done for this feature is fine, but in
LU> MyFaces land
LU> we have had a hard time doing a proper implementation. I agree there are
LU> things to improve, like everything, but there is nothing we cannot do in JSF
LU> 2.3
LU> or later.

EB> I think you're right that we have some more work to do regarding this
EB> method and the return feature.

I would like to align the spec so it can support some complex cases like
return from 2 flows using GET or enter into multiple flows at once.

LU> Some comments over the suggestions done by Hantsy:

HB> Spring Web Flow provides extended persistence support in the whole flow,
HB> so you never worry about the LazyInitializationException thrown
LU> by Hibernate.

LU> That's CDI responsibility. Nothing to do from JSF side.

HB> Spring Web Flow supports events on enter and exit point in every node.

LU> Use f:viewAction

HB> Spring Web Flow supports Ajax, thus the page fragment can participate
HB> into the flow.

LU> Something we need to think. An use case or example is welcomed.

EB> I think this may come into play regarding pop-up windows that function
EB> as modal dialogs.

I have been thinking here that a flow scope can have a different
context (session, current window, current view). For example, the information
related to a login flow or a shopping cart must be in a "session" or be in
a "persistent" scope, but it also can be placed associated to the current
window, so if another window is open in the same browser, the information
should not be shared. Until this moment we have been thinking on a flow as
something bound to the current window context, but that's not necessary
true in some cases, and it can be good to allow more flexibility in this
part.

HB> Spring Web Flow supports subflows in the main flow, and allow you share
HB> the data state between flows directly, the
LU> inbound-parameter/outbound-parameter in the
HB> Faces Flow is very stupid and ugly.

EB> A formal notion of subflow is probably worth investigating.

In my opinion, the current spec already supports that, as it has been
demonstrated before, but we have not taken into consideration the scopes
where a flow is valid.

LU> Take a look at MyFaces, A @FlowScoped bean from a parent flow can be shared
LU> by the child flow.

EB> That runs counter to the "flows are like methods" idea. If flows are
EB> like methods, then @FlowScoped beans are like local variables. In that
EB> case, it's not possible to access them unless the you are on the same
EB> level of the call stack as where they are defined. In any case, we do
EB> need to specify how visibility is supposed to work.

I think it can be more accurate to say that "each flow has its own context
that can or cannot be bound or related to a parent flow". At start I tried
to model the flow interaction with a stack but later it was found another
structure is required.

I think in some cases get the current flowScopeMap is not enought. It would
be great to have something like:

FlowHandler.getCurrentFlowScope(String definingDocumentId, String id)

And have something in EL to grab the right flow scope map.

regards,

Leonardo Uribe


2013/12/27 Edward Burns <edward.burns_at_oracle.com>:
>>>>>> On Fri, 20 Dec 2013 16:36:39 -0500, Rossen Stoyanchev <rstoyanchev_at_gopivotal.com> said:
>
> H> Personally, the new Faces Flow is neither good nor bad, but I am
> H> bitterly disappointed with it. JSF EG declares it is inspired by ADF
> H> Task Flow and Spring Web Flow, but it seems they just copied ADF Task
> H> Flow and made it standardized.
>
> EB> Now, if Faces Flows does not represent enough of Spring Web Flow, that
> EB> is only a function of the participation of the EG members representing
> EB> Spring Web Flow.
>
> RS> Faces Flows does not need to represent, or look like Spring Web Flow. The
> RS> user is simply comparing ideas from his own experience with different
> RS> projects and that is important to do. The list of items at the end of the
> RS> blog post are features that Faces Flow can target in subsequent releases.
> RS> This is part of natural evolution.
>
> Yes, that's certainly true.
>
> RS> For me however there are more fundamental questions that may have
> RS> played a role in the final conclusion even if not verbalized
> RS> clearly.
>
> RS> Specifically the stated design goal of encapsulating the details of a flow
> RS> in my mind is not met. It's easier to explain by talking about Web Flow. I
> RS> recommend running a sample in addition to this very basic description. I'm
> RS> happy to provide further explanations.
>
> RS> In Web Flow there is a single flow definition artifact that encapsulates
> RS> all flow navigation rules.
>
> Does that artifact encapsulate all the flows for the entire app?
>
> RS> Those rules are enforced and cannot be bypassed. From a client
> RS> perspective that means a flow is represented by a single URL
> RS> throughout all steps of the flow (and any sub-flows). It also means
> RS> a user cannot just type a URL and jump into the middle of the
> RS> flow. It would break the defined navigation rules. This may seem
> RS> radical at first but consider that you'd never want to jump into the
> RS> middle of a shopping cart checkout process (a typical use case for
> RS> using flows). This is quite different from free browsing where the
> RS> user should be able to jump to anywhere they want to.
>
> RS> I found major differences in the way Faces Flow worked and raised them
> RS> before (see links below).
>
> RS> https://java.net/projects/javaserverfaces-spec-public/lists/jsr344-experts/archive/2012-03/message/29
>
> RS> Perhaps there are specific cases that motivate including flow
> RS> information in Facelet pages but I can't see what they are. It's
> RS> worth mentioning them explicitly since putting flow information
> RS> (navigation?) in Facelet pages seems contrary to the goal of Task
> RS> Flow encapsulation.
>
> Yes, we abandoned this approach. Now it is not possible to put flow
> information in Facelet pages.
>
> RS> https://java.net/projects/javaserverfaces-spec-public/lists/jsr344-experts/archive/2012-11/message/81
>
> 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.
>
> One could argue that such an exception is the right response. The
> context is indeed not active. However, if you are not using any flow
> scoped data, such an exception would not be thrown, and it would give
> the impression that the navigation is supported.
>
> I think we can add some language to the spec to detect these "jump in"
> cases.
>
> RS> When flows are defined under the web application's root, I can
> RS> easily access their source from a browser which is a security
> RS> concern. I can see there is an example that puts the flow definition
> RS> under WEB-INF but the pages are still under the web application root
> RS> and hence the flow is in two different places. It would be useful to
> RS> be able to keep a flow with all its files in a folder under WEB-INF.
>
> I've captured these here.
>
> https://java.net/jira/browse/JAVASERVERFACES_SPEC_PUBLIC-1246
>
> RS> It does make it difficult to reason about access to flow data in the
> RS> context of an active flow, without an active flow, or of some other
> RS> flow.
>
> RS> A second question is about what defines the navigation rules of a
> RS> flow? In a number of examples, flow definitions don't contain
> RS> navigation rules. Instead command buttons have actions that point to
> RS> other pages. This is a convenient convention but the end result is
> RS> that the flow definition does not ncapsulate all navigation rules,
> RS> nor can it enforce them.
>
> This problem is not specific to flows. Rather, it is a consequence of
> implicit navigation, added in JSF 2.0. The design intent of implicit
> navigation is to take away ammunition from railsfans that complain that
> explicit navigation is too verbose. No-one has complained about the
> flexibility of implicit navigation outside of the context of flows.
> Perhaps it would be sufficient to add a configuration element to the
> flow definition that disables implicit navigation just for that flow?
>
> https://java.net/jira/browse/JAVASERVERFACES_SPEC_PUBLIC-1247
>
> RS> For example a shopping cart followed by
> RS> shipping options and then payment options must be navigated in that
> RS> order. Note that this point relates closely to the previous point
> RS> above where a page associated with a flow can be reached directly
> RS> without formally entering the flow, i.e. nothing enforces what's
> RS> possible in terms of navigation.
>
> We can tighten things up under the work for 1247.
>
>>>>>> On Thu, 19 Dec 2013 07:09:52 -0800, Leonardo Uribe <lu4242_at_gmail.com> said:
>
> LU> Implementing Faces Flow feature for MyFaces, I have found some
> LU> observations that I would like to discuss. In fact, I have a list of
> LU> things I have seen in JSF 2.2 that we can improve, and that I hope
> LU> to discuss in this mailing list in the future.
>
> I hope so too.
>
> 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.
>
> But from the perspective that flows should be analogous to method
> invocations this does not make sense.
>
> public void foo() { bar(); }
>
> public void bar() { println("bar"); }
>
> It's not possible for foo() to return without bar() returning first.
>
> LU> Support these two concepts are very important because users want
> LU> to model everything as a flow, and this is esential to allow better flow
> LU> reusal. In MyFaces I did this:
>
> LU> - Take into account an ordered list of the active flows instead the
> LU> current flow only in the navigation step.
> LU> - If the flow is called using its flow name, it is considered and
> LU> independent flow.
> LU> - If the flow is called using a call flow node, it is considered a subflow, so
> LU> if the parent flow ends, the children ends too.
>
> LU> I don't know if you have seen these examples done by a colleague
> LU> at Irian, Michael Kurz:
>
> LU> https://github.com/jsflive/jsf22-examples
>
> 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.
>
> I'm not sure if that's a bug or not, but I want to make it so when
> you're in the inner flow, you can not access data from the calling
> flow unless it is specifically passed in via parameters.
>
> 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.
>
> If you want to enter a flow that is already active, from where are you
> entering the flow? If you are in another flow, then you must return
> from that flow to get back to the previous flow. If you are not in a
> flow, then the previous flow can't be active. I don't understand this one.
>
> LU> The other problem is about how FlowHandler.clientWindowTransition(...).
> LU> The way how flow navigation using GET is different between MyFaces
> LU> and Mojarra, even if both are compatible with the spec. In MyFaces, we
> LU> are using a NavigationCase wrapper to override fromOutcome and
> LU> toFlowDocumentId:
>
> LU> * Wrapper that helps overriding toFlowDocumentId and fromOutcome, to build
> LU> * correctly a navigation case that cause a flow action (enter into a flow
> LU> * or return from a flow).
> LU> *
> LU> * The idea is if is necessary to enter into a flow set fromOutcome as the
> LU> * flow id and toFlowDocumentId as the flow document id. If it is a return,
> LU> * set fromOutcome as the return node and toFlowDocumentId as
> LU> FlowHandler.NULL_FLOW
>
> LU> Later, this will be useful to keep track of the navigation and start/end the
> LU> flows properly in FlowHandler.clientWindowTransition(...) This is how I
> LU> understood this part:
>
> LU> /**
> LU> * There are two basic cases: Enter into a flow and return from a flow.
> LU> *
> LU> * - FlowHandler.TO_FLOW_DOCUMENT_ID_REQUEST_PARAM_NAME : value of
> LU> the toFlowDocumentId property
> LU> * of the navigation case when enter into a flow OR
> LU> FlowHandler.NULL_FLOW when return from a flow.
> LU> *
> LU> * - FlowHandler.FLOW_ID_REQUEST_PARAM_NAME : value of the
> LU> fromOutcome property of the navigation case.
> LU> * According to the intention it has multiple options:
> LU> *
> LU> * 1. It can be a flowId, which means enter into a flow.
> LU> * 2. It can be a flow call id, which means enter into a flow.
> LU> * 3. It can be a flow return id, which means return from a flow.
>
> LU> * - The javadoc of NavigationCase.getToFlowDocumentId() says this:
> LU> * "... If this navigation case represents a flow invocation, this
> LU> property is the documentId in
> LU> * which the flow whose id is given by the return from
> LU> getFromOutcome() is defined. Implementations
> LU> * must override this method to return the value defined in the
> LU> corresponding application
> LU> * configuration resources element. The base implementation
> LU> returns the empty string. ..."
> LU> *
> LU> * This is consistent with the previous interpretation, but we
> LU> need to include the case where
> LU> * toFlowDocumentId is FlowHandler.NULL_FLOW too, which is derived
> LU> implicitly. The key of the trick
> LU> * is override fromOutcome / toFlowDocumentId in the navigation
> LU> algorithm to indicate when the
> LU> * navigation case is entering into a flow or return from a flow.
> LU> In that way, it is possible
> LU> * to use ConfigurableNavigationHandler.getNavigationCase(...) to
> LU> know the "route" using the
> LU> * initial fromOutcome given in FLOW_ID_REQUEST_PARAM_NAME.
> LU> *
> LU> **/
>
> LU> In my opinion, the specification done for this feature is fine, but in
> LU> MyFaces land
> LU> we have had a hard time doing a proper implementation. I agree there are
> LU> things to improve, like everything, but there is nothing we cannot do in JSF
> LU> 2.3
> LU> or later.
>
> I think you're right that we have some more work to do regarding this
> method and the return feature.
>
> LU> Some comments over the suggestions done by Hantsy:
>
> HB> Spring Web Flow provides extended persistence support in the whole flow,
> HB> so you never worry about the LazyInitializationException thrown
> LU> by Hibernate.
>
> LU> That's CDI responsibility. Nothing to do from JSF side.
>
> HB> Spring Web Flow supports events on enter and exit point in every node.
>
> LU> Use f:viewAction
>
> HB> Spring Web Flow supports Ajax, thus the page fragment can participate
> HB> into the flow.
>
> LU> Something we need to think. An use case or example is welcomed.
>
> I think this may come into play regarding pop-up windows that function
> as modal dialogs.
>
> HB> Spring Web Flow supports subflows in the main flow, and allow you share
> HB> the data state between flows directly, the
> LU> inbound-parameter/outbound-parameter in the
> HB> Faces Flow is very stupid and ugly.
>
> A formal notion of subflow is probably worth investigating.
>
> LU> Take a look at MyFaces, A @FlowScoped bean from a parent flow can be shared
> LU> by the child flow.
>
> That runs counter to the "flows are like methods" idea. If flows are
> like methods, then @FlowScoped beans are like local variables. In that
> case, it's not possible to access them unless the you are on the same
> level of the call stack as where they are defined. In any case, we do
> need to specify how visibility is supposed to work.
>
> Ed