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.x
  • jgroups.jar — JGroups 2.6.x
Ehcache
  • ehcache.jar — Ehcache 1.2.3
In EAR deployments of Seam, it is recommended that cache JARs 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>