- 

Available since OmniFaces 1.1

The <o:cache> component allows to cache a fragment of rendered markup. The first request for a page that has this component on it will cause this markup to be put into the cache. Then for subsequent requests the cached content is used directly and none of the components, backing beans and services that were used to generate this content in the first place will be consulted.

Caching can take place in application scope, or in session scope. For individual fragments a time can be specified for which the cached content is valid. After this time is elapsed, the very first request to the page containing the cache component in question will cause new content to be rendered and put into the cache. A default time can be set per scope in web.xml.

For each scope a maximum capacity can be set. If the capacity for that scope is exceeded, an element will be removed following a least recently used policy (LRU).

Via a cache provider mechanism an alternative cache implementation can be configured in web.xml. The default cache is based on https://github.com/ben-manes/concurrentlinkedhashmap.

Setting a custom caching provider

A custom caching provider can be set by using the org.omnifaces.CACHE_PROVIDER context parameter in web.xml to point to an implementation of org.omnifaces.component.output.cache.CacheProvider. For example:

<context-param>
    <param-name>org.omnifaces.CACHE_PROVIDER</param-name>
    <param-value>com.example.MyProvider</param-value>
</context-param>

The default provider, org.omnifaces.component.output.cache.DefaultCacheProvider can be used as an example.

Global settings

For the default provider, the maximum capacity and the default time to live can be specified for the supported scopes "session" and "application". If the maximum capacity is reached, an entry will be evicted following a least recently used policy. The default time to live specifies for how long entries are considered to be valid. A value for the time attribute on this component will override this global default time to live. The following context parameters can be used in web.xml:

All available context parameters
org.omnifaces.CACHE_SETTING_APPLICATION_MAX_CAPACITY Sets the maximum number of elements that will be stored per web module (application scope). Default: no limit
org.omnifaces.CACHE_SETTING_SESSION_MAX_CAPACITY Sets the maximum number of elements that will be stored per session. Default: no limit.
org.omnifaces.CACHE_SETTING_APPLICATION_TTL Sets the maximum amount of time in seconds that cached content is valid for the application scope. Can be overriden by individal cache components. Default: no limit.
org.omnifaces.CACHE_SETTING_SESSION_TTL Sets the maximum amount of time in seconds that cached content is valid for the session scope. Can be overriden by individal cache components. Default: no limit.
org.omnifaces.CACHE_INSTALL_BUFFER_FILTER Boolean that when true installs a Servlet Filter (Servlet 3.0+ only) that works in conjunction with the useBuffer attribute of the Cache component to enable an alternative way to grab the content that needs to be cached. This is a convenience setting that is a short-cut for installing the org.omnifaces.servlet.BufferedHttpServletResponse filter manually. If more finegrained control is needed regarding which place in the filter chain the filter appears and which resources it exactly filters, this setting should not be used and the mentioned filter should be manually configured. Default: false.
Demo

Now: 2024-03-28T13:35:16.957546+01:00[Europe/Amsterdam]


Example 1 - Fixed cache

The following renders a couple of items in a default cache, which means the session scope is used without a timeout. On the first page refresh, all items should have the same time as indicated by "Now" above. After a page refresh the time of the items should stay constant, indicating cached content is used.

A - Thu Mar 28 13:35:16 CET 2024
B - Thu Mar 28 13:35:16 CET 2024
C - Thu Mar 28 13:35:16 CET 2024


Example 2 - Timed cache

The following renders a time-stamp in the session scoped cache, for which a time to live has been set to 15 seconds. On the first page refresh, it should be equal again to the time as indicated by "Now" above. For page refreshes done within 15 seconds after this first request it should be constant. After this time, it should change into the "Now" time again, meaning a timeout took place and new content was rendered.

Session scope content valid for 15 seconds generated at: 2024-03-28T13:35:16.957546+01:00[Europe/Amsterdam]


Example 3 - Treshold on number of cached items

The following renders a time-stamp in the session scoped cache again, this time using an explicit and dynamic key. For this show case application, the number of items in the session cache has been set to 4 using the following setting in web.xml:

<context-param>
	<param-name>org.omnifaces.CACHE_SETTING_SESSION_MAX_CAPACITY</param-name>
	<param-value>6</param-value>
</context-param>
		

Since 4 items have been used already by the other examples, only 2 more fit it.

This can be demonstrated by generating cache items via the numbered buttons shown below. Clicking on any two of the same numbers below will switch between the cached content for those and will keep showing the time-stamp associated with them on their first click. Upon clicking a third number, and then clicking the number of the original 2 that was clicked the longest time ago (except 0, the default), a new time-stamp will be associated with that.

This means the threshold of 6 cached items had been exceeded and the last accessed item was evicted.

E.g. click 0, click 1, click 0 (notice it has the same time-stamp), click 1 (same timestamp), click 2, click 1 (gets new time-stamp)

Set Cache key to: , , ,

Session scope content for key 0 generated at: 2024-03-28T13:35:16.957546+01:00[Europe/Amsterdam]


Example 4 - Explicitly resetting/expiring cache

Cached data may need to be reset at the point that the underlying data is known to have changed. Any cache entry can be programmatically removed based on the scope and the key of this entry. By default the cache component generates an internal key, but this can be overridden by using an explicit key. The default scope is "session".

Cached data can also be reset via the reset attribute on the cache component. Using this method it's not necessary to know the scope and key of the cache. Instead, the attribute can be bound to a property of a backing bean which can return true (reset cache) or false. If the attribute is bound to a view scoped backing bean, care should be taken to reset the property back to false (e.g via a PreRenderView event on the next request, or CallbackPhaseListener on the current request).

Programmatic reset

Pressing the following button will reset the cache that's used in example 1 at the start of this page via the programmatic method. With this method the attribute reset="#{cacheBean.reset}" is NOT used.

Reset via component attribute

Pressing the following button will reset the cache that's used in example 1 at the start of this page by setting a bean property that's bound to the attribute reset="#{cacheBean.reset}" on the component to true.


Example 5 - Caching value expressions

Sometimes it might be convenient to cache an EL value expression instead or in addition of a fully rendered piece of markup. This is especially useful for a postback, since after a postback Faces (of course) won't evaluate the cached markup, but will evaluate value expressions again.

Without o:cacheValue

Clicking on any of the 'Postback' buttons below will cause a new date to be generated at each click, instead of the actually selected date. This is because Faces doesn't send the actual row data along, but merely a row number. Because the backing bean is request scoped and the expression #{cacheBean.items} is evaluated fresh after each postback, we'll end up selecting a new date each time.

Item: (none)

A - Thu Mar 28 13:35:16 CET 2024
B - Thu Mar 28 13:35:16 CET 2024
C - Thu Mar 28 13:35:16 CET 2024

With o:cacheValue

The following example makes use of o:cacheValue to cache the value expression as well. Clicking any of the 'Postback' buttons in the example below will cause the same date to be selected as the one in the row where the button is clicked.

Item: (none)

A - Thu Mar 28 13:35:16 CET 2024
B - Thu Mar 28 13:35:16 CET 2024
C - Thu Mar 28 13:35:16 CET 2024

Example 6 - Disabling the cache

The OmniFaces cache can be disabled per request. If the cache is disabled the cache's children will be directly rendered without influencing the cached data. So if initially the value "1" was rendered and cached, and after disabling the cache the value "2" is rendered, then enabling the cache again (assuming no reset or timeout has taken place) will render the original "1" again and there will be no memory of there ever having been a "2".

Demo source code
<p>
    <b>Now:</b> <span id="now">#{now}</span>
</p>

<hr/>

<h3>Example 1 - Fixed cache</h3>
<p>
    The following renders a couple of items in a default cache, which means the session scope is used without a timeout. 
    On the first page refresh, all items should have the same time as indicated by "<code>Now</code>" above.
    After a page refresh the time of the items should stay constant, indicating cached content is used. 
</p>
<p>
    <o:cache key="firstCache" reset="#{cacheBean.reset}" disabled="#{cacheBean.disabled}">
        <h:dataTable id="example1" value="#{cacheBean.items}" var="item">
            <h:column>
                <i>#{item}</i> <br/>
            </h:column>
        </h:dataTable>
    </o:cache>
</p>

<hr/>

<h3>Example 2 - Timed cache</h3>
<p>
    The following renders a time-stamp in the session scoped cache, for which a time to live has been set to 15 seconds.
    On the first page refresh, it should be equal again to the time as indicated by "<code>Now</code>" above. 
    For page refreshes done within 15 seconds after this first request it should be constant. After this time, 
    it should change into the "<code>Now</code>" time again, meaning a timeout took place and new content was rendered.
</p>
<p>
    <o:cache time="15">
        <i>Session scope content valid for 15 seconds generated at: #{now}</i>
    </o:cache>
</p>

<hr/>

<h3>Example 3 - Treshold on number of cached items</h3>
<p>
    The following renders a time-stamp in the session scoped cache again, this time using an explicit and dynamic key.
    For this show case application, the number of items in the session cache has been set to <code>4</code>
    using the following setting in <code>web.xml</code>:
</p>
<pre>
&lt;context-param&gt;
    &lt;param-name&gt;org.omnifaces.CACHE_SETTING_SESSION_MAX_CAPACITY&lt;/param-name&gt;
    &lt;param-value&gt;6&lt;/param-value&gt;
&lt;/context-param&gt;
</pre>
<p>
    Since 4 items have been used already by the other examples, only 2 more fit it.
</p>
<p>
    This can be demonstrated by generating cache items via the numbered buttons shown below. 
    Clicking on any two of the same numbers below will switch between the cached content for those
    and will keep showing the time-stamp associated with them on their first click. 
    Upon clicking a third number, and then clicking the number of the original 2 that was clicked the
    longest time ago (except 0, the default), a new time-stamp will be associated with that.
</p>
<p>
    This means the threshold of 6 cached items had been exceeded and the last accessed item was evicted. 
</p>
<p>
    E.g. click 0, click 1, click 0 (notice it has the same time-stamp), click 1 (same timestamp), 
    click 2, click 1 (gets new time-stamp)
</p>
<h:form>
    Set Cache key to: 
    <h:commandButton action="#{cacheBean.setKey(0)}" value="0" />, 
    <h:commandButton action="#{cacheBean.setKey(1)}" value="1" />, 
    <h:commandButton action="#{cacheBean.setKey(2)}" value="2" />, 
    <h:commandButton action="#{cacheBean.setKey(3)}" value="3" />
</h:form>
<p>
    <o:cache key="test-#{cacheBean.key}">
        <i>Session scope content for key #{cacheBean.key} generated at: #{now}</i>
    </o:cache>
</p>

<hr/>

<h3>Example 4 - Explicitly resetting/expiring cache</h3>
<p>
    Cached data may need to be reset at the point that the underlying data is known to have changed. Any cache
    entry can be programmatically removed based on the scope and the key of this entry. By default the cache
    component generates an internal key, but this can be overridden by using an explicit key. The default scope is "session". 
</p>
<p>
    Cached data can also be reset via the <code>reset</code> attribute on the <code>cache</code> component. Using this method
    it's not necessary to know the scope and key of the cache. Instead, the attribute can be bound to a property of a backing bean
    which can return <code>true</code> (reset cache) or <code>false</code>. If the attribute is bound to a view scoped backing bean,
    care should be taken to reset the property back to false (e.g via a <code>PreRenderView</code> event on the next request, or
    <code>CallbackPhaseListener</code> on the current request).
</p>

<h4>Programmatic reset</h4>
<p>
    Pressing the following button will reset the cache that's used in example 1 at the start of this page via the 
    programmatic method. With this method the attribute <span style="white-space: nowrap">
    <code>reset="\#{cacheBean.reset}"</code></span> is <b>NOT</b>
    used.
</p>
<h:form id="resetProgrammaticForm">
    <h:commandButton id="button" action="#{cacheBean.resetProgrammatic}" value="Reset Example 1 cache programmatically" />
</h:form>

<h4>Reset via component attribute</h4>
<p>
    Pressing the following button will reset the cache that's used in example 1 at the start of this page by setting
    a bean property that's bound to the attribute <span style="white-space: nowrap">
    <code>reset="\#{cacheBean.reset}"</code></span> on the 
    component to <code>true</code>.
</p>
<h:form id="resetAttributeForm">
    <h:commandButton id="button" action="#{cacheBean.resetAttribute}" value="Reset Example 1 cache via attribute" />
</h:form>

<hr/>

<h3>Example 5 - Caching value expressions</h3>
<p>
    Sometimes it might be convenient to cache an EL value expression instead or in addition of a fully rendered piece of markup.
    This is especially useful for a postback, since after a postback Faces (of course) won't evaluate the cached markup, but will
    evaluate value expressions again.
</p>

<h4>Without o:cacheValue</h4>
<p>
    Clicking on any of the 'Postback' buttons below will cause a new date to be generated at each click, instead
    of the actually selected date. This is because Faces doesn't send the actual row data along, but merely a row number.
    Because the backing bean is request scoped and the expression <code>\#{cacheBean.items}</code> is evaluated 
    fresh after each postback, we'll end up selecting a new date each time.
</p>
<h:form id="example5aForm">
    <p>
        Item: <span id="example5aOutput">#{empty cacheBean.item? '(none)' : cacheBean.item}</span>
    </p>
    <o:cache>
        <h:dataTable id="table" value="#{cacheBean.items}" var="item">
            <h:column>
                <i>#{item}</i>
            </h:column>
            <h:column>
                <h:commandButton id="button" value="Postback" action="#{cacheBean.saveItem(item)}" >
                    <f:ajax execute="@form" render="@form" />
                </h:commandButton>
            </h:column>
        </h:dataTable>
    </o:cache>
</h:form>

<h4>With o:cacheValue</h4>
<p>
    The following example makes use of <code>o:cacheValue</code> to cache the value expression as well. Clicking
    any of the 'Postback' buttons in the example below will cause the same date to be selected as the one in the row
    where the button is clicked.
</p>
<h:form id="example5bForm">
    <p>
        Item: <span id="example5bOutput">#{empty cacheBean.item? '(none)' : cacheBean.item}</span>
    </p>
    <o:cache>
        <o:cacheValue name="items" value="#{cacheBean.items}"/>
        
        <h:dataTable id="table" value="#{items}" var="item">
            <h:column>
                <i>#{item}</i>
            </h:column>
            <h:column>
                <h:commandButton id="button" value="Postback" action="#{cacheBean.saveItem(item)}" >
                    <f:ajax execute="@form" render="@form" />
                </h:commandButton>
            </h:column> 
        </h:dataTable>
    </o:cache>
</h:form>

<hr/>

<h3>Example 6 - Disabling the cache</h3>
<p>
    The OmniFaces cache can be disabled per request. If the cache is disabled the cache's children will be
    directly rendered <b>without influencing the cached data</b>. So if initially the value "1" was rendered
    and cached, and after disabling the cache the value "2" is rendered, then enabling the cache again 
    (assuming no reset or timeout has taken place) will render the original "1" again and there will be no
    memory of there ever having been a "2".
</p>
<h:form id="disableAttributeForm">
    <h:commandButton id="button" action="#{cacheBean.disable}" value="Disable Example 1 cache via attribute (for one request)" />
</h:form>