The idea of the view pool comes from ideas of the community about how to improve JSF performance and the speculation behind how the state affects performance in web frameworks. The primary perception was that a stateful web framework by definition does not have a good performance and a stateless web framework can be faster, because a stateful web framework needs to deal with the “state” problem and that logic can expensive according to how it is implemented.
But that way to see the problem is completely wrong, because in practice there are other things that can affect performance more significantly and with a good design it is possible to improve things to a very good level. Proof of that is with the introduction of JSF 2.0 Partial State Saving (PSS), the perception changed, because it was demonstrated that it is possible to reduce the state size used by the views of an application.
In JSF 2.2 another step forward has been done with the introduction of “stateless JSF”. The idea is just mark the views in the application that does not require state to work properly, and then skip the state saving and restoring algorithm to improve performance. It works for Mojarra but it doesn't have any effect with MyFaces because its PSS algorithm has been carefully designed and improved over a long time, and it is very efficient at the time of restore and save the state.
If the state is not the problem, or in other words, if the state calculation does not have a significant impact over performance, which one is the real problem? is there any problem? The evidence gathered suggest there is not any problem, and the proof is that an application using MyFaces is very near in terms of performance than the same application written with hardcoded Servlets and JSP.
Why the solution using Servlets and JSP is the fastest one? There are two fundamental reasons:
Please remember JSP pages are compiled into a servlet. The servlet creates a JSP tag tree and then this tree is processed. The result is most of the objects used by JSP are static constants. Technically nothing will be as fast as Servlet/JSP. But the objective is not be the fastest framework. Instead, the objective is be as fast as possible without sacrifice the abstraction layer that provides JSF.
From other point of view, some tests done long time ago about JSF lifecycle and its performance in MyFaces suggest the following conclusions:
According to the previous analysis, it is possible to improve the way how views are built to get better response times. There are some options:
The first option is not a good solution, because anyway there is some information that is inside UIComponent and should be stored per thread, and precisely UIComponent is a place specifically designed to store that information. In fact, the first option is not really an option and it was discarded after some attempts.
Build a view can be described as a repetitive calculation. Many components are created and assembled in a predictable way with attributes that most of them are defined to their associated templates or facelets.
In JSF 2.0, PSS algorithm takes advantage of that predictable behavior and make a distinction between the “initial state” and the “delta state”. In this way, a view is a composition of the initial state and its delta state. But in the same way, if it is substracted the delta from the current state, the result is the initial state. So, if we can somehow “substract” or “reset” the delta from the current state, we could derive a view into its initial state in the same way as a view is created from scratch.
In other words, we are taking advantage of the current JSF spec and we are taking it to the next level. PSS algorithm can be used to check if a view has been modified or not, checking its state. That information can help us to decide if a component is reusable (no state) or not (with state). If the component has state, the idea is reset the state and get it back to the initial state defined by the first call of markInitialState().
This can be done quite easily adding a context attribute oam.view.resetSaveStateMode
and checking the value of that attribute and take proper actions into saveState() method. here are two modes: soft reset [1] and hard reset [2]. A soft reset clears the “transient” state or those internal attributes used by the component but it does not clear the “delta” state. A hard reset destroy both the “transient” state and the “delta” state. If the component cannot be reset, saveState() should return a non null value.
If a component in a view cannot be reset fully, it is possible to just remove the component from the tree and use facelets refreshing algorithm to rebuild the missing part and reuse a view partially.
But according to the spec, create a full view involves the following steps:
vdl.createMetadataView(...)
and call internally ViewHandler.createView(...)
or just call ViewHandler.createView(...)
vdl.buildView(...)
In the case of UIViewRoot, we can just take a “snapshot” of the component and exclude it from the reuse step. So the view pool does not reuse the root node but all the children, excluding the content of f:metadata facet.
There exists 4 fundamental attributes that define a view in JSF:
This means if a view has the same values for the previous attributes, we can consider the structure is the same and the view can be reused partially or fully. The pool uses these attributes as a key to group similar views, so there are different sets of views according to these values.
But a view can have a different structure too if it is used one of the following facelets tags.
According to its dynamic behavior, views can be classified into two groups: “static structure views” and “dynamic structure views”. This distinction is important because dynamic structure views requires a special treatement.
There are two basic operation that any pool must do: create objects and store them in the pool and take the objects from the pool. In the JSF lifecycle, there are the following points where the pool must take some action:
The pool has the following web config parameters:
org.apache.myfaces.VIEW_POOL_MAX_POOL_SIZE
: Indicates the number of views stored in the pool per key.org.apache.myfaces.VIEW_POOL_MAX_DYNAMIC_PARTIAL_LIMIT
: Indicates the number of dynamic views that can be reused as partial views.org.apache.myfaces.VIEW_POOL_ENTRY_MODE
: Indicates how the view is hold by a “soft” or a “weak” reference into the pool. It affects how GC collects the views.org.apache.myfaces.VIEW_POOL_DEFERRED_NAVIGATION
: reuse views that are navigated using default algorithm (increase reusal but it uses a hack that does not follow jsf spec) ).There are two ways to enable a view to be used by the view pool:
Add an entry into a faces-config.xml file like this (with this mode you can override the global web config parameters):
<faces-config-extension> <view-pool-mapping> <url-pattern>/*</url-pattern> <parameter> <name>org.apache.myfaces.VIEW_POOL_MAX_POOL_SIZE</name> <value>5</value> </parameter> </view-pool-mapping> </faces-config-extension>
Or encapsulate your top level page with:
<f:view oamEnableViewPool=true>
:warning: If the view is marked as “transient” or stateless and poolable at the same time the view pool will disabled, because the view pool reuse the state saving algorithm.
The disadvantages of the view pooling technique are:
The following improvements has been confirmed after some performance tests done with the know demo application used in the performance comparison:
Note these measures depends on the application studied but it can give an idea about what to expect.
The recommendation is first evaluate your application requirements and use this technique only in cases where performance is a critical concern. Most of the time an application using MyFaces without the view pool is good enough. It is a good idea to select the views that receives the higher load and use the pool in only those cases, but that decision depends on the available memory and the load you application must face. If the view is too dynamic, disable the pool could give better results.