[jboss-cvs] jboss-seam/doc/reference/en/modules ...

Gavin King gavin.king at jboss.com
Mon Feb 26 23:18:40 EST 2007


  User: gavin   
  Date: 07/02/26 23:18:40

  Added:       doc/reference/en/modules  cache.xml
  Log:
  JBSEAM-936
  
  Revision  Changes    Path
  1.1      date: 2007/02/27 04:18:40;  author: gavin;  state: Exp;jboss-seam/doc/reference/en/modules/cache.xml
  
  Index: cache.xml
  ===================================================================
  <chapter id="cache">
      <title>Caching</title>
      
      <para>
          In almost all enterprise applications, the database is the primary 
          bottleneck, and the least scalable tier of the runtime environment. 
          People from a PHP/Ruby environment will try to tell you that so-called
          "shared nothing" architectures scale well. While that may be literally
          true, I don't know of many interesting multi-user applications which 
          can be implemented with no sharing of resources between different 
          nodes of the cluster. What these silly people are really thinking of 
          is a "share nothing except for the database" architecture. Of course, 
          sharing the database is the primary problem with scaling a multi-user 
          application&mdash;so the claim that this architecture is highly scalable 
          is absurd, and tells you a lot about the kind of applications that these
          folks spend most of their time working on. 
      </para>
      
      <para>
          Almost anything we can possibly do to share the database <emphasis>less 
          often</emphasis> is worth doing.
      </para>
      
      <para>
          This calls for a cache. Well, not just one cache. A well designed Seam
          application will feature a rich, multi-layered caching strategy that
          impacts every layer of the application:
      </para>
      
      <itemizedlist>
          <listitem>
              <para>
                  The database, of course, has its own cache. This is 
                  super-important, but can't scale like a cache in the
                  application tier.
              </para>
          </listitem>
          <listitem>
              <para>
                  Your ORM solution (Hibernate, or some other JPA implementation)
                  has a second-level cache of data from the database. This is
                  a very powerful capability, but is often misused. In a clustered
                  environment, keeping the data in the cache transactionally
                  consistent across the whole cluster, and with the database,
                  is quite expensive. It makes most sense for data which is shared
                  between many users, and is updated rarely. In traditional 
                  stateless architectures, people often try to use the second-level
                  cache for conversational state. This is always bad, and is
                  especially wrong in Seam.
              </para>
          </listitem>
          <listitem>
              <para>
                  The Seam conversation context is a cache of conversational state.
                  Components you put into the conversation context can hold and cache
                  state relating to the current user interaction.
              </para>
          </listitem>
          <listitem>
              <para>
                  In particular, the
                  Seam-managed persistence context (or an extended EJB container-managed
                  persistence context associated with a conversation-scoped stateful
                  session bean) acts as a cache of data that has been read in the
                  current conversation. This cache tends to have a pretty high
                  hitrate! Seam optimizes the replication of Seam-managed persistence 
                  contexts in a clustered environment, and there is no requirement for 
                  transactional consistency with the database (optimistic locking is 
                  sufficient) so you don't need to worry too much about the performance 
                  implications of this cache, unless you read thousands of objects into
                  a single persistence context.
              </para>
          </listitem>
         <listitem>
              <para>
                  The application can cache non-transactional state in the Seam
                  application context. State kept in the application context is
                  of course not visible to other nodes in the cluster.
              </para>
          </listitem>
         <listitem>
              <para>
                  The application can cache transactional state using the Seam
                  <literal>pojoCache</literal> component, which integrates
                  JBossCache into the Seam environment. This state will be visible
                  to other nodes if you run JBoss cache in a clustered mode.
              </para>
          </listitem>
         <listitem>
              <para>
                  Finally, Seam lets you cache rendered fragments of a JSF page. Unlike  
                  the ORM second-level cache, this cache is not automatically invalidated 
                  when data changes, so you need to write application code to perform
                  explicit invalidation, or set appropriate expiration policies.
              </para>
          </listitem>
      </itemizedlist>
      
      <para>
          For more information about the second-level cache, you'll need to refer to
          the documentation of your ORM solution, since this is an extremely complex
          topic. In this section we'll discuss the use of JBossCache directly, via
          the <literal>pojoCache</literal> component, or as the page fragment cache,
          via the <literal>&lt;s:cache&gt;</literal> control.
      </para>
      
      <section>
          <title>Using JBossCache in Seam</title>
          
          <para>
              The built-in <literal>pojoCache</literal> component manages an instance
              of <literal>org.jboss.cache.aop.PojoCache</literal>. You can safely put 
              any immutable Java object in the cache, and it will be replicated across
              the cluster (assuming that replication is enabled). If you want to
              keep mutable objects in the cache, you'll need to run the JBossCache
              bytecode preprocessor to ensure that changes to the objects will be
              automatically detected and replicated.
          </para>
          
          <para>
              To use <literal>pojoCache</literal>, all you need to do is put the
              JBossCache jars in the classpath, and provide a resource named
              <literal>treecache.xml</literal> with an appropriate cache configuration.
              JBossCache has many scary and confusing configuration settings, so
              we won't discuss them here. Please refer to the JBossCache documentation
              for more information.
          </para>
          
          <para>
              For an EAR depoyment of Seam, we recommend that the JBossCache jars and
              configuration go directly into the EAR. Make sure you declare the jars  
              in <literal>application.xml</literal>.
          </para>
          
          <para>
              Now you can inject the cache into any Seam component:
          </para>
          
          <programlisting><![CDATA[@Name("chatroom")
  public class Chatroom {
      @In PojoCache pojoCache;
      
      public void join(String username) {
        try
        {
           Set<String> userList = (Set<String>) pojoCache.get("chatroom", "userList");
           if (users==null) 
           {
              userList = new HashSet<String>();
              pojoCache.put("chatroom", "users", username);
           }
        }
        catch (CacheException ce)
        {
           throw new RuntimeException(ce);
        }
      }
  }]]></programlisting>
  
          <para>
              If you want to have multiple JBossCache configurations in your application,
              use <literal>components.xml</literal>:
          </para>
          
          <programlisting><![CDATA[<core:pojo-cache name="myCache" cfg-resource-name="myown/cache.xml"/>]]></programlisting>
          
      </section>
      
      <section>
          <title>Page fragment caching</title>
          
          <para>
              The most interesting user of JBossCache is the <literal>&lt;s:cache&gt;</literal>
              tag, Seam's solution to the problem of page fragment caching in JSF.
              <literal>&lt;s:cache&gt;</literal> uses <literal>pojoCache</literal> internally,
              so you need to follow the steps listed above before you can use it. (Put the jars 
              in the EAR, wade through the scary configuration options, etc.)
          </para>
          
          <para>
              <literal>&lt;s:cache&gt;</literal> is used for caching some rendered content which
              changes rarely. For example, the welcome page of our blog displays the recent
              blog entries:
          </para>
          
          <programlisting><![CDATA[<s:cache key="recentEntries-#{blog.id}" region="welcomePageFragments">
     <h:dataTable value="#{blog.recentEntries}" var="blogEntry">
        <h:column>
           <h3>#{blogEntry.title}</h3>
           <div>
              <s:formattedText value="#{blogEntry.body}"/>
           </div>
        </h:column>
     </h:dataTable>
  </s:cache>]]></programlisting>
  
          <para>
              The <literal>key</literal> let's you have multiple cached versions of each page
              fragment. In this case, there is one cached version per blog. The 
              <literal>region</literal> determines the JBossCache node that all version will
              be stored in. Different nodes may have different expiry policies. (That's the 
              stuff you set up using the aforementioned scary configuration options.)
          </para>
          
          <para>
              Of course, the big problem with <literal>&lt;s:cache&gt;</literal> is that it 
              is too stupid to know when the underlying data changes (for example, when the 
              blogger posts a new entry). So you need to evict the cached fragment manually:
          </para>
          
          <programlisting><![CDATA[public void post() {
      ...
      entityManager.persist(blogEntry);
      pojoCache.remove("welcomePageFragments", "recentEntries-" + blog.getId() );
  }]]></programlisting>
          
          <para>
              Alternatively, if it is not critical that changes are immediately visible to the
              user, you could set a short expiry time on the JbossCache node.
          </para>
          
      </section>
      
  </chapter>
  
  



More information about the jboss-cvs-commits mailing list