How to cache component rendering in JSF example
Introduction
Caching mechanisms are commonly used in web applications, especially when we have sections of a web page that are frequently accessed and the displayed content within those sections is not subject to constant changes. In this tutorial we will see how to easily cache components - or even sections - of a JSF view using the Omnifaces component library.
This tutorial considers the following environment:
- Ubuntu 12.04
- JDK 1.7.0.21
- JSF 2.2.4
- Omnifaces 1.6.1
Configuring Maven dependencies
Since we will be using Omnifaces we must declare the dependency in our Maven project:
<dependencies> <dependency> <groupId>org.omnifaces</groupId> <artifactId>omnifaces</artifactId> <version>1.6.1</version> </dependency> </dependencies>
The JSF managed bean
We will use a JSF managed bean to hold the information that we will present in the view:
package com.byteslounge.jsf; import java.util.ArrayList; import java.util.List; import javax.annotation.PostConstruct; import javax.faces.bean.ManagedBean; import javax.faces.bean.RequestScoped; @ManagedBean @RequestScoped public class TestBean { private String title; private List<String> items; @PostConstruct private void init() { title = "JSF - cache component rendering"; items = new ArrayList<>(); items.add("Item 1"); items.add("Item 2"); items.add("Item 3"); System.out.println("TestBean initialized"); } public String getTitle() { return title; } public List<String> getItems() { return items; } }
We are basically defining a title and a list of items that will be shown in the JSF view. Note that the bean is RequestScoped so under regular conditions it would be initialized in every request made against a view that referenced the bean.
We are writing a message to the console during bean initialization so we may later confirm that the bean will not be initialized in every request and the cache will work correctly.
The JSF view
Now we define the view:
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:o="http://omnifaces.org/ui"> <h:head> <title>JSF - cache component rendering</title> </h:head> <h:body> <o:cache> <div> <h:outputText value="#{testBean.title}" /> </div> <ui:repeat var="item" value="#{testBean.items}"> <div> <h:outputText value="#{item}" /> </div> </ui:repeat> </o:cache> </h:body> </html>
The view is writing the title we defined in the managed bean to the screen and also iterating over the items list - defined in the managed bean as well - and showing them to the user.
Note the o:cache element. This component is provided by Omnifaces and it will cache the HTML that is generated by the other components that are declared inside it.
By default the o:cache component will cache its contents using session scope. This means that for every distinct user session it will only generate the rendered HTML once, and consequently it will only initialize the managed bean and fetch its properties once. The scope may be changed but we will see that later in this tutorial.
Testing
When we access the following URL:
http://localhost:8080/myapp/pages/test.xhtml
The expected output will be generated:
Now when we refresh the page we will see that the managed bean will not be initialized again even if the bean is RequestScoped. The HTML will also not be rendered again: it is cached in memory.
Remember that by default the cache is session scoped so as soon as the page is accessed by a different session the component will be rendered again. We will see other scopes in the next article section.
The following message will be written to the standard output only once per session:
Scope attribute
By default the Omnifaces cache component will have its scope defined as session. This means that the component rendering will be cached per session. Other possible value for scope attribute is application scope:
<o:cache scope="application"> ... </o:cache>
By setting the scope to application the component will be rendered once, cached and reused across the entire application.
Key attribute
The key attribute is the key used to store the component rendering result in the cache (like a key-value pair). It may be set dynamically through an EL expression so we may use it to cache components rendering more flexibly, ie. multiple (or distinct) renderings of the same component.
<o:cache key="pizzaMenu#{pizzaBean.pizzaType}"> ... </o:cache>
Note that if you use an EL expression associated with a managed bean in the key attribute value - as we just did - you must keep in mind that the expression will be evaluated every time the o:cache component is being rendered. This is because Omnifaces must know what value the key attribute will resolve in order to check if it is already in cache. So make sure that the EL expression doesn't do any expensive operation.
Time attribute
As the name states, the time attribute specifies the time to live (TTL) in seconds of a cached element:
<o:cache time="120"> ... </o:cache>
After the time to live expires the content will be rendered again.
Reset attribute
The reset attribute is used to instruct Omnifaces that the component should be rendered again:
<o:cache reset="#{someBean.resetCache}"> ... </o:cache>
This attribute may be an EL expression. When the result of the EL expression evaluates to true the content is rendered again.
Programmatically resetting the cache
It is possible to programmatically reset the Omnifaces component cache:
CacheFactory.getCache( FacesContext.getCurrentInstance(), "session").remove("someKey");
Where "session" is the scope of the cached item to remove and "someKey" is the item key.
Downloadable sample
You may find a downloadable sample at the end of this page. Please keep in mind that the sample is a Maven project ready to be deployed in Tomcat 7.
References
Omnifaces may be found here.