JSF2 LogicalViews - Losing the Current Page From the Map - jsf

We recently updated our Application to use JSF 2.1.3
We have an existing page that has three framesets (left nav, main, footer). Clicking on something in the main frame, causes the footer frame to be reloaded with new content. Because of this, we are losing the main frame from the logical view map after (eg. if com.sun.faces.numberOfLogicalViews is set to 10, we lose the main frame after 9 clicks). After which, trying to submit the main frame causes it to re-render, skipping the action method it's bound too.
Is there any way to keep that logical view from being removed from the map without increasing the number of logical views? I'm not sure why, but this behavior didn't occur when out app was using JSF 1.2.

We solved this problem by overriding the StateManagerImpl, and storing this particular view client side, instead of server side.
It appears in previous versions of jsf, you only needed to override the isClientSide method, but you now need to override the methods to write and restore state.

Related

JSF 2.2 Memory Consumption: Why does Mojarra keep the ViewScoped Beans of the last 25 Views in Memory?

Memory per Session grows
We are experiencing high memory consumption using JSF 2.2 (2.2.12) with Mojarra. After investigating our load tests, it turned out that the size of data in our ViewScoped Beans is quite high (sometimes more than 1MB). Anyway - when navigating from view to view, the session memory size grows and grows. We can't decrease the size of the beans on short-term, so this behavior has quite some impact.
Solution 1 - Changing Context Params (not working)
Now - we played around with the official context parameter from Mojarra which are set to 15 by default:
com.sun.faces.numberOfLogicalViews
com.sun.faces.numberOfViewsInSession
Changing those parameters to a lower value did not have any impact on the memory consumption in our load tests.
Solution 2 - Changing activeViewMapsSize (working)
We were debugging Mojarra and found the following Code in ViewScopeManager:
Integer size = (Integer) sessionMap.get(ACTIVE_VIEW_MAPS_SIZE);
if (size == null) {
size = 25;
}
The default size for keeping the last visited views seems to be 25. Seeing this, we implemented a Session Listener which sets this value to 1:
public class SetActiveViewMapsSizeSessionListener implements HttpSessionListener {
#Override
public void sessionCreated(HttpSessionEvent event) {
event.getSession().setAttribute(ViewScopeManager.ACTIVE_VIEW_MAPS_SIZE, 1);
}
}
That obviously worked. The memory stopped growing since only 1 view is kept.
So why 25 Views in Memory ?
So Mojarra keeps a history of 25 Views in Memory in case of not a different value is defined in Session. I can't find any documentation about this. Can someone explain what this is for? Is it for Browser Back? We have caching disabled on our JSF pages. So browser back will always create a new view. This shouldn't be an issue for us.
Is Solution 2 a valid approach? Could someone explain the drawbacks of this approach?
Update 1
After various comments and a deeper debugging, it turned out that:
com.sun.faces.numberOfLogicalViews defines the logicalViewMap size, which stores only(!) the state of the ui component tree
com.sun.faces.application.view.activeViewMapsSize defines the size of the activeViewMap, which holds the ViewScoped beans
When changing numberOfLogicalViews to 1, mojarra will still keep track of all view scoped beans of the last 25 views. When you configure it the other way around - numberOfLogicalViews to 15 and activeViewMapsSize to 1 - the view cannot be correctly initialized due to missing data I guess. We didn't even get an exception. I would like to understand, why mojarra chose to set the activeViewMapsSize higher than the numberOfLogicalViews and not the same since we want to tune our memory consumption without getting an unpredictable behavior.
Update 2
We created an issue at Mojarra: JAVASERVERFACES-4015.
Why does Mojarra keep the ViewScoped Beans of the last 25 Views in Memory?
Because a web page can also be opened in a new browser tab rather than the current browser tab. There is unfortunately no bulletproof way to determine based on a plain vanilla HTTP GET request whether a view is being opened in an existing browser tab or in a new browser tab. Hence all the associated beans are kept in memory regardless of whether the web page is opened in the same browser tab or not.
Anyway - when navigating from view to view, the session memory size grows and grows.
This makes indeed no sense if you navigate from view to view within the same browser tab. But this does make sense when you open the next view in a new browser tab. This way the view in previous browser tab keeps working fine when you switch back to the previous browser tab and continue interacting with the view over there.
We can't decrease the size of the beans on short-term, so this behavior has quite some impact.
It's technically possible to detect in the client side whether the current page has been unloaded or not and notify the server about this condition. These days, the pagehide event can be used to check whether the current view has been destroyed in the client side, and the navigator.sendBeacon can be used to notify the server about this condition in a reliable way (using the combination of e.g. unload and XMLHttpRequest is less reliable as there is no guarantee whether it will actually hit the server on time).
This all is implemented in the logic behind OmniFaces #ViewScoped since OmniFaces 2.2 (November 2015) and across years crystallized into its current shape since OmniFaces 2.7.3 (November 2019). If you're already using CDI to manage beans, then it should be a matter of swapping out import javax.faces.view.ViewScoped; lines in your source code by import org.omnifaces.cdi.ViewScoped; in order to utilize this. In one project I've worked with, the memory usage has decreased with 70% since the migration of native JSF view scoped beans to OmniFaces view scoped beans.
See also:
com.sun.faces.numberOfViewsInSession vs com.sun.faces.numberOfLogicalViews
How detect and remove (during a session) unused #ViewScoped beans that can't be garbage collected
JSF: Mojarra vs. OmniFaces #ViewScoped: #PreDestroy called but bean can't be garbage collected

Is JSF navigation to same page without loosing state possible?

I'm trying to implement a composite that has a few buttons in it.
This component is aimed to be used in several contexts, so the navigation cases may be passed as parameters to the component but in some cases I would like to just refresh the view (by returning null or using a void method, as mentioned in this answer) - when I use this, the view is refreshed but the component stays open (it should close on action completion) - if a navigation case is used, the page reloads (the component is effectively closed) but all selection states are lost.
Is there any way to navigate to current page without loosing state, or even just hide the composite after the actions are run?
Note: view-scope.

A few questions reagarding UI components state and phases

Having gone through these excellent posts:
Why JSF saves the state of UI components on server?
Why does JSF save component tree state?
and midway the JavaEE6 tutorial I still have the following questions:
When I am developing a custom UI component whose values (styleClass, value, etc) are either defined statically(in the xhtml) or set via a bean, do I need to explicitly save/restore state in the extended component as well?
Is it correct to say that the scope of the UI components is view scoped?
How is the view identified behaviour? (If I navigate away from a view, the view gets rebuild the next time around. But if I open another tab, it is restored - at least the bean!)
When I am executing an Ajax call, I would expect that 'execute' part of the UI component would be restored&processed and the 'rendered' part would be restored&updated. After running into some problems with UI:repeat, it is not clear to which extend the component tree is to be restored and if is possible to partially edit.
As an example (I am not sure that it works like this): I define a UI:repeat that iterates over some values and creates some Ajax commandlinks. Whenever I call the command, it will restore the whole ui:repeat regardless of the Ajax scope (execute/render) that I have defined. So it will re-render the whole ui:repeat. Furthermore, I don't understand how it could ever -not- restore the ui:repeat as due to being a namingcontainer it will edit the id of my newly added component.
How can I define a build-time component (vs render-time) and why would I want to do this? (It seems that build time components are troublesome when mixed with rendertime, so why have both)
Thanks
When I am developing a custom UI component whose values (styleClass, value, etc) are either defined statically(in the xhtml) or set via a bean, do I need to explicitly save/restore state in the extended component as well?
Yes. You normally use StateHelper for this.
See also:
How to save state when extending UIComponentBase
JSF custom component: support for arguments of custom types, the attribute setter is never invoked
Adding Custom Attributes to Primefaces Autocomplete Component in JSF
Is it correct to say that the scope of the UI components is view scoped?
Absolutely not. UI component instances are request scoped. Only anything which is stored via StateHelper is in essence view scoped (and restored into newly created component instances during "restore view" phase).
See also:
JSF composite component - weird behavior when trying to save state
Backing bean in composite component is recreated on every request
How is the view identified behaviour? (If I navigate away from a view, the view gets rebuild the next time around. But if I open another tab, it is restored - at least the bean!)
It's likely requested from browser cache. Try submitting a form therein. The chance is big that you get a ViewExpiredException. You need to tell the browser to not cache dynamic pages. Putting a breakpoint on bean's constructor would also confirm that it's never been invoked.
See also:
Avoid back button on JSF web application
Is JSF 2.0 View Scope back-button safe?
javax.faces.application.ViewExpiredException: View could not be restored
When I am executing an Ajax call, I would expect that 'execute' part of the UI component would be restored&processed and the 'rendered' part would be restored&updated.
This is not true as to restore part. The "whole" view state is restored. Note that the view state does since JSF 2.0 not necessarily represent the entire component tree. You've found the explanation/answer to that already in the two links mentioned in your question.
How can I define a build-time component (vs render-time) and why would I want to do this? (It seems that build time components are troublesome when mixed with rendertime, so why have both)
This is called a "tag handler". I.e. just extend from TagHandler instead of UIComponent and implement according its contract. Tag handlers are useful if the sole goal is to build the view (the JSF component tree). They do not appear in the JSF component tree. As to when to create a custom component or a custom tag handler, check the "components" and "taghandlers" sections of OmniFaces showcase, it may give some new insights as to real world use cases of those things.
See also:
Custom Facelet component in JSF
JSTL in JSF2 Facelets... makes sense?

ExtJS 4 - Rendering components that are initially hidden

One major difference that I have noticed between ExtJS 3.x and 4.x is how the rendering/layout calculation is handled for components that are rendered inside of a containing element that has display:none (NOT an Ext created/monitored containing element). In 3.x, upon showing the containing element, the Ext component it contained would be properly rendered and sized to whatever dimensions i set for it.
However, in 4.x, that same component will not be displayed at all and have a zero height and width when its containing element was shown. After it was visible if I do a call to .setSize() it would then properly be displayed. Problem is, in my application there is just no way to be able to go through all the events that could cause a hidden component to be shown, and add code to make sure its layout is manually forced to be recalculated.
So my question is, is there any way to get back 3.x's behavior in this situation for all components across the board in 4.x?
What you can try to do is set up listeners on your components that delegate to the underlying DOM elements, perhaps that will solve your issue.
However my suggestion is if at all possible is to use the framework to manage the entire page layout using Viewport. You can still suck in the HTML (if you must) and render it inside containers or panels for example. Perfect use case here is Header and Footer which are generated by server side code (jsp, gsp, asp..) and then displayed in the North or South regions of the Viewport using contentEl : 'myDivId' configuration.

Maintaining RichFaces panelMenu state between requests

I'm attempting to use a RichFaces panelMenu as a navigation menu but have yet to figure out a way for it to maintain state between requests. Is there anyway to tell the panelMenu to render itself with certain groups expanded without using ajax?
I've attempted setting the value on the panelMenu and using selectedChild, all to no avail.
The state of the expanded sections is controlled by the rich:panelMenuGroup children, in particular their expanded attributes; these can be bound to server-side objects. You need to ensure their server-side state is updated somehow, though, either through setting the rich:panelMenu with the attribute expandMode="server" (which will cause a server-side submit) or by making it a child of whatever form is being submitted.

Resources