<chapter id="architecture">

   <title>Architecture</title>

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

      <sect1>
         <title>Dynamic AOP interception</title>

         <para>JBossAop provides an API (<literal>appendInterceptor</literal>) to
            add an interceptor at runtime. PojoCache uses this feature
            extensively to provide user transparency. Every "aspectized" 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), PojoCache 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
            PojoCache to dynamically manage cache contents.) If it has not, one
            will be added to
            <literal>InstanceAdvisor</literal>
            object. Afterward,
            any POJO field modification will invoke the corresponding
            <literal>CacheInterceptor</literal>
            instance. Below is a schematic
            illustration of this process.
         </para>

         <para>JBossAop has the capability to intercept both method level call and field level read write. From the
            perspective of PojoCache, field level interception is the appropriate mechanism to synchronize with
            the backend cache store. Please note that,
            <itemizedlist>
               <listitem>
                  the filed level interception applies to all access qualifiers.
                  That is, regardless whether it is <literal>public</literal>, <literal>protected</literal>, or
                  <literal>private</literal>
               </listitem>
               <listitem>
                  we skip interception for field with <literal>final</literal>, <literal>static</literal>,
                  and <literal>transient</literal> qualifiers. As a result, any field with these 3 qualifiers will not
                  be replicated or persisted.
               </listitem>
            </itemizedlist>
         </para>
<para>The figures shown below illustrate 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, you should see the difference between
            these figures. While field write operation will go to cache first and,
            then, invoke the in-memory update, the field read invocation does not
            involve in-memory reference at all. This is because the value in cache
            and memory should have been synchronized during write operation. As a
            result, the field value from the cache is returned.
         </para>

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

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

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

            <mediaobject>
               <imageobject>
                  <imagedata fileref="images/get.png"/>
               </imageobject>
            </mediaobject>
         </figure>
      </sect1>

      <sect1>
         <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
            "prepared" (e.g., a
            <literal>Person</literal>
            object), during the
            <literal>putObject(Fqn fqn, Object pojo)</literal>
            operation,
            PojoCache 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.</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 class definition from the Appendix
            section where we have a
            <literal>Person</literal>
            object that has
            composite non-primitive types (e.g., List and Address). After we execute
            the
            <literal>putObject</literal>
            call, the resulting tree node will
            schematically look like the cache node in the following figures:
         </para>

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

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

         <para>The PojoCache APIs will be explained in fuller details later.
            But notice the illustration of object mapping by reachability in the
            following figure. The fqn <literal>/aop/joe</literal> is associated with the POJO
            <literal>joe</literal>.
         Then under that fqn, there are three
            children nodes:
            <literal>addr,</literal>

            <literal>skills,</literal>
             and
            <literal>languages.</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>skills</literal>
            is a
            <literal>Set</literal>
            , and
            <literal>languages</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
            inside the respective
            node's HashMap (e.g.,
            <literal>addr</literal>
            will have
            <literal>Zip</literal>
            ,
            <literal>Street</literal>
            , etc. stored there).
         </para>

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

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

         <para>Here is a code snippet to demonstrate the object mapping by
            reachability feature that we just explained. 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.PojoCache;
import org.jboss.test.cache.test.standAloneAop.Person;
import org.jboss.test.cache.test.standAloneAop.Address;

PojoCache tree = new PojoCache();
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 aspectized, use of plain get/set methods will take care of cache contents automatically.
joe.setAge(41);
</programlisting>

         <para>Note that a typical
            <literal>PojoCache</literal>
            usage involves
            instantiating the
            <literal>PojoCache</literal>
            , configuring, and
            starting the cache instance. Then, a user creates the aspectized POJO
            that will be put into the cache using
            <literal>putObject()</literal>
            API.
         </para>

         <para>In addition, PojoCache 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>
            ) automatically. For example, the following code snippet in
            addition to the above example will trigger PojoCache to manage the
            states for the
            <literal>Languages</literal>
            list as well. The following figure illustrates the node structure from the replicated GUI point of view.
            Details of Collection class support will be given later.
         </para>

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

         <figure>
            <title>Schematic illustration of List class mapping</title>

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

      <sect1>
         <title>Object relationship management</title>

         <para>Like we have mentioned, traditional cache system does not support object relationship
            management during serialization (be it to the persistent data store or
            replicated to the other in-memory nodes.) Examples of object
            relationship are like an address object is shared by members of the
            household, and a child-parent relationship. All these relationship will
            be lost once the objects are replicated or persisted. As a result, explicit mapping
            will be needed outside of the cache system to express the object
            relationship. PojoCache, in contrast, can manage object relationship
            transparently for users.</para>

         <para>During the mapping process, we will 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 a new object created in the cache referenced to another
            POJO, a referenced <literal>fqn</literal> will be stored there to redirect any query and update
            to the original node.
         </para>

         <para>To look at one example, let's say that multiple
            <literal>Person</literal>s ("joe" and "mary")
            objects can own the same
            <literal>Address</literal>
            (e.g., a household). Graphically, here is
            what it will look like in the tree nodes. Like we have covered in the previous section on the mapping by
            reachability, the POJO will map recursively into the cache. However, when we detect a multiple reference
            (in this case, the <literal>Address</literal>), we will keep track of the reference counting for the sub-object
            <literal>addr.</literal>
         </para>

         <figure>
            <title>Schematic illustration of object relationship mapping</title>

            <mediaobject>
               <imageobject>
                  <imagedata fileref="images/reference.png"/>
               </imageobject>
            </mediaobject>
         </figure>

         <para>In the following code snippet, we show programmatically the object sharing example.
         </para>

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

PojoCache tree = new PojoCache();
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 instance

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>

         <para>To further illustrate this relationship management, let's examine the Java code under a replicated
         environment. Imagine that we have two separate cache instances in the cluster now (<literal>cache1</literal>
            and <literal>cache2</literal>). Let's say, on the first cache instance, we put both <literal>joe</literal>
            and <literal>mary</literal> under cache management as above. Then, we failover to <literal>cache2.</literal>
            Here is the code
            snippet:
         </para>
<programlisting>
/**
 * Code snippet on cache2 during fail-over
 */
import org.jboss.cache.PropertyConfigurator;
import org.jboss.cache.aop.PojoCache;
import org.jboss.test.cache.test.standAloneAop.Person;
import org.jboss.test.cache.test.standAloneAop.Address;

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

tree.startService(); // kick start tree
Person joe = tree.getObject("/aop/joe"); // retrieve the POJO reference.
Person mary = tree.getObject("/aop/mary"); // retrieve the POJO reference.

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

maryAddr = mary.getAddress().setZip(95123);
int zip = joeAddr.getAddress().getZip(); // Should be 95123 as well instead of 94086!
</programlisting>


      </sect1>

      <sect1>
         <title>Object inheritance hierarchy</title>

         <para>PojoCache 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 POJO definition in the Appendix section), then once
            <literal>Student</literal>
            is put into the cache, all the class
            attributes of
            <literal>Person</literal>
            can be managed as well.
         </para>

         <para>Following is a code snippet that illustrates how the inheritance
            behavior of a POJO is maintained. Again, no special configuration is
            needed.</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>
      </sect1>

      <sect1>
         <title>Collection class proxy</title>

         <para>The POJO classes that inherit from
            <literal>Set</literal>
            ,
            <literal>List</literal>
            , and
            <literal>Map</literal>
            are treated as
            "aspectized" automatically. That is, users need not declare them
            "prepared" in the xml configuration file or via annotation.
            Since we are not allowed to instrument the Java system library, we will use a
            proxy approach instead. That is, when we encounter any Collection instance, we will:
            <itemizedlist>
               <listitem>
                  Create a Collection proxy instance and place it in the cache (instead of the original reference).
                  The mapping of the Collection elements will still be carried out recursively as expected.
               </listitem>
               <listitem>
                  If the Collection instance is a sub-object, e.g., inside another POJO, we will swap out the original reference
                  with the new proxy one to promote transparent usage.
               </listitem>
            </itemizedlist>
            To obtain the proxy reference,
            users can then use another
            <literal>getObject</literal>
            to retrieve this proxy reference and use
            this reference to perform POJO operations.
         </para>

         <para>Here is a code snippet that illustrates the usage of a Collection proxy reference:
          </para>
<programlisting>
List list = new ArrayList();
list.add("ONE");
list.add("TWO");

tree.putObject("/aop/list", list);
list.add("THREE"); // This won't intercept by the cache!

List proxyList = tree.getObject("/aop/list"; // Note that list is a proxy reference
proxyList.add("FOUR"); // This will be intercepted by the cache
</programlisting>

         <para>
            Here is another snippet to illustrate the dynamic swapping of the Collection reference when it
            is embedded inside another object:
         </para>
<programlisting>
Person joe = new Person();
joe.setName("Joe Black"); // This is base class attributes
ArrayList lang = new ArrayList();
lang.add("English");
lang.add("Mandarin");
joe.setLanguages(lang);
// This will map the languages List automatically and swap it out with the proxy reference.
tree.putObject("/aop/student/joe", joe);
ArrayList lang = joe.getLanguages(); // Note that lang is a proxy reference
lang.add("French"); // This will be intercepted by the cache
</programlisting>

         <para>As you can see,
            <literal>getLanguages</literal>
            simply returns the
            field reference that has been swapped out for the proxy reference
            counterpart.
         </para>

         <para>
            Finally, when you remove a Collection reference from the cache (e.g., via <literal>removeObject</literal>),
             you still can use the proxy reference since we will update the in-memory copy of that reference during
            detachment.  Below is a code snippet illustrating this:
         </para>
<programlisting>
List list = new ArrayList();
list.add("ONE");
list.add("TWO");

tree.putObject("/aop/list", list);
List proxyList = tree.getObject("/aop/list"); // Note that list is a proxy reference
proxyList.add("THREE"); // This will be intercepted by the cache

tree.removeObject("/aop/list"); // detach from the cache
proxyList.add("FOUR"); // proxyList has 4 elements still.
</programlisting>
         <sect2>
            <title>Limitation</title>
            <para>Use of Collection class in PojoCache helps you to track fine-grained changes
            in your collection fields automatically. However, current implementation has the follow
            limitation that we plan to address soon.</para>
            <para>Currently, we only support a limited implementation of Collection classes. That is,
                   we support APIs in List, Set, and Map. However, since the APIs do not stipulate
                   of constraints like NULL key or value, it makes mapping of user instance to our proxy tricky.
                   For example, ArrayList would allow NULL value and some other implementation would not.
                   The Set interface maps to java.util.HashSet implementation.  The List interface maps
                   to java.util.ArrayList implementation.  The Map interface maps to java.util.HashMap
                   implementation.
                </para>
             <para>Another related issue is the expected performance. For example, the current implementation is ordered, so
                 that makes insert/delete from the Collection slow.  Performance between Set, Map and List collections also vary.
                 Adding items to a Set is slower than a List or Map, since Set does not allow duplicate entries.</para>
         </sect2>
      </sect1>

</chapter>
