jsfjsf-2viewstatemojarraviewexpiredexception

com.sun.faces.numberOfViewsInSession vs com.sun.faces.numberOfLogicalViews


Mojarra Implementation of JSF 2 has the following context params:

What is the difference between them? The documentation doesn't speak much about these. My app was having trouble with ViewExpiredException for some pages, but after we bumped these settings to a (much) higher value, we stopped having problems.

My app is a financial, form-heavy, ajax-enabled app (some screens have 50+ inputs, with the option of adding alot more data/inputs via AJAX).

what can be the cause for this behaviour? I understand that the first param defines the number of "pages" that are kept in session, which may be useful for the back button, but my use cases that trigger the ViewExpiredException don't use the back button. What does the second param refer to? If I stay in the same screen but keep adding alot of data via AJAX, does this cause the need of a larger number of logical views for page?


Solution

  • First of all, the Mojarra implementation unintentionally swapped the meaning of those context parameters. So if you have the impression that the description is exactly the other way round than what the literal context parameter name implies, then this is indeed true.


    com.sun.faces.numberOfLogicalViews

    This is basically GET request based. Every GET request creates a new view in session.

    To experiment with it, set it to a value of 3, start a new browser session and open 4 different browser tabs (regardless of the URL; may be same, may be different) in sequence and then go back to the 1st tab and submit the form in there. You will get a ViewExpiredException, because this view has been pushed out from the LRU (Least Recently Used) map for views in session. This will not happen if you opened max 3 tabs.

    With the default value of 15, this is a rare real world problem. If your webapp is really designed to be used this way (e.g. a social/community site which invites to being opened in multiple tabs, such as discussion forum or Q&A), then you might consider using client side state saving instead of increasing the default value. With client side state saving, you will never face this exception. An alternative would be to use OmniFaces <o:enableRestorableView> in combination a request scoped bean and request parameters, or a view scoped bean which checks in (post)construct if its own state needs to be restored. Again another alternative is to go stateless with <f:view transient="true">, this way the views are not saved anymore, but you cannot use view scoped beans anymore.

    The MyFaces equivalent is org.apache.myfaces.NUMBER_OF_VIEWS_IN_SESSION which defaults to 20.


    com.sun.faces.numberOfViewsInSession

    This is basically synchronous (non-ajax!) POST request based. Every synchronous POST request creates a new logical view. They are all stored on basis of a physical view like so Map<PhysicalView, Map<LogicalView, ViewState>>. So, with max 15 physical views and max 15 logical views, you can theoretically have 15*15 = 225 views in session.

    To experiment with it, set it to a value of 3, open a view with a synchronous form, submit it 4 times and then press the browser's back button 4 times and then submit the form again. You will get a ViewExpiredException, because this view has been pushed out from the LRU (Least Recently Used) map for logical views. This will not happen if you go back max 3 times and then resubmit it.

    Note that ajax submits reuse the same logical view (you can confirm it by seeing exactly the same javax.faces.ViewState value being returned on ajax postbacks). There's no browser's back button support for it anyway. The browser's back button only brings you back to the previous synchronous request, it would therefore not make any sense to store all those ajax postbacks as logical views in session.

    With the default value of 15 and the current trend of ajax-only forms and disabled cache on dynamic pages, this is a very rare real world problem. Properly designed forms shouldn't invite to pressing the browser's back button. Instead, they should on successful submit redirect to the target view and on failure just redisplay the same form with validation errors. See for hints also How to navigate in JSF? How to make URL reflect current page (and not previous one). Also, cache is more than often disabled on dynamic pages, so the back button would basically give you a brand new view back. See also Avoid back button on JSF web application. If this is also for your application the case, then you can safely set the value to 1.

    MyFaces originally had no equivalent for this, and counted this as a physical view in session as well. In version 2.0.6, org.apache.myfaces.NUMBER_OF_SEQUENTIAL_VIEWS_IN_SESSION was introduced, with a similar purpose, though with a different implementation and by default disabled.


    See also: