<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
"http://www.oasis-open.org/docbook/xml/4.0/docbookx.dtd" [
<!ENTITY JBossCache "<literal>JBossCache</literal>">
<!ENTITY TreeCache "<literal>TreeCache</literal>">
<!ENTITY TreeCacheAop "<literal>TreeCacheAop</literal>">
]>
<article lang="en">
  <articleinfo>
    <title>TreeCacheAOP</title>

    <author>
      <firstname>Ben</firstname>

      <surname>Wang</surname>

      <email>ben.wang@jboss.com</email>
    </author>

    <author>

      <firstname>Bela</firstname>

      <surname>Ban</surname>

      <email>bela@jboss.com</email>
    </author>

    <pubdate>August, 2004</pubdate>
  </articleinfo>

  <section>
    <title id="s1">Introduction</title>

    <para>The <literal>JBossCache</literal> component,
    <literal>TreeCacheAop</literal>, is a replicated and transactional
    "object-oriented" cache. By "object-oriented", we mean that TreeCacheAop
    provides tight integration with the object-oriented Java language
    paradigm, specifically it automatically : 1) manages object mapping and
    relationship, 2) provides support for inheritance relationship between
    "advised" POJOs (plain old Java object), and 3) preserves object identity,
    for a client under both local and replicated cache modes.</para>

    <para>By leveraging the dynamic AOP capability in JBossAop, it is able to
    map a complex object into the cache store while preserving the object
    relationship behind the scene. During replication mode, it also performs
    fine-granularity (i.e., on a per-field basis) updates, and thus has the
    potential to boost cache performance and minimize network traffic.</para>

    <para><literal>TreeCacheAop</literal> extends the functionality of
    TreeCache to POJOs with dynamic AOP (Aspect-Oriented Programming)-enabled
    capability. That is, in addition to the TreeCache basic features such as
    transaction, replication, and eviction policy,
    <literal>TreeCacheAop</literal> also provides transparent object cache
    retrieval and update for user-specified POJOs (configured in a
    <literal>jboss-aop.xml</literal> file) through plain get/set methods
    associated with the POJOs.</para>

    <para><literal>TreeCacheAop</literal> employs the <ulink
    url="http://jboss.org/wiki/Wiki.jsp?page=JBossAOP"> JBoss standalone AOP
    framework in the JBoss4.0 release</ulink> to perform dynamic AOP
    interception. The framework provides declarative semantics (e.g., a
    <literal>jboss-aop.xml</literal> configuration) to "advise" POJOs. Once it
    is declared, a user will only need to initiate the transparent cache
    mechanism on that POJO by issuing a <literal>putObject(String fqn, Object
    pojo)</literal> method call first. After that, plain get/set methods from
    that POJO [e.g., <literal>getName()</literal>,
    <literal>setName()</literal>, etc], or direct field read and write
    operations, will be intercepted by the AOP framework, and the framework
    will, in turn, invoke the
    <literal>org.jboss.cache.aop.CacheInterceptor</literal> that will retrieve
    or update the object contents from the cache, respectively.</para>

    <para><literal>TreeCacheAop</literal> can also be used as a plain
    <literal>TreeCache</literal>. For example, if a POJO is not declared
    aop-enabled, a user will need to use the <literal>TreeCache</literal> API
    [e.g., <literal>get(String fqn)</literal> and <literal>set(String fqn,
    String key, String value)</literal>] to manage the cache states. Of
    course, users will need to consider the extra cost in doing this.</para>
  </section>

  <section>
    <title id="s2">Feature</title>

    <para>Here are the current features and benefits of TreeCacheAop:</para>

    <itemizedlist>
      <listitem>
        <para>Replication, transaction, and eviction policy. All three aspects
        can be configured through a TreeCache/Aop configuration xml file.
        Please see the documentation of <ulink
        url="TreeCache.html">JBossCache</ulink> for details.</para>
      </listitem>

      <listitem>
        <para>Object cache by reachability, i.e., recursive object mapping
        into the cache store. For example, if a POJO has a reference to
        another advised POJO, <literal>TreeCacheAop</literal> will
        transparently manage the sub-object states as well. During the initial
        <literal>putObject()</literal> call, <literal>TreeCacheAop</literal>
        will traverse the object tree and map it accordingly to the internal
        TreeCache nodes. This feature is explained in full details
        later.</para>
      </listitem>

      <listitem>
        <para>Object reference handling. In TreeCacheAop, multiple and
        recursive object references are handled automatically. That is, a user
        does not need to declare any object relationship (e.g., one-to-one, or
        one-to-many) to use the cache.</para>
      </listitem>

      <listitem>
        <para>Automatic support of object identity. In TreeCacheAop, each
        object is uniquely identified by an internal FQN. Client can determine
        the object equality through the usual <literal>equal</literal> method.
        For example, an object such as <literal>Address</literal> may be
        multiple referenced by two <literal>Person</literal>s (e.g.,
        <literal>joe</literal> and <literal>mary</literal>). The objects
        retrieved from <literal>joe.getAddress()</literal> and
        <literal>mary.getAddress()</literal> should be identical.</para>
      </listitem>

      <listitem>
        <para>Inheritance relationship. TreeCacheAop preserves the POJO
        inheritance hierarchy after the object item is stored in the
        cache.</para>
      </listitem>

      <listitem>
        <para>Replication on modified attribute nodes only (as in per-field
        update basis), e.g., when an object is put into
        <literal>TreeCacheAop</literal> , a replication request will be sent
        out only when any attribute is modified and only the node corresponds
        to that modified attribute. This can have potential performance boost
        during replication process, e.g., update a single key in a big HashMap
        will only replicate one single field instead of the map!</para>
      </listitem>

      <listitem>
        <para>Support List, Set, and Map based objects automatically without
        declaring them as aop-enabled. That is, you can use them either as a
        plain POJO or a sub-object to POJO without declaring them as
        advisable.</para>
      </listitem>

      <listitem>
        <para>Support pre-compiling of POJOs. The latest JBossAop has a
        feature to pre-compile (called <literal>aopc</literal>) and generate
        the byte code necessary for AOP system. By pre-compiling the
        user-specified POJOs, there is no need for additional declaration file
        (e.g., <literal>jboss-aop.xml</literal>) or specifying a JBossAop
        system classloader. A user can treat the pre-generated classes as
        regular ones and use TreeCacheAop in a non-intrusive way.</para>

        <para>This provides easy integration to existing Java runtime
        programs, eliminating the need for ad-hoc specification of a system
        class loader, for example. Please see the <literal>Ant</literal> build
        file (<literal>build.xml</literal>) under the standalone package for
        an example of pre-compiling.</para>
      </listitem>

      <listitem>
        <para>Ease of use. Once a POJO is declared to be managed by cache
        (i.e., putObject() call), the POJO object is mapped into the cache
        store behind the scene. Client will not need to manage any object
        relationship and cache contents synchronization.</para>
      </listitem>
    </itemizedlist>
  </section>

  <section>
    <title>Concepts</title>

    <para>Following explains the concepts and top-level design consideration
    of TreeCacheAop.</para>

    <section>
      <title>Dynamic AOP interception</title>

      <para>JBossAop provides a mechanism to add an interceptor at runtime.
      TreeCacheAop uses this feature extensively to provide user transparency.
      Every "advised" POJO class will have an associated
      <literal>org.jboss.aop.InstanceAdvisor</literal> instance. During a
      <literal>putObject(FQN fqn, Object pojo)</literal> operation (API
      explained below), TreeCacheAop will examine to see if there is already a
      <literal>org.jboss.cache.aop.CacheInterceptor</literal> attached. (Note
      that a <literal>CacheInterceptor</literal> is the entrance of
      TreeCacheAop to dynamically manage cache contents.) If it has not, one
      will be added to <literal>InstanceAdvisor</literal> object. Afterwards,
      any POJO field modification will invoke the corresponding
      <literal>CacheInterceptor</literal> instance. Below is a schematic
      illustration of this process.</para>

      <para>The figures shown are operations to perform field read and write.
      Once a POJO is managed by cache (i.e., after a
      <literal>putObject</literal> method has been called), AOP will invoke
      <literal>CacheInterceptor</literal> automatically every time there is a
      field read or write. However, as you can see the difference between
      these figures, while field write operation will go to cache first
      before, eventually, invoke the in-memory update, the field read
      invocation does not involve in-memory reference at all, since the value
      in cache and memory should be synchronized. Instead, the field value
      from the cache is returned.</para>

      <figure>
        <title>Dynamic AOP interception for field write</title>

        <mediaobject>
          <imageobject>
            <imagedata fileref="images/set.gif" />
          </imageobject>
        </mediaobject>
      </figure>

      <figure>
        <title>Dynamic AOP Interception for field read</title>

        <mediaobject>
          <imageobject>
            <imagedata fileref="images/get.gif" />
          </imageobject>
        </mediaobject>
      </figure>
    </section>

    <section>
      <title>Object mapping by reachability</title>

      <para>A complex object by definition is an object that may consist of
      composite object references. Once a complex object is declared "prepare"
      (e.g., a <literal>Person</literal> object), during the
      <literal>putObject(Fqn fqn, Object pojo)</literal> operation,
      TreeCacheAop will add a <literal>CacheInterceptor</literal> instance to
      the <literal>InstanceAdvisor</literal> associated with that object, as
      we have discussed above. In addition, the cache will map recursively the
      primitive object fields into the corresponding cache nodes.</para>

      <para>The mapping rule is as follows:</para>

      <itemizedlist>
        <listitem>
          <para>Create a tree node using <literal>fqn</literal>, if not yet
          existed.</para>
        </listitem>

        <listitem>
          <para>Go through all the fields (say, with an association
          <literal>java.lang.reflect.Field</literal> type field) in
          POJO,</para>

          <itemizedlist>
            <listitem>
              <para>If it is a primitive type, the field value will be stored
              under <literal>fqn</literal> with <literal>(key,
              value)</literal> pair of <literal>(field.getName(),
              field.getValue())</literal>. The following are primitive types
              supported now: <literal>String, Boolean, Double, Float, Integer,
              Long, Short, Character, Boolean</literal>.</para>
            </listitem>

            <listitem>
              <para>If it is a non-primitive type, creates a child
              <literal>FQN</literal> and then recursively executes another
              <literal>pubObject</literal> until it reaches all primitive
              types.</para>
            </listitem>
          </itemizedlist>
        </listitem>
      </itemizedlist>

      <para>Following is a code snippet that illustrates this mapping
      process</para>

      <programlisting>         for (Iterator i = type.getFields().iterator(); i.hasNext();) {
            Field field = (Field) i.next();
            Object value = field.get(obj);
            CachedType fieldType = getCachedType(field.getType());
            if (fieldType.isImmediate()) {
               immediates.put(field.getName(), value);
            } else {
               putObject(new Fqn(fqn, field.getName()), value);
            }
         }</programlisting>

      <para>Let's take an example POJO classes definition from Example POJO
      section below where we have a <literal>Person</literal> object that has
      composite non-primitive types (e.g., Set and Address). After we execute
      the <literal>pubObject</literal> call, the resulting tree node will
      schematically look like the cache node in the following figures after we
      have executed calls:</para>

      <programlisting>Person joe = new Person();
joe.setAddress(new Address());

cache.putObject("/aop/joe", joe);</programlisting>

      <para>The TreeCacheAop APIs will be explained in fuller details later.
      But notice the illustration of object mapping by reachability in the
      following figure.<figure>
          <title>Object mapping by reachability</title>

          <mediaobject>
            <imageobject>
              <imagedata fileref="images/map.gif" />
            </imageobject>
          </mediaobject>
        </figure></para>

      <para>Under the fqn <literal>"/aop/joe"</literal>, there are three
      children nodes: <literal>addr</literal>, <literal>skill</literal>, and
      <literal>language</literal>. If you look at the
      <literal>Person</literal> class declaration, you will find that
      <literal>addr</literal> is an <literal>Address</literal> class,
      <literal>skill</literal> is a <literal>Set</literal>, and
      <literal>language</literal> is a <literal>List</literal> type. Since
      they are non-primitive, they are recursively inserted under the parent
      object (<literal>joe</literal>) until all primitive types are reached.
      In this way, we have broken down the object graph into a tree view which
      fit into our internal structure nicely. Also note that all the primitive
      types will be stored as a <literal>Map</literal> inside the respective
      node (e.g., <literal>addr</literal> will have <literal>Zip</literal>,
      <literal>Street</literal>, etc. store there).</para>

      <para>Here is a code snippet to demonstrate the object mapping by
      reachability feature. Notice how a <literal>Person</literal> object
      (e.g., <literal>joe</literal>) that has complex object references will
      be mapped into the underlying cache store as explained above.</para>

      <programlisting>import org.jboss.cache.PropertyConfigurator;
import org.jboss.cache.aop.TreeCacheAop;
import org.jboss.test.cache.test.standAloneAop.Person;
import org.jboss.test.cache.test.standAloneAop.Address;

TreeCacheAop tree = new TreeCacheAop();
PropertyConfigurator config = new PropertyConfigurator(); // configure tree cache.
config.configure(tree, "META-INF/replSync-service.xml");

Person joe = new Person(); // instantiate a Person object named joe
joe.setName("Joe Black");
joe.setAge(31);

Address addr = new Address(); // instantiate a Address object named addr
addr.setCity("Sunnyvale");
addr.setStreet("123 Albert Ave");
addr.setZip(94086);
joe.setAddress(addr); // set the address reference

tree.startService(); // kick start tree cache
tree.putObject("/aop/joe", joe); // add aop sanctioned object (and sub-objects) into cache.
// since it is advisable, use of plain get/set methods will take care of cache contents automatically.
joe.setAge(41);</programlisting>

      <para>Note that a typical <literal>TreeCacheAop</literal> usage involves
      instantiating the <literal>TreeCacheAop</literal>, configuration, and
      start the cache instance. Then, a user creates the advisable POJO that
      will be put into the cache using <literal>putObject()</literal>
      API.</para>

      <para>In addition, TreeCacheAop also supports get/set with parameter
      type of some <literal>Collection</literal> classes (i.e.,
      <literal>List</literal>, <literal>Map</literal>, and
      <literal>Set</literal>). For example, the following code snippet in
      addition to the above example will trigger TreeCacheAop to manage the
      states for the <literal>Lanugages</literal> list as well:</para>

      <programlisting>ArrayList lang = new ArrayList();
lang.add("Ensligh");
lang.add("Mandarin");
joe.setLanguages(lang);</programlisting>
    </section>

    <section>
      <title>Object relationship management</title>

      <para>During the mapping process, we will also need to check whether any
      of its associated object is multiple or circular referenced. A reference
      counting mechanism has been implemented associating with the
      <literal>CacheInterceptor</literal>. If the cache detects an object has
      been referenced more than twice for the first time, it will re-locate
      the current object node to an internal area. Afterwards, all object node
      will be referenced indirectly to there.</para>

      <para>To look at one example, we say that multiple
      <literal>Person</literal> objects can own the same
      <literal>Address</literal> (e.g., a household). Graphically, here is
      what it will look like in the tree nodes:</para>

      <figure>
        <title>Schematic illustration of object graph handling</title>

        <mediaobject>
          <imageobject>
            <imagedata fileref="images/graph.gif" />
          </imageobject>
        </mediaobject>
      </figure>

      <para>Notice in the <literal>addr</literal> node for both
      <literal>joe</literal> and <literal>mary</literal>, they are grayed out
      and have dashed arrows pointing toward an internal
      <literal>addr</literal> node instead.</para>

      <para>In the following code snippet, we show how a TreeCacheAop can
      handle multiple object references, namely, an <literal>Address</literal>
      can be shared among multiple <literal>Person</literal> objects (e.g.,
      <literal>joe</literal> and <literal>mary</literal>).</para>

      <programlisting>import org.jboss.cache.PropertyConfigurator;
import org.jboss.cache.aop.TreeCacheAop;
import org.jboss.test.cache.test.standAloneAop.Person;
import org.jboss.test.cache.test.standAloneAop.Address;

TreeCacheAop tree = new TreeCacheAop();
PropertyConfigurator config = new PropertyConfigurator(); // configure tree cache.
config.configure(tree, "META-INF/replSync-service.xml");

Person joe = new Person(); // instantiate a Person object named joe
joe.setName("Joe Black");
joe.setAge(31);

Person mary = new Person(); // instantiate a Person object named mary
mary.setName("Mary White");
mary.setAge(30);

Address addr = new Address(); // instantiate a Address object named addr
addr.setCity("Sunnyvale");
addr.setStreet("123 Albert Ave");
addr.setZip(94086);

joe.setAddress(addr); // set the address reference
mary.setAddress(addr); // set the address reference

tree.startService(); // kick start tree
tree.putObject("/aop/joe", joe); // add aop sanctioned object (and sub-objects) into cache.
tree.putObject("/aop/mary", mary); // add aop sanctioned object (and sub-objects) into cache.

Address joeAddr = joe.getAddress();
Address maryAddr = mary.getAddress(); // joeAddr and maryAddr should be the same

tree.removeObject("/aop/joe");
maryAddr = mary.getAddress(); // Should still have the address.</programlisting>

      <para>Notice that after we remove <literal>joe</literal> instance from
      the cache, <literal>mary</literal> should still have reference the same
      <literal>Address</literal> object in the cache store.</para>
    </section>

    <section>
      <title>Object inheritance hierarchy</title>

      <para>TreeCacheAop preserves the POJO object inheritance hierarchy
      automatically. For example, if a <literal>Student</literal> extends
      <literal>Person</literal> with an additional field
      <literal>year</literal> (see following POJO example), then once
      <literal>Student</literal> is put into the cache, all the base class
      attributes of <literal>Person</literal> will be managed as well.</para>

      <para>Following is a code snippet that illustrates how the inheritance
      behavior of a POJO is maintained.</para>

      <programlisting>import org.jboss.test.cache.test.standAloneAop.Student;

Student joe = new Student();  // Student extends Person class
joe.setName("Joe Black"); // This is base class attributes
joe.setAge(22);  // This is also base class attributes
joe.setYear("Senior"); // This is Student class attribute

tree.putObject("/aop/student/joe", joe);

//...

joe = (Student)tree.putObject("/aop/student/joe");
Person person = (Person)joe; // it will be correct here
joe.setYear("Junior"); // will be intercepted by the cache
joe.setName("Joe Black II"); // also intercepted by the cache</programlisting>
    </section>
  </section>

  <section>
    <title>Requirement</title>

    <para><literal>TreeCacheAop</literal> requires the following libraries (in
    addition to jboss-cache.jar and the required libraries for the plain
    TreeCache), and specific class loader (if it is not per-compiled) during
    start up:</para>

    <itemizedlist>
      <listitem>
        <para>Library: jboss-aop.jar, trove.jar, and javassist.jar.</para>
      </listitem>

      <listitem>
        <para>Classloader: To run under the <ulink
        url="http://jboss.org/wiki/Wiki.jsp?page=JBossAOP"> JBoss standalone
        Aop framework</ulink> without pre-compiling, you will need to use the
        AOP system class loader, i.e., you will need to specify the class
        loader during start up as:
        -Djava.system.class.loader=org.jboss.aop.standalone.SystemClassLoader.</para>
      </listitem>
    </itemizedlist>
  </section>

  <section>
    <title>API</title>

    <para>There are 3 core APIs for <literal>TreeCacheAop</literal>:</para>

    <itemizedlist>
      <listitem>
        <para><literal>Object putObject(String fqn, Object pojo)</literal>
        where <literal>fqn</literal> is a user-specified fully qualified name
        (<literal>FQN</literal>) to store the node in the underlying cache,
        e.g., "<literal>/aop/joe</literal>", and <literal>pojo</literal> is
        the object instance to be managed by
        <literal>TreeCacheAop</literal>.</para>

        <para>This call returns the existing object under fqn. As a result, a
        successful call will replace that old value with pojo instance, if it
        exists. Note that a user will only need to issue this call once for
        each pojo. Once it is executed, <literal>TreeCacheAop</literal> will
        assign an interceptor for the pojo instance and its
        sub-objects.</para>
      </listitem>

      <listitem>
        <para><literal>Object getObject(String fqn)</literal>. This call will
        return the current object content located under
        <literal>fqn</literal>. This method call is useful when you start a
        replicated node and you want to get the object reference.</para>
      </listitem>

      <listitem>
        <para><literal>Object removeObject(String fqn)</literal>. This call
        will remove the contents under fqn and return the POJO instance stored
        there (null if it doesn't exist). Note this call will also remove
        everything stored under <literal>fqn</literal>.</para>
      </listitem>
    </itemizedlist>
  </section>

  <section>
    <title>Configuration</title>

    <para>In addition to the TreeCache configuration xml file, you will also
    need a <literal>META-INF/jboss-aop.xml</literal> file located under the
    class path (unless you use aopc to pre-compile the byte code). JBoss AOP
    framework will read this file during startup to make necessary byte code
    manipulation for advice and introduction. You will need to declare any of
    your POJO to be "advisable" so that AOP framework knows to start
    intercepting either method, field, or constructor invocations. The
    standalone <literal>TreeCacheAop</literal> package provides an example
    declaration for the tutorial classes, namely, <literal>Person</literal>
    and <literal>Address</literal>. Detailed class declaration for
    <literal>Person</literal> and <literal>Address</literal> are provided in
    the next section. But here is the snippet for
    <literal>META-INF/jboss-aop.xml</literal>:</para>

    <programlisting>&lt;aop&gt; 
  &lt;prepare expr="field(* $instanceof{org.jboss.test.cache.test.standAloneAop.Address}-&gt;*)" /&gt;
  &lt;prepare expr="field(* $instanceof{org.jboss.test.cache.test.standAloneAop.Person}-&gt;*)" /&gt;
&lt;/aop&gt;</programlisting>

    <para>Detailed semantics of <literal>jboss-aop.xml</literal> can be found
    in JBossAop. But above statements basically declare all field read and
    write will be "advised".</para>
  </section>

  <section>
    <title id="examplePOJO">Example POJO</title>

    <para>The example POJO classes used for <ulink
    url="Tutorial.html">Tutorial</ulink> are: <literal>Person</literal> and
    <literal>Address</literal>. Here is the snippet of the class definition
    for <literal>Person</literal> and <literal>Address</literal> (note that
    neither class implements <literal>Serializable</literal>).</para>

    <programlisting>public class Person {
   String name=null; 
   int age=0; 
   Map hobbies=null; 
   Address address=null; 
   Set skills; 
   List languages; 
   
   public String getName() { return name; } 
   public void setName(String name) { this.name=name; } 

   public int getAge() { return age; }
   public void setAge(int age) { this.age = age; }

   public Map getHobbies() { return hobbies; }
   public void setHobbies(Map hobbies) { this.hobbies = hobbies; }

   public Address getAddress() { return address; }
   public void setAddress(Address address) { this.address = address; }

   public Set getSkills() { return skills; }
   public void setSkills(Set skills) { this.skills = skills; }

   public List getLanguages() { return languages; }
   public void setLanguages(List languages) { this.languages = languages; }
}</programlisting>

    <programlisting>public class Student extends Person {
   String year=null;

   public String getYear() { return year; }
   public void setYear(String year) { this.year=year; }
}</programlisting>

    <programlisting>public class Address {
   String street=null; 
   String city=null; 
   int zip=0; 

   public String getStreet() { return street; } 
   public void setStreet(String street) { this.street=street; } 
   ...
}</programlisting>
  </section>

  <section>
    <title>TreeCacheAop eviction policy</title>

    <para>TreeCacheAop also provides an eviction policy,
    <literal>org.jboss.cache.eviction.AopLRUPolicy</literal>, that is a
    subclass of <literal>org.jboss.cache.eviction.LRUPolicy</literal> with the
    same configuration parameters. Eviction in TreeCacheAop is quite different
    in that in aop world, first of all the concept of a unit is
    <emphasis>object</emphasis> (which can have multiple nodes and children
    nodes).</para>

    <para>Second of all, once a user obtains a POJO reference, everything is
    supposed to be transparent, e.g., cache retrieval and update operations.
    But if an object is evicted, that means there is no
    <literal>CacheInterceptor</literal> for the POJO, and the contents are not
    intercepted by the cache. Instead, everything will be channeled to the
    in-memory reference. Then a user has no way of knowing this fact!</para>

    <para>We could have thrown a runtime exception when a user is accessing a
    "evicted" node. But this is intrusive and not ideal. What we should do
    then is to evict an object (by removing all the nodes and children nodes).
    But we leave the <literal>CacheInterceptor</literal> for that POJO (and
    any sub-POJOs) alone. This way, when a user is using the POJO methods, it
    will still get intercepted by the <literal>CacheInterceptor</literal>. And
    if it finds that the node is empty, then it will also check to see if the
    eventual invocation from the in-memory reference is null or not. If not
    null, we know this is an evicted node and we will need to populate this
    object in TreeCacheAop based on the in-memory value.</para>
  </section>

  <section>
    <title>Limitations and Problems</title>

    <para>Here are some of the current limitation in
    <literal>TreeCacheAop</literal>.</para>

    <itemizedlist>
      <listitem>
        <para>Currently, plain TreeCache and <literal>TreeCacheAop</literal>
        can share the same fqn name, i.e., the same node. However, if you do
        remove the node, you will remove any contents associated with that
        nodes, say, both remove(String fqn) and removeObject(String fqn,
        Object pojo) method calls. Both method calls currently do not
        differentiate the content type. This limit will be removed in the
        future release.</para>
      </listitem>
    </itemizedlist>
  </section>
</article>