[jbosscache-commits] JBoss Cache SVN: r5410 - in pojo/trunk/src: main/java/org/jboss/cache/pojo/impl and 5 other directories.

jbosscache-commits at lists.jboss.org jbosscache-commits at lists.jboss.org
Tue Mar 11 00:05:28 EDT 2008


Author: jason.greene at jboss.com
Date: 2008-03-11 00:05:28 -0400 (Tue, 11 Mar 2008)
New Revision: 5410

Added:
   pojo/trunk/src/main/java/org/jboss/cache/pojo/collection/CachedArray.java
   pojo/trunk/src/main/java/org/jboss/cache/pojo/collection/CachedArrayRegistry.java
   pojo/trunk/src/main/java/org/jboss/cache/pojo/collection/CachedObjectArray.java
   pojo/trunk/src/main/java/org/jboss/cache/pojo/collection/CachedPrimitiveArray.java
   pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/AbstractHandler.java
   pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/ArrayHandler.java
   pojo/trunk/src/main/java/org/jboss/cache/pojo/interceptors/dynamic/ArrayInterceptor.java
Modified:
   pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/AdvisedPojoHandler.java
   pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/CachedType.java
   pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/CollectionClassHandler.java
   pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/ObjectGraphHandler.java
   pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/PojoCacheDelegate.java
   pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/PojoCacheImpl.java
   pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/SerializableObjectHandler.java
   pojo/trunk/src/main/java/org/jboss/cache/pojo/interceptors/dynamic/CacheFieldInterceptor.java
   pojo/trunk/src/main/java/org/jboss/cache/pojo/util/AopUtil.java
   pojo/trunk/src/main/resources/META-INF/pojocache-aop.xml
   pojo/trunk/src/test/java/org/jboss/cache/pojo/ArrayTest.java
   pojo/trunk/src/test/java/org/jboss/cache/pojo/ReplicatedByteTest.java
   pojo/trunk/src/test/java/org/jboss/cache/pojo/test/ArrayObject.java
   pojo/trunk/src/test/java/org/jboss/cache/pojo/test/Resource.java
Log:
Add initial support for array interception - PCACHE-13
Cleanup some of the code


Added: pojo/trunk/src/main/java/org/jboss/cache/pojo/collection/CachedArray.java
===================================================================
--- pojo/trunk/src/main/java/org/jboss/cache/pojo/collection/CachedArray.java	                        (rev 0)
+++ pojo/trunk/src/main/java/org/jboss/cache/pojo/collection/CachedArray.java	2008-03-11 04:05:28 UTC (rev 5410)
@@ -0,0 +1,124 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.cache.pojo.collection;
+
+import java.lang.reflect.Array;
+
+import org.jboss.cache.CacheException;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.pojo.impl.CachedType;
+import org.jboss.cache.pojo.impl.PojoCacheImpl;
+
+/**
+ * A CachedArray is the base class for cache backed array access. It replicates the Java array contract.
+ *
+ * @author Jason T. Greene
+ */
+public abstract class CachedArray
+{
+   private static final String LENGTH = "ARRAY.LENGTH";
+   protected PojoCacheImpl cache;
+   protected Fqn<?> fqn;
+   private int length = -1;
+   private Class<?> type;
+   
+   public static CachedArray load(Fqn<?> fqn, PojoCacheImpl cache, Class<?> type)
+   {
+      boolean primitive = CachedType.isImmediate(type.getComponentType());
+      CachedArray array = primitive ? new CachedPrimitiveArray(fqn, type, cache) : new CachedObjectArray(fqn, type, cache);
+      return array;
+   }
+
+   public static CachedArray create(Fqn<?> fqn, PojoCacheImpl cache, Object originalArray)
+   {
+      Class<?> type = originalArray.getClass();
+      assert type.isArray();
+         
+      Class<?> component = type.getComponentType();
+      boolean primitive = CachedType.isImmediate(component);
+      CachedArray array = primitive ? new CachedPrimitiveArray(fqn, type, cache) : new CachedObjectArray(fqn, type, cache);
+      
+      int length = Array.getLength(originalArray);
+      for (int c = 0; c < length; c++)
+         array.set(c, Array.get(originalArray, c));
+      
+      array.length = length;
+      array.writeInfo();
+      
+      return array;
+   }
+   
+   protected CachedArray(Fqn<?> fqn, Class<?> type, PojoCacheImpl cache)
+   {
+      this.fqn = fqn;
+      this.type = type;
+      this.cache = cache;
+   }
+   
+   public Fqn<?> getFqn()
+   {
+      return fqn;
+   }
+   
+   public abstract void set(int index, Object element);
+   
+   public abstract Object get(int index);
+   
+   protected void writeInfo()
+   {
+      cache.getCache().put(fqn, LENGTH, length);
+   }
+   
+   public void destroy()
+   {
+      cache.getCache().removeNode(fqn);
+      length = 0;
+   }
+   
+   public int length()
+   {
+      if (length == -1)
+      {
+         Integer i = (Integer)cache.getCache().get(fqn, LENGTH);
+         length = i != null ? i.intValue() : 0; 
+      }
+      
+      return length;
+   }
+   
+   public Object toArray()
+   { 
+      try
+      {
+         int len = length();
+         Object array = Array.newInstance(type.getComponentType(), len);
+         for (int i = 0; i < len; i++)
+            Array.set(array, i, get(i));
+         
+         return array;
+      }
+      catch (Exception e)
+      {
+         throw new CacheException("Could not construct array " + type);
+      }
+   }   
+}
\ No newline at end of file

Added: pojo/trunk/src/main/java/org/jboss/cache/pojo/collection/CachedArrayRegistry.java
===================================================================
--- pojo/trunk/src/main/java/org/jboss/cache/pojo/collection/CachedArrayRegistry.java	                        (rev 0)
+++ pojo/trunk/src/main/java/org/jboss/cache/pojo/collection/CachedArrayRegistry.java	2008-03-11 04:05:28 UTC (rev 5410)
@@ -0,0 +1,51 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.cache.pojo.collection;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * A n internal registry which is responsible for mapping a Java array 
+ * instance to a <code>CachedArray</code>.
+ *
+ * @author Jason T. Greene
+ */
+public class CachedArrayRegistry
+{
+   private static ConcurrentMap<Object, CachedArray> map = new ConcurrentHashMap<Object, CachedArray>();
+   
+   public static void register(Object array, CachedArray cached)
+   {
+      map.put(array, cached);
+   }
+   
+   public static CachedArray unregister(Object array)
+   {
+      return map.remove(array);
+   }
+   
+   public static CachedArray lookup(Object array)
+   {
+      return map.get(array);
+   }
+}

Added: pojo/trunk/src/main/java/org/jboss/cache/pojo/collection/CachedObjectArray.java
===================================================================
--- pojo/trunk/src/main/java/org/jboss/cache/pojo/collection/CachedObjectArray.java	                        (rev 0)
+++ pojo/trunk/src/main/java/org/jboss/cache/pojo/collection/CachedObjectArray.java	2008-03-11 04:05:28 UTC (rev 5410)
@@ -0,0 +1,64 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.cache.pojo.collection;
+
+import org.jboss.cache.Fqn;
+import org.jboss.cache.pojo.impl.PojoCacheImpl;
+import org.jboss.cache.pojo.util.AopUtil;
+import org.jboss.cache.pojo.util.Null;
+
+/**
+ * A CachedObjectArray is used to back arrays with a component type that extends Object.
+ * It currently maps each array element to a cache Node, to support fine-grained locking.
+ *
+ * @author Jason T. Greene
+ */
+public class CachedObjectArray extends CachedArray
+{
+   protected CachedObjectArray(Fqn<?> fqn, Class<?> type, PojoCacheImpl cache)
+   {
+      super(fqn, type, cache);
+   }
+ 
+   public void set(int index, Object element)
+   {
+      Fqn<?> fqn = AopUtil.constructFqn(this.fqn, IntegerCache.toString(index));
+
+      cache.attach(fqn, Null.toNullObject(element));
+   }
+   
+   public Object get(int index)
+   {
+      Fqn<?> fqn = AopUtil.constructFqn(this.fqn, IntegerCache.toString(index));
+      
+      return Null.toNullValue(cache.find(fqn));
+   }
+   
+   public void destroy()
+   {
+      // Detach all children to ensure reference cleanup
+      for (int i = 0; i < length(); i++)
+         cache.detach(AopUtil.constructFqn(this.fqn, IntegerCache.toString(i)));
+      
+      super.destroy();
+   }
+}

Added: pojo/trunk/src/main/java/org/jboss/cache/pojo/collection/CachedPrimitiveArray.java
===================================================================
--- pojo/trunk/src/main/java/org/jboss/cache/pojo/collection/CachedPrimitiveArray.java	                        (rev 0)
+++ pojo/trunk/src/main/java/org/jboss/cache/pojo/collection/CachedPrimitiveArray.java	2008-03-11 04:05:28 UTC (rev 5410)
@@ -0,0 +1,51 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.cache.pojo.collection;
+
+import org.jboss.cache.Fqn;
+import org.jboss.cache.pojo.impl.PojoCacheImpl;
+
+/**
+ * A CachedPrimitiveArray is used to back arrays which have primitive 
+ * component types. These are currently mapped to attributes on a single node.  
+ *
+ * @author Jason T. Greene
+ */
+public class CachedPrimitiveArray extends CachedArray
+{
+   private static final String ELEMENT = "ARRAY.PELEMENT.";
+   
+   protected CachedPrimitiveArray(Fqn<?> fqn, Class<?> type, PojoCacheImpl cache)
+   {
+      super(fqn, type, cache);
+   }
+   
+   public void set(int index, Object element)
+   {
+      cache.getCache().put(fqn, ELEMENT + index, element);
+   }
+   
+   public Object get(int index)
+   {     
+      return cache.getCache().get(fqn, ELEMENT + index);
+   }
+}
\ No newline at end of file

Added: pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/AbstractHandler.java
===================================================================
--- pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/AbstractHandler.java	                        (rev 0)
+++ pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/AbstractHandler.java	2008-03-11 04:05:28 UTC (rev 5410)
@@ -0,0 +1,43 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.cache.pojo.impl;
+
+import org.jboss.cache.Fqn;
+
+abstract class AbstractHandler
+{
+
+   public AbstractHandler()
+   {
+      super();
+   }
+   
+   protected abstract boolean handles(Class<?> clazz);
+
+   protected abstract Object remove(Fqn<?> fqn, Fqn<?> referenceingFqn, Object result);
+
+   protected abstract void put(Fqn<?> fqn, Fqn<?> referencingFqn, Object obj);
+
+   protected abstract Object get(Fqn<?> fqn, Class<?> clazz, PojoInstance pojoInstance);
+
+   protected abstract Fqn<?> getFqn(Object obj);
+}
\ No newline at end of file

Modified: pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/AdvisedPojoHandler.java
===================================================================
--- pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/AdvisedPojoHandler.java	2008-03-11 01:48:44 UTC (rev 5409)
+++ pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/AdvisedPojoHandler.java	2008-03-11 04:05:28 UTC (rev 5410)
@@ -1,10 +1,24 @@
 /*
- * JBoss, Home of Professional Open Source
- *
- * Distributable under LGPL license.
- * See terms of license at gnu.org.
- */
-
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
 package org.jboss.cache.pojo.impl;
 
 import java.lang.reflect.Field;
@@ -22,6 +36,8 @@
 import org.jboss.cache.Cache;
 import org.jboss.cache.CacheException;
 import org.jboss.cache.Fqn;
+import org.jboss.cache.pojo.PojoCacheException;
+import org.jboss.cache.pojo.interceptors.dynamic.BaseInterceptor;
 import org.jboss.cache.pojo.interceptors.dynamic.CacheFieldInterceptor;
 import org.jboss.cache.pojo.memory.FieldPersistentReference;
 import org.jboss.cache.pojo.util.AopUtil;
@@ -35,7 +51,7 @@
  *         Date: Aug 4, 2005
  * @version $Id$
  */
-class AdvisedPojoHandler
+class AdvisedPojoHandler extends AbstractHandler
 {
    private final Log log = LogFactory.getLog(AdvisedPojoHandler.class);
    private Cache<Object, Object> cache_;
@@ -49,8 +65,27 @@
       cache_ = pCache_.getCache();
       util_ = util;
    }
+   
+   @Override
+   protected Fqn<?> getFqn(Object obj)
+   {
+      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
+         BaseInterceptor interceptor = AopUtil.findCacheInterceptor(advisor);
+         if (interceptor != null)
+            return interceptor.getFqn();
+      }
+      
+      return null;        
+   }
 
-   public Object get(Fqn<?> fqn, Class<?> clazz, PojoInstance pojoInstance) throws CacheException
+   @Override
+   protected Object get(Fqn<?> fqn, Class<?> clazz, PojoInstance pojoInstance) throws CacheException
    {
       CachedType type = pCache_.getCachedType(clazz);
       Object obj = Instantiator.newInstance(clazz);
@@ -84,7 +119,8 @@
       return obj;
    }
 
-   void put(Fqn<?> fqn, Fqn<?> referencingFqn, Object obj) throws CacheException
+   @Override
+   protected void put(Fqn<?> fqn, Fqn<?> referencingFqn, Object obj) throws CacheException
    {
       CachedType type = pCache_.getCachedType(obj.getClass());
       // We have a clean slate then.
@@ -168,9 +204,10 @@
       }
    }
 
-   Object remove(Fqn<?> fqn, Object result, Class<?> clazz) throws CacheException
+   @Override
+   protected Object remove(Fqn<?> fqn, Fqn<?> referencingFqn, Object result) throws CacheException
    {
-      CachedType type = pCache_.getCachedType(clazz);
+      CachedType type = pCache_.getCachedType(result.getClass());
       InstanceAdvisor advisor = ((Advised) result)._getInstanceAdvisor();
       for (Iterator i = type.getFields().iterator(); i.hasNext();)
       {
@@ -196,8 +233,7 @@
          }
       }
 
-      // batch remove
-      cache_.getRoot().getChild(fqn).clearData();
+      cache_.removeNode(fqn);
       // 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.
@@ -210,6 +246,12 @@
          util_.detachInterceptor(advisor, interceptor);
       }
 
-      return null; // Not really null though.
+      return result;
    }
+
+   @Override
+   protected boolean handles(Class<?> clazz)
+   {
+      return Advised.class.isAssignableFrom(clazz);
+   }
 }

Added: pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/ArrayHandler.java
===================================================================
--- pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/ArrayHandler.java	                        (rev 0)
+++ pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/ArrayHandler.java	2008-03-11 04:05:28 UTC (rev 5410)
@@ -0,0 +1,88 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.cache.pojo.impl;
+
+import org.jboss.cache.Fqn;
+import org.jboss.cache.pojo.collection.CachedArray;
+import org.jboss.cache.pojo.collection.CachedArrayRegistry;
+
+/**
+ * Handles array types.
+ *
+ * @author Jason T. Greene
+ */
+public class ArrayHandler extends AbstractHandler
+{
+   private final PojoCacheImpl cache;
+   
+   ArrayHandler(PojoCacheImpl cache)
+   {
+      this.cache = cache;
+   }
+   
+   protected Fqn<?> getFqn(Object array)
+   {
+      CachedArray cached = CachedArrayRegistry.lookup(array);
+      return cached != null ? cached.getFqn() : null;
+   }
+   
+   
+   protected void put(Fqn<?> fqn, Fqn<?> referencingFqn, Object obj)
+   {
+      // Always initialize the ref count so that we can mark this as an AopNode.
+      PojoInstance pojoInstance = InternalHelper.initializeAopInstance(referencingFqn);
+      pojoInstance.set(obj);
+      pojoInstance.setPojoClass(obj.getClass());
+      cache.getCache().put(fqn, PojoInstance.KEY, pojoInstance);
+      
+      CachedArray cached = CachedArray.create(fqn, cache, obj);
+      CachedArrayRegistry.register(obj, cached);
+   }
+   
+   @Override
+   protected Object get(Fqn<?> fqn, Class<?> clazz, PojoInstance pojo)
+   {
+      CachedArray cached = CachedArray.load(fqn, cache, clazz);
+      Object array = cached.toArray();
+      CachedArrayRegistry.register(array, cached);
+      
+      return array;
+   }
+   
+   @Override
+   protected Object remove(Fqn<?> fqn, Fqn<?> referencingFqn, Object obj)
+   {
+      CachedArray cached = CachedArrayRegistry.unregister(obj);
+      if (cached != null)
+         cached.destroy();
+      
+      return obj;
+   }
+
+   @Override
+   protected boolean handles(Class<?> clazz)
+   {
+      return false;
+      //return clazz.isArray();
+   }
+
+}

Modified: pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/CachedType.java
===================================================================
--- pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/CachedType.java	2008-03-11 01:48:44 UTC (rev 5409)
+++ pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/CachedType.java	2008-03-11 04:05:28 UTC (rev 5410)
@@ -82,7 +82,7 @@
       return immediate;
    }
 
-   private static boolean isImmediate(Class clazz)
+   public static boolean isImmediate(Class clazz)
    {
       // Treat enums as a simple type since they serialize to a simple string
       return immediates.contains(clazz) || Enum.class.isAssignableFrom(clazz);

Modified: pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/CollectionClassHandler.java
===================================================================
--- pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/CollectionClassHandler.java	2008-03-11 01:48:44 UTC (rev 5409)
+++ pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/CollectionClassHandler.java	2008-03-11 04:05:28 UTC (rev 5410)
@@ -7,24 +7,23 @@
 
 package org.jboss.cache.pojo.impl;
 
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
 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.Cache;
 import org.jboss.cache.CacheException;
-import org.jboss.cache.CacheSPI;
 import org.jboss.cache.Fqn;
 import org.jboss.cache.pojo.PojoCacheException;
 import org.jboss.cache.pojo.collection.CollectionInterceptorUtil;
 import org.jboss.cache.pojo.interceptors.dynamic.AbstractCollectionInterceptor;
 import org.jboss.cache.pojo.interceptors.dynamic.BaseInterceptor;
 
-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.
  *
@@ -32,7 +31,7 @@
  *         Date: Aug 4, 2005
  * @version $Id$
  */
-class CollectionClassHandler
+class CollectionClassHandler extends AbstractHandler
 {
    private final Log log = LogFactory.getLog(CollectionClassHandler.class);
    private Cache<Object, Object> cache_;
@@ -45,8 +44,17 @@
       cache_ = pCache_.getCache();
       internal_ = internal;
    }
+   
+   protected Fqn<?> getFqn(Object collection)
+   {
+      if (! (collection instanceof ClassProxy))
+         return null;
+     
+      BaseInterceptor interceptor = CollectionInterceptorUtil.getInterceptor((ClassProxy) collection);
+      return interceptor != null ? interceptor.getFqn() : null;
+   }
 
-   Object get(Fqn fqn, Class clazz, PojoInstance pojoInstance)
+   protected Object get(Fqn fqn, Class clazz, PojoInstance pojoInstance)
          throws CacheException
    {
       Object obj = null;
@@ -76,7 +84,7 @@
       return obj;
    }
 
-   void put(Fqn fqn, Fqn referencingFqn, Object obj) throws CacheException
+   protected void put(Fqn fqn, Fqn referencingFqn, Object obj) throws CacheException
    {
       boolean isCollection = false;
 
@@ -260,17 +268,26 @@
       }
    }
 
-   Object remove(Fqn fqn, Object obj) throws CacheException
+   @Override
+   protected Object remove(Fqn<?> fqn, Fqn<?> referencingFqn, 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;
+      AbstractCollectionInterceptor interceptor = (AbstractCollectionInterceptor) CollectionInterceptorUtil.getInterceptor((ClassProxy) obj);
       // detach the interceptor. This will trigger a copy and remove.
-      ((AbstractCollectionInterceptor) interceptor).detach(removeFromCache);
-      return ((AbstractCollectionInterceptor) interceptor).getCurrentCopy();
+      interceptor.detach(true);
+      cache_.removeNode(fqn);
+      
+      return interceptor.getCurrentCopy();
    }
+
+
+   @Override
+   protected boolean handles(Class<?> clazz)
+   {
+      return Collection.class.isAssignableFrom(clazz) || Map.class.isAssignableFrom(clazz);
+   }
 }

Modified: pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/ObjectGraphHandler.java
===================================================================
--- pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/ObjectGraphHandler.java	2008-03-11 01:48:44 UTC (rev 5409)
+++ pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/ObjectGraphHandler.java	2008-03-11 04:05:28 UTC (rev 5410)
@@ -9,18 +9,9 @@
 
 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.advice.Interceptor;
-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.Fqn;
 import org.jboss.cache.pojo.PojoCacheException;
-import org.jboss.cache.pojo.collection.CollectionInterceptorUtil;
-import org.jboss.cache.pojo.interceptors.dynamic.BaseInterceptor;
-import org.jboss.cache.pojo.util.AopUtil;
 
 /**
  * Handle the object graph management.
@@ -29,7 +20,7 @@
  *         Date: Aug 4, 2005
  * @version $Id$
  */
-class ObjectGraphHandler
+class ObjectGraphHandler extends AbstractHandler
 {
    private PojoCacheImpl cache;
    private InternalHelper internal_;
@@ -40,8 +31,19 @@
       this.cache = cache;
       internal_ = internal;
    }
+   
+   protected Fqn<?> getFqn(Object obj)
+   {
+      return null;
+   }
+   
+   protected boolean handles(Class<?> clazz)
+   {
+      return false;
+   }
 
-   Object get(Fqn fqn, Class clazz, PojoInstance pojoInstance) throws CacheException
+   @Override
+   protected Object get(Fqn<?> fqn, Class<?> clazz, PojoInstance pojoInstance) throws CacheException
    {
       // Note this is actually the aliasFqn, not the real fqn!
       Object obj;
@@ -54,44 +56,13 @@
       return obj; // No need to set the instance under fqn. It is located in refFqn anyway.
    }
 
-   void put(Fqn fqn, Object obj, String field) throws CacheException
+   @Override
+   protected void put(Fqn<?> fqn, Fqn<?> referencingFqn, Object obj) throws CacheException
    {
-      CachedType type = cache.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, field);
+      setupRefCounting(fqn, referencingFqn);
    }
 
-   boolean isMultipleReferenced(Fqn internalFqn)
+   boolean isMultipleReferenced(Fqn<?> internalFqn)
    {
       // Note this is actually the aliasFqn, not the real fqn!
       PojoInstance pojoInstance = null;
@@ -108,7 +79,8 @@
 
    }
 
-   void remove(Fqn referencingFqn, Fqn internalFqn, Object pojo)
+   @Override
+   protected Object remove(Fqn<?> fqn, Fqn<?> referencingFqn, Object pojo)
          throws CacheException
    {
       if (log.isDebugEnabled())
@@ -116,22 +88,24 @@
          log.debug("remove(): removing object fqn: " + referencingFqn
                    + " Will just de-reference it.");
       }
-      removeFromReference(referencingFqn, internalFqn);
+      removeFromReference(fqn, referencingFqn);
+      
+      return null; 
    }
 
    /**
     * Remove the object from the the reference fqn, meaning just decrement the ref counter.
     */
-   private void removeFromReference(Fqn referencingFqn, Fqn originalFqn) throws CacheException
+   private void removeFromReference(Fqn<?> originalFqn, Fqn<?> referencingFqn) throws CacheException
    {
-      synchronized (referencingFqn)
+      synchronized (originalFqn)
       {  // 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)
+         if (decrementRefCount(originalFqn, referencingFqn) == 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.
-            cache.detach(referencingFqn);
+            cache.detach(originalFqn);
          }
       }
    }
@@ -143,26 +117,21 @@
     * @param fqn    The original fqn node
     * @param refFqn The new internal fqn node
     */
-   private void setupRefCounting(Fqn fqn, Fqn refFqn) throws CacheException
+   private void setupRefCounting(Fqn<?> fqn, Fqn<?> referencingFqn) throws CacheException
    {
-      synchronized (refFqn)
-      { // we lock the ref fqn here so no one else has access.
+      synchronized (fqn)
+      {
          // 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);
-         }
+         incrementRefCount(fqn, referencingFqn);
       }
    }
 
-   private int incrementRefCount(Fqn originalFqn, Fqn referencingFqn) throws CacheException
+   private int incrementRefCount(Fqn<?> originalFqn, Fqn<?> referencingFqn) throws CacheException
    {
       return internal_.incrementRefCount(originalFqn, referencingFqn);
    }
 
-   private int decrementRefCount(Fqn referencingFqn, Fqn originalFqn) throws CacheException
+   private int decrementRefCount(Fqn<?> originalFqn, Fqn<?> referencingFqn) throws CacheException
    {
       int count = 0;
       if ((count = internal_.decrementRefCount(originalFqn, referencingFqn)) == (PojoInstance.INITIAL_COUNTER_VALUE + 1))

Modified: pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/PojoCacheDelegate.java
===================================================================
--- pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/PojoCacheDelegate.java	2008-03-11 01:48:44 UTC (rev 5409)
+++ pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/PojoCacheDelegate.java	2008-03-11 04:05:28 UTC (rev 5410)
@@ -48,6 +48,7 @@
    private AdvisedPojoHandler advisedHandler_;
    private ObjectGraphHandler graphHandler_;
    private CollectionClassHandler collectionHandler_;
+   private ArrayHandler arrayHandler;
    private SerializableObjectHandler serializableHandler_;
    // Use ThreadLocal to hold a boolean isBulkRemove
    private ThreadLocal<Boolean> bulkRemove_ = new ThreadLocal<Boolean>();
@@ -63,6 +64,7 @@
       collectionHandler_ = new CollectionClassHandler(pojoCache, internal_);
       serializableHandler_ = new SerializableObjectHandler(pojoCache, internal_);
       advisedHandler_ = new AdvisedPojoHandler(pojoCache, internal_, util_);
+      arrayHandler = new ArrayHandler(pojoCache);
    }
 
    public void setBulkRemove(boolean bulk)
@@ -107,29 +109,14 @@
       }
    }
 
-   public Object putObjectI(Fqn fqn, Object obj, String field) throws CacheException
-   {
-      // Skip some un-necessary update if obj is the same class as the old one
-      Object oldValue = internal_.getPojo(fqn, field);
-      if (oldValue == obj && (obj instanceof Advised || obj instanceof ClassProxy))
-      {
-         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, String field) throws CacheException
+   public Object putObject(Fqn fqn, Object obj, String field) throws CacheException
    {
       // Skip some un-necessary update if obj is the same class as the old one
       Object oldValue = internal_.getPojo(fqn, field);
-      if (oldValue == obj)
+      if (oldValue == obj && skipDuplicateAttach(obj))
       {
          if (log.isDebugEnabled())
          {
@@ -143,59 +130,63 @@
       pojoCache.detach(fqn, field);
 
       if (obj == null)
-      {
          return oldValue;// we are done
-      }
 
-      // creates the internal node first without going thru the interceptor.
-      // This way we don't block on __JBossInternal__ node.
-      //createChildNodeFirstWithoutLocking(internalFqn);
-
-      if ((obj instanceof Advised || obj instanceof ClassProxy) && isMultipleReferencedPut(obj))
+      AbstractHandler handler = getHandler(obj.getClass());
+      Fqn<?> internalFqn = handler.getFqn(obj);
+      
+      if (internalFqn != null)
       {
-         // we pass in the originating fqn intentionaly
-         graphHandler_.put(fqn, obj, field);
+         graphHandler_.put(internalFqn, fqn, obj);
       }
       else
       {
-         Fqn internalFqn = createInternalFqn(fqn, obj);
-         if (log.isDebugEnabled())
-         {
-            log.debug("putObject(): id: " + fqn + " will store the pojo in the internal area: "
-                      + internalFqn);
-         }
+         internalFqn = createInternalFqn(fqn, obj);
+         if (log.isDebugEnabled())        
+            log.debug("attach(): id: " + fqn + " will store the pojo in the internal area: " + internalFqn);
+        
+         handler.put(internalFqn, fqn, obj);
 
-         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);
-         }
-
          // Used by notification sub-system
          cache.put(internalFqn, InternalConstant.POJOCACHE_STATUS, "ATTACHED");
-
-         setPojoReference(fqn, obj, field, internalFqn);
       }
+      
+      setPojoReference(fqn, obj, field, internalFqn);
 
       return oldValue;
    }
+   
+   private boolean skipDuplicateAttach(Object obj)
+   {
+      return obj == null || getHandler(obj.getClass()) != serializableHandler_;
+   }
+   
+   private AbstractHandler getHandler(Class<?> clazz)
+   {
+      if (advisedHandler_.handles(clazz))
+         return advisedHandler_;
+      
+      if (collectionHandler_.handles(clazz))
+         return collectionHandler_;
+      
+      if (arrayHandler.handles(clazz))
+         return arrayHandler;
+      
+      if (serializableHandler_.handles(clazz))
+         return serializableHandler_;
+    
+      throw new CacheException("Can not manage object. It must be either instrumented, a collection, an array, or Serializable: " 
+            + clazz.getName());
+   }
+   
 
-   Fqn createInternalFqn(Fqn fqn, Object obj) throws CacheException
+   private Fqn createInternalFqn(Fqn fqn, Object obj) throws CacheException
    {
       // Create an internal Fqn name
       return AopUtil.createInternalFqn(fqn, cache);
    }
 
-   Fqn setPojoReference(Fqn fqn, Object obj, String field, Fqn internalFqn) throws CacheException
+   private Fqn setPojoReference(Fqn fqn, Object obj, String field, Fqn internalFqn) throws CacheException
    {
       // Create PojoReference
       CachedType type = pojoCache.getCachedType(obj.getClass());
@@ -249,8 +240,7 @@
          return null;
       }
 
-      Class clazz = pojoReference.getPojoClass();
-      Fqn internalFqn = pojoReference.getFqn();
+      Fqn<?> internalFqn = pojoReference.getFqn();
 
       if (log.isDebugEnabled())
       {
@@ -260,37 +250,20 @@
 
       Object result = pojoCache.getObject(internalFqn);
       if (result == null)
-      {
          return null;
-      }
 
       if (graphHandler_.isMultipleReferenced(internalFqn))
       {
-         graphHandler_.remove(fqn, internalFqn, result);
+         graphHandler_.remove(internalFqn, fqn, result);
       }
       else
       {
          cache.put(internalFqn, InternalConstant.POJOCACHE_STATUS, "DETACHING");
-         if (Advised.class.isAssignableFrom(clazz))
-         {
-            advisedHandler_.remove(internalFqn, result, clazz);
-            internal_.cleanUp(internalFqn, null);
-         }
-         else if (isCollectionGet(clazz))
-         {
-            // We need to return the original reference
-            result = collectionHandler_.remove(internalFqn, result);
-            internal_.cleanUp(internalFqn, null);
-         }
-         else
-         {// Just Serializable objects. Do a brute force remove is ok.
-            serializableHandler_.remove();
-            internal_.cleanUp(internalFqn, null);
-         }
+         
+         result = getHandler(result.getClass()).remove(internalFqn, fqn, result);
       }
 
       internal_.cleanUp(fqn, field);
-      // remove the interceptor as well.
       return result;
    }
 
@@ -314,9 +287,9 @@
       return map;
    }
 
-   private Object getObjectInternal(Fqn fqn, String field) throws CacheException
+   private Object getObjectInternal(Fqn<?> fqn, String field) throws CacheException
    {
-      Fqn internalFqn = fqn;
+      Fqn<?> internalFqn = fqn;
       PojoReference pojoReference = internal_.getPojoReference(fqn, field);
       if (pojoReference != null)
       {
@@ -341,115 +314,13 @@
          return null;
          //throw new PojoCacheException("PojoCacheDelegate.getObjectInternal(): null PojoInstance for fqn: " + internalFqn);
 
-      Class clazz = pojoInstance.getPojoClass();
+      Class<?> clazz = pojoInstance.getPojoClass();
+      obj = getHandler(clazz).get(internalFqn, clazz, pojoInstance);
 
-      // 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;
-
-   }
-
-   private void detachInterceptor(InstanceAdvisor advisor, Interceptor interceptor,
-                                  boolean detachOnly, Map undoMap)
-   {
-      if (!detachOnly)
-      {
-         util_.detachInterceptor(advisor, interceptor);
-         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);
-         }
-      }
-   }
-
    private void findChildObjects(Fqn fqn, Map map) throws CacheException
    {
       // We need to traverse then

Modified: pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/PojoCacheImpl.java
===================================================================
--- pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/PojoCacheImpl.java	2008-03-11 01:48:44 UTC (rev 5409)
+++ pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/PojoCacheImpl.java	2008-03-11 04:05:28 UTC (rev 5410)
@@ -125,17 +125,9 @@
    /**
     * This public API is called from internal package only.
     */
-   public Object putObject(Fqn<?> id, Object pojo, String field)
-           throws CacheException
+   public Object putObject(Fqn<?> id, Object pojo, String field) throws CacheException
    {
-      Object obj = null;
-
-      // Maybe this is the same instance already.
-      obj = delegate_.putObjectI(id, pojo, field);
-      if (obj != null) return obj;
-
-      obj = delegate_.putObjectII(id, pojo, field);
-      return obj;
+      return delegate_.putObject(id, pojo, field);
    }
 
    public Object detach(String id) throws PojoCacheException

Modified: pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/SerializableObjectHandler.java
===================================================================
--- pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/SerializableObjectHandler.java	2008-03-11 01:48:44 UTC (rev 5409)
+++ pojo/trunk/src/main/java/org/jboss/cache/pojo/impl/SerializableObjectHandler.java	2008-03-11 04:05:28 UTC (rev 5410)
@@ -7,6 +7,7 @@
 
 package org.jboss.cache.pojo.impl;
 
+import java.io.Serializable;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -23,7 +24,7 @@
  * @author Ben Wang
  * @version $Id$
  */
-class SerializableObjectHandler
+class SerializableObjectHandler extends AbstractHandler
 {
    private Cache<Object, Object> cache;
    private PojoCacheImpl pojoCache;
@@ -36,17 +37,29 @@
       this.cache = pojoCache.getCache();
       internal_ = internal;
    }
+   
+   protected Fqn<?> getFqn(Object obj)
+   {
+      // Not supported
+      return null;
+   }
+   
+   @Override
+   protected boolean handles(Class<?> clazz)
+   {
+      return Serializable.class.isAssignableFrom(clazz);
+   }
 
-   Object get(Fqn fqn, Class clazz, PojoInstance pojoInstance)
-         throws CacheException
+   @Override
+   protected 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
+   @Override
+   protected void put(Fqn<?> fqn, Fqn<?> referencingFqn, Object obj) throws CacheException
    {
       // Note that JBoss Serialization can serialize any type now.
       if (log_.isDebugEnabled())
@@ -56,7 +69,6 @@
       }
 
       putIntoCache(fqn, obj);
-      return true;
    }
 
    private void putIntoCache(Fqn fqn, Object obj)
@@ -74,10 +86,10 @@
       internal_.put(fqn, map);
    }
 
-   @SuppressWarnings({"CanBeStatic"})
-   void remove()
+   @Override
+   protected Object remove(Fqn<?> fqn, Fqn<?> referenceingFqn, Object result) throws CacheException
    {
-      // No need to do anything here since we will do clean up afterwards.
+      cache.removeNode(fqn);
+      return result;
    }
-
-}
+}
\ No newline at end of file

Added: pojo/trunk/src/main/java/org/jboss/cache/pojo/interceptors/dynamic/ArrayInterceptor.java
===================================================================
--- pojo/trunk/src/main/java/org/jboss/cache/pojo/interceptors/dynamic/ArrayInterceptor.java	                        (rev 0)
+++ pojo/trunk/src/main/java/org/jboss/cache/pojo/interceptors/dynamic/ArrayInterceptor.java	2008-03-11 04:05:28 UTC (rev 5410)
@@ -0,0 +1,86 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2005, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.cache.pojo.interceptors.dynamic;
+
+import org.jboss.aop.advice.Interceptor;
+import org.jboss.aop.array.ArrayElementReadInvocation;
+import org.jboss.aop.array.ArrayElementWriteInvocation;
+import org.jboss.aop.array.ArrayRegistry;
+import org.jboss.aop.joinpoint.Invocation;
+import org.jboss.cache.pojo.collection.CachedArray;
+import org.jboss.cache.pojo.collection.CachedArrayRegistry;
+
+/**
+ * AOP interceptor which delegates to POJO Cache
+ *
+ * @author Jason T. Greene
+ */
+public class ArrayInterceptor implements Interceptor
+{
+
+   /* (non-Javadoc)
+    * @see org.jboss.aop.advice.Interceptor#getName()
+    */
+   public String getName()
+   {
+      // TODO Auto-generated method stub
+      return this.getClass().getName();
+   }
+
+   /* (non-Javadoc)
+    * @see org.jboss.aop.advice.Interceptor#invoke(org.jboss.aop.joinpoint.Invocation)
+    */
+   public Object invoke(Invocation invocation) throws Throwable
+   {
+      if (invocation instanceof ArrayElementReadInvocation)
+      {
+         ArrayElementReadInvocation read = (ArrayElementReadInvocation)invocation;
+         Object array = read.getTargetObject();
+         CachedArray cached = CachedArrayRegistry.lookup(array);
+         if (cached != null)
+         {
+            int index = read.getIndex();
+            Object element = cached.get(index);
+            
+            // AOP only registers on write, work around for now
+            if (element != null && element.getClass().isArray())
+               ArrayRegistry.getInstance().addElementReference(array, index, element);
+            
+            return element;
+         }
+      }
+      else if (invocation instanceof ArrayElementWriteInvocation)
+      {
+         ArrayElementWriteInvocation write = (ArrayElementWriteInvocation) invocation;
+         Object array = write.getTargetObject();
+         CachedArray cached = CachedArrayRegistry.lookup(array);
+         if (cached != null)
+         {
+            cached.set(write.getIndex(), write.getValue());
+            return null;
+         }
+      }
+      
+      return invocation.invokeNext();
+   }
+
+}

Modified: pojo/trunk/src/main/java/org/jboss/cache/pojo/interceptors/dynamic/CacheFieldInterceptor.java
===================================================================
--- pojo/trunk/src/main/java/org/jboss/cache/pojo/interceptors/dynamic/CacheFieldInterceptor.java	2008-03-11 01:48:44 UTC (rev 5409)
+++ pojo/trunk/src/main/java/org/jboss/cache/pojo/interceptors/dynamic/CacheFieldInterceptor.java	2008-03-11 04:05:28 UTC (rev 5410)
@@ -12,6 +12,7 @@
 import org.apache.commons.logging.LogFactory;
 import org.jboss.aop.Advisor;
 import org.jboss.aop.advice.Interceptor;
+import org.jboss.aop.array.ArrayRegistry;
 import org.jboss.aop.joinpoint.FieldInvocation;
 import org.jboss.aop.joinpoint.FieldReadInvocation;
 import org.jboss.aop.joinpoint.FieldWriteInvocation;
@@ -20,6 +21,8 @@
 import org.jboss.cache.Cache;
 import org.jboss.cache.Fqn;
 import org.jboss.cache.pojo.PojoCacheAlreadyDetachedException;
+import org.jboss.cache.pojo.collection.CachedArray;
+import org.jboss.cache.pojo.collection.CachedArrayRegistry;
 import org.jboss.cache.pojo.impl.CachedType;
 import org.jboss.cache.pojo.impl.PojoCacheImpl;
 import org.jboss.cache.pojo.impl.PojoInstance;
@@ -93,10 +96,10 @@
       // Kind of ad hoc now. MethodInvocation should not invoke this.
       if (invocation instanceof MethodInvocation)
          return invocation.invokeNext();
-
-
+      
       if (invocation instanceof FieldWriteInvocation)
       {
+         Object target = invocation.getTargetObject();
          FieldInvocation fieldInvocation =
                (FieldInvocation) invocation;
 
@@ -107,7 +110,7 @@
             log_.trace("invoke(): field write interception for fqn: " + fqn_ + " and field: " + field);
          }
 
-         verifyAttached(invocation.getTargetObject());
+         verifyAttached(target);
 
          // Only if this field is replicatable. static, transient and final are not.
          CachedType fieldType = pCache_.getCachedType(field.getType());
@@ -124,11 +127,11 @@
             }
          }
 
-         Object obj = fieldInvocation.getTargetObject();
-         util_.inMemorySubstitution(obj, field, value);
+         util_.inMemorySubstitution(target, field, value);
       }
       else if (invocation instanceof FieldReadInvocation)
       {
+         Object target = invocation.getTargetObject();
          FieldInvocation fieldInvocation =
                (FieldInvocation) invocation;
          Field field = fieldInvocation.getField();
@@ -146,14 +149,18 @@
             else
             {
                result = pCache_.getObject(fqn_, field.getName());
+               
+               // Work around AOP issue with field reads
+               if (result != null && result.getClass().isArray())
+                  registerArrayWithAOP(target, field.getName(), result);
             }
 
             // If the result is null, the object might have been detached
             if (result == null)
-               verifyAttached(invocation.getTargetObject());
+               verifyAttached(target);
 
             // Update last known state associated with this pojo.
-            util_.inMemorySubstitution(invocation.getTargetObject(), field, result);
+            util_.inMemorySubstitution(target, field, result);
 
             // Allow interceptor chain to process, but ignore the result
             invocation.invokeNext();
@@ -165,6 +172,13 @@
       return invocation.invokeNext();
    }
 
+   private void registerArrayWithAOP(Object owner, String fieldName, Object array) 
+   {
+      CachedArray cached = CachedArrayRegistry.lookup(array);
+      if (cached != null)
+         ArrayRegistry.getInstance().addFieldReference(owner, fieldName, array);         
+   }
+   
    /**
     * Check if the pojo is detached already.
     */

Modified: pojo/trunk/src/main/java/org/jboss/cache/pojo/util/AopUtil.java
===================================================================
--- pojo/trunk/src/main/java/org/jboss/cache/pojo/util/AopUtil.java	2008-03-11 01:48:44 UTC (rev 5409)
+++ pojo/trunk/src/main/java/org/jboss/cache/pojo/util/AopUtil.java	2008-03-11 04:05:28 UTC (rev 5410)
@@ -67,7 +67,7 @@
     * @param advisor
     * @return Interceptor
     */
-   static public Interceptor findCacheInterceptor(InstanceAdvisor advisor)
+   static public CacheFieldInterceptor findCacheInterceptor(InstanceAdvisor advisor)
    {
       // TODO we assume there is only one interceptor now.
       Interceptor[] interceptors = advisor.getInterceptors();
@@ -77,7 +77,7 @@
          Interceptor interceptor = interceptors[i];
          if (interceptor instanceof CacheFieldInterceptor)
          {
-            return interceptor;
+            return (CacheFieldInterceptor)interceptor;
          }
       }
       return null;

Modified: pojo/trunk/src/main/resources/META-INF/pojocache-aop.xml
===================================================================
--- pojo/trunk/src/main/resources/META-INF/pojocache-aop.xml	2008-03-11 01:48:44 UTC (rev 5409)
+++ pojo/trunk/src/main/resources/META-INF/pojocache-aop.xml	2008-03-11 04:05:28 UTC (rev 5410)
@@ -145,5 +145,13 @@
    <prepare expr="field(* $instanceof{@org.jboss.cache.pojo.annotation.Replicable}->*)" />
    
    <!--  Work around that ensures annotated classes which do not access fields are instrumented -->
-   <introduction expr="class($instanceof{@org.jboss.cache.pojo.annotation.Replicable})"/>   
-</aop>
\ No newline at end of file
+   <introduction expr="class($instanceof{@org.jboss.cache.pojo.annotation.Replicable})"/>
+
+   <!-- Array support 
+   <arrayreplacement expr="class($instanceof{@org.jboss.cache.pojo.annotation.Replicable})"/>
+   <interceptor name="array" class="org.jboss.cache.pojo.interceptors.dynamic.ArrayInterceptor"/>
+   <arraybind type="READ_WRITE">
+      <interceptor-ref name="array"/>
+   </arraybind>
+   -->
+</aop>

Modified: pojo/trunk/src/test/java/org/jboss/cache/pojo/ArrayTest.java
===================================================================
--- pojo/trunk/src/test/java/org/jboss/cache/pojo/ArrayTest.java	2008-03-11 01:48:44 UTC (rev 5409)
+++ pojo/trunk/src/test/java/org/jboss/cache/pojo/ArrayTest.java	2008-03-11 04:05:28 UTC (rev 5410)
@@ -8,10 +8,25 @@
 package org.jboss.cache.pojo;
 
 
+import java.util.HashMap;
+
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.jboss.cache.config.Configuration.CacheMode;
+import org.jboss.cache.factories.UnitTestCacheConfigurationFactory;
+import org.jboss.cache.notifications.annotation.CacheListener;
+import org.jboss.cache.notifications.annotation.NodeActivated;
+import org.jboss.cache.notifications.annotation.NodeCreated;
+import org.jboss.cache.notifications.annotation.NodeModified;
+import org.jboss.cache.notifications.annotation.NodePassivated;
+import org.jboss.cache.notifications.annotation.NodeRemoved;
+import org.jboss.cache.notifications.annotation.NodeVisited;
+import org.jboss.cache.notifications.event.NodeEvent;
+import org.jboss.cache.notifications.event.NodeModifiedEvent;
+import org.jboss.cache.notifications.event.NodeVisitedEvent;
 import org.jboss.cache.pojo.test.ArrayObject;
-import org.jboss.cache.pojo.test.Person;
+import org.jboss.cache.pojo.test.ArrayObject.Person;
+import org.testng.AssertJUnit;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
@@ -25,22 +40,26 @@
 public class ArrayTest 
 {
    Log log = LogFactory.getLog(ArrayTest.class);
-   PojoCache cache_;
+   PojoCache cache1, cache2;
 
    @BeforeMethod(alwaysRun = true)
    protected void setUp() throws Exception
    {
       log.info("setUp() ....");
-      String configFile = "META-INF/local-service.xml";
-      boolean toStart = false;
-      cache_ = PojoCacheFactory.createCache(configFile, toStart);
-      cache_.start();
+      cache1 = PojoCacheFactory.createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), false);
+      cache1.getCache().addCacheListener(new MyCacheListener(false));
+      cache1.start();
+      
+      cache2 = PojoCacheFactory.createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), false);
+      cache2.getCache().addCacheListener(new MyCacheListener(true));
+      cache2.start();
+
    }
 
    @AfterMethod(alwaysRun = true)
    protected void tearDown() throws Exception
    {
-      cache_.stop();
+      cache1.stop();
    }
 
    public void testSimple() throws Exception
@@ -57,14 +76,61 @@
 
       ao.setPerson(0, joe);
 
-      cache_.attach("ao", ao);
+      cache1.attach("/ao", ao);
 
-      // TODO This should trigger a write on team but instead it does a read only. Why?
       ao.setPerson(1, ben);
+      ao.setPerson(2, joe);
+      ao.setPerson(3, ben);
+      ao.setPerson(4, joe);
+      ao.setPerson(5, ben);
+      
+      AssertJUnit.assertSame(ao.getPerson(1), ben);
+      AssertJUnit.assertSame(ao.getPerson(2), joe);
+      AssertJUnit.assertSame(ao.getPerson(3), ben);
+      AssertJUnit.assertSame(ao.getPerson(4), joe);
+      AssertJUnit.assertSame(ao.getPerson(5), ben);
+      
+      ArrayObject obj = (ArrayObject) cache2.find("/ao");
+      Person person = obj.getPerson(4);
+      obj.setPerson(5, person);
+      AssertJUnit.assertSame(ao.getPerson(5), ao.getPerson(4));
+      
+      ao.setNum(5, 4);
+      AssertJUnit.assertEquals(4, obj.getNum(5));
+   }
 
+   @CacheListener
+   public class MyCacheListener
+   {
+      private boolean visits;
+      
+      public MyCacheListener(boolean visits)
+      {
+         this.visits = visits;
+      }
+      
+      @NodeActivated
+      @NodePassivated
+      @NodeCreated
+      @NodeRemoved
+      @NodeVisited
+      public void print(NodeEvent ne)
+      {
+         if (visits != ne instanceof NodeVisitedEvent)
+            return;
+         
+         if (!ne.isPre())
+            System.out.println((!visits ? "[one] " : "[two] ") + ne.getType() + " " + ne.getFqn());
+      }
+      
+      @NodeModified
+      public void print(NodeModifiedEvent ne)
+      {
+         if (!visits && !ne.isPre())
+            System.out.println((!visits ? "[one] " : "[two] ") + ne.getType() + " " + ne.getFqn() + " - " + new HashMap(ne.getData()));
+      }
    }
 
 
 
-
 }

Modified: pojo/trunk/src/test/java/org/jboss/cache/pojo/ReplicatedByteTest.java
===================================================================
--- pojo/trunk/src/test/java/org/jboss/cache/pojo/ReplicatedByteTest.java	2008-03-11 01:48:44 UTC (rev 5409)
+++ pojo/trunk/src/test/java/org/jboss/cache/pojo/ReplicatedByteTest.java	2008-03-11 04:05:28 UTC (rev 5410)
@@ -80,13 +80,12 @@
       Resource res1 = (Resource) cache1.find("resource");
       assertEquals("Name ", res.getName(), res1.getName());
 
-      assertEquals("byte ", res.getByte()[0], res1.getByte()[0]);
+      assertEquals("byte ", res.getByte(0), res1.getByte(0));
 
       // field modification
       by = 2;
-      b[0] = by;
-      res1.setByte(b);
+      res.setByte(0, by);
       assertEquals("Name ", res.getName(), res1.getName());
-      assertEquals("byte ", res.getByte()[0], res1.getByte()[0]);
+      assertEquals("byte ", res.getByte(0), res1.getByte(0));
    }
 }

Modified: pojo/trunk/src/test/java/org/jboss/cache/pojo/test/ArrayObject.java
===================================================================
--- pojo/trunk/src/test/java/org/jboss/cache/pojo/test/ArrayObject.java	2008-03-11 01:48:44 UTC (rev 5409)
+++ pojo/trunk/src/test/java/org/jboss/cache/pojo/test/ArrayObject.java	2008-03-11 04:05:28 UTC (rev 5410)
@@ -7,17 +7,46 @@
 
 package org.jboss.cache.pojo.test;
 
+import java.io.Serializable;
+
 /**
  */
 // We are using JDK1.5 annotation.
 @org.jboss.cache.pojo.annotation.Replicable
 public class ArrayObject
 {
+   // Support array interception and serializable
+   @org.jboss.cache.pojo.annotation.Replicable
+   public static class Person implements Serializable
+   {
+      private static final long serialVersionUID = 1L;
+      private String name;
+      private int age;
+
+      public String getName()
+      {
+         return name;
+      }
+      public void setName(String name)
+      {
+         this.name = name;
+      }
+      public int getAge()
+      {
+         return age;
+      }
+      public void setAge(int age)
+      {
+         this.age = age;
+      }
+   }
    private Person[] team;
+   private int[] nums;
 
    public ArrayObject()
    {
       team = new Person[10];
+      nums = new int[] {1,2,3,4,5,6,7,8,9};
    }
 
    public Person[] getTeam()
@@ -29,14 +58,35 @@
    {
       team = t;
    }
+   
+   public void setNum(int index, int value)
+   {
+      int nums[] = this.nums;
+      nums[index] = value;
+      
+      // This will be optimized away if array interception is enabled
+      this.nums = nums;
+   }
+   
+   public int getNum(int index)
+   {
+      return nums[index];
+   }
 
    public Person getPerson(int index)
    {
-      return team[index];
+      Object o = team[index];
+      return (Person)o;
    }
 
    public void setPerson(int index, Person p)
    {
-      team[index] = p;
+      Person[] array = team;
+      array[index] = p;
+      
+      // This will be optimized away if array interception is enabled
+      team = array;
    }
 }
+
+

Modified: pojo/trunk/src/test/java/org/jboss/cache/pojo/test/Resource.java
===================================================================
--- pojo/trunk/src/test/java/org/jboss/cache/pojo/test/Resource.java	2008-03-11 01:48:44 UTC (rev 5409)
+++ pojo/trunk/src/test/java/org/jboss/cache/pojo/test/Resource.java	2008-03-11 04:05:28 UTC (rev 5410)
@@ -52,6 +52,21 @@
    {
       bin = b;
    }
+   
+   public void setByte(int index, byte b)
+   {
+      byte[] array = bin;
+      array[index] = b;
+      
+      // This will be optimized away if array interception is enabled
+      // However, it is needed if it is disabled (since serialization is used then)
+      bin = array;
+   }
+   
+   public byte getByte(int index)
+   {
+      return bin[index];
+   }
 
    public String toString()
    {




More information about the jbosscache-commits mailing list