Hi
2010/3/29 Martin Marinschek <mmarinschek(a)apache.org>
Hi guys,
I´ll work with Leonardo´s proposal:
1. Do not call UIComponent.markInitialState() on
ComponentTagHandlerDelegate, as ComponentHandler javadoc says, instead
call it after PostAddToViewEvent are published on vdl.buildView(). We
need to call it from root to nodes, so the parent component is marked
first. I know the place where this call comes is from trinidad tag
handler, but this call needs to be fixed in a more predictable way.
I think I believe I understand why you need this, but can you explain
it further?
In few words, facelets algorithm create components and attach them from
leaves
to root. That means, when markInitialState() is called, all children was
already
marked. The last one that needs to be marked is root, but in this case it is
responsibility of vdl.buildView() to mark it. If new components are attached
during
the process (for example, the panel storing components inside
composite:implementation) we should mark the too. In theory, all components
on the tree should be marked in this step (in the patch I'm excluding
transient ones
but thinking more about it we should not).
If we have some code on UIData.markInitialState() we have to call
clearInitialState
on all children, save the component initial state and call again
markInitialState. The
idea behind do not call UIComponent.markInitialState() on
ComponentTagHandlerDelegate,
is in avoid do that in reverse order and just do it in one step.
2. Use an attribute on facesContext to identify when the VDL is
marking the initial state (in myfaces there is already an attribute
called "org.apache.myfaces.MARK_INITIAL_STATE"). This is necessary to
indicate UIData instances that it is time to save the full state of
all component children,
+1 on this! we should always have an API to know what JSF is doing
where in its lifecycle
3. Allow UIData to hold a map where the key are client ids and the
value are the deltas of all components per row. This map should be
saved and restored.
Isn't that an implementation detail? If we talk about implementation
details, I think we should have the full state of all sub-components
once, and the deltas per row (just like the full-state now).
Yes, that is an implementation detail, and the code proposed do what you
said, we have full state of one row, saved when UIData.markInitialState() is
called:
private Object _initialDescendantFullComponentState = null;
and a map of delta per row:
private Map<String, Object> _rowDeltaStates = new HashMap<String, Object>();
4. Change UIData.setRowIndex() to restore and save the component
state.
Isn´t UIData doing this as of today?
Yes. The current implementation just save and restore components that
implements
EditableValueHolder interface, but only per request, so those values are not
saved
on the state. The code proposed additionally save and restore the delta, and
the
delta per row is saved on UIData state.
By the way - I don´t like Alexander´s idea of having a
restoreRowState/saveRowState (this additional API would only be useful
inside repeater-components). Instead, I would propagate a
getTransientAttributesMap() API, and the repeating-component would
just take care of saving/restoring this as well. I believe this API
could be used for a lot of other stuff. It would essentially a
component-request scope, which we don´t have yet, and for which we had
quite a few usages in cs-JSF already.
We could also embed this state in the stateMap with a transient=true
attribute, that´s the other option - then it won´t be a public API
however.
The important here is provide methods to save and restore that map/variable
set.
As long as we have some way to do that, we could call it from
UIData.setRowIndex. I like the idea to extend StateHelper with accesors
and save/restore methods for transient components.
best regards,
Martin
-
On Sun, Mar 21, 2010 at 7:21 PM, Leonardo Uribe <lu4242(a)gmail.com> wrote:
> Hi
>
> It is probably that myfaces implementation of UIData do something
different
> that RI. Myfaces uses a map to hold dataModel instances to handle better
> the use case when nested datatables occur.
>
> The problems described on the last email are different to the one I'm
trying
> to
> solve. My only intention is make note that it is possible to solve the
> problem
> described on the original proposal, only do very small changes on the
spec
> (in other words the scope of the proposal is not that huge, the dataModel
> handling is different from the component state handling, but both
problems
> are related).
>
> Really I would like to see a way to call markInitialState like the one
> described.
> The current way to call it is a chaos. I had to put this call on
different
> tag handlers
> and the order how this method is called is the inverse one than someone
> could
> expect. Without this change, if someone wants to do something similar
needs
> to
> call clearInitialState, save and then call again to markInitialState on
the
> code
> that saves the state.
>
> The other change described, a param to indicate when the current view is
> using
> partial state saving and all calls to markInitialState should occur, to
> diferentiate
> between the call when the view is built and the call when a row is set is
> document
> an implementation detail that both myfaces and mojarra contains (I
suppose,
> otherwise how ComponentHandlerDelegate implementation could know when
> do not call this method if the current view is not using PSS ). Note the
> documentation
> of ComponentHandler just says this method is called but does not says
> anything
> about the conditions, so the implementation should decide that but there
is
> no
> formal way to comunicate this state, but if you look closely,
> ComponentHandler
> "delegate" all that steps described on the javadoc, so in reality this
class
> does
> not do anything described there and that can be considered an error in
the
> javadoc.
> All that documentation should be on
> TagHandlerDelegateFactory.createComponentHandlerDelegate.
>
> regards,
>
> Leonardo Uribe
>
> 2010/3/19 Alexander Smirnov <asmirnov(a)exadel.com>
>>
>> The problem is far worse then you think :-( UIData itself has transient
>> properties, the most important of them is dataModel. Standalone UIData
>> caches model between phases, but resets it if you have enclosed
>> iteration components that gives huge impact on performance if data model
>> does not cache results itself. In Mojarra, UIRepeat knows about UIData
>> but latest has no clue about repeater, so if you put data table inside
>> the <ui:repeat> , results will be unpredictable. Is MyFaces has a better
>> implementation ? I haven't seen deep into its UIData code for a while...
>> Also, it is permanent pain in the complex components that require to
>> store some information during request or work like iterator ( that are
>> really all trees, menus, tab panels, Tomahawk columns, and more ).
>> Numeric row ids in the data model is additional limitation for models
>> that connected to the database in concurrent environment there
>> additional code requires to keep references between row number and
>> primary key; otherwise application have a chance to update/delete wrong
>> record.
>> For these reasons, I think that all UIRepeat/UIData should be rewritten
>> from scratch as it already done in the Trinidad/ADF. I'll take a closer
>> look to your patch.
>>
>> On 03/19/2010 12:07 PM, Leonardo Uribe wrote:
>> > Hi
>> >
>> > Yes, I know this too. In few words, all "transient" properties,
that
>> > means,
>> > all properties that are not stored in the state are not saved. There
are
>> > very
>> > few examples. To solve this one, there are multiple alternatives. I
was
>> > thinking
>> > on extend StateHelper interface to add some methods like
>> > putTransientProperty and getTransientProperty. Trinidad stores all
>> > properties
>> > on the FacesBean object.
>> >
>> > Anyway, I think an "mixed" solution could solve this problem:
create
an
>> > interface called RowStateTransientHolder, so only the components that
>> > implements this type of attributes could have the chance to
>> > save and restore its state.
>> >
>> > But note in any case, it is necessary to do something like this to
solve
>> > the
>> > use case described.
>> >
>> > I didn't knew that localValueSet was a transient property too.
>> >
>> > regards,
>> >
>> > Leonardo Uribe
>> >
>> > 2010/3/19 Alexander Smirnov <asmirnov(a)exadel.com
>> > <mailto:asmirnov@exadel.com>>
>> >
>> > I've thought about these problems for a long time too. Your
proposal
>> > has
>> > a disadvantage because per-row state is quite different from the
>> > component state. Take a look for the UIInput implementation:
>> > localValueSet field is never saved in the state while it should be
>> > vary
>> > from row to row. The same thing happens to the UIForm.submitted
>> > attribute, that has been eventually added to the UIData spec
instead
>> > of
>> > common solution, that would save/restore all local variables. My
>> > proposal was a separate RowStateHolder interface with
>> > save/restoreRowState methods there component developer can
preserve
>> > internal variables. In the your stenario, there should be some
flag
>> > that
>> > tells component to save all values, even these that are not
supposed
>> > to
>> > be preserved between requests.
>> >
>> > On 03/19/2010 10:27 AM, Leonardo Uribe wrote:
>> > > In short, this topic is an attempt to add full state to
components
>> > > inside UIData. I'll do a brief resume, so people can
understand
>> > this one
>> > > easily.
>> > >
>> > > UIData uses the same component instances to render multiple
rows.
>> > > Suppose this example:
>> > >
>> > > <h:dataTable id="cities" var="city"
value="#{country.cities}">
>> > > <h:column>
>> > > <h:outputText value="#{city}" />
>> > > </h:column>
>> > > </h:dataTable>
>> > >
>> > > In the component tree it is created this hierarchy:
>> > >
>> > > HtmlDatatable
>> > > UIColumn
>> > > HtmlOutputText
>> > >
>> > > If we have 10 cities, the same component is used over and over
to
>> > render
>> > > all 10 cities. The reason to do that in this way is keep state
as
>> > small
>> > > as possible.
>> > >
>> > > Now let's suppose something like this:
>> > >
>> > > <h:dataTable id="cities" var="city"
value="#{country.cities}">
>> > > <h:column>
>> > > <h:inputText value="#{city}" />
>> > > </h:column>
>> > > </h:dataTable>
>> > >
>> > > It was changed the output component for an input one. If this
>> > table is
>> > > in a form and the values are submitted, the same component is
used
>> > to
>> > > apply request values, validate and apply them to the model
(update
>> > > process). To make this possible, UIData class has some code to
>> > preserve
>> > > this values between phases (using EditableValueHolder
interface),
>> > so
>> > > when each phase is processed, all rows are traversed and you get
>> > the
>> > > expected behavior.
>> > >
>> > > Now suppose something more complex: We have a code that use
>> > > invokeOnComponent to change the style of my inputText. In
theory,
>> > only
>> > > one row should change. But in fact, all rows are rendered with
the
>> > same
>> > > color. Why? because we use the same component to render all
rows,
>> > and we
>> > > don't preserve the children component state between rows.
>> > >
>> > > There is a lot of issues, questions, and side effects related to
>> > this
>> > > issue, but just to put some of them here:
>> > >
>> > > TOMAHAWK-1062 InputTextArea doesn't work properly inside
facet
>> > DetailStamp
>> > > TOMAHAWK-96 Data table Scroller not working the dataTable which
>> > was
>> > > actually contained in other DataTable
>> > >
>> >
>> >
https://javaserverfaces-spec-public.dev.java.net/issues/show_bug.cgi?id=153
>> > > Problems with UIData state saving
>> > >
>> > > Also, it is well know that one reason why people uses c:forEach
in
>> > > facelets, is because this one create "full" components
per each
>> > row. It
>> > > is very easy to find articles on internet.
>> > >
>> > > Now, with jsf 2.0 we have partial state saving, so we have a
>> > chance to
>> > > fix this one once and for all. I tried fix this one per months
>> > (maybe
>> > > years!), but talking with Martin Marinschek on JSFDays, some
ideas
>> > came
>> > > out and finally it was found a possibility to fix this one using
>> > the
>> > > existing api and with little changes on the spec.
>> > >
>> > > The proposal is this:
>> > >
>> > > 1. Do not call UIComponent.markInitialState() on
>> > > ComponentTagHandlerDelegate, as ComponentHandler javadoc says,
>> > instead
>> > > call it after PostAddToViewEvent are published on
vdl.buildView().
>> > We
>> > > need to call it from root to nodes, so the parent component is
>> > marked
>> > > first. I know the place where this call comes is from trinidad
tag
>> > > handler, but this call needs to be fixed in a more predictable
>> > way.
>> > > 2. Use an attribute on facesContext to identify when the VDL is
>> > marking
>> > > the initial state (in myfaces there is already an attribute
called
>> > > "org.apache.myfaces.MARK_INITIAL_STATE"). This is
necessary to
>> > indicate
>> > > UIData instances that it is time to save the full state of all
>> > component
>> > > children,
>> > > 3. Allow UIData to hold a map where the key are client ids and
the
>> > value
>> > > are the deltas of all components per row. This map should be
saved
>> > and
>> > > restored.
>> > > 4. Change UIData.setRowIndex() to restore and save the component
>> > state.
>> > >
>> > > I'll attach a patch on myfaces issue tracker with the
algorithm
>> > proposed
>> > > (because it is based on myfaces codebase). It was tested and it
>> > works.
>> > > But note it is necessary to fix the javadoc for
>> > > UIData.markInitialState(), ComponentHandler and maybe
>> > vdl.buildView(),
>> > > so the intention is propose this change for jsf 2.0 rev A. Note
>> > this
>> > > works only with PSS enabled because without it we don't have
a
>> > place to
>> > > notify UIData instances that it is necessary to get the full
>> > state.
>> > > Also, note this patch preserve backward compatibility, because
the
>> > old
>> > > way to store/save is applied after the full state is restored.
>> > >
>> > > Really, I have the strong temptation to apply some similar code
on
>> > > myfaces UIRepeat component (because this class is private), but
I
>> > prefer
>> > > first ask to EG to know what you guys think about it.
>> > >
>> > > Suggestions are welcome,
>> > >
>> > > regards,
>> > >
>> > > Leonardo Uribe
>> > >
>> >
>> >
>
>
--
http://www.irian.at
Your JSF powerhouse -
JSF Consulting, Development and
Courses in English and German
Professional Support for Apache MyFaces