[jboss-cvs] JBossCache/src/org/jboss/cache/pojo/impl ...

Ben Wang bwang at jboss.com
Sat Jan 13 10:55:06 EST 2007


  User: bwang   
  Date: 07/01/13 10:55:06

  Added:       src/org/jboss/cache/pojo/impl       
                        SerializableObjectHandler.java
                        PojoCacheDelegate.java PojoCacheImpl.java
                        ObjectGraphHandler.java CollectionClassHandler.java
                        InternalHelper.java AdvisedPojoHandler.java
  Log:
  JBCACHE-922 Merged src-50 and tests-50 into src and tests, respectively.
  
  Revision  Changes    Path
  1.1      date: 2007/01/13 15:55:06;  author: bwang;  state: Exp;JBossCache/src/org/jboss/cache/pojo/impl/SerializableObjectHandler.java
  
  Index: SerializableObjectHandler.java
  ===================================================================
  /*
   * JBoss, Home of Professional Open Source
   *
   * Distributable under LGPL license.
   * See terms of license at gnu.org.
   */
  
  package org.jboss.cache.pojo.impl;
  
  import org.apache.commons.logging.Log;
  import org.apache.commons.logging.LogFactory;
  import org.jboss.cache.CacheException;
  import org.jboss.cache.Fqn;
  import org.jboss.cache.CacheSPI;
  import org.jboss.cache.pojo.PojoTreeCache;
  import org.jboss.cache.pojo.PojoInstance;
  import org.jboss.cache.pojo.InternalConstant;
  
  import java.util.HashMap;
  import java.util.Map;
  
  /**
   * Handle Serializable object cache management.
   *
   * @author Ben Wang
   * @version $Id: SerializableObjectHandler.java,v 1.1 2007/01/13 15:55:06 bwang Exp $
   */
  class SerializableObjectHandler
  {
     private CacheSPI cache_;
     private PojoCacheImpl pCache_;
     private InternalHelper internal_;
     private final Log log_ = LogFactory.getLog(SerializableObjectHandler.class);
  
     public SerializableObjectHandler(PojoCacheImpl cache, InternalHelper internal)
     {
        pCache_ = cache;
        cache_ = (CacheSPI)pCache_.getCache();
        internal_ = internal;
     }
  
     Object get(Fqn fqn, Class clazz, PojoInstance pojoInstance)
             throws CacheException
     {
        Object obj = internal_.get(fqn, InternalConstant.SERIALIZED);
        return obj;
     }
  
  
     boolean put(Fqn fqn, Object obj)
             throws CacheException
     {
          // Note that JBoss Serialization can serialize any type now.
           if (log_.isDebugEnabled())
           {
              log_.debug("put(): obj (" + obj.getClass() + ") is non-advisable but serialize it anyway. "
              + "Note that if it is non-serializable we require to use JBoss Serialization.");
           }
  
           putIntoCache(fqn, obj);
           return true;
     }
  
     private void putIntoCache(Fqn fqn, Object obj)
             throws CacheException
     {
        Map map = new HashMap();
  
        // Special optimization here.
        PojoInstance pojoInstance = new PojoInstance();
        pojoInstance.set(obj);
        pojoInstance.setPojoClass(obj.getClass());
        map.put(PojoInstance.KEY, pojoInstance);
        // Note that we will only have one key in this fqn.
        map.put(InternalConstant.SERIALIZED, obj);
        internal_.put(fqn, map);
     }
  
     @SuppressWarnings({"CanBeStatic"})
     void remove()
     {
        // No need to do anything here since we will do clean up afterwards.
     }
  
  }
  
  
  
  1.1      date: 2007/01/13 15:55:06;  author: bwang;  state: Exp;JBossCache/src/org/jboss/cache/pojo/impl/PojoCacheDelegate.java
  
  Index: PojoCacheDelegate.java
  ===================================================================
  /*
   * JBoss, the OpenSource J2EE webOS
   *
   * Distributable under LGPL license.
   * See terms of license at gnu.org.
   */
  package org.jboss.cache.pojo.impl;
  
  import org.apache.commons.logging.Log;
  import org.apache.commons.logging.LogFactory;
  import org.jboss.aop.Advised;
  import org.jboss.aop.Advisor;
  import org.jboss.aop.InstanceAdvisor;
  import org.jboss.aop.advice.Interceptor;
  import org.jboss.aop.proxy.ClassProxy;
  import org.jboss.cache.CacheException;
  import org.jboss.cache.CacheSPI;
  import org.jboss.cache.Fqn;
  import org.jboss.cache.Node;
  import org.jboss.cache.NodeSPI;
  import org.jboss.cache.pojo.CachedType;
  import org.jboss.cache.pojo.PojoCacheException;
  import org.jboss.cache.pojo.PojoInstance;
  import org.jboss.cache.pojo.PojoReference;
  import org.jboss.cache.pojo.PojoUtil;
  import org.jboss.cache.pojo.collection.CollectionInterceptorUtil;
  import org.jboss.cache.pojo.interceptors.dynamic.AbstractCollectionInterceptor;
  import org.jboss.cache.pojo.interceptors.dynamic.BaseInterceptor;
  import org.jboss.cache.pojo.memory.FieldPersistentReference;
  import org.jboss.cache.pojo.observable.Observer;
  import org.jboss.cache.pojo.util.AopUtil;
  
  import java.lang.reflect.Field;
  import java.util.Collection;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  
  /**
   * Delegate class for PojoCache, the real implementation code happens here.
   *
   * @author Ben Wang
   */
  public class PojoCacheDelegate
  {
     private PojoCacheImpl pCache_;
     private CacheSPI cache_;
     private final static Log log = LogFactory.getLog(PojoCacheDelegate.class);
     private InternalHelper internal_;
     private AdvisedPojoHandler advisedHandler_;
     private ObjectGraphHandler graphHandler_;
     private CollectionClassHandler collectionHandler_;
     private SerializableObjectHandler serializableHandler_;
     // Use ThreadLocal to hold a boolean isBulkRemove
     private ThreadLocal<Boolean> bulkRemove_ = new ThreadLocal<Boolean>();
     private final String DETACH = "DETACH";
     private PojoUtil util_ = new PojoUtil();
     // Observer for field event notification
     private Observer observer_;
  
     public PojoCacheDelegate(PojoCacheImpl cache, Observer observer)
     {
        pCache_ = cache;
        cache_ = (CacheSPI) pCache_.getCache();
        internal_ = new InternalHelper(cache_);
        graphHandler_ = new ObjectGraphHandler(pCache_, internal_);
        collectionHandler_ = new CollectionClassHandler(pCache_, internal_);
        serializableHandler_ = new SerializableObjectHandler(pCache_, internal_);
        observer_ = observer;
        advisedHandler_ = new AdvisedPojoHandler(pCache_, internal_, util_, observer_);
     }
  
     public void setBulkRemove(boolean bulk)
     {
        bulkRemove_.set(bulk);
     }
  
     private boolean getBulkRemove()
     {
        return bulkRemove_.get();
     }
  
     public Object getObject(Fqn fqn) throws CacheException
     {
        // TODO Must we really to couple with BR? JBCACHE-669
        Object pojo = internal_.getPojo(fqn);
        if (pojo != null)
        {
           // we already have an advised instance
           if (log.isDebugEnabled())
           {
              log.debug("getObject(): id: " + fqn + " retrieved from existing instance directly. ");
           }
           return pojo;
        }
  
        // OK. So we are here meaning that this is a failover or passivation since the transient
        // pojo instance is not around. Let's also make sure the right classloader is used
        // as well.
        ClassLoader prevCL = Thread.currentThread().getContextClassLoader();
        try
        {
           if (cache_.getRegionManager() != null)
           {
              cache_.getRegionManager().setContextClassLoaderAsCurrent(fqn);
           }
           return getObjectInternal(fqn);
        }
        finally
        {
           Thread.currentThread().setContextClassLoader(prevCL);
        }
     }
  
     public Object putObjectI(Fqn fqn, Object obj) throws CacheException
     {
        // Skip some un-necessary update if obj is the same class as the old one
        Object oldValue = internal_.getPojo(fqn);
        if (oldValue == obj)
        {
           if (log.isDebugEnabled())
           {
              log.debug("putObject(): id: " + fqn + " pojo is already in the cache. Return right away.");
           }
           return obj;
        }
        return null;
     }
  
     /**
      * Note that caller of this method will take care of synchronization within the <code>fqn</code> sub-tree.
      */
     public Object putObjectII(Fqn fqn, Object obj) throws CacheException
     {
        // Skip some un-necessary update if obj is the same class as the old one
        Object oldValue = internal_.getPojo(fqn);
        if (oldValue == obj)
        {
           if (log.isDebugEnabled())
           {
              log.debug("putObject(): id: " + fqn + " pojo is already in the cache. Return right away.");
           }
           return obj;
        }
  
        // remove old value before overwriting it. This is necessary to detach any interceptor.
        // TODO Or can we simply walk thru that somewhere? Well, there is also implication of Collection though
        pCache_.detach(fqn);
  
        if (obj == null)
        {
           return oldValue;// we are done
        }
  
        // This create a PojoReference to store a pointer to the real fqn.
        Fqn internalFqn = createInternalFqn(fqn, obj);
        setPojoReference(fqn, obj, internalFqn);
        // creates the internal node first without going thru the interceptor.
        // This way we don't block on __JBossInternal__ node.
        createChildNodeFirstWithoutLocking(internalFqn);
  
        if (log.isDebugEnabled())
        {
           log.debug("putObject(): id: " + fqn + " will store the pojo in the internal area: "
                   + internalFqn);
        }
  
        if ((obj instanceof Advised || obj instanceof ClassProxy) && isMultipleReferencedPut(obj))
        {
           // we pass in the originating fqn intentionaly
           graphHandler_.put(fqn, obj);
        }
        else if (obj instanceof Advised)
        {
           advisedHandler_.put(internalFqn, fqn, obj);
        }
        else if (isCollection(obj))
        {
           collectionHandler_.put(internalFqn, fqn, obj);
           //
        }
        else
        {
           // must be Serializable, including primitive types
           serializableHandler_.put(internalFqn, obj);
        }
  
        return oldValue;
     }
  
     Fqn createInternalFqn(Fqn fqn, Object obj) throws CacheException
     {
        // Create an internal Fqn name
        return AopUtil.createInternalFqn(fqn, cache_);
     }
  
     Fqn setPojoReference(Fqn fqn, Object obj, Fqn internalFqn) throws CacheException
     {
        // Create PojoReference
        CachedType type = pCache_.getCachedType(obj.getClass());
        PojoReference pojoReference = new PojoReference();
        pojoReference.setPojoClass(type.getType());
  
        // store PojoReference
        pojoReference.setFqn(internalFqn);
        internal_.putPojoReference(fqn, pojoReference);
        if (log.isDebugEnabled())
        {
           log.debug("put(): inserting PojoReference with id: " + fqn);
        }
        // store obj in the internal fqn
        return internalFqn;
     }
  
     private void createChildNodeFirstWithoutLocking(Fqn internalFqn)
     {
        int size = internalFqn.size();
        Fqn f = internalFqn.getFqnChild(0, size - 1);
        Fqn child = internalFqn.getFqnChild(size - 1, size);
  
        NodeSPI base = cache_.getRoot().getChildDirect(f);
        if (base == null)
        {
           log.debug("The node retrieved is null from fqn: " + f);
           return;
        }
        base.addChildDirect(child);
     }
  
     /**
      * Note that caller of this method will take care of synchronization within the <code>fqn</code> sub-tree.
      *
      * @param fqn
      * @return
      * @throws CacheException
      */
     public Object removeObject(Fqn fqn) throws CacheException
     {
        // the class attribute is implicitly stored as an immutable read-only attribute
        PojoReference pojoReference = internal_.getPojoReference(fqn);
        if (pojoReference == null)
        {
           //  clazz and pojoReference can be not null if this node is the replicated brother node.
           if (log.isTraceEnabled())
           {
              log.trace("removeObject(): clazz is null. id: " + fqn + " No need to remove.");
           }
           return null;
        }
  
        Class clazz = pojoReference.getPojoClass();
        Fqn internalFqn = pojoReference.getFqn();
  
        if (log.isDebugEnabled())
        {
           log.debug("removeObject(): removing object from id: " + fqn
                   + " with the corresponding internal id: " + internalFqn);
        }
  
        Object result = pCache_.getObject(internalFqn);
        if (result == null)
        {
           return null;
        }
  
        if (graphHandler_.isMultipleReferenced(internalFqn))
        {
           graphHandler_.remove(fqn, internalFqn, result);
        }
        else if (Advised.class.isAssignableFrom(clazz))
        {
           advisedHandler_.remove(internalFqn, result, clazz);
           internal_.cleanUp(internalFqn);
        }
        else if (isCollectionGet(clazz))
        {
           // We need to return the original reference
           result = collectionHandler_.remove(internalFqn, result);
           internal_.cleanUp(internalFqn);
        }
        else
        {// Just Serializable objects. Do a brute force remove is ok.
           serializableHandler_.remove();
           internal_.cleanUp(internalFqn);
        }
  
        internal_.cleanUp(fqn);
        // remove the interceptor as well.
        return result;
     }
  
     public Map findObjects(Fqn fqn) throws CacheException
     {
  
        // Traverse from fqn to do getObject, if it return a pojo we then stop.
        Map map = new HashMap();
        Object pojo = getObject(fqn);
        if (pojo != null)
        {
           map.put(fqn, pojo);// we are done!
           return map;
        }
  
        findChildObjects(fqn, map);
        if (log.isDebugEnabled())
        {
           log.debug("_findObjects(): id: " + fqn + " size of pojos found: " + map.size());
        }
        return map;
     }
  
     private Object getObjectInternal(Fqn fqn) throws CacheException
     {
        // the class attribute is implicitly stored as an immutable read-only attribute
        PojoReference pojoReference = internal_.getPojoReference(fqn);
        if (pojoReference == null)
        {
           //  clazz and pojoReference can be not null if this node is the replicated brother node.
           return null;
        }
  
        // This is the real location for the pojo.
        Fqn internalFqn = pojoReference.getFqn();
        Class clazz = pojoReference.getPojoClass();
        if (log.isDebugEnabled())
        {
           log.debug("getObject(): id: " + fqn + " with a corresponding internal id: "
                   + internalFqn);
        }
  
        /**
         * Reconstruct the managed POJO
         */
        Object obj;
  
        PojoInstance pojoInstance = internal_.getAopInstance(internalFqn);
        if (pojoInstance == null)
        {
           throw new PojoCacheException("PojoCacheDelegate.getObjectInternal(): null PojoInstance for fqn: " +
                   internalFqn);
        }
  
        // Check for both Advised and Collection classes for object graph.
        // Note: no need to worry about multiple referencing here. If there is a graph, we won't come this far.
        if (Advised.class.isAssignableFrom(clazz))
        {
           obj = advisedHandler_.get(internalFqn, clazz, pojoInstance);
        }
        else if (isCollectionGet(clazz))
        {// Must be Collection classes. We will use aop.ClassProxy instance instead.
           obj = collectionHandler_.get(internalFqn, clazz, pojoInstance);
        }
        else
        {
           // Maybe it is just a serialized object.
           obj = serializableHandler_.get(internalFqn, clazz, pojoInstance);
        }
  
        InternalHelper.setPojo(pojoInstance, obj);
        return obj;
     }
  
     private boolean isCollectionGet(Class clazz)
     {
        if (Map.class.isAssignableFrom(clazz) || Collection.class.isAssignableFrom(clazz))
        {
           return true;
        }
  
        return false;
     }
  
  
     private boolean isMultipleReferencedPut(Object obj)
     {
        Interceptor interceptor = null;
        if (obj instanceof Advised)
        {
           InstanceAdvisor advisor = ((Advised) obj)._getInstanceAdvisor();
           if (advisor == null)
           {
              throw new PojoCacheException("_putObject(): InstanceAdvisor is null for: " + obj);
           }
  
           // Step Check for cross references
           interceptor = AopUtil.findCacheInterceptor(advisor);
        }
        else
        {
           interceptor = CollectionInterceptorUtil.getInterceptor((ClassProxy) obj);
        }
        if (interceptor == null) return false;
  
        Fqn originalFqn = null;
  
        // ah, found something. So this will be multiple referenced.
        originalFqn = ((BaseInterceptor) interceptor).getFqn();
  
        return originalFqn != null;
  
     }
  
     private boolean isCollection(Object obj)
     {
        return obj instanceof Collection || obj instanceof Map;
  
     }
  
     /**
      * Based on the pojo to perform a bulk remove recursively if there is no object graph
      * relationship for performance optimization.
      */
     private boolean bulkRemove(Fqn fqn, Object obj) throws CacheException
     {
        // Check for cross-reference. If there is, we can't do bulk remove
        // map contains (pojo, cacheinterceptor) pair that needs to rollback the the removal.
        Map undoMap = new HashMap();
        if (pojoGraphMultipleReferenced(obj, undoMap))
        {
           undoInterceptorDetach(undoMap);
           return false;
        }
        else
        {
           cache_.removeNode(fqn);// interceptor has been removed so it is safe to do bulk remove now.
        }
        return true;
     }
  
     private void detachInterceptor(InstanceAdvisor advisor, Interceptor interceptor,
                                    boolean detachOnly, Map undoMap)
     {
        if (!detachOnly)
        {
           util_.detachInterceptor(advisor, interceptor, observer_);
           undoMap.put(advisor, interceptor);
        }
        else
        {
           undoMap.put(DETACH, interceptor);
        }
     }
  
     private static void undoInterceptorDetach(Map undoMap)
     {
        for (Iterator it = undoMap.keySet().iterator(); it.hasNext();)
        {
           Object obj = it.next();
  
           if (obj instanceof InstanceAdvisor)
           {
              InstanceAdvisor advisor = (InstanceAdvisor) obj;
              BaseInterceptor interceptor = (BaseInterceptor) undoMap.get(advisor);
  
              if (interceptor == null)
              {
                 throw new IllegalStateException("PojoCacheDelegate.undoInterceptorDetach(): null interceptor");
              }
  
              advisor.appendInterceptor(interceptor);
           }
           else
           {
              BaseInterceptor interceptor = (BaseInterceptor) undoMap.get(obj);
              boolean copyToCache = false;
              ((AbstractCollectionInterceptor) interceptor).attach(null, copyToCache);
           }
        }
     }
  
     /**
      * Check recursively if the pojo and its graph is multiple referenced. If it is, we can't
      * do a bulk remove.
      */
     private boolean pojoGraphMultipleReferenced(Object obj, Map undoMap) throws CacheException
     {
        return true;
     }
  
     private boolean XpojoGraphMultipleReferenced(Object obj, Map undoMap) throws CacheException
     {
        // store object in cache
        if (obj instanceof Advised)
        {
           CachedType type = pCache_.getCachedType(obj.getClass());
           // add interceptor
           InstanceAdvisor advisor = ((Advised) obj)._getInstanceAdvisor();
           if (advisor == null)
           {
              throw new PojoCacheException("pojoGraphMultipleReferenced(): InstanceAdvisor is null for: " + obj);
           }
  
           BaseInterceptor interceptor = (BaseInterceptor) AopUtil.findCacheInterceptor(advisor);
           // just in case
           if (interceptor == null)
           {
              return false;
           }
           PojoInstance pojoInstance = interceptor.getAopInstance();
           // Check if there is cross referenced.
           if (pojoInstance.getRefCount() != 0) return true;// I have been referenced
           if (pojoInstance.getInternalFqn() != null) return true;// I am referencing others
  
           boolean hasFieldAnnotation = hasAnnotation(obj.getClass(), ((Advised) obj)._getAdvisor(), type);
           // Check the fields
           for (Iterator i = type.getFields().iterator(); i.hasNext();)
           {
              Field field = (Field) (((FieldPersistentReference) i.next())).get();
              Object value = null;
              try
              {
                 value = field.get(obj);
              }
              catch (IllegalAccessException e)
              {
                 throw new CacheException("field access failed", e);
              }
  
              CachedType fieldType = pCache_.getCachedType(field.getType());
  
              // we simply treat field that has @Serializable as a primitive type.
              if (fieldType.isImmediate() ||
                      (hasFieldAnnotation &&
                              CachedType.hasSerializableAnnotation(field, ((Advised) obj)._getAdvisor())))
              {
                 continue;
              }
  
              // check for non-replicatable types
              if (CachedType.isPrimitiveNonReplicatable(field))
              {
                 continue;
              }
  
              if (!hasFieldAnnotation)
              {
                 if (CachedType.hasTransientAnnotation(field, ((Advised) obj)._getAdvisor()))
                 {
                    continue;
                 }
              }
  
              // Need to do a getObject just in case this is a failover removeObject.
              if (value == null)
              {
                 value = getObject(new Fqn(interceptor.getFqn(), field.getName()));
              }
  
              if (value == null) continue;// this is no brainer.
  
              if (pojoGraphMultipleReferenced(value, undoMap)) return true;
           }
           boolean detachOnly = false;
           detachInterceptor(advisor, interceptor, detachOnly, undoMap);
        }
        else if (obj instanceof Map || obj instanceof List || obj instanceof Set)
        {
           // TODO Is this really necessary?
           if (!(obj instanceof ClassProxy)) return false;
  
           InstanceAdvisor advisor = ((ClassProxy) obj)._getInstanceAdvisor();
           BaseInterceptor interceptor = (BaseInterceptor) AopUtil.findCollectionInterceptor(advisor);
           PojoInstance pojoInstance = interceptor.getAopInstance();
           if (pojoInstance == null) return false;// safeguard
           // Check if there is cross referenced.
           if (pojoInstance.getRefCount() != 0) return true;// I have been referenced
           if (pojoInstance.getInternalFqn() != null) return true;// I am referencing others
           // iterate thru the keys
           if (obj instanceof Map)
           {
              for (Iterator it = ((Map) obj).keySet().iterator(); it.hasNext();)
              {
                 Object subObj = ((Map) obj).get(it.next());
                 if (pojoGraphMultipleReferenced(subObj, undoMap)) return true;
              }
           }
           else if (obj instanceof List || obj instanceof Set)
           {
              for (Iterator it = ((Collection) obj).iterator(); it.hasNext();)
              {
                 Object subObj = it.next();
                 if (pojoGraphMultipleReferenced(subObj, undoMap)) return true;
              }
           }
           // Don't remove now.
           boolean removeFromCache = false;
           ((AbstractCollectionInterceptor) interceptor).detach(removeFromCache);// detach the interceptor. This will trigger a copy and remove.
           boolean detachOnly = true;
           detachInterceptor(advisor, interceptor, detachOnly, undoMap);
        }
  
        return false;
     }
  
     private static boolean hasAnnotation(Class clazz, Advisor advisor, CachedType type)
     {
        return CachedType.hasAnnotation(clazz, advisor, type);
     }
  
     private void findChildObjects(Fqn fqn, Map map) throws CacheException
     {
        // We need to traverse then
        Node root = cache_.getRoot();
        Node current = root.getChild(fqn);
  
        if (current == null) return;
  
        Collection<Node> col = current.getChildren();
        if (col == null) return;
        for (Node n : col)
        {
           Fqn newFqn = n.getFqn();
           if (InternalHelper.isInternalNode(newFqn)) continue;// skip
  
           Object pojo = getObject(newFqn);
           if (pojo != null)
           {
              map.put(newFqn, pojo);
           }
           else
           {
              findChildObjects(newFqn, map);
           }
        }
     }
  }
  
  
  
  1.1      date: 2007/01/13 15:55:06;  author: bwang;  state: Exp;JBossCache/src/org/jboss/cache/pojo/impl/PojoCacheImpl.java
  
  Index: PojoCacheImpl.java
  ===================================================================
  /*
   * JBoss, Home of Professional Open Source
   *
   * Distributable under LGPL license.
   * See terms of license at gnu.org.
   */
  
  package org.jboss.cache.pojo.impl;
  
  import EDU.oswego.cs.dl.util.concurrent.CopyOnWriteArraySet;
  import org.apache.commons.logging.Log;
  import org.apache.commons.logging.LogFactory;
  import org.jboss.aop.Advised;
  import org.jboss.aop.proxy.ClassProxy;
  import org.jboss.cache.Cache;
  import org.jboss.cache.CacheException;
  import org.jboss.cache.CacheSPI;
  import org.jboss.cache.DefaultCacheFactory;
  import org.jboss.cache.Fqn;
  import org.jboss.cache.Version;
  import org.jboss.cache.config.Configuration;
  import org.jboss.cache.factories.XmlConfigurationParser;
  import org.jboss.cache.pojo.CachedType;
  import org.jboss.cache.pojo.PojoCache;
  import org.jboss.cache.pojo.PojoCacheException;
  import org.jboss.cache.pojo.PojoCacheListener;
  import org.jboss.cache.pojo.annotation.Attach;
  import org.jboss.cache.pojo.annotation.Detach;
  import org.jboss.cache.pojo.annotation.Find;
  import org.jboss.cache.pojo.observable.Observer;
  import org.jboss.cache.pojo.observable.Subject;
  
  import java.lang.reflect.Field;
  import java.util.Collection;
  import java.util.Collections;
  import java.util.Iterator;
  import java.util.Map;
  import java.util.Set;
  import java.util.WeakHashMap;
  
  /**
   * Implementation class for PojoCache interface
   *
   * @author Ben Wang
   * @version $Id: PojoCacheImpl.java,v 1.1 2007/01/13 15:55:06 bwang Exp $
   */
  public class PojoCacheImpl implements PojoCache, Observer
  {
     private CacheSPI cache_ = null;
     protected final Log log_ = LogFactory.getLog(PojoCacheImpl.this.getClass());
     private PojoCacheDelegate delegate_;
     // Class -> CachedType
     // use WeakHashMap to allow class reloading
     private Map cachedTypes_ = new WeakHashMap();
     private boolean hasCreate_ = false;
  
     /**
      * Set of TreeCacheListener.
      *
      * @see #addListener
      */
     private final Set listeners = new CopyOnWriteArraySet();
     /**
      * True if listeners are initialized.
      */
     protected boolean hasListeners = false;
  
     public PojoCacheImpl(String configStr, boolean toStart)
     {
        try
        {
           //         cache_ = new PojoTreeCache();
           //         cache_.setConfiguration(new XmlConfigurationParser().parseFile(configStr));
  
           XmlConfigurationParser parser = new XmlConfigurationParser();
           Configuration expected = parser.parseFile(configStr);
  
           init(expected, toStart);
        }
        catch (Exception e)
        {
           e.printStackTrace();
           throw new PojoCacheException("PojoTreeCache: " + e);
        }
     }
  
     public PojoCacheImpl(Configuration config, boolean toStart)
     {
        init(config, toStart);
     }
  
     private void init(Configuration config, boolean toStart)
     {
        try
        {
           cache_ = (CacheSPI) DefaultCacheFactory.getInstance().createCache(config, toStart);
        }
        catch (Exception e)
        {
           e.printStackTrace();
           throw new PojoCacheException("PojoTreeCache: " + e);
        }
  
        delegate_ = new PojoCacheDelegate(this, this);
     }
  
     public CacheSPI getUnderlyingCache()
     {
        return cache_;
     }
  
     public Object attach(String id, Object pojo) throws PojoCacheException
     {
        return attach(Fqn.fromString(id), pojo);
     }
  
     @Attach
     public Object attach(Fqn id, Object pojo) throws PojoCacheException
     {
        try
        {
           Object obj = putObject(id, pojo);
           return obj;
        }
        catch (CacheException e)
        {
           e.printStackTrace();
           throw new PojoCacheException("PojoTreeCache: " + e);
        }
     }
  
     /**
      * This public API is called from internal package only.
      */
     public Object putObject(Fqn id, Object pojo)
             throws CacheException
     {
        Object obj = null;
  
        // Maybe this is the same instance already.
        obj = delegate_.putObjectI(id, pojo);
        if (obj != null) return obj;
  
        notifyAttach(pojo, true);
        obj = delegate_.putObjectII(id, pojo);
        notifyAttach(pojo, false);
        return obj;
     }
  
     public Object detach(String id) throws PojoCacheException
     {
        return detach(Fqn.fromString(id));
     }
  
     @Detach
     public Object detach(Fqn id) throws PojoCacheException
     {
        try
        {
           Object pojo = find(id);// TODO need optimization here since it will be redundant here
           if (pojo == null) return pojo;
  
           notifyDetach(pojo, true);
           Object obj = removeObject(id);
           notifyDetach(pojo, false);
           return obj;
        }
        catch (CacheException e)
        {
           e.printStackTrace();// TODO
           throw new PojoCacheException("PojoTreeCache: " + e);
        }
     }
  
     public Object removeObject(Fqn id) throws CacheException
     {
        delegate_.setBulkRemove(false);
        return delegate_.removeObject(id);
     }
  
     public String getPojoID(Object pojo)
     {
        throw new PojoCacheException("getPojoID not yet implemented");
     }
  
     public Object find(String id) throws PojoCacheException
     {
        return find(Fqn.fromString(id));
     }
  
     @Find
     public Object find(Fqn id) throws PojoCacheException
     {
        try
        {
           return getObject(id);
        }
        catch (CacheException e)
        {
           e.printStackTrace();// TODO
           throw new PojoCacheException("PojoTreeCache: " + e);
        }
     }
  
     public Object getObject(Fqn id)
             throws CacheException
     {
        return delegate_.getObject(id);
     }
  
     public Map findAll(String id) throws PojoCacheException
     {
        return findAll(Fqn.fromString(id));
     }
  
     @Find
     public Map findAll(Fqn id) throws PojoCacheException
     {
        // Should produce "/"
        if (id == null) id = Fqn.fromString(null);
  
        try
        {
           return delegate_.findObjects(id);
        }
        catch (CacheException e)
        {
           e.printStackTrace();// TODO
           throw new PojoCacheException("PojoTreeCache: " + e);
        }
     }
  
     public String getVersion()
     {
        return Version.printVersion();
     }
  
     public void create() throws PojoCacheException
     {
        log_.info("PojoCache version: " + getVersion());
        try
        {
           cache_.create();
        }
        catch (Exception e)
        {
           throw new PojoCacheException("PojoCache create exception", e);
        }
  
        hasCreate_ = true;
     }
  
     public void start() throws PojoCacheException
     {
        if (!hasCreate_)
        {
           create();
        }
  
        try
        {
           log_.info("PojoCache version: " + getVersion());
           cache_.start();
        }
        catch (Exception e)
        {
           e.printStackTrace();
           throw new PojoCacheException("PojoCacheImpl: " + e);
        }
     }
  
     public void stop() throws PojoCacheException
     {
        cache_.stop();
     }
  
     public void destroy() throws PojoCacheException
     {
        cache_.destroy();
     }
  
     public void addListener(PojoCacheListener listener)
     {
        // synchronize on listenrs just to
        // ensure hasListeners is set correctly
        // based on possibility of concurrent adds/removes
        //
        synchronized (listeners)
        {
           listeners.add(listener);
           hasListeners = true;
        }
     }
  
     public Collection getListeners()
     {
        return Collections.unmodifiableCollection(listeners);
     }
  
     public void removeListener(PojoCacheListener listener)
     {
        // synchronize on listenrs just to
        // ensure hasListeners is set correctly
        // based on possibility of concurrent adds/removes
        //
        synchronized (listeners)
        {
           listeners.remove(listener);
           hasListeners = !listeners.isEmpty();
        }
     }
  
     public Cache getCache()
     {
        // TODO Need to return the real Cache interface later on.
        return (Cache) cache_;
     }
  
     /**
      * Used by internal implementation. Not for general public.
      */
     public Object _evictObject(Fqn fqn) throws CacheException
     {
        boolean evict = true;
        boolean removeCacheInterceptor = false;
  
        // Configurable option to see if we want to remove the cache interceptor when the pojo is
        // evicted.
        //      if(detachPojoWhenEvicted_) removeCacheInterceptor = true;
        delegate_.setBulkRemove(false);
        //      return delegate_._removeObject(fqn, removeCacheInterceptor);
        return null;
     }
  
     /**
      * Obtain a cache aop type for user to traverse the defined "primitive" types in aop.
      * Note that this is not a synchronized call now for speed optimization.
      *
      * @param clazz The original pojo class
      * @return CachedType
      */
     public synchronized CachedType getCachedType(Class clazz)
     {
        CachedType type = (CachedType) cachedTypes_.get(clazz);
        if (type == null)
        {
           type = new CachedType(clazz);
           cachedTypes_.put(clazz, type);
           return type;
        }
        else
        {
           return type;
        }
     }
  
     /********************************************************************************
      * Internal API for event notification
      ********************************************************************************/
     /**
      * Callback for Observable to notify of field modification
      *
      * @param subject
      */
     public void fireChange(Subject subject, Field modifiedField, boolean pre)
     {
        boolean isLocal = true;// TODO Not yet supported. Always true now.
        if (hasListeners)
        {
           for (Iterator it = listeners.iterator(); it.hasNext();)
           {
              ((PojoCacheListener) it.next()).modify((Object) subject, modifiedField, pre, isLocal);
           }
        }
  
     }
  
     private void notifyAttach(Object pojo, boolean pre)
     {
        if (pojo == null) return;
        if (!shouldNotify(pojo)) return;
  
        boolean isLocal = true;// TODO Not yet supported. Always true now.
        if (hasListeners)
        {
           for (Iterator it = listeners.iterator(); it.hasNext();)
           {
              ((PojoCacheListener) it.next()).attach(pojo, pre, isLocal);
           }
        }
     }
  
     private void notifyDetach(Object pojo, boolean pre)
     {
        if (pojo == null) return;
        if (!shouldNotify(pojo)) return;
  
        boolean isLocal = true;// TODO Not yet supported. Always true now.
        if (hasListeners)
        {
           for (Iterator it = listeners.iterator(); it.hasNext();)
           {
              ((PojoCacheListener) it.next()).detach(pojo, pre, isLocal);
           }
        }
     }
  
     /**
      * Decide whether we want to emit notification event or not.
      *
      * @param pojo
      */
     private boolean shouldNotify(Object pojo)
     {
        // Not Advised or not Collection
        if (!(pojo instanceof Advised) &&
                !(pojo instanceof ClassProxy || pojo instanceof Collection ||
                        pojo instanceof Map))
        {
           return false;
        }
        return true;
     }
  }
  
  
  
  1.1      date: 2007/01/13 15:55:06;  author: bwang;  state: Exp;JBossCache/src/org/jboss/cache/pojo/impl/ObjectGraphHandler.java
  
  Index: ObjectGraphHandler.java
  ===================================================================
  /*
   * JBoss, Home of Professional Open Source
   *
   * Distributable under LGPL license.
   * See terms of license at gnu.org.
   */
  
  package org.jboss.cache.pojo.impl;
  
  import org.apache.commons.logging.Log;
  import org.apache.commons.logging.LogFactory;
  import org.jboss.aop.Advised;
  import org.jboss.aop.InstanceAdvisor;
  import org.jboss.aop.proxy.ClassProxy;
  import org.jboss.aop.advice.Interceptor;
  import org.jboss.cache.CacheException;
  import org.jboss.cache.Fqn;
  import org.jboss.cache.CacheSPI;
  import org.jboss.cache.pojo.util.AopUtil;
  import org.jboss.cache.pojo.interceptors.dynamic.BaseInterceptor;
  import org.jboss.cache.pojo.interceptors.dynamic.CacheFieldInterceptor;
  import org.jboss.cache.pojo.PojoTreeCache;
  import org.jboss.cache.pojo.CachedType;
  import org.jboss.cache.pojo.PojoInstance;
  import org.jboss.cache.pojo.PojoReference;
  import org.jboss.cache.pojo.PojoCacheException;
  import org.jboss.cache.pojo.collection.CollectionInterceptorUtil;
  
  /**
   * Handle the object graph management.
   *
   * @author Ben Wang
   *         Date: Aug 4, 2005
   * @version $Id: ObjectGraphHandler.java,v 1.1 2007/01/13 15:55:06 bwang Exp $
   */
  class ObjectGraphHandler
  {
     private CacheSPI cache_;
     private PojoCacheImpl pCache_;
     private InternalHelper internal_;
     private final static Log log = LogFactory.getLog(ObjectGraphHandler.class);
  
     public ObjectGraphHandler(PojoCacheImpl cache, InternalHelper internal)
     {
        pCache_ = cache;
        cache_ = (CacheSPI)pCache_.getCache();
        internal_ = internal;
     }
  
     Object get(Fqn fqn, Class clazz, PojoInstance pojoInstance) throws CacheException
     {
        // Note this is actually the aliasFqn, not the real fqn!
        Object obj;
  
        obj = pCache_.getObject(fqn);
        if (obj == null)
           throw new PojoCacheException("ObjectGraphHandler.get(): null object from internal ref node." +
                      " Internal ref node: " + fqn);
  
        return obj; // No need to set the instance under fqn. It is located in refFqn anyway.
     }
  
     void put(Fqn fqn, Object obj) throws CacheException
     {
        CachedType type = pCache_.getCachedType(obj.getClass());
  
        InstanceAdvisor advisor = null;
        Interceptor interceptor = null;
  
        if(obj instanceof Advised)
        {
           advisor = ((Advised) obj)._getInstanceAdvisor();
           if (advisor == null)
              throw new PojoCacheException("put(): InstanceAdvisor is null for: " + obj);
           // Step Check for cross references
           interceptor = AopUtil.findCacheInterceptor(advisor);
        } else
        {
           advisor = ((ClassProxy) obj)._getInstanceAdvisor();
           if (advisor == null)
              throw new PojoCacheException("put(): InstanceAdvisor is null for: " + obj);
           interceptor = CollectionInterceptorUtil.getInterceptor((ClassProxy) obj);
        }
  
        Fqn originalFqn = null;
  
        // ah, found something. So this will be multiple referenced.
        originalFqn = ((BaseInterceptor) interceptor).getFqn();
  
        // This will increment the ref count, reset, and add ref fqn in the current fqn node.
        setupRefCounting(fqn, originalFqn);
        // Store a PojoReference in the external fqn node
        PojoReference pojoReference = new PojoReference();
        pojoReference.setFqn(originalFqn);
        pojoReference.setPojoClass(type.getType());
        internal_.putPojoReference(fqn, pojoReference);
     }
  
     boolean isMultipleReferenced(Fqn internalFqn)
     {
        // Note this is actually the aliasFqn, not the real fqn!
        PojoInstance pojoInstance = null;
        try
        {
           pojoInstance = internal_.getAopInstance(internalFqn);
        } catch (CacheException e)
        {
           e.printStackTrace();
           throw new PojoCacheException("Exception in isMultipleReferenced " + e);
        }
        // check if this is a refernce
        return InternalHelper.isMultipleReferenced(pojoInstance);
  
     }
  
     void remove(Fqn referencingFqn, Fqn internalFqn, Object pojo)
             throws CacheException
     {
        if (log.isDebugEnabled())
        {
           log.debug("remove(): removing object fqn: " + referencingFqn
                + " Will just de-reference it.");
        }
        removeFromReference(referencingFqn, internalFqn);
     }
  
     /**
      * Remove the object from the the reference fqn, meaning just decrement the ref counter.
      *
      */
     private void removeFromReference(Fqn referencingFqn, Fqn originalFqn) throws CacheException
     {
        synchronized (referencingFqn)
        {  // we lock the internal fqn here so no one else has access.
           // Decrement ref counting on the internal node
           if (decrementRefCount(referencingFqn, originalFqn) == PojoInstance.INITIAL_COUNTER_VALUE)
           {
              // No one is referring it so it is safe to remove
              // TODO we should make sure the parent nodes are also removed they are empty as well.
              pCache_.detach(referencingFqn);
           }
        }
     }
  
     /**
      * 1. increment reference counter
      * 2. put in refFqn so we can get it.
      *
      * @param fqn    The original fqn node
      * @param refFqn The new internal fqn node
      */
     private void setupRefCounting(Fqn fqn, Fqn refFqn) throws CacheException
     {
        synchronized (refFqn)
        { // we lock the ref fqn here so no one else has access.
           // increment the reference counting
           incrementRefCount(refFqn, fqn);
           // set the internal fqn in fqn so we can reference it.
           if (log.isTraceEnabled())
           {
              log.trace("setupRefCounting(): current fqn: " + fqn + " set to point to: " + refFqn);
           }
        }
     }
  
     private int incrementRefCount(Fqn originalFqn, Fqn referencingFqn) throws CacheException
     {
        return internal_.incrementRefCount(originalFqn, referencingFqn);
     }
  
     private int decrementRefCount(Fqn referencingFqn, Fqn originalFqn) throws CacheException
     {
        int count = 0;
        if ((count = internal_.decrementRefCount(originalFqn, referencingFqn)) == (PojoInstance.INITIAL_COUNTER_VALUE + 1))
        {
           internal_.removeIndirectFqn(originalFqn.toString());
        }
  
        return count;
     }
  }
  
  
  
  1.1      date: 2007/01/13 15:55:06;  author: bwang;  state: Exp;JBossCache/src/org/jboss/cache/pojo/impl/CollectionClassHandler.java
  
  Index: CollectionClassHandler.java
  ===================================================================
  /*
   * JBoss, Home of Professional Open Source
   *
   * Distributable under LGPL license.
   * See terms of license at gnu.org.
   */
  
  package org.jboss.cache.pojo.impl;
  
  import org.apache.commons.logging.Log;
  import org.apache.commons.logging.LogFactory;
  import org.jboss.aop.advice.Interceptor;
  import org.jboss.aop.proxy.ClassProxy;
  import org.jboss.cache.CacheException;
  import org.jboss.cache.Fqn;
  import org.jboss.cache.CacheSPI;
  import org.jboss.cache.pojo.interceptors.dynamic.AbstractCollectionInterceptor;
  import org.jboss.cache.pojo.collection.CollectionInterceptorUtil;
  import org.jboss.cache.pojo.interceptors.dynamic.BaseInterceptor;
  import org.jboss.cache.pojo.PojoTreeCache;
  import org.jboss.cache.pojo.CachedType;
  import org.jboss.cache.pojo.PojoInstance;
  import org.jboss.cache.pojo.PojoCacheException;
  
  import java.util.Iterator;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  
  /**
   * Handling the Collection class management. Has no consideration of object graph here.
   *
   * @author Ben Wang
   *         Date: Aug 4, 2005
   * @version $Id: CollectionClassHandler.java,v 1.1 2007/01/13 15:55:06 bwang Exp $
   */
  class CollectionClassHandler
  {
     private final Log log = LogFactory.getLog(CollectionClassHandler.class);
     private CacheSPI cache_;
     private PojoCacheImpl pCache_;
     private InternalHelper internal_;
  
     public CollectionClassHandler(PojoCacheImpl pCache, InternalHelper internal)
     {
        pCache_ = pCache;
        cache_ = (CacheSPI)pCache_.getCache();
        internal_ = internal;
     }
  
     Object get(Fqn fqn, Class clazz, PojoInstance pojoInstance)
             throws CacheException
     {
        Object obj = null;
        try
        {
           if (Map.class.isAssignableFrom(clazz))
           {
              Object map = clazz.newInstance();
              obj = CollectionInterceptorUtil.createMapProxy(pCache_, fqn, clazz, (Map) map);
           } else if (List.class.isAssignableFrom(clazz))
           {
              Object list = clazz.newInstance();
              obj = CollectionInterceptorUtil.createListProxy(pCache_, fqn, clazz, (List) list);
           } else if (Set.class.isAssignableFrom(clazz))
           {
              Object set = clazz.newInstance();
              obj = CollectionInterceptorUtil.createSetProxy(pCache_, fqn, clazz, (Set) set);
           }
        } catch (Exception e)
        {
           throw new CacheException("failure creating proxy", e);
        }
  
        return obj;
     }
  
     void put(Fqn fqn, Fqn referencingFqn, Object obj) throws CacheException
     {
        boolean isCollection = false;
  
        CachedType type = null;
        if (obj instanceof ClassProxy)
        {
           throw new IllegalStateException("CollectionClassHandler.put(): obj is an ClassProxy instance "+ obj);
        }
  
        type = pCache_.getCachedType(obj.getClass());
  
        //JBCACHE-760: for collection - put initialized aopInstance in fqn
        if (!(obj instanceof Map || obj instanceof List || obj instanceof Set)) {
            return;
        }
  
        // Always initialize the ref count so that we can mark this as an AopNode.
        PojoInstance pojoInstance = InternalHelper.initializeAopInstance(referencingFqn);
        pojoInstance.set(obj);
        pojoInstance.setPojoClass(type.getType());
        cache_.put(fqn, PojoInstance.KEY, pojoInstance);
  
        if (obj instanceof Map)
        {
           if (log.isDebugEnabled())
           {
              log.debug("collectionPutObject(): aspectized obj is a Map type of size: " + ((Map) obj).size());
           }
  
           // Let's replace it with a proxy if necessary
           Map map = (Map) obj;
           if (!(obj instanceof ClassProxy))
           {
              Class clazz = obj.getClass();
              try
              {
                 obj = CollectionInterceptorUtil.createMapProxy(pCache_, fqn, clazz, (Map) obj);
              } catch (Exception e)
              {
                 throw new CacheException("failure creating proxy", e);
              }
  
              checkMapRecursion(map, obj);
           }
  
           isCollection = true;
           // populate via the proxied collection
           for (Iterator i = map.entrySet().iterator(); i.hasNext();)
           {
              Map.Entry entry = (Map.Entry) i.next();
              ((Map) obj).put(entry.getKey(), entry.getValue());
           }
  
        } else if (obj instanceof List)
        {
           if (log.isDebugEnabled())
           {
              log.debug("collectionPutObject(): aspectized obj is a List type of size: "
                      + ((List) obj).size());
           }
  
           List list = (List) obj;
  
           // Let's replace it with a proxy if necessary
           if (!(obj instanceof ClassProxy))
           {
              Class clazz = obj.getClass();
              try
              {
                 obj = CollectionInterceptorUtil.createListProxy(pCache_, fqn, clazz, (List) obj);
              } catch (Exception e)
              {
                 throw new CacheException("failure creating proxy", e);
              }
  
              checkListRecursion(list, obj);
           }
  
           isCollection = true;
           // populate via the proxied collection
           for (Iterator i = list.iterator(); i.hasNext();)
           {
              ((List) obj).add(i.next());
           }
  
        } else if (obj instanceof Set)
        {
           if (log.isDebugEnabled())
           {
              log.debug("collectionPutObject(): aspectized obj is a Set type of size: "
                      + ((Set) obj).size());
           }
  
           Set set = (Set) obj;
  
           // Let's replace it with a proxy if necessary
           if (!(obj instanceof ClassProxy))
           {
              Class clazz = obj.getClass();
              try
              {
                 obj = CollectionInterceptorUtil.createSetProxy(pCache_, fqn, clazz, (Set) obj);
              } catch (Exception e)
              {
                 throw new CacheException("failure creating proxy", e);
              }
  
              checkSetRecursion(set, obj);
           }
  
           isCollection = true;
           // populate via the proxied collection
           for (Iterator i = set.iterator(); i.hasNext();)
           {
              ((Set) obj).add(i.next());
           }
  
        }
  
        if (isCollection)
        {
           // Need to reset it here in case this is a new proxy instance
           pojoInstance.set(obj);
           // Attach pojoReference to that interceptor
           BaseInterceptor baseInterceptor = (BaseInterceptor) CollectionInterceptorUtil.getInterceptor(
                   (ClassProxy) obj);
           baseInterceptor.setAopInstance(pojoInstance);
        }
     }
  
     private void checkListRecursion(List list, Object obj)
     {
        while(true)
        {
           int i = list.indexOf(list); // check for recursion
           if(i == -1) break;
  
           list.remove(list);
           list.add(i, obj);
        }
     }
  
     private void checkSetRecursion(Set set, Object obj)
     {
        if(set.remove(set))
        {
           set.add(obj); // replace with proxy
           throw new PojoCacheException("CollectionClassHandler.checkSetRecursion(): " +
           "detect a recursive set (e.g., set inside the same set). This will fail to " +
           "replicate even outside of PojoCache with HashSet. " +set);
        }
     }
  
     private void checkMapRecursion(Map map, Object obj)
     {
        Map m = java.util.Collections.unmodifiableMap(map);
  
        for( Object k : m.keySet() )
        {
           if( m == k)
           {
              throw new PojoCacheException("CollectionClassHandler.checkMapRecursion(): " +
              " Can't handle the recursion map where it is nested in a constant key " +map);
           }
  
           Object v = m.get(k);
           if( v == map )
           {
              throw new PojoCacheException("CollectionClassHandler.checkMapRecursion(): " +
              "detect a recursive map (e.g., map inside the same map). This will fail to " +
              "replicate even outside of PojoCache with HashMap because of hashCode. " +map);
              // recursion here, replace it with proxy
  //            map.put(k, obj);
           }
        }
     }
  
     Object remove(Fqn fqn, Object obj) throws CacheException
     {
        if (!(obj instanceof ClassProxy))
        {
           throw new PojoCacheException("CollectionClassHandler.collectionRemoveObject(): object is not a proxy :" + obj);
        }
  
        Interceptor interceptor = CollectionInterceptorUtil.getInterceptor((ClassProxy) obj);
        boolean removeFromCache = true;
        // detach the interceptor. This will trigger a copy and remove.
        ((AbstractCollectionInterceptor) interceptor).detach(removeFromCache);
        return ((AbstractCollectionInterceptor) interceptor).getCurrentCopy();
     }
  }
  
  
  
  1.1      date: 2007/01/13 15:55:06;  author: bwang;  state: Exp;JBossCache/src/org/jboss/cache/pojo/impl/InternalHelper.java
  
  Index: InternalHelper.java
  ===================================================================
  /*
   * JBoss, the OpenSource J2EE webOS
   *
   * Distributable under LGPL license.
   * See terms of license at gnu.org.
   */
  package org.jboss.cache.pojo.impl;
  
  import org.apache.commons.logging.Log;
  import org.apache.commons.logging.LogFactory;
  import org.jboss.cache.CacheException;
  import org.jboss.cache.CacheSPI;
  import org.jboss.cache.Fqn;
  import org.jboss.cache.config.Option;
  import org.jboss.cache.pojo.InternalConstant;
  import org.jboss.cache.pojo.PojoInstance;
  import org.jboss.cache.pojo.PojoReference;
  import org.jboss.cache.pojo.PojoCacheException;
  import org.jboss.cache.pojo.util.ObjectUtil;
  
  import java.util.Map;
  
  /**
   * Internal helper class to handle internal cache sotre, that is, the portion that is not part of
   * user's data.
   *
   * @author Ben Wang
   */
  public class InternalHelper
  {
     private Log log = LogFactory.getLog(InternalHelper.class.getName());
     // This is an optimization flag to skip put lock when we are sure that it has been locked from
     // putObject. However, if later on there are transactional field updates, then we we will need
     // this off to protected the write lock.
     private boolean cacheOperationSkipLocking_ = true;
     private Option skipLockOption_;
     private Option gravitateOption_;
     private Option localModeOption_;
  
     private CacheSPI cache_;
  
     InternalHelper(CacheSPI cache)
     {
        cache_ = cache;
  
        skipLockOption_ = new Option();
        if (cacheOperationSkipLocking_)
        {
           skipLockOption_.setSuppressLocking(true);
        }
        else
        {
           skipLockOption_.setSuppressLocking(false);
        }
  
        localModeOption_ = new Option();
        localModeOption_.setCacheModeLocal(true);
  
        gravitateOption_ = new Option();
        gravitateOption_.setForceDataGravitation(true);
     }
  
     Option getLockOption()
     {
        return skipLockOption_;
     }
  
     PojoInstance getAopInstance(Fqn fqn) throws CacheException
     {
        // Not very efficient now since we are peeking every single time.
        // Should have cache it without going to local cache.
        return (PojoInstance) get(fqn, PojoInstance.KEY, false);
     }
  
     PojoReference getPojoReference(Fqn fqn) throws CacheException
     {
        // Not very efficient now since we are peeking every single time.
        // Should have cache it without going to local cache.
        return (PojoReference) get(fqn, PojoReference.KEY, false);
     }
  
     PojoReference getPojoReferenceWithGravitation(Fqn fqn) throws CacheException
     {
        // Not very efficient now since we are peeking every single time.
        // Should have cache it without going to local cache.
        return (PojoReference) get(fqn, PojoReference.KEY, true);
     }
  
     private PojoInstance getAopInstanceWithGravitation(Fqn fqn) throws CacheException
     {
        // Not very efficient now since we are peeking every single time.
        // Should have cache it without going to local cache.
        return (PojoInstance) get(fqn, PojoInstance.KEY, true);
     }
  
     static PojoInstance initializeAopInstance(Fqn sourceFqn)
     {
        PojoInstance pojoInstance = new PojoInstance();
  
        pojoInstance.incrementRefCount(sourceFqn);
        return pojoInstance;
     }
  
     /**
      * Increment reference count for the pojo. Note that this is not thread safe or atomic.
      */
     int incrementRefCount(Fqn originalFqn, Fqn referencingFqn) throws CacheException
     {
        PojoInstance pojoInstance = getAopInstance(originalFqn);
        if (pojoInstance == null)
           throw new PojoCacheException("InternalDelegate.incrementRefCount(): null pojoReference for fqn: " + originalFqn);
  
        int count = pojoInstance.incrementRefCount(referencingFqn);
        // need to update it.
        put(originalFqn, PojoInstance.KEY, pojoInstance);
        return count;
     }
  
     /**
      * Has a delegate method so we can use the switch.
      */
  
     Object get(Fqn fqn, Object key) throws CacheException
     {
        return get(fqn, key, false);
     }
  
     private Object get(Fqn fqn, Object key, boolean gravitate) throws CacheException
     {
        // TODO let's find a better way to decouple this.
        if (gravitate && cache_.getBuddyManager() != null)
        {
           cache_.getInvocationContext().getOptionOverrides().setForceDataGravitation(true);
           Object obj = cache_.get(fqn, key);
           cache_.getInvocationContext().getOptionOverrides().setForceDataGravitation(false);
           return obj;
        }
        else if (cache_.getCacheLoaderManager() != null)
        {
           // We have cache loader, we can't get it directly from the local get.
  //         cache_.getInvocationContext().getOptionOverrides().setSuppressLocking(true);
           Object obj = cache_.get(fqn, key);
  //         cache_.getInvocationContext().getOptionOverrides().setSuppressLocking(false);
           return obj;
        }
        else
        {
  //         return cache_._get(fqn, key, false);
           return cache_.get(fqn, key);
        }
     }
  
     private void put(Fqn fqn, Object key, Object value) throws CacheException
     {
        // Use option to ski locking since we have parent lock already.
  //      cache_.getInvocationContext().getOptionOverrides().setSuppressLocking(true);
        cache_.put(fqn, key, value);
  //      cache_.getInvocationContext().getOptionOverrides().setSuppressLocking(false);
  //      cache_.put(fqn, key, value);
     }
  
     void put(Fqn fqn, Map map) throws CacheException
     {
        // Use option to ski locking since we have parent lock already.
  //      cache_.getInvocationContext().getOptionOverrides().setSuppressLocking(true);
        cache_.put(fqn, map);
  //      cache_.getInvocationContext().getOptionOverrides().setSuppressLocking(false);
  //      cache_.put(fqn, key, value);
     }
  
     protected void localPut(Fqn fqn, Object key, Object value) throws CacheException
     {
        // Use option to ski locking since we have parent lock already.
        // TODO Need to make sure there is no tx here otherwise it won't work.
  //      cache_.getInvocationContext().getOptionOverrides().setSuppressLocking(true);
        cache_.put(fqn, key, value);
  //      cache_.getInvocationContext().getOptionOverrides().setSuppressLocking(false);
     }
  
  
     /**
      * decrement reference count for the pojo. Note that this is not thread safe or atomic.
      */
     int decrementRefCount(Fqn originalFqn, Fqn referencingFqn) throws CacheException
     {
        PojoInstance pojoInstance = getAopInstance(originalFqn);
        if (pojoInstance == null)
           throw new PojoCacheException("InternalDelegate.decrementRefCount(): null pojoReference.");
  
        int count = pojoInstance.decrementRefCount(referencingFqn);
  
        if (count < -1)  // can't dip below -1
           throw new PojoCacheException("InternalDelegate.decrementRefCount(): null pojoReference.");
  
        // need to update it.
        put(originalFqn, PojoInstance.KEY, pojoInstance);
        return count;
     }
  
     static boolean isReferenced(PojoInstance pojoInstance)
     {
        // If ref counter is greater than 0, we fqn is being referenced.
        return (pojoInstance.getRefCount() > 0);
     }
  
     int getRefCount(Fqn fqn) throws CacheException
     {
        return getAopInstance(fqn).getRefCount();
     }
  
     String XgetRefFqn(Fqn fqn) throws CacheException
     {
        PojoInstance pojoInstance = getAopInstance(fqn);
        return getRefFqn(pojoInstance);
     }
  
     String getRefFqn(PojoInstance pojoInstance) throws CacheException
     {
        if (pojoInstance == null)
           return null;
  
        String aliasFqn = pojoInstance.getInternalFqn();
  
        if (aliasFqn == null || aliasFqn.length() == 0) return null;
  
        return getRefFqnFromAlias(aliasFqn);
     }
  
     void setRefFqn(Fqn fqn, String internalFqn) throws CacheException
     {
        PojoInstance pojoInstance = getAopInstance(fqn);
        if (pojoInstance == null)
           pojoInstance = new PojoInstance();
  
        pojoInstance.setInternalFqn(internalFqn);
        put(fqn, PojoInstance.KEY, pojoInstance);
     }
  
     void removeRefFqn(Fqn fqn) throws CacheException
     {
        PojoInstance pojoInstance = getAopInstance(fqn);
        if (pojoInstance == null)
           throw new PojoCacheException("InternalDelegate.getInternalFqn(): null pojoReference.");
  
        pojoInstance.removeInternalFqn();
        put(fqn, PojoInstance.KEY, pojoInstance);
     }
  
     Object getPojo(Fqn fqn) throws CacheException
     {
        PojoReference pojoReference = getPojoReference(fqn);
        Fqn realFqn = null;
        if (pojoReference != null)
        {
           // This is outward facing node
           realFqn = pojoReference.getFqn();
        }
        else
        {
           // This is the internal node.
           realFqn = fqn;
        }
  
        PojoInstance pojoInstance = getAopInstance(realFqn);
        if (pojoInstance == null)
           return null;
  
        return pojoInstance.get();
     }
  
     Object getPojoWithGravitation(Fqn fqn) throws CacheException
     {
        // This is for buddy replication
        PojoReference pojoReference = getPojoReferenceWithGravitation(fqn);
        // Find the real fqn where it is stored.
        Fqn internalFqn = pojoReference.getFqn();
  
        PojoInstance pojoInstance = getAopInstanceWithGravitation(internalFqn);
        if (pojoInstance == null)
           return null;
  
        return pojoInstance.get();
     }
  
     void setPojo(Fqn fqn, Object pojo) throws CacheException
     {
        PojoInstance pojoInstance = getAopInstance(fqn);
        if (pojoInstance == null)
        {
           pojoInstance = new PojoInstance();
           put(fqn, PojoInstance.KEY, pojoInstance);
        }
  
        pojoInstance.set(pojo);
        // No need to do a cache put since pojo is transient anyway.
     }
  
     static boolean isMultipleReferenced(PojoInstance pojoInstance)
     {
        if (pojoInstance.getRefCount() > (PojoInstance.INITIAL_COUNTER_VALUE + 1)) return true;
  
        return false;
     }
  
     static void setPojo(PojoInstance pojoInstance, Object pojo)
     {
        // No need to do a cache put since pojo is transient anyway.
        pojoInstance.set(pojo);
     }
  
     void setPojo(Fqn fqn, Object pojo, PojoInstance pojoInstance) throws CacheException
     {
        if (pojoInstance == null)
        {
           pojoInstance = new PojoInstance();
           put(fqn, PojoInstance.KEY, pojoInstance);
        }
  
        pojoInstance.set(pojo);
        // No need to do a cache put since pojo is transient anyway.
     }
  
     void putPojoReference(Fqn fqn, PojoReference pojoReference) throws CacheException
     {
        put(fqn, PojoReference.KEY, pojoReference);
     }
  
     void putAopClazz(Fqn fqn, Class clazz) throws CacheException
     {
        put(fqn, InternalConstant.CLASS_INTERNAL, clazz);
     }
  
     /**
      * We store the class name in string and put it in map instead of directly putting
      * it into cache for optimization.
      */
     static void putAopClazz(Class clazz, Map map)
     {
        map.put(InternalConstant.CLASS_INTERNAL, clazz);
     }
  
     Class peekAopClazz(Fqn fqn) throws CacheException
     {
        return (Class) get(fqn, InternalConstant.CLASS_INTERNAL);
     }
  
     void removeInternalAttributes(Fqn fqn) throws CacheException
     {
        cache_.remove(fqn, PojoInstance.KEY);
        cache_.remove(fqn, InternalConstant.CLASS_INTERNAL);
     }
  
     void cleanUp(Fqn fqn) throws CacheException
     {
        // We can't do a brute force remove anymore?
        if (cache_.getRoot().getChild(fqn).getChildren() != null)
        {
           // remove everything
           cache_.removeNode(fqn);
  //         cache_.getRoot().getChild(fqn).clearData();
  //         removeNodeWithoutInterceptor(fqn);
        }
        else
        {
           // Assume everything here is all PojoCache data for optimization
           cache_.getRoot().getChild(fqn).clearData();
           if (log.isTraceEnabled())
           {
              log.trace("cleanup(): fqn: " + fqn + " is not empty. That means it has sub-pojos. Will not remove node");
           }
        }
     }
  
     private void removeNodeWithoutInterceptor(Fqn fqn)
     {
        Option option = cache_.getInvocationContext().getOptionOverrides();
  //      option.setBypassInterceptorChain(false);
        option.setBypassInterceptorChain(true);
        cache_.removeNode(fqn);
     }
  
  
     String createIndirectFqn(String fqn) throws CacheException
     {
        String indirectFqn = getIndirectFqn(fqn);
        Fqn internalFqn = getInternalFqn(fqn);
        put(internalFqn, indirectFqn, fqn);
        return indirectFqn;
     }
  
     private Fqn getInternalFqn(String fqn)
     {
        if (fqn == null || fqn.length() == 0)
           throw new IllegalStateException("InternalDelegate.getInternalFqn(). fqn is either null or empty!");
  
        String indirectFqn = getIndirectFqn(fqn);
        return new Fqn(InternalConstant.JBOSS_INTERNAL_MAP, indirectFqn);
  //      return JBOSS_INTERNAL_MAP;
     }
  
     static String getIndirectFqn(String fqn)
     {
        // TODO This is not unique. Will need to come up with a better one in the future.
        return ObjectUtil.getIndirectFqn(fqn);
     }
  
     void removeIndirectFqn(String oldFqn) throws CacheException
     {
        String indirectFqn = getIndirectFqn(oldFqn);
        cache_.remove(getInternalFqn(oldFqn), indirectFqn);
     }
  
     void setIndirectFqn(String oldFqn, String newFqn) throws CacheException
     {
        String indirectFqn = getIndirectFqn(oldFqn);
        Fqn tmpFqn = getInternalFqn(oldFqn);
        put(tmpFqn, indirectFqn, newFqn);
     }
  
     void updateIndirectFqn(Fqn originalFqn, Fqn newFqn) throws CacheException
     {
        put(getInternalFqn(originalFqn.toString()), getIndirectFqn(originalFqn.toString()), newFqn.toString());
     }
  
     private String getRefFqnFromAlias(String aliasFqn) throws CacheException
     {
        return (String) get(getInternalFqn(aliasFqn), aliasFqn, true);
     }
  
     Fqn getNextFqnInLine(Fqn currentFqn) throws CacheException
     {
        PojoInstance ai = getAopInstance(currentFqn);
        return ai.getAndRemoveFirstFqnInList();
     }
  
     /**
      * Test if this internal node.
      *
      * @param fqn
      */
     public static boolean isInternalNode(Fqn fqn)
     {
        // we ignore all the node events corresponding to JBOSS_INTERNAL
        if (fqn.isChildOrEquals(InternalConstant.JBOSS_INTERNAL)) return true;
  
        return false;
     }
  }
  
  
  
  1.1      date: 2007/01/13 15:55:06;  author: bwang;  state: Exp;JBossCache/src/org/jboss/cache/pojo/impl/AdvisedPojoHandler.java
  
  Index: AdvisedPojoHandler.java
  ===================================================================
  /*
   * JBoss, Home of Professional Open Source
   *
   * Distributable under LGPL license.
   * See terms of license at gnu.org.
   */
  
  package org.jboss.cache.pojo.impl;
  
  import org.apache.commons.logging.Log;
  import org.apache.commons.logging.LogFactory;
  import org.jboss.cache.pojo.CachedType;
  import org.jboss.cache.pojo.PojoInstance;
  import org.jboss.cache.pojo.PojoUtil;
  import org.jboss.cache.pojo.InternalConstant;
  import org.jboss.cache.pojo.observable.Observer;
  import org.jboss.cache.pojo.memory.FieldPersistentReference;
  import org.jboss.cache.pojo.util.AopUtil;
  import org.jboss.cache.pojo.interceptors.dynamic.CacheFieldInterceptor;
  import org.jboss.cache.Fqn;
  import org.jboss.cache.CacheException;
  import org.jboss.cache.CacheSPI;
  import org.jboss.aop.InstanceAdvisor;
  import org.jboss.aop.Advised;
  import org.jboss.aop.ClassInstanceAdvisor;
  import org.jboss.aop.Advisor;
  
  import java.util.Map;
  import java.util.List;
  import java.util.Set;
  import java.util.Iterator;
  import java.util.HashMap;
  import java.lang.reflect.Field;
  
  /**
   * Handling the advised pojo operations. No consideration of object graph here.
   *
   * @author Ben Wang
   *         Date: Aug 4, 2005
   * @version $Id: AdvisedPojoHandler.java,v 1.1 2007/01/13 15:55:06 bwang Exp $
   */
  class AdvisedPojoHandler
  {
     private final Log log = LogFactory.getLog(AdvisedPojoHandler.class);
     private CacheSPI cache_;
     private PojoCacheImpl pCache_;
     private PojoUtil util_;
     private Observer observer_;
  
     public AdvisedPojoHandler(PojoCacheImpl pCache, InternalHelper internal,
                               PojoUtil util, Observer observer)
     {
        pCache_ = pCache;
        cache_ = (CacheSPI)pCache_.getCache();
        util_ = util;
        observer_ = observer;
     }
  
     public Object get(Fqn fqn, Class clazz, PojoInstance pojoInstance)
             throws CacheException
     {
        CachedType type = pCache_.getCachedType(clazz);
        Object obj = null;
        try
        {
           obj = clazz.newInstance();
           // TODO Need to populate the object from the cache as well.
        }
        catch (Exception e)
        {
           // Is this a case of no-default constructor, e.g., Enum? Let's try it.
           try
           {
              obj = loadFromOthers(fqn, clazz);
              if(obj == null) throw e;
           }
           catch (Exception e1)
           {
              throw new CacheException("failed creating instance of " + clazz.getName(), e);
           }
        }
        // Insert interceptor at runtime
        InstanceAdvisor advisor = ((Advised) obj)._getInstanceAdvisor();
        CacheFieldInterceptor interceptor = new CacheFieldInterceptor(pCache_, fqn, type);
        interceptor.setAopInstance(pojoInstance);
        util_.attachInterceptor(obj, advisor, interceptor, observer_);
        return obj;
     }
  
     private Object loadFromOthers(Fqn fqn, Class clazz) throws Exception
     {
        // Try Enum first.
        if(clazz.isEnum())
        {
           String name = (String)pCache_.getCache().get(fqn, InternalConstant.ENUM_KEY);
           if(name == null)
           {
              throw new CacheException(
                      "AdvisedPojoHandler.loadFromOthers(): can't retrieve java.lang.Enum name field."
                       + " fqn: " +fqn);
           }
           Object obj = Enum.valueOf(clazz, name);
           return obj;
        }
  
        return null;
     }
  
     void put(Fqn fqn, Fqn referencingFqn, Object obj) throws CacheException
     {
        CachedType type = pCache_.getCachedType(obj.getClass());
        // We have a clean slate then.
        InstanceAdvisor advisor = ((Advised) obj)._getInstanceAdvisor();
        // TODO workaround for deserialiased objects
        if (advisor == null)
        {
           advisor = new ClassInstanceAdvisor(obj);
           ((Advised) obj)._setInstanceAdvisor(advisor);
        }
  
        // Let's do batch update via Map instead
        Map map = new HashMap();
        // Always initialize the ref count so we can mark this as an AopNode.
        PojoInstance pojoInstance = InternalHelper.initializeAopInstance(referencingFqn);
        // Insert interceptor at runtime
        CacheFieldInterceptor interceptor = new CacheFieldInterceptor(pCache_, fqn, type);
        interceptor.setAopInstance(pojoInstance);
        util_.attachInterceptor(obj, advisor, interceptor, observer_);
  
        map.put(PojoInstance.KEY, pojoInstance);
        pojoInstance.setPojoClass(type.getType());
        // we will do it recursively.
        // Map of sub-objects that are non-primitive
        Map subPojoMap = new HashMap();
        boolean hasFieldAnnotation = hasAnnotation(obj.getClass(), ((Advised) obj)._getAdvisor(), type);
  
        for (Iterator i = type.getFields().iterator(); i.hasNext();)
        {
           Field field = (Field) (((FieldPersistentReference) i.next())).get();
           Object value = null;
           try
           {
              value = field.get(obj);
           }
           catch (IllegalAccessException e)
           {
              throw new CacheException("field access failed", e);
           }
           CachedType fieldType = pCache_.getCachedType(field.getType());
           // check for non-replicatable types
           if (CachedType.isPrimitiveNonReplicatable(field))
           {
              continue;
           }
  
           if (hasFieldAnnotation)
           {
              if (CachedType.hasTransientAnnotation(field, ((Advised) obj)._getAdvisor()))
              {
                 continue;
              }
           }
  
           // we simply treat field that has @Serializable as a primitive type.
           if (fieldType.isImmediate() ||
                   (hasFieldAnnotation &&
                           CachedType.hasSerializableAnnotation(field, ((Advised) obj)._getAdvisor())))
           {
              // switched using batch update
              map.put(field.getName(), value);
           } else
           {
              subPojoMap.put(field, value);
           }
        }
  
        // Use option to skip locking since we have parent lock already.
  //      cache_.getInvocationContext().getOptionOverrides().setSuppressLocking(true);
  
        cache_.getRoot().addChild(fqn).put(map);
  //      cache_.getInvocationContext().getOptionOverrides().setSuppressLocking(false);
        // This is in-memory operation only
        InternalHelper.setPojo(pojoInstance, obj);
  
        for (Object o : subPojoMap.keySet())
        {
           Field field = (Field) o;
           Object value = subPojoMap.get(field);
           if(value == null) continue; // really no need to map the POJO.
           Fqn tmpFqn = new Fqn(fqn, field.getName());
           pCache_.attach(tmpFqn, value);
           // If it is Collection classes, we replace it with dynamic proxy.
           // But we will have to ignore it if value is null
           if (value instanceof Map || value instanceof List || value instanceof Set)
           {
              Object newValue = pCache_.getObject(tmpFqn);
              util_.inMemorySubstitution(obj, field, newValue);
           }
        }
  
        // Need to make sure this is behind put such that obj.toString is done correctly.
        if (log.isDebugEnabled())
        {
           log.debug("internalPut(): inserting with fqn: " + fqn);
        }
     }
  
     Object remove(Fqn fqn, Object result, Class clazz
                  ) throws CacheException
     {
        CachedType type = pCache_.getCachedType(clazz);
        InstanceAdvisor advisor = ((Advised) result)._getInstanceAdvisor();
        for (Iterator i = type.getFields().iterator(); i.hasNext();)
        {
           Field field = (Field) (((FieldPersistentReference) i.next())).get();
           CachedType fieldType = pCache_.getCachedType(field.getType());
           Object value = null;
           if (!fieldType.isImmediate())
           {
              value = pCache_.detach(new Fqn(fqn, field.getName()));
  
              // Check for Collection. If it is, we need to reset the original reference.
              if ((value instanceof Map || value instanceof List || value instanceof Set))
              {
                 // If this Collection class, we are returning the original value already
                 util_.inMemorySubstitution(result, field, value);
              }
  
           }
        }
  
        // batch remove
        cache_.getRoot().getChild(fqn).clearData();
        // Determine if we want to keep the interceptor for later use.
        CacheFieldInterceptor interceptor = (CacheFieldInterceptor) AopUtil.findCacheInterceptor(advisor);
        // Remember to remove the interceptor from in-memory object but make sure it belongs to me first.
        if (interceptor != null)
        {
           if (log.isDebugEnabled()) {
              log.debug("regularRemoveObject(): removed cache interceptor fqn: " + fqn + " interceptor: "+interceptor);
           }
           util_.detachInterceptor(advisor, interceptor, observer_);
        }
  
        return null; // Not really null though.
     }
  
     private static boolean hasAnnotation(Class clazz, Advisor advisor, CachedType type)
     {
        return CachedType.hasAnnotation(clazz, advisor, type);
     }
  
  }
  
  
  



More information about the jboss-cvs-commits mailing list