1 Reply Latest reply on Apr 27, 2003 12:15 PM by Bela Ban

    Open task on AOP and TreeCache - volunteers ?

    Bela Ban Master

      Hi all,

      I have an open task, the scope of which is fairly well-defined, and I believe it is very interesting work. I'm posting this job because I currently lack the time to do it myself, and because it is a fairly self-contained job, so it won't interfere with my other cache work.

      The task is described below and is also in the CVS at jboss-head/cache/docs/TreeCacheAop.txt.

      This is an opportunity to get involved and get to know (a) the new replicated cache and (b) the AOP framework in JBoss as implemented by Bill. You will be working on the bleeding edge :-)

      Requirements:
      - knowledge of reflection API
      - some knowledge of JBoss architecture
      - knowledge of or willingness to learn the AOP framework
      - knowledge of or willingness to learn the Cache framework (org.jboss.cache.TreeCache)

      Please contact me if you're interested.




      AOP-enhanced TreeCache
      ======================



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



      Goal:
      -----

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

      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.



      Task:
      -----

      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
      methods.

      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
      files).

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

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

      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
      well).

      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
      hashmap.

      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
      well.

      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:
      -------------------------

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

      /persons/322649/addr:
      - "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...