I have been spending time looking partial state saving lately - mostly
trying to understand what Trinidad/ADF Faces need to do to fully support
this. I have some questions regarding the use of tree visiting to
save/restore partial state. This touches on implementation, though think
it is important to have a consistent story, so sending to jsr-314-open.
At the moment our implementations are somewhat mixed in how they handle
the partial state traversals:
- Mojarra 2.1 performs a tree visit for both the partial state save and
- Mojarra 2.0 performs a tree visit for the partial state restore but
uses a good old facets + children traversal for the save.
- MyFaces 2.0 performs a facets + children traversal for both save and
I believe the reason for the inconsistency has to do with the fact that
we did not have an explicit VisitHint.SKIP_ITERATION in 2.0. (Though, at
some point last year we agreed to temporarily go with a FacesContext
property until we could add the new VisitHint to the API.) As a result,
the implementation teams avoided tree visiting (at least partially) in
order to avoid unnecessary/incorrect UIData iteration. Now that we've
got our visit hint in 2.1, in theory this should make the correct
behavior clearer: we can now use tree visits for both the save and
restore and specify the SKIP_ITERATION visit hint to avoid iterating
over rows in iterating components.
However, after thinking this through in more detail, I am not quite sure
whether the correct behavior is as obvious as I originally thought it was.
One of the main benefits of tree visiting over a traditional facets +
children traversal is that tree visiting allows components to be visited
"in context" - ie. ancestor components can override visitTree() to set
up context on behalf of their children. The most typical case is an
ancestor setting up context that influences EL evaluation for the
We (ADF) have some cases where setting up context can be expensive and
as such we'll want to avoid this work if not strictly necessary. More
generally, we don't want to set up any tree visiting-related context
during state saving/restoring since it is simply not necessary. We have
never performed context set up/tear down during full state saving (ie.
processRestoreState()/processSaveState() don't do anything interesting).
There is no point in introducing this now for partial state saving.
Doing so would introduce unnecessary overhead, plus result in
inconsistent behavior between partial and full state saving.
I had been thinking that the way to handle this is to have some way for
our components to distinguish between visitTree() calls that occur for
partial state saving/restoring purposes (in which case we don't want to
set up context) vs. other tree visits (in which case we do want to set
up context). As of 2.1, we have all of the APIs in place to allow us to
do just this. In particular:
- We can detect the partial state saving tree visit via the
StateManager.IS_SAVING_STATE FacesContext property.
- We can detect the partial state restoring tree visit by checking the
phase id + VisitHint.EXECUTE_LIFECYCLE. (Though implementations will
need to be sure to set this hint - logged Mojarra issue 1929 for this.)
By examining these properties/hints, a visitTree() implementation can
make an informed decision about whether it is necessary to set up context.
While it is now possible for visitTree() implementations to do the right
thing, I worry that this is overly complex and possibly unnecessary. If
we want to:
- Visit all (non-transient) components, but…
- Skip iteration (for UIData-like cases). And…
- Avoid setting up context.
Is tree visiting even the right tool for the job?
Should we perhaps avoid this complexity altogether by sticking with a
facets + children traversal (eg. as currently implemented by MyFaces)
for partial state saving/restoring?
Or are there cases where component implementations do in fact need
control over the way that this traversal is performed, in which case an
internal iteration solution (like tree visiting) is necessary?
I recently triaged a bug that complained that the "type" attribute of
<cc:attribute> was not behaving correctly. The user expected the
following attribute specification:
<cc:attribute name="foo" type="java.lang.Integer"/>
To result in a warning/error when used as follows:
This seems like a reasonable expectation. An equivalent test case
using, for example, JSP tag files does result in an error.
This raises a few questions:
1. Should JSF implementations be making use of composite component
metadata (like "type") at runtime to enforce the intentions of the
composite component author?
2. Should this behavior be specified?
3. Are there other places where we have similar metadata (eg. Facelets
taglib.xml files) that we should review as well?