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 18:33:53 +0000

On the other hand, this could also be considered a bug in the JSF
implementation itself, that it doesn't destroy the server side view state
during PreDestroyViewMapEvent.

And while thinking, another possibility to skip the implicit buildView()
step during restoreView() is to have a context.setBuildingView(false) like
as there is already a context.setProcessingEvents(true).

Cheers, B

On Thu, Jul 21, 2016, 20:10 Bauke Scholtz <balusc_at_gmail.com> wrote:

> 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
>>>>
>>>
>>>
>>
>