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

[jsr344-experts] Re: [1055-StatelessJSF] PROPOSAL

From: Leonardo Uribe <lu4242_at_gmail.com>
Date: Mon, 25 Feb 2013 23:56:15 -0500

Hi

At first view it sounds like a good idea, but looking in deep there are some
details that makes me doubt about the convenience of use "transient"
attribute, and instead use an specific attribute for this one.

In few words, the problem is @ViewScope uses an structure stored in the
view attribute map to hold the state, so inside UIViewRoot we have two
separate states:

- The state related with UIViewRoot component itself.
- The state related with @ViewScope.

In this moment we have 3 methods:

public void restoreState(FacesContext context, Object state)
public Object saveState(FacesContext context)
/**
 * @since 2.2
 **/
public void restoreViewScopeState(FacesContext context, Object state)

If "transient" attribute is used, saveState() will be skipped, and the
view scope will not work.

Why use something like "transient" attribute? the justification is that
with an attribute it is possible to just skip save/restore logic and make
the request a little bit faster. I have measured the time spent and in
my opinion the gain is small compared with the time spent rendering
the view or inclusive building the view.

Probably the right thing to do is provide a new method to save the
view scope state, so "transient" flag only takes effect over UIViewRoot
component state.

I still have doubts about if it is convenient to use "transient" flag
in practice, because the evidence suggest that the calculation of
the view state is quite cheap, and the side effects that can be
introduced by the user without warning when using components that relies
on the state can be negative. Why? because it suppose the user should
know which components can be used in "stateless" mode and which others
should not be used, and that goes against encapsulation principles
(you shouldn't worry about how a component implements its logic).

Anyway, it seems to be there is an "audience" that strongly wants something
like this just to say "... JSF has a stateless mode and in that way it has
nothing to be afraid about other stateless web frameworks from performance
perspective ...". But in reality, "... JSF takes care about stateful/stateless
logic for you, so in that sense it is a far more advanced technology and it
performs pretty well from performance perspective even against stateless
frameworks ... ".

In my opinion, if the people really wants something like this we should do
it, but from performance perspective it is more interesting to change the
spec just a little bit to allow "Poolable Views".

The concept has been already studied and there is a prototype for MyFaces
here:

http://markmail.org/message/54rb3aphc6txwzbr

"... In few words, the proposal is go "Beyond JSF Stateless Mode", and instead
blame the state, make it your friend. Let's just take advantage of the
stateful nature of JSF to allow reuse views fully or partially. ..."

The interesting part of this study is to make it feasible, the only change
we really need into JSF spec is introduce a way to reset the state, doing
some changes over UIComponent.saveState() method.

In theory there are two kinds of reset:

 - RESET_MODE_SOFT : Clear only all transient state (datatable model,
 transient attributes, view listeners, ...)
 - RESET_MODE_HARD : Clear transient state and restore the component to
 the state it was before the call to markInitialState().

The idea is add some methods in UIViewRoot:

int getResetSaveStateMode()
void setResetSaveStateMode(int resetMode)

public boolean isPoolable();
public void setPoolable(boolean poolable);

And in UIComponent.saveState() implementations check this flag and do what's
necessary:

public Object saveState(FacesContext context)
{
    //1. Check the reset flag
    ...
    //2. If there is a soft reset clear transient data, calculate the delta
    // state as usual and return it.
    ...
    //3. If there is a hard reset, clear transient data and the state, and
    // calculate the delta state if applies. If the hard reset cannot be
    // done, just do the usual, calculate the delta and return it.
    ...
    //4. If no reset flag do the usual.
    ...
}

Maybe add some methods in StateHelper to deal with this logic:

public Object resetHardState(FacesContext context)
public Object resetSoftState(FacesContext context)

And that's it. With these changes, it is possible to create a reusable view
pool for JSF. The only limitation is that on navigation, the views cannot be
put on the pool because a broadcast is in progress so the reset does not work
correctly, but we can go further in this part doing some changes over how
navigation works and reuse a view across navigations if we just make a
"deferred navigation", which means do not do the navigation directly on the
default ActionListener, just add a flag, let the broadcast ends and before
the phase ends do the navigation.

The limitations of this technique is that PostAddToViewEvent is not executed
at each request, so some tricks that worked before when creating dynamic views
will not work, but in most cases it will work.

The hack is promising, but there was a discussion about this topic in MyFaces
dev list:

http://markmail.org/message/pc42cbcvvhlboivb

and the problem of include the code is mostly related to the spec. If this is
not in the JSF spec, third party libraries will not implements what's
necessary to make it work, so the scope of the hack will be limited only to
MyFaces libraries or views using only components included in JSF by default.

In conclusion:

- +1 for include "transient" as an attribute of f:view, but add
saveViewScopeState and keep view scope working even for transient views.

- ++1 for allow poolable views in JSF, doing some hack like the one proposed
here or something better.

regards,

Leonardo Uribe

2013/2/25 Neil Griffin <neil.griffin_at_portletfaces.org>:
> +1 for JSF 2.2 because it using it in it's current form with JSF 2.1 creates
> warnings in IDEs, and I wouldn't want to see these warnings persist until we
> get JSF 2.3 out the door.
>
> Details: The feature relies on getTransient/setTransient from
> UIComponentBase [1] but there is no "transient" attribute defined in the
> Facelet taglib.xml file, and no such documented property in the VDLDocs for
> f:view [2] -- that's what causes IDEs to report warnings in XHTML files.
>
> By adding this for JSF 2.2, we can change the VDLDocs (technically part of
> the Spec right?) so that they reflect the transient attribute.
>
> [1]
> http://javaserverfaces.java.net/nonav/docs/2.1/javadocs/javax/faces/component/UIComponentBase.html
> [2]
> http://javaserverfaces.java.net/nonav/docs/2.1/vdldocs/facelets/f/view.html
>
> On Feb 25, 2013, at 3:37 PM, Edward Burns <edward.burns_at_oracle.com> wrote:
>
> Hello Volunteers,
>
> You may have seen Manfred Riem's blog entry about stateless JSF. [1] I
> wonder what you think about adding this for 2.2? Here are the spec
> changes we would need for this minimal, yet effective approach.
>
> * Expose existing transient attribute on UIComponent on VDLDoc for
> <f:view>.
>
> The text of the attribute will be based on UIComponent.isTransient():
>
> If true, the component (and therefore the children of the component)
> must not participate in state saving or restoring.
>
> * In section 7.7.2.8 ViewDeclarationLanguage.restoreView(), change the
> text to be the following.
>
> The JSP implementation must:
>
> [include the existing text of the section.]
>
> The Facelet implementation must:
>
> Call ResponseStateManager.isStateless(). If true, take the
> following action (I will put this in English rather than code).
>
> ViewDeclarationLanguage vdl =
> vdlFactory.getViewDeclarationLanguage(viewId);
> viewRoot = vdl.createView(context, viewId);
> @@ -543,9 +547,9 @@
> ViewDeclarationLanguage vdl =
> vdlFactory.getViewDeclarationLanguage(viewId);
> viewRoot = vdl.getViewMetadata(context,
> viewId).createMetadataView(context);
> context.setViewRoot(viewRoot);
>
> and return, otherwise [...include existing text of the section].
>
> * In ResponseStateManager.writeState(), if the UIViewRoot is transient,
> take impl specific action to make it so the call to
> ResponseStateManager.isStateless() during the the next call, from
> ViewDeclarationLanguage.restoreView(), returns true.
>
> * Spec for new method ResponseStateManager.isStateless(). If the
> preceding writeState() was stateless, return true. If the preceding
> writeState() was statefull return false, otherwise throw
> IllegalStateException.
>
> Thanks,
>
> Ed
>
>
> [1]
> http://weblogs.java.net/blog/mriem/archive/2013/02/08/jsf-going-stateless
>
> --
>
>