Commonly,
the JSF applications' performance is directly related to CPU memory, serialization/deserialization
tasks, and network bandwidth. When these variables start to become the source
of headache, or errors of type ViewExpiredException or NotSerializableException
occur, it is time to find out about JSF's managing view state feature and how
it can be finely tuned to increase the performance. Therefore, further, we will
discuss about JSF saving the view state - JSF's partial saving view state
feature and JSF saving the view state on server/client.
JSF saving the view state
First, you have
to know that JSF saves and restores the view state between requests using the ViewHandler/StateManager
API. JSF does this during its lifecycle, the view state is saved in the session
(or on the client machine) at the end of a request and is restored at the
beginning of a request.
JSF uses
this technique because it needs to preserve the views state over the HTTP protocol,
which is a stateless protocol. Since JSF is stateful, it needs to save the
state of views in order to perform the JSF lifecycle over multiple requests
from the same user. Each page has a view state that acts as a ping-pong ball
between the client and the server. A view is basically a component tree that
may be dynamically changed (altered) during HTTP GET and POST requests. Each
request will successfully go through the JSF lifecycle only if the component
tree was previously saved and is fully capable to provide the needed
information, that is, Faces Servlet succeeds to call the needed view handler
implementations to restore or build the view. So, when the component tree is
programmatically changed (for example, from backing beans or static components)
it can't be successfully recreated from scratch (or rebuilt). The only solution
is to use the existing state saved at the Render Response phase. Trying to
recreate it from scratch will make the programmatic changes useless, since they
would no longer be available.
Note Keep
in mind that the component tree is just a hand of UI components hierarchically
and logically related. The view state maintains the tree structure and the
components state (selected/deselected, enabled/disabled, and so on). Therefore,
the component tree contains only references to backing beans properties/actions
through EL expressions, and does not store the model values.
JSF partial saving view state
Starting
with JSF 2.0, the performance of managing the state was seriously increased by
adding the partial state saving feature. Basically, JSF will not save the
entire component tree, only a piece of it. Obviously this will require less
memory. In other words, this means that instead of saving the entire component
tree (the whole view, <html>), now, for every request during restore
view, JSF will recreate the entire component tree from scratch and initialize
the components from their tag attributes. In this way, JSF will save only the
things that are deserved to be saved. These are the things that are susceptible
to changes (for example, <h:form>) that cannot be recreated from
scratch and/or represent inland details of components. These details are:
dynamic (programmatic) changes that alter the component tree, different kinds of
values that were determined for some components (usually at first postback),
and values that were changed for components but have not been submitted (for
example,
moving a
slider or checking a checkbox). On the other hand, the things that cannot be
changed by the client will not be saved.
Partial state saving and tree visiting
In JSF 2.0,
the JSF partial state saving feature raised a question similar to how a JSF implementation
should visit all the components in the component tree and ask them for their
state (partial)? The answer in JSF 2.1 (and earlier versions) was specific to
this implementation: Mojarra used a tree visiting algorithm, while MyFaces used
a so-called "facets + children" traversal. But, technically speaking,
these two approaches are pretty different, because Mojarra provides a pluggable
algorithm, while MyFaces doesn't. Moreover, the Mojarra approach is in context
(before children are visited, the parent component can choose to use a
context/scope), while the MyFaces approach follows a pointer design.
Furthermore, the Mojarra algorithm can visit virtual components. (These kinds
of components are obtained by looping components such as UIData)
On the other hand, from the saving state perspective, using a
context/scope and looping virtual components is not desirable, even if affecting
the visiting process can be major and useful. In order to solve this problem,
JSF 2.1 offers some hints, which can be considered deprecated starting with JSF
2.2. Starting with JSF 2.2, tree visiting is fully capable of partial state
saving; thanks to the StateManagementStrategy.saveView() and StateManagementStrategy.restoreView()
methods. These two methods are meant to replace their counterparts from the StateManager
class, and their implementations are now mandatory to use the visit API. (A
good point to start studying may be the UIComponent.visitTree() method.)
As a JSF developer, you will probably never interact with this feature, but for
the sake of completeness, it may be good to be aware of it.
JSF saving view state on the server or
client
Saving the
view state can be accomplished on the server that hosts the application, or on
the client machine. We can easily choose between the client and the server by adding
the context parameter named javax.faces.STATE_SAVING_METHOD to the web.xml
file. The value of this method can be server or client as shown in the following
code:
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>server</param-value>
</context-param>
Starting
with JSF 2.2, the values of this context parameter are case insensitive. Saving
the state on the server means to save it in a session with a special ID known as
the view state ID that refers to the state stored in the server memory. This is
sent to the client as the value of a hidden input field named, javax.faces.ViewState.
This can be easily tested by running an application which produces the HTML code
that contains this field, as shown in the following screenshot:
If the state
is saved on the client, JSF stores it as the value of the same hidden input field.
This value is a base64 encrypted string representing the serialization of the state:
Specifying
where the view state will be saved is a piece of cake, but choosing between
saving the view state on a client or on a server can be a difficult choice, because
each has its own advantages and disadvantages. Both have a cost, and everybody
wants to pay a lower price. Choosing the client will increase network traffic
because the serialized state will generate a larger value for the javax.faces.
ViewState
input field. Moreover, encoding/decoding the view state and possible trespasser
attacks are also important drawbacks of this approach. On the other hand, the
server uses less memory because nothing is stored in the session. Moreover, storing
the view state on the client will also be a good solution to prevent losing it
when the server is down, and to prevent ViewExpiredException that
occurs when the session has expired, or when the maximum number of opened views
was reached. Saving the state on the server has an opposite effect: the network
traffic is lower, the usage of memory by the server increases, and the server
failures will result in loss of the state and possible ViewExpiredException instances.
Note Usually,
developers prefer to have a lower network traffic and use more memory on the
server, because memory is easy to provide to an application server. But this is
not a rule; you just have to think what's
cheaper for you. Some heavy benchmarks can also provide compelling indications
about storing the state on the client or on the server.
In order to
make the right choice, do not forget that JSF 2.0 comes, by default, with partial
state saving, which will be reflected in a smaller size of the javax.faces.ViewState
input field (the state saved on the client) or in less memory needed (the state
saved on the server). You can disable partial state saving by adding the following
context parameter in web.xml:
<context-param>
<param-name>javax.faces.PARTIAL_STATE_SAVING</param-name>
<param-value>false</param-value>
</context-param>
For a simple
visual test, you can choose to save the state on the client and run the same
application twice: first time, enable partial state saving, and second time,
disable it—the result shown in the following screenshot speaks for itself:
Furthermore,
in the same application, you can use partial state saving for some views and
full state saving for other views. Skip the javax.faces.PARTIAL_STATE_SAVING
context parameter and use the javax.faces.FULL_STATE_SAVING_VIEW_IDS context
parameter. The value of this context parameter contains a list of view IDs for
which the partial state saving will be disabled. The IDs should be comma
separated, as shown in the following code (suppose you have three pages: index.xhtml,
done.xhtml,
and error.xhtml,
partial state saving is used only for index.xhtml):
<context-param>
<param-name>javax.faces.FULL_STATE_SAVING_VIEW_IDS</param-name>
<param-value>/done.xhtml,/error.xhtml</param-value>
</context-param>
Programmatically,
you can check if the state is saved on the client as follows:
·
In view/page the code is as follows:
#{facesContext.application.stateManager.isSavingStateInClient(facesContext)}
·
In backing bean, the code is as follows:
FacesContext
facesContext = FacesContext.getCurrentInstance();
Application
application = facesContext.getApplication();
StateManager
stateManager = application.getStateManager();
logger.log(Level.INFO,
"Is view state saved on client ? {0}",
stateManager.isSavingStateInClient(facesContext));
You also may
be interested in:
Niciun comentariu :
Trimiteți un comentariu