[jbosscache-commits] JBoss Cache SVN: r4678 - in core/trunk/src: main/java/org/jboss/cache/util and 1 other directories.

jbosscache-commits at lists.jboss.org jbosscache-commits at lists.jboss.org
Tue Oct 23 16:53:27 EDT 2007


Author: genman
Date: 2007-10-23 16:53:27 -0400 (Tue, 23 Oct 2007)
New Revision: 4678

Added:
   core/trunk/src/main/java/org/jboss/cache/util/MinMapUtil.java
   core/trunk/src/test/java/org/jboss/cache/util/MinMapUtilTest.java
Modified:
   core/trunk/src/main/java/org/jboss/cache/AbstractNode.java
   core/trunk/src/main/java/org/jboss/cache/Fqn.java
   core/trunk/src/main/java/org/jboss/cache/UnversionedNode.java
   core/trunk/src/main/java/org/jboss/cache/util/DeltaMap.java
   core/trunk/src/test/java/org/jboss/cache/util/DeltaMapTest.java
Log:
Save some Node memory by using "MinMapUtil"
Add assertValid() to AbstractNode
Fix size() compute for DeltaMap

Modified: core/trunk/src/main/java/org/jboss/cache/AbstractNode.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/AbstractNode.java	2007-10-23 17:47:12 UTC (rev 4677)
+++ core/trunk/src/main/java/org/jboss/cache/AbstractNode.java	2007-10-23 20:53:27 UTC (rev 4678)
@@ -14,7 +14,7 @@
 {
    protected boolean deleted;
    protected Map<Object, Node<K, V>> children;
-   protected Fqn fqn;
+   protected Fqn<?> fqn;
    protected boolean resident;
 
    public boolean isDeleted()
@@ -34,7 +34,7 @@
       {
          synchronized (this)
          {
-            for (Node child : children.values())
+            for (Node<?,?> child : children.values())
             {
                ((AbstractNode) child).markAsDeleted(marker, true);
             }
@@ -59,7 +59,7 @@
    {
       if (another instanceof AbstractNode)
       {
-         AbstractNode anotherNode = (AbstractNode) another;
+         AbstractNode<?,?> anotherNode = (AbstractNode) another;
          return fqn == null && anotherNode.fqn == null || !(fqn == null || anotherNode.fqn == null) && fqn.equals(anotherNode.fqn);
       }
       return false;

Modified: core/trunk/src/main/java/org/jboss/cache/Fqn.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/Fqn.java	2007-10-23 17:47:12 UTC (rev 4677)
+++ core/trunk/src/main/java/org/jboss/cache/Fqn.java	2007-10-23 20:53:27 UTC (rev 4678)
@@ -61,23 +61,30 @@
  * @version $Revision$
  */
 @Immutable
-public class Fqn<E> implements Cloneable, Externalizable, Comparable<Fqn>
+public class Fqn<E> implements Cloneable, Externalizable, Comparable<Fqn<?>>
 {
 
    /**
     * Separator between FQN elements.
     */
    public static final String SEPARATOR = "/";
+   
    private static final long serialVersionUID = -5351930616956603651L;
+   
+   private static final Log log = LogFactory.getLog(Fqn.class);
+   
    private List<E> elements;
    private transient int hash_code = 0;
 
    /**
     * Immutable root FQN.
     */
+   @SuppressWarnings("unchecked")
    public static final Fqn ROOT = new Fqn();
-   private final static Log log = LogFactory.getLog(Fqn.class);
-   // a cached string representation of this Fqn, used by toString to it isn't calculated again every time.
+   
+   /**
+    * A cached string representation of this Fqn, used by toString to it isn't calculated again every time.
+    */ 
    private String cachedStringRep;
 
    /**
@@ -306,7 +313,7 @@
       {
          return false;
       }
-      Fqn other = (Fqn) obj;
+      Fqn<?> other = (Fqn<?>) obj;
       return elements.equals(other.elements);
    }
 
@@ -342,7 +349,7 @@
          }
          else
          {
-            StringBuffer sb = new StringBuffer();
+            StringBuilder sb = new StringBuilder();
             for (E element : elements)
             {
                sb.append(SEPARATOR).append(element);
@@ -519,7 +526,7 @@
    /**
     * Compares this Fqn to another using {@link FqnComparator}.
     */
-   public int compareTo(Fqn Fqn)
+   public int compareTo(Fqn<?> Fqn)
    {
       return FqnComparator.INSTANCE.compare(this, Fqn);
    }

Modified: core/trunk/src/main/java/org/jboss/cache/UnversionedNode.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/UnversionedNode.java	2007-10-23 17:47:12 UTC (rev 4677)
+++ core/trunk/src/main/java/org/jboss/cache/UnversionedNode.java	2007-10-23 20:53:27 UTC (rev 4678)
@@ -14,6 +14,7 @@
 import org.jboss.cache.marshall.MethodDeclarations;
 import org.jboss.cache.optimistic.DataVersion;
 import org.jboss.cache.transaction.GlobalTransaction;
+import org.jboss.cache.util.MinMapUtil;
 
 import java.util.Collections;
 import java.util.HashMap;
@@ -66,9 +67,13 @@
    /**
     * Map of general data keys to values.
     */
-   private final Map<K, V> data = new HashMap<K, V>();
+   private Map<K, V> data = Collections.emptyMap();
 
    private boolean lockForChildInsertRemove;
+   
+   /**
+    * Node moved or removed.
+    */
    private boolean valid = true;
 
    /**
@@ -91,7 +96,7 @@
       init(child_name, fqn, cache);
       setInternalState(data);
    }
-
+   
    /**
     * Initializes with a name and FQN and cache.
     */
@@ -162,15 +167,20 @@
       childrenLoaded = flag;
    }
 
+   private void assertValid() {
+      if (!valid)
+         throw new NodeNotValidException("Node " + getFqn() + " is not valid.  Perhaps it has been moved or removed.");
+   }
+
    public V get(K key)
    {
-      if (!valid) throw new NodeNotValidException("Node " + getFqn() + " is not valid.  Perhaps it has been moved or removed.");
+      assertValid();
       return cache.get(getFqn(), key);
    }
 
    public V getDirect(K key)
    {
-      return data == null ? null : data.get(key);
+      return data.get(key);
    }
 
 
@@ -192,35 +202,28 @@
 
    public Map<K, V> getData()
    {
-      if (!valid) throw new NodeNotValidException("Node " + getFqn() + " is not valid.  Perhaps it has been moved or removed.");
-      if (cache == null) return Collections.emptyMap();
+      assertValid();
+      if (cache == null)
+         return Collections.emptyMap();
       return cache.getData(getFqn());
-
-/*
-      Map<K, V> dMap = new HashMap<K, V>();
-      for (K k : cache.getKeys(getFqn()))
-      {
-         dMap.put(k, cache.get(fqn, k));
-      }
-      return Collections.unmodifiableMap(dMap);
-*/
    }
 
    public Map<K, V> getDataDirect()
    {
-      if (data == null) return Collections.emptyMap();
       return Collections.unmodifiableMap(data);
    }
 
    public V put(K key, V value)
    {
-      if (!valid) throw new NodeNotValidException("Node " + getFqn() + " is not valid.  Perhaps it has been moved or removed.");
+      assertValid();
       return cache.put(getFqn(), key, value);
    }
 
    public V putDirect(K key, V value)
    {
-      return data.put(key, value);
+      V v = data.get(key);
+      data = MinMapUtil.put(data, key, value);
+      return v;
    }
 
    public NodeSPI<K, V> getOrCreateChild(Object child_name, GlobalTransaction gtx)
@@ -287,14 +290,15 @@
 
    public V remove(K key)
    {
-      if (!valid) throw new NodeNotValidException("Node " + getFqn() + " is not valid.  Perhaps it has been moved or removed.");
+      assertValid();
       return cache.remove(getFqn(), key);
    }
 
    public V removeDirect(K key)
    {
-      if (data == null) return null;
-      return data.remove(key);
+      V v = data.get(key);
+      data = MinMapUtil.remove(data, key);
+      return v;
    }
 
    public void printDetails(StringBuffer sb, int indent)
@@ -320,12 +324,9 @@
       {
          sb.append("[ ").append(fqn);
       }
-      if (data != null)
+      synchronized (data)
       {
-         synchronized (data)
-         {
-            sb.append(" data=").append(data.keySet());
-         }
+         sb.append(" data=").append(data.keySet());
       }
       if (children != null && !children.isEmpty())
       {
@@ -348,7 +349,7 @@
 
    public Node<K, V> addChild(Fqn f)
    {
-      if (!valid) throw new NodeNotValidException("Node " + getFqn() + " is not valid.  Perhaps it has been moved or removed.");
+      assertValid();
       Fqn nf = new Fqn(getFqn(), f);
       cache.put(nf, null);
       return getChild(f);
@@ -383,18 +384,18 @@
 
    public void clearData()
    {
-      if (!valid) throw new NodeNotValidException("Node " + getFqn() + " is not valid.  Perhaps it has been moved or removed.");
+      assertValid();
       cache.removeData(getFqn());
    }
 
    public void clearDataDirect()
    {
-      if (data != null) data.clear();
+      data = Collections.emptyMap();
    }
 
    public Node<K, V> getChild(Fqn fqn)
    {
-      if (!valid) throw new NodeNotValidException("Node " + getFqn() + " is not valid.  Perhaps it has been moved or removed.");
+      assertValid();
       return cache.get(new Fqn(getFqn(), fqn));
    }
 
@@ -419,7 +420,7 @@
 
    public Set<Object> getChildrenNames()
    {
-      if (!valid) throw new NodeNotValidException("Node " + getFqn() + " is not valid.  Perhaps it has been moved or removed.");
+      assertValid();
       return cache.getChildrenNames(getFqn());
    }
 
@@ -430,14 +431,14 @@
 
    public Set<K> getKeys()
    {
-      if (!valid) throw new NodeNotValidException("Node " + getFqn() + " is not valid.  Perhaps it has been moved or removed.");
+      assertValid();
       Set<K> keys = cache.getKeys(getFqn());
       return keys == null ? Collections.<K>emptySet() : Collections.<K>unmodifiableSet(keys);
    }
 
    public Set<K> getKeysDirect()
    {
-      if (data == null)
+      if (data.isEmpty())
       {
          return Collections.emptySet();
       }
@@ -446,19 +447,19 @@
 
    public boolean hasChild(Fqn f)
    {
-      if (!valid) throw new NodeNotValidException("Node " + getFqn() + " is not valid.  Perhaps it has been moved or removed.");
+      assertValid();
       return getChild(f) != null;
    }
 
    public boolean hasChild(Object o)
    {
-      if (!valid) throw new NodeNotValidException("Node " + getFqn() + " is not valid.  Perhaps it has been moved or removed.");
+      assertValid();
       return getChild(o) != null;
    }
 
    public V putIfAbsent(K k, V v)
    {
-      if (!valid) throw new NodeNotValidException("Node " + getFqn() + " is not valid.  Perhaps it has been moved or removed.");
+      assertValid();
       // make sure this is atomic.  Not hugely performant at the moment (should use the locking interceptors) but for now ...
       synchronized (this)
       {
@@ -471,7 +472,7 @@
 
    public V replace(K key, V value)
    {
-      if (!valid) throw new NodeNotValidException("Node " + getFqn() + " is not valid.  Perhaps it has been moved or removed.");
+      assertValid();
       // make sure this is atomic.  Not hugely performant at the moment (should use the locking interceptors) but for now ...
       synchronized (this)
       {
@@ -486,7 +487,7 @@
 
    public boolean replace(K key, V oldValue, V newValue)
    {
-      if (!valid) throw new NodeNotValidException("Node " + getFqn() + " is not valid.  Perhaps it has been moved or removed.");
+      assertValid();
       // make sure this is atomic.  Not hugely performant at the moment (should use the locking interceptors) but for now ...
       synchronized (this)
       {
@@ -502,19 +503,19 @@
 
    public boolean removeChild(Fqn fqn)
    {
-      if (!valid) throw new NodeNotValidException("Node " + getFqn() + " is not valid.  Perhaps it has been moved or removed.");
+      assertValid();
       return cache.removeNode(new Fqn(getFqn(), fqn));
    }
 
    public int dataSize()
    {
-      if (!valid) throw new NodeNotValidException("Node " + getFqn() + " is not valid.  Perhaps it has been moved or removed.");
+      assertValid();
       return cache.getKeys(getFqn()).size();
    }
 
    public boolean removeChild(Object childName)
    {
-      if (!valid) throw new NodeNotValidException("Node " + getFqn() + " is not valid.  Perhaps it has been moved or removed.");
+      assertValid();
       return cache.removeNode(new Fqn(getFqn(), childName));
    }
 
@@ -549,20 +550,19 @@
 
    public void putAll(Map<K, V> data)
    {
-      if (!valid) throw new NodeNotValidException("Node " + getFqn() + " is not valid.  Perhaps it has been moved or removed.");
+      assertValid();
       cache.put(fqn, data);
    }
 
    public void replaceAll(Map<K, V> data)
    {
-      if (!valid) throw new NodeNotValidException("Node " + getFqn() + " is not valid.  Perhaps it has been moved or removed.");
+      assertValid();
       cache.put(fqn, data, true);
    }
 
    public void putAllDirect(Map<K, V> data)
    {
-      if (data == null) return;
-      this.data.putAll(data);
+      MinMapUtil.putAll(this.data, data);
    }
 
    public void removeChildrenDirect()
@@ -670,8 +670,9 @@
 
    public Set<Node<K, V>> getChildren()
    {
-      if (!valid) throw new NodeNotValidException("Node " + getFqn() + " is not valid.  Perhaps it has been moved or removed.");
-      if (cache == null) return Collections.emptySet();
+      assertValid();
+      if (cache == null)
+         return Collections.emptySet();
       Set<Node<K, V>> children = new HashSet<Node<K, V>>();
       for (Object c : cache.getChildrenNames(getFqn()))
       {
@@ -797,9 +798,9 @@
    @SuppressWarnings("unchecked")
    public Map getInternalState(boolean onlyInternalState)
    {
-      if (onlyInternalState) return new HashMap();
+      if (onlyInternalState)
+         return new HashMap(0);
       // don't bother doing anything here
-      if (data == null) return new HashMap();
       return new HashMap(data);
    }
 }

Modified: core/trunk/src/main/java/org/jboss/cache/util/DeltaMap.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/util/DeltaMap.java	2007-10-23 17:47:12 UTC (rev 4677)
+++ core/trunk/src/main/java/org/jboss/cache/util/DeltaMap.java	2007-10-23 20:53:27 UTC (rev 4678)
@@ -77,6 +77,15 @@
    }
 
    /**
+    * Creates and returns a DeltaMap for an empty map.
+    * @return a new instance
+    */
+   public static <K, V> DeltaMap<K, V> create()
+   {
+      return create(new HashMap<K,V>(0));
+   }
+
+   /**
     * Creates and returns a DeltaMap for an original map, excluding some key mappings.
     * 
     * @param original will not be modified, except by {@link #commit()}
@@ -111,18 +120,30 @@
          @Override
          public int size()
          {
-            int size = original.size() - exclude.size();
-            for (Object o : changed.keySet())
-            {
-               if (!original.containsKey(o))
-                  size++;
-            }
-            return size;
+            return DeltaMap.this.size();
          }
+
       };
    }
 
    @Override
+   public int size()
+   {
+      int size = original.size();
+      for (Object o : changed.keySet())
+      {
+         if (!original.containsKey(o))
+            size++;
+      }
+      for (Object o : exclude)
+      {
+         if (original.containsKey(o))
+            size--;
+      }
+      return size;
+   }
+     
+   @Override
    public boolean containsKey(Object key)
    {
       if (exclude.contains(key))
@@ -281,6 +302,24 @@
    }
 
    /**
+    * Returns the original wrapped Map.
+    */
+   public Map<K, V> getOriginal()
+   {
+      return original;
+   }
+
+   /**
+    * Sets the original values of this delta map.
+    */
+   public void setOriginal(Map<K, V> original)
+   {
+      if (original == null)
+         throw new NullPointerException("original");
+      this.original = original;
+   }
+
+   /**
     * Returns a Map of the entries changed, not including those removed.
     */
    public Map<K, V> getChanged()

Added: core/trunk/src/main/java/org/jboss/cache/util/MinMapUtil.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/util/MinMapUtil.java	                        (rev 0)
+++ core/trunk/src/main/java/org/jboss/cache/util/MinMapUtil.java	2007-10-23 20:53:27 UTC (rev 4678)
@@ -0,0 +1,96 @@
+package org.jboss.cache.util;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * Minimizes Map memory usage by changing the map 
+ * instance based on the number of stored entries.
+ *  
+ * @author Elias Ross
+ */
+public class MinMapUtil
+{
+
+   private static Class<?> singleton = Collections.singletonMap(null, null).getClass();
+
+   private static Class<?> empty = Collections.emptyMap().getClass();
+
+   private MinMapUtil()
+   {
+   }
+
+   /**
+    * Puts a mapping into a map, returns a map with the mapping.
+    * @param map destination
+    */
+   public static <K, V> Map<K, V> put(Map<K, V> map, K key, V value)
+   {
+      int size = map.size();
+      if (size == 0)
+         return Collections.singletonMap(key, value);
+      if (size == 1)
+      {
+         HashMap<K, V> map2 = new HashMap<K, V>(map);
+         map2.put(key, value);
+         return map2;
+      }
+      else
+      {
+         map.put(key, value);
+         return map;
+      }
+   }
+
+   /**
+    * Puts a number of entries into a map, returns a map.
+    * @param dest destination map
+    * @param src source map
+    */
+   public static <K, V> Map<K, V> putAll(Map<K, V> dest, Map<K, V> src)
+   {
+      if (src == null)
+      {
+         return dest;
+      }
+      int srcSize = src.size();
+      if (srcSize == 0)
+      {
+         return dest;
+      }
+      if (srcSize == 1)
+      {
+         Entry<K, V> next = src.entrySet().iterator().next();
+         return Collections.singletonMap(next.getKey(), next.getValue());
+      }
+      Class<?> c = dest.getClass();
+      if (c == empty || c == singleton)
+         return new HashMap<K, V>(src);
+      dest.putAll(src);
+      return dest;
+   }
+
+   /**
+    * Removes a mapping by key from a map, returns the map.
+    */
+   public static <K, V> Map<K, V> remove(Map<K, V> map, K key)
+   {
+      int size = map.size();
+      if (size == 0)
+      {
+         return map;
+      }
+      if (size == 1)
+      {
+         if (map.containsKey(key))
+            return Collections.emptyMap();
+         else
+            return map;
+      }
+      map.remove(key);
+      return map;
+   }
+
+}

Modified: core/trunk/src/test/java/org/jboss/cache/util/DeltaMapTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/util/DeltaMapTest.java	2007-10-23 17:47:12 UTC (rev 4677)
+++ core/trunk/src/test/java/org/jboss/cache/util/DeltaMapTest.java	2007-10-23 20:53:27 UTC (rev 4678)
@@ -41,6 +41,20 @@
       assertEquals(hm, dm);
    }
    
+   public void testSize()
+   {
+      assertEquals(3, dm.size());
+      dm.put(Y, "HI");
+      assertEquals(3, dm.size());
+      dm.remove(Y);
+      assertEquals(2, dm.size());
+      hm.clear();
+      assertEquals(0, dm.size());
+      dm.put(Z, Z);
+      dm.getRemoved().add("NOT HERE");
+      assertEquals(1, dm.size());
+   }
+   
    public void testConcurrent() throws Exception
    {
       ConcurrentHashMap<Object, String> m = new ConcurrentHashMap<Object, String>();

Added: core/trunk/src/test/java/org/jboss/cache/util/MinMapUtilTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/util/MinMapUtilTest.java	                        (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/util/MinMapUtilTest.java	2007-10-23 20:53:27 UTC (rev 4678)
@@ -0,0 +1,63 @@
+package org.jboss.cache.util;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertSame;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.HashMap;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+ at Test(groups =
+{"functional", "transaction"})
+public class MinMapUtilTest
+{
+
+   private static final Map<String, String> empty = Collections.emptyMap();
+   private Map<String, String> map;
+   private String key = "a";
+   private String key2 = "b";
+   private String value = "y";
+   
+   @BeforeMethod
+   public void before() {
+      map = empty;
+   }
+
+   public void testRemove()
+   {
+      assertSame(map, MinMapUtil.remove(map, key));
+      map = MinMapUtil.put(map, key, value);
+      map = MinMapUtil.put(map, key2, value);
+      assertEquals(value, map.get(key));
+      assertEquals(value, map.get(key2));
+      MinMapUtil.remove(map, key2);
+      assertSame(map, MinMapUtil.remove(map, "not here"));
+      assertSame(empty, MinMapUtil.remove(map, key));
+   }
+   
+   public void testPut()
+   {
+      assertSame(map, MinMapUtil.remove(map, key));
+      map = MinMapUtil.put(map, key, value);
+      assertEquals(1, map.size());
+      assertEquals(true, map.containsKey(key));
+      map = MinMapUtil.put(map, key, value);
+      assertEquals(1, map.size());
+      assertEquals(true, map.containsKey(key));
+      map = MinMapUtil.put(map, key2, value);
+      assertEquals(2, map.size());
+   }
+   
+   public void testPutAll()
+   {
+      HashMap<String, String> hm = new HashMap<String, String>();
+      assertSame(empty, MinMapUtil.putAll(map, hm));
+      hm.put(key, value);
+      assertEquals(1, MinMapUtil.putAll(map, hm).size());
+      hm.put(key2, value);
+      assertEquals(2, MinMapUtil.putAll(map, hm).size());
+   }
+}




More information about the jbosscache-commits mailing list