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

[jsr372-experts] Re: [jsr372-experts mirror] JSF API to extract component state from view state, and to physically remove view state from server side state

From: Bauke Scholtz <balusc_at_gmail.com>
Date: Thu, 21 Jul 2016 20:10:40 +0200

Hi,

A real world use case where explicitly destroying server side view state
makes sense is below:

- Set number of physical views to 3 (in Mojarra, use
com.sun.faces.numberOfLogicalViews context param and in MyFaces use
org.apache.myfaces.NUMBER_OF_VIEWS_IN_SESSION context param).
- Open a JSF page in a tab and keep it open all time.
- Open the same page in another tab and then close this tab.
- Open the same page in another tab and then close this tab.
- Open the same page in another tab and then close this tab.
- Submit a JSF form in the first tab.

Without explicitly destroying the server side view state, it would fail
with ViewExpiredException. If the page references a OmniFaces @ViewScoped,
then it will work.

Cheers, B

On Thu, Jul 21, 2016 at 8:05 PM, Bauke Scholtz <balusc_at_gmail.com> wrote:

> Hi,
>
> 1. The technical reason that I'd like to restore UIViewRoot's state is
> being able to properly invoke all listeners associated with
> the PreDestroyViewMapEvent. To the point, only restoring the component's
> own state (preferably only those listeners) is sufficient for the task of
> OmniFaces @ViewScoped, not restoring the whole tree.
>
> 2. The PreDestroyViewMapEvent already deals with destroying view map and
> associated view scoped beans. Only the server side view state remains in
> memory. Client side view state is not a problem. The main goal is
> immediately destroying view scope map and beans. As to inactive views,
> that's exactly why OmniFaces @ViewScoped uses the unload trick for this and
> this indeed sends a command through synchronous ajax. The
> ViewHandler#restoreView() could be used instead, but this implicitly also
> invokes buildView() which is unnecessarily expensive for the purpose of
> only firing the PreDestroyViewMapEvent. The alternative would be extracting
> buildView() step from the restoreView() method into a separate method.
>
> Cheers, B
>
>
> On Thu, Jul 21, 2016 at 7:19 PM, Leonardo Uribe <leonardo.uribe_at_irian.at>
> wrote:
>
>> Hi
>>
>> About 1.
>>
>> UIViewRoot is an special component that requires some steps to be
>> restored, but I'm not sure if that's required outside the state saving
>> algorithm.
>>
>> I think the reason you need to restore the UIViewRoot is to have a
>> reference to a view, so you can destroy it later, right? If that so, the
>> problem is another different one.
>>
>> In MyFaces restore the UIViewRoot is a complex task, because UIViewRoot
>> not only holds state related to the component itself, but also information
>> about the tree structure (FaceletState and component id counters) and
>> viewScope bindings. It started with restoreState(...), but now there are a
>> lot of lines of code to restore it properly.
>>
>> About 2.
>>
>> In MyFaces there a CDI interface to deal with views called
>> ViewScopeProvider. There is a method called onSessionDestroyed() and
>> another one called destroyViewScopeMap(facesContext,viewScopeId) which
>> deals with the problem.
>>
>> On server side state saving, the algorithm applies some logic to remove
>> the stored view states that are not being used, and use the SPI api to
>> remove the associated viewScope if necessary.
>>
>> But please note the big problem is not the current view, but the inactive
>> views that needs to be removed and their association with CDI (because
>> there is no UIViewRoot instance to grab them). ViewScopeProvider SPI
>> interface helps to fill the gap between the view state and the CDI.
>>
>> This is considered an implementation detail, so we don't really need
>> anything from spec perspective. But there is nothing when client side state
>> saving is used.
>>
>> Remember there was a change in the spec where ViewScope is now stored in
>> the server only, so when client side state saving is used, there is no way
>> to clean the scope, because we don't have the API to do it. So, a possible
>> use case I can imagine is, if we have something to detect when a window or
>> tab has been closed on the client, we could send a message to the server
>> and with the api we could clear the scope.
>>
>> In conclusion an API to remove the view state or the associated viewScope
>> state has sense to me, but I would like to see the possible scenarios where
>> this API could be useful. I have only identified one use case, but it looks
>> that it requires something to send a command through ajax.
>>
>> regards,
>>
>> Leonardo Uribe
>>
>> 2016-07-21 8:29 GMT-05:00 Bauke Scholtz <balusc_at_gmail.com>:
>>
>>> Hi,
>>>
>>> During working on "immediately destroy bean instance and server side
>>> view state on page unload" feature of OmniFaces @ViewScoped, I stumbled
>>> upon two shortcomings in public JSF API.
>>>
>>>
>>> 1. There is no clean way to restore only the state of a specific
>>> component. The ResponseStateManager#getState() returns the entire view
>>> state while I'd like to restore only the component state of UIViewRoot
>>> instance itself. See also
>>> https://github.com/omnifaces/omnifaces/blob/da3847ff41f93e2a4f847eb2176c2343a32b968b/src/main/java/org/omnifaces/viewhandler/OmniViewHandler.java#L141
>>> In other words, there is no JSF API provided way to obtain exactly the part
>>> of the view state as expected by 2nd argument of
>>> UIComponent#restoreState().
>>>
>>> I'm only not exactly sure if adding a public API on that would be a
>>> great idea. Perhaps we should leave it, but just throwing here for thoughts.
>>>
>>>
>>> 2. There is no JSF API provided way to physically remove the entire view
>>> state from the server side state. See also:
>>> https://github.com/omnifaces/omnifaces/blob/296c4d03f47a8d69360f14ef74c5d6e3fe243ff6/src/main/java/org/omnifaces/util/Hacks.java#L362
>>>
>>>
>>> I'd expect to see a ResponseStateManager#destroyViewState() or
>>> #removeViewState() taking FacesContext argument which does exactly the task
>>> of destroying/removing the server side view state associated with the
>>> currently set view root. This could also be called by the implementation
>>> directly after PreDestroyViewMapEvent, just for more optimal memory usage,
>>> and also a more robust LRU sequence of view states in Mojarra.
>>>
>>>
>>> What do you guys think?
>>>
>>> Cheers, B
>>>
>>
>>
>