Chapter 22. Caching
The database is the primary bottleneck in almost all enterprise applications, and the least-scalable tier of the runtime environment, so anything we can do to reduce the number of times the database is accessed can dramatically improve application performance.
A well-designed Seam application will feature a rich, multi-layered caching strategy that impacts every layer of the application, including:
- A cache for the database. This is vital, but cannot scale like a cache in the application tier.
- A secondary cache of data from the database, provided by your ORM solution (Hibernate, or another JPA implementation). In a clustered environment, keeping cache data transactionally consistent with both the database and the rest of the cluster can be very expensive to implement effectively. Therefore, this secondary cache is best used to store data that is rarely updated, and shared between many users. In traditional stateless architectures, this space is often used (ineffectively) to store conversational state.
- The Seam conversational context, which is a cache of conversational state. Components in the conversation context store state relating to the current user interaction.
- The Seam-managed persistence context, which acts as a cache of data read in the current conversation. (An Enterprise JavaBean [EJB] container-managed persistence context associated with a conversation-scoped stateful session bean can be used in place of a Seam-managed persistence context.) Seam optimizes the replication of Seam-managed persistence contexts in a clustered environment, and optimistic locking provides sufficient transactional consistency with the database. Unless you read thousands of objects into a single persistence context, the performance implications of this cache are minimal.
- The Seam application context, which can be used to cache non-transactional state. State held here is not visible to other nodes in the cluster.
- The Seam
cacheProvider
component within the application, which integrates JBossCache, or Ehcache into the Seam environment. State held here is visible to other nodes if your cache supports running in clustered mode. - Finally, Seam can cache rendered fragments of a JSF page. Unlike the ORM secondary cache, this is not automatically invalidated when data is updated, so you will need to write application code to perform explicit invalidation, or set appropriate expiry policies.
For more information about the secondary cache, you will need to refer to the documentation of your ORM solution, since this can be quite complex. In this section, we discuss the use of caching directly via the
cacheProvider
component, or caching as stored page fragments, via the <s:cache>
control.
22.1. Using Caching in Seam
The built-in
cacheProvider
component manages an instance of:
- JBoss Cache 3.2.x
org.jboss.cache.Cache
- EhCache
net.sf.ehcache.CacheManager
Any immutable Java object placed in the cache will be stored there and replicated across the cluster (if replication is supported and enabled). To keep mutable objects in the cache, read the underlying caching project documentation for information about notifying the cache of changes made to stored objects.
To use
cacheProvider
, you need to include the JARs of the cache implementation in your project:
- JBoss Cache 3.2.x
jbosscache-core.jar
— JBoss Cache 3.2.xjgroups.jar
— JGroups 2.6.x
- Ehcache
ehcache.jar
— Ehcache 1.2.3
In
EAR
deployments of Seam, it is recommended that cache JAR
s and configuration go directly into the EAR
.
You will also need to provide a configuration file for JBossCache. Place
cache-configuration.xml
with an appropriate cache configuration into the classpath — for example, the EJB JAR or WEB-INF/classes
. Refer to the JBossCache documentation for more information about configuring the JBossCache.
You can find a sample
cache-configuration.xml
in examples/blog/resources/META-INF/cache-configuration.xml
.
Ehcache will run in its default configuration without a configuration file.
To alter the configuration file in use, configure your cache in
components.xml
:
<components xmlns="http://jboss.com/products/seam/components" xmlns:cache="http://jboss.com/products/seam/cache"> <cache:jboss-cache-provider configuration="META-INF/cache/cache-configuration.xml" /> </components>
Now you can inject the cache into any Seam component:
@Name("chatroomUsers") @Scope(ScopeType.STATELESS) public class ChatroomUsers { @In CacheProvider cacheProvider; @Unwrap public Set<String> getUsers() throws CacheException { Set<String> userList = (Set<String>) cacheProvider.get("chatroom", "userList"); if (userList==null) { userList = new HashSet<String>(); cacheProvider.put("chatroom", "userList", userList); } return userList; } }
If you want multiple cache configurations available to your application, use
components.xml
to configure multiple cache providers:
<components xmlns="http://jboss.com/products/seam/components" xmlns:cache="http://jboss.com/products/seam/cache"> <cache:jboss-cache3-provider name="myCache" configuration="myown/cache.xml"/> <cache:jboss-cache3-provider name="myOtherCache" configuration="myother/cache.xml"/> </components>