0 Replies Latest reply on Apr 24, 2003 8:13 PM by Bela Ban

    Some thoughts on AOP and TreeCache (long posting)

    Bela Ban Master

      I have written a short document outlining my ideas for using AOP to transparently replicating POJOs using the TreeCache.

      I'll past it below. It is also available in the CVS under jboss-head/cache/docs/TreeCacheAop.txt.

      Comments welcome !


      AOP-enhanced TreeCache

      Author: Bela Ban (bela@jboss.org)
      Revision: $Id: TreeCacheAop.txt,v 1.5 2003/04/24 23:45:26 belaban Exp $


      Put any plain old Java object into the TreeCache and have it
      replicated transactionally (or however the cache is configured) across

      Any change to the object should not require an app programmer to tell
      the cache, in order for the cache to replicate the
      modification. Instead, the cache 'knows' (using AOP) when the object
      has been modified, and what fields have been changed.


      Provide a subclass org.jboss.cache.TreeCacheAop (skeleton already
      exists) of org.jboss.cache.TreeCache.

      Add methods putObject(String fqn, Object pojo), getObject(String fqn)
      and removeObject(String fqn) to TreeCacheAop. These are the main

      Users who want to use the AOP-enhanced cache can instantiate
      TreeCacheAop, rather than TreeCache. I may merge those 2 classes at
      some later time. But currently this allows me to separate non-AOP and
      AOP code.

      All objects to be put into the cache must be instrumented (see AOP XML

      Whenever an object is added to the cache, putObject() needs to do the

      First it needs to cast the object to Advisable, get the list of
      interceptors and add itself (CacheInterceptor) to the list (if not yet

      A CacheInterceptor either intercepts all set*() and get*() methods
      (using the setter/getter paradigm used e.g. for entity beans in CMP
      2.0), or intercepts all field access (reads and writes)

      When a CacheInterceptor is created, it is given the location in the
      tree where the object resides, e.g. "/persons/322649".

      Then, the putObject() method needs to map the fields of the object to
      the cache: e.g. "age" and "name" become keys in the "/persons/322649"
      node's hashmap (see TreeCache javadoc for details about the structure
      of the TreeCache). This obviously requires reflection (or maybe AOP as

      Whenever a read is intercepted (e.g. getter method getAge() or field
      access get("age")), we return the value associated with the key in the

      Whenever a field changes (setter method or field access), we modify
      the value in the hashmap.

      When getObject(String fqn) is called, we locate the node in the
      tree. Each object has its fully qualified classname in the hashmap
      associated with that node. We create an instance of that class (object
      has to have a null constructor), and set all fields recursively using
      reflection and the values in the tree.

      When removeObject() is called, we remove the object from the cache,
      and also remove our interceptor from its interceptor chain.

      Instrumenting object graphs:

      When an object has non-primitive fields, e.g. Address and LinkedList,
      we will need to recursively add interceptors for those objects as

      E.g. when the Person object has a field "addr" of type Address, we
      will need to add an interceptor to the "addr" object whenever it is
      set. That interceptor also needs to 'know' where in the tree the
      "addr" object is located, e.g. "/persons/322649/addr".

      If any field inside the "addr" object changes (e.g.
      p.getAddress().setCity("San Jose CA")), the interceptor needs to
      update the corresponding value in that node's hashmap, e.g.
      super.put("/persons/322649/addr", "city", "San Jose"). The cache will
      take care of acidity and replication.

      For system classes such as java.util.LinkedList etc, we will need to
      use either (a) ClassProxies or (b) modify the ClassPool directly a la
      Bill Burke's proposal.

      Example of mapped object:

      - "class_name_unique" --> "my.package.Person
      - "age" --> 38 (Long)
      - "name" --> "Bela Ban" (String)

      - "class_name_unique" --> my.package.Address
      - "city" --> "San Jose CA"
      - "street" --> "1704 Almond Blossom Lane"

      Optimistic locking (versioning):
      (pasted from an email conversation with Bill Burke)

      I'm trying to get my head around how interceptors could be used in the
      TreeCache for hanslding an aspect such as locking. I looked at (a)
      interceptors for each object in the cache and (b) interceptors for the
      cache (TreeCache MBean) itself.

      I couldn't find a solution, have to invest some more time. It would be nice if we could extract the 'locking' aspect into interceptors.

      What I *did* come up with, however, is an easy scheme how to add versioning to the current TreeCache:

      * In findNode(String fqn), instead of locking the path to the node,
      I create a copy of the node in a versioned workspace (if it
      doesn't yet exist). Otherwise I just return the Node from the
      workspace associated with the TX
      * On commit, I do *exactly the same I already do*: I ship all
      updates to all nodes in a PREPARE phase and on success send a
      COMMIT. The one change is that before applying an update I have to
      check for the version ID and acquire the lock. In my scheme, it is
      just lock acquisition, no version check.

      Maybe I'm missing something, but this seems trivial. I guess the trick
      is to avoid copying too much...

      So, don't be disappointed: for now I don't use locking/versioning in
      an interceptor, but I will think more about this when I'm done with
      the cache. Also, the TreeCacheAop class looks hot ! I definitely want
      to demo this one at JBossTwo. You game ?