[jbosscache-commits] JBoss Cache SVN: r4507 - in core/trunk/src: main/java/org/jboss/cache/interceptors and 3 other directories.

jbosscache-commits at lists.jboss.org jbosscache-commits at lists.jboss.org
Wed Sep 26 06:15:08 EDT 2007


Author: mircea.markus
Date: 2007-09-26 06:15:08 -0400 (Wed, 26 Sep 2007)
New Revision: 4507

Added:
   core/trunk/src/main/java/org/jboss/cache/MetadataNodeWrapper.java
   core/trunk/src/test/java/org/jboss/cache/api/ResidentNodesTest.java
Modified:
   core/trunk/src/main/java/org/jboss/cache/AbstractNode.java
   core/trunk/src/main/java/org/jboss/cache/CacheImpl.java
   core/trunk/src/main/java/org/jboss/cache/Node.java
   core/trunk/src/main/java/org/jboss/cache/NodeSPI.java
   core/trunk/src/main/java/org/jboss/cache/Region.java
   core/trunk/src/main/java/org/jboss/cache/RegionImpl.java
   core/trunk/src/main/java/org/jboss/cache/RegionManager.java
   core/trunk/src/main/java/org/jboss/cache/UnversionedNode.java
   core/trunk/src/main/java/org/jboss/cache/interceptors/EvictionInterceptor.java
   core/trunk/src/main/java/org/jboss/cache/marshall/MethodCall.java
   core/trunk/src/main/resources/META-INF/local-lru-eviction-service.xml
Log:
JBCACHE-1154

Modified: core/trunk/src/main/java/org/jboss/cache/AbstractNode.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/AbstractNode.java	2007-09-25 20:55:20 UTC (rev 4506)
+++ core/trunk/src/main/java/org/jboss/cache/AbstractNode.java	2007-09-26 10:15:08 UTC (rev 4507)
@@ -15,7 +15,13 @@
    protected boolean deleted;
    protected Map<Object, Node<K, V>> children;
    protected Fqn fqn;
+   protected boolean resident;
 
+   /**
+    * Under this key the {@link #resident} attribute will be keept within the data map.
+    */
+   protected static final String INTERNAL_IS_RESIDENT = "_jbosscache.internal.resident";
+
    public boolean isDeleted()
    {
       return deleted;
@@ -41,6 +47,26 @@
       }
    }
 
+   public void setResident(boolean resident)
+   {
+      if (!resident)
+      {
+         remove((K)INTERNAL_IS_RESIDENT);
+      } else
+      {
+         put((K)INTERNAL_IS_RESIDENT, (V)"True");
+      }
+   }
+
+
+   public boolean isResident()
+   {
+      //hack - see setResident internals for details
+      return "True".equals(get((K)INTERNAL_IS_RESIDENT));
+   }
+
+
+
    public boolean equals(Object another)
    {
       if (another instanceof AbstractNode)
@@ -51,6 +77,7 @@
       return false;
    }
 
+
    public int hashCode()
    {
       return fqn.hashCode();

Modified: core/trunk/src/main/java/org/jboss/cache/CacheImpl.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/CacheImpl.java	2007-09-25 20:55:20 UTC (rev 4506)
+++ core/trunk/src/main/java/org/jboss/cache/CacheImpl.java	2007-09-26 10:15:08 UTC (rev 4507)
@@ -281,7 +281,7 @@
     */
    public NodeSPI<K, V> getRoot()
    {
-      return root;
+      return MetadataNodeWrapper.wrapNodeData(root);
    }
 
    /**
@@ -4438,12 +4438,17 @@
 
    public void evict(Fqn<?> fqn, boolean recursive)
    {
+      //this method should be called by eviction thread only, so no transaction - expected (sec param is false)
+      Node<K, V> node = peek(fqn, false);
+      if (node != null && node.isResident())
+      {
+         return;
+      }
       if (recursive)
       {
-         Node<K, V> n = get(fqn);
-         if (n != null)
+         if (node != null)
          {
-            evictChildren((NodeSPI<K, V>) n);
+            evictChildren((NodeSPI<K, V>) node);
          }
       }
       else

Added: core/trunk/src/main/java/org/jboss/cache/MetadataNodeWrapper.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/MetadataNodeWrapper.java	                        (rev 0)
+++ core/trunk/src/main/java/org/jboss/cache/MetadataNodeWrapper.java	2007-09-26 10:15:08 UTC (rev 4507)
@@ -0,0 +1,420 @@
+package org.jboss.cache;
+
+import org.jboss.cache.Node;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.optimistic.DataVersion;
+import org.jboss.cache.lock.NodeLock;
+import org.jboss.cache.transaction.GlobalTransaction;
+
+import java.util.*;
+
+/**
+ * Metadata information from class <tt>Node</tt> (metadata = all configuration information except attribute map,
+ * e.g. Node.isResident) is internally also held in the internal attribute map. The role of this class is not the
+ * expose those internal map information to the end user - so it wraps such a node and expose it to the the user
+ * removing wrapped info.
+ *
+ * @author <a href="mailto:mircea.markus at jboss.com">Mircea Markus</a>
+ * @since 2.1.0
+ */
+public class MetadataNodeWrapper implements NodeSPI
+{
+
+   public static final String JBOSSCACHE_INTERNAL_RESIDENT = "_jbosscache.internal.resident";
+
+   private Node wrappedNode;
+
+   public MetadataNodeWrapper(Node wrappedNode)
+   {
+      this.wrappedNode = wrappedNode;
+   }
+
+   public NodeSPI getParent()
+   {
+      return wrapNodeData(wrappedNode);
+   }
+
+   public Set getChildren()
+   {
+      Iterator it = wrappedNode.getChildren().iterator();
+      Set result = new HashSet();
+      while (it.hasNext())
+      {
+         result.add(wrapNodeData((Node) it.next()));
+      }
+      return Collections.unmodifiableSet(result);
+   }
+
+   public Set getChildrenNames()
+   {
+      return wrappedNode.getChildrenNames();
+   }
+
+   public Map getData()
+   {
+      Map realData = wrappedNode.getData();
+      if (!realData.containsKey(JBOSSCACHE_INTERNAL_RESIDENT))
+      {
+         return realData;
+      } else
+      {
+         Map clearedData = new HashMap(realData);
+         clearedData.remove(JBOSSCACHE_INTERNAL_RESIDENT);
+         return Collections.unmodifiableMap(clearedData);
+      }
+   }
+
+   public Set getKeys()
+   {
+      Set realSet = wrappedNode.getKeys();
+      if (!realSet.contains(JBOSSCACHE_INTERNAL_RESIDENT))
+      {
+         return realSet;
+      } else
+      {
+         Set clearedData = new HashSet(realSet);
+         clearedData.remove(JBOSSCACHE_INTERNAL_RESIDENT);
+         return Collections.unmodifiableSet(clearedData);
+      }
+   }
+
+   public Fqn getFqn()
+   {
+      return wrappedNode.getFqn();
+   }
+
+   public Node addChild(Fqn f)
+   {
+      return wrapNodeData(wrappedNode.addChild(f));
+   }
+
+   public boolean removeChild(Fqn f)
+   {
+      return wrappedNode.removeChild(f);
+   }
+
+   public boolean removeChild(Object childName)
+   {
+      return wrappedNode.removeChild(childName);
+   }
+
+   public Node getChild(Fqn f)
+   {
+      return wrapNodeData(wrappedNode.getChild(f));
+   }
+
+   public Node getChild(Object name)
+   {
+      return wrapNodeData(wrappedNode.getChild(name));
+   }
+
+   public Object put(Object key, Object value)
+   {
+      return wrappedNode.put(key, value);
+   }
+
+   public Object putIfAbsent(Object key, Object value)
+   {
+      return wrappedNode.putIfAbsent(key, value);
+   }
+
+   public Object replace(Object key, Object value)
+   {
+      return wrappedNode.replace(key, value);
+   }
+
+   public boolean replace(Object key, Object oldValue, Object newValue)
+   {
+      return wrappedNode.replace(key, oldValue, newValue);
+   }
+
+   public void putAll(Map map)
+   {
+      wrappedNode.putAll(map);
+   }
+
+   public void replaceAll(Map map)
+   {
+      wrappedNode.replaceAll(map);
+   }
+
+   public Object get(Object key)
+   {
+      return wrappedNode.get(key);
+   }
+
+   public Object remove(Object key)
+   {
+      if (JBOSSCACHE_INTERNAL_RESIDENT.equals(key))
+      {
+         throw new IllegalArgumentException("The '" + JBOSSCACHE_INTERNAL_RESIDENT + "' is an reserved key, please " +
+            "refrain using it!");
+      }
+      return wrappedNode.remove(key);
+   }
+
+   public void clearData()
+   {
+      if (wrappedNode.get(JBOSSCACHE_INTERNAL_RESIDENT) == null)
+      {
+         wrappedNode.clearData();
+      } else
+      {
+         Iterator it = wrappedNode.getKeys().iterator();
+         while (it.hasNext())
+         {
+            Object key = it.next();
+            if (!JBOSSCACHE_INTERNAL_RESIDENT.equals(key))
+            {
+               wrappedNode.remove(key);
+            }
+         }
+      }
+   }
+
+   public int dataSize()
+   {
+      if (wrappedNode.getKeys().contains(JBOSSCACHE_INTERNAL_RESIDENT))
+      {
+         return wrappedNode.dataSize() - 1;
+      }
+      return wrappedNode.dataSize();
+   }
+
+   public boolean hasChild(Fqn f)
+   {
+      return wrappedNode.hasChild(f);
+   }
+
+   public boolean hasChild(Object o)
+   {
+      return wrappedNode.hasChild(o);
+   }
+
+   public boolean isValid()
+   {
+      return wrappedNode.isValid();
+   }
+
+   public boolean isResident()
+   {
+      return wrappedNode.isResident();
+   }
+
+   public void setResident(boolean resident)
+   {
+      wrappedNode.setResident(resident);
+   }
+
+   public static MetadataNodeWrapper wrapNodeData(Node node)
+   {
+      if (node == null)
+      {
+         return null;
+      }
+      if (node instanceof MetadataNodeWrapper)
+      {
+         return (MetadataNodeWrapper) node;
+      } else
+      {
+         return new MetadataNodeWrapper(node);
+      }
+   }
+
+
+   public boolean isChildrenLoaded()
+   {
+      return ((NodeSPI) wrappedNode).isChildrenLoaded();
+   }
+
+   public void setChildrenLoaded(boolean loaded)
+   {
+      ((NodeSPI) wrappedNode).setChildrenLoaded(loaded);
+   }
+
+   public boolean isDataLoaded()
+   {
+      return ((NodeSPI) wrappedNode).isDataLoaded();
+   }
+
+   public void setDataLoaded(boolean dataLoaded)
+   {
+      ((NodeSPI) wrappedNode).setDataLoaded(dataLoaded);
+   }
+
+   public Map getChildrenMapDirect()
+   {
+      return ((NodeSPI) wrappedNode).getChildrenMapDirect();
+   }
+
+   public void setChildrenMapDirect(Map children)
+   {
+      ((NodeSPI) wrappedNode).setChildrenMapDirect(children);
+   }
+
+   public NodeSPI getOrCreateChild(Object name, GlobalTransaction tx)
+   {
+      return ((NodeSPI) wrappedNode).getOrCreateChild(name, tx);
+   }
+
+   public NodeLock getLock()
+   {
+      return ((NodeSPI) wrappedNode).getLock();
+   }
+
+   public void setFqn(Fqn f)
+   {
+      ((NodeSPI) wrappedNode).setFqn(f);
+   }
+
+   public boolean isDeleted()
+   {
+      return ((NodeSPI) wrappedNode).isDeleted();
+   }
+
+   public void markAsDeleted(boolean marker)
+   {
+      ((NodeSPI) wrappedNode).markAsDeleted(marker);
+   }
+
+   public void markAsDeleted(boolean marker, boolean recursive)
+   {
+      ((NodeSPI) wrappedNode).markAsDeleted(marker, recursive);
+   }
+
+   public void addChild(Object nodeName, Node nodeToAdd)
+   {
+      ((NodeSPI) wrappedNode).addChild(nodeName, nodeToAdd);
+   }
+
+   public void printDetails(StringBuffer sb, int indent)
+   {
+      ((NodeSPI) wrappedNode).printDetails(sb, indent);
+   }
+
+   public void print(StringBuffer sb, int indent)
+   {
+      ((NodeSPI) wrappedNode).print(sb, indent);
+   }
+
+   public void setVersion(DataVersion version)
+   {
+      ((NodeSPI) wrappedNode).setVersion(version);
+   }
+
+   public DataVersion getVersion()
+   {
+      return ((NodeSPI) wrappedNode).getVersion();
+   }
+
+   public Set getChildrenDirect()
+   {
+      return ((NodeSPI) wrappedNode).getChildrenDirect();
+   }
+
+   public void removeChildrenDirect()
+   {
+      ((NodeSPI) wrappedNode).removeChildrenDirect();
+   }
+
+   public Set getChildrenDirect(boolean includeMarkedAsDeleted)
+   {
+      return ((NodeSPI) wrappedNode).getChildrenDirect(includeMarkedAsDeleted);
+   }
+
+   public NodeSPI getChildDirect(Object childName)
+   {
+      return ((NodeSPI) wrappedNode).getChildDirect(childName);
+   }
+
+   public NodeSPI addChildDirect(Fqn childName)
+   {
+      return ((NodeSPI) wrappedNode).addChildDirect(childName);
+   }
+
+   public void addChildDirect(NodeSPI child)
+   {
+      ((NodeSPI) wrappedNode).addChildDirect(child);
+   }
+
+   public NodeSPI getChildDirect(Fqn childName)
+   {
+      return ((NodeSPI) wrappedNode).getChildDirect(childName);
+   }
+
+   public boolean removeChildDirect(Fqn fqn)
+   {
+      return ((NodeSPI) wrappedNode).removeChildDirect(fqn);
+   }
+
+   public boolean removeChildDirect(Object childName)
+   {
+      return ((NodeSPI) wrappedNode).removeChildDirect(childName);
+   }
+
+   public Object removeDirect(Object key)
+   {
+      return ((NodeSPI) wrappedNode).removeDirect(key);
+   }
+
+   public Object putDirect(Object key, Object value)
+   {
+      return ((NodeSPI) wrappedNode).putDirect(key, value);
+   }
+
+   public void putAllDirect(Map data)
+   {
+      ((NodeSPI) wrappedNode).putAllDirect(data);
+   }
+
+   public Map getDataDirect()
+   {
+      return ((NodeSPI) wrappedNode).getDataDirect();
+   }
+
+   public Object getDirect(Object key)
+   {
+      return ((NodeSPI) wrappedNode).getDirect(key);
+   }
+
+   public void clearDataDirect()
+   {
+      ((NodeSPI) wrappedNode).clearDataDirect();
+   }
+
+   public Set getKeysDirect()
+   {
+      return ((NodeSPI) wrappedNode).getKeysDirect();
+   }
+
+   public Set getChildrenNamesDirect()
+   {
+      return ((NodeSPI) wrappedNode).getChildrenNamesDirect();
+   }
+
+   public CacheSPI getCache()
+   {
+      return ((NodeSPI) wrappedNode).getCache();
+   }
+
+   public boolean hasChildrenDirect()
+   {
+      return ((NodeSPI) wrappedNode).hasChildrenDirect();
+   }
+
+   public boolean isResidentDirect()
+   {
+      return ((NodeSPI) wrappedNode).isResidentDirect();
+   }
+
+
+   public boolean isLockForChildInsertRemove()
+   {
+      return wrappedNode.isLockForChildInsertRemove();
+   }
+
+   public void setLockForChildInsertRemove(boolean lockForChildInsertRemove)
+   {
+      wrappedNode.setLockForChildInsertRemove(lockForChildInsertRemove);
+   }
+}

Modified: core/trunk/src/main/java/org/jboss/cache/Node.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/Node.java	2007-09-25 20:55:20 UTC (rev 4506)
+++ core/trunk/src/main/java/org/jboss/cache/Node.java	2007-09-26 10:15:08 UTC (rev 4507)
@@ -277,7 +277,21 @@
     */
    boolean isValid();
 
+     /**
+    * Nodes marked resident would be ignored by the eviction algorithms. E.g. if the algorithm is
+    * "keep LRU 10 nodes" - the resident nodes won't be counted within those 10 nodes,
+    * and also won't be evicted when the threshold is reached.
+    * N.B. calling this method won't have any effect on node's eviction, e.g. we won't consider this node as being
+    * 'used' in a LRU scenario
+    */
+   boolean isResident();
+
    /**
+    * @see #isResident()
+    */
+   void setResident(boolean resident);
+
+   /**
     * Tests whether this node is configured to be exclusively locked when inserting or removing children.
     * <p />
     * The default
@@ -297,5 +311,4 @@
     * @since 2.1.0
     */
    void setLockForChildInsertRemove(boolean lockForChildInsertRemove);
-
 }

Modified: core/trunk/src/main/java/org/jboss/cache/NodeSPI.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/NodeSPI.java	2007-09-25 20:55:20 UTC (rev 4506)
+++ core/trunk/src/main/java/org/jboss/cache/NodeSPI.java	2007-09-26 10:15:08 UTC (rev 4507)
@@ -437,4 +437,10 @@
     * @return true if the node has one or more child nodes; false otherwise.
     */
    boolean hasChildrenDirect();
+
+
+   /**
+    * Same as {@link #isResident()} but it bypasses the interceptors chain.
+    */
+   boolean isResidentDirect();
 }

Modified: core/trunk/src/main/java/org/jboss/cache/Region.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/Region.java	2007-09-25 20:55:20 UTC (rev 4506)
+++ core/trunk/src/main/java/org/jboss/cache/Region.java	2007-09-26 10:15:08 UTC (rev 4507)
@@ -168,6 +168,8 @@
     *
     * @param fqn Fqn of the node.
     * @see #unmarkNodeCurrentlyInUse(Fqn)
+    * @deprecated you are now able to specify Node.setResident which has (almost -> i.e. you cannot specify a timeout)
+    * the same behavior. That metod also replicates the change clusterwise
     */
    void markNodeCurrentlyInUse(Fqn fqn, long timeout);
 
@@ -176,6 +178,8 @@
     *
     * @param fqn Fqn of the node.
     * @see #markNodeCurrentlyInUse(Fqn,long)
+    * @deprecated you are now able to specify Node.isResident which has (almost) the same behavior. That metod also 
+    * replicates the change clusterwise
     */
    void unmarkNodeCurrentlyInUse(Fqn fqn);
 

Modified: core/trunk/src/main/java/org/jboss/cache/RegionImpl.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/RegionImpl.java	2007-09-25 20:55:20 UTC (rev 4506)
+++ core/trunk/src/main/java/org/jboss/cache/RegionImpl.java	2007-09-26 10:15:08 UTC (rev 4507)
@@ -149,7 +149,10 @@
                     " You will need to reduce the wakeUpIntervalSeconds parameter.");
          }
 
-         nodeEventQueue.put(event);
+         if (!regionManager.isNodeResident(event.getFqn()))
+         {
+            nodeEventQueue.put(event);
+         }
       }
       catch (InterruptedException e)
       {

Modified: core/trunk/src/main/java/org/jboss/cache/RegionManager.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/RegionManager.java	2007-09-25 20:55:20 UTC (rev 4506)
+++ core/trunk/src/main/java/org/jboss/cache/RegionManager.java	2007-09-26 10:15:08 UTC (rev 4507)
@@ -813,4 +813,19 @@
       return this.cache;
    }
 
+   /**
+    * Will check to see if the given node is resident or not.
+    * N.B. This method MUST not raise any eviction events (i.e. do not call any intercepted methods on the cache, but
+    * rather use direct calls). Otherwise an endless loop of events being generated/consumed will take place, as this
+    * is called in the context of consuming events.
+    */
+   public boolean isNodeResident(Fqn fqn)
+   {
+      if (cache == null)
+      {
+        return false;
+      }
+      NodeSPI theNode = cache.getRoot().getChildDirect(fqn);
+      return theNode == null ? false : theNode.isResidentDirect();
+   }
 }

Modified: core/trunk/src/main/java/org/jboss/cache/UnversionedNode.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/UnversionedNode.java	2007-09-25 20:55:20 UTC (rev 4506)
+++ core/trunk/src/main/java/org/jboss/cache/UnversionedNode.java	2007-09-26 10:15:08 UTC (rev 4507)
@@ -681,6 +681,11 @@
       return children != null && children.size() != 0;
    }
 
+   public boolean isResidentDirect()
+   {
+      return getDataDirect().containsKey(AbstractNode.INTERNAL_IS_RESIDENT);
+   }
+
    public Set<NodeSPI<K, V>> getChildrenDirect(boolean includeMarkedForRemoval)
    {
       if (includeMarkedForRemoval)

Modified: core/trunk/src/main/java/org/jboss/cache/interceptors/EvictionInterceptor.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/interceptors/EvictionInterceptor.java	2007-09-25 20:55:20 UTC (rev 4506)
+++ core/trunk/src/main/java/org/jboss/cache/interceptors/EvictionInterceptor.java	2007-09-26 10:15:08 UTC (rev 4507)
@@ -9,11 +9,7 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.jboss.cache.CacheSPI;
-import org.jboss.cache.Fqn;
-import org.jboss.cache.InvocationContext;
-import org.jboss.cache.Region;
-import org.jboss.cache.RegionManager;
+import org.jboss.cache.*;
 import org.jboss.cache.eviction.EvictedEventNode;
 import org.jboss.cache.eviction.NodeEventType;
 import org.jboss.cache.marshall.MethodCall;
@@ -98,6 +94,8 @@
       // is thrown, this interceptor is terminated. there is no need for explicit rollback logic.
       this.updateNode(m, ret);
 
+
+
       if (log.isTraceEnabled())
       {
          log.trace("Finished invoking EvictionInterceptor");
@@ -200,6 +198,10 @@
          Object args[] = mc.getArgs();
          Fqn fqn = (Fqn) args[0];
          Object key = args[1];
+         /*see hack comment inside PutKeyEvictionMethodHandler.extractEvictedEventNode */
+         if (MetadataNodeWrapper.JBOSSCACHE_INTERNAL_RESIDENT.equals(key)) {
+            return null;
+         }
          if (fqn != null && key != null
                  && !EvictionInterceptor.this.canIgnoreEvent(fqn, NodeEventType.VISIT_NODE_EVENT))
          {
@@ -328,11 +330,21 @@
 
    protected class PutKeyEvictionMethodHandler implements EvictionMethodHandler
    {
+
       public EvictedEventNode extractEvictedEventNode(MethodCall mc, Object retVal)
       {
          Object[] args = mc.getArgs();
          Fqn fqn = (Fqn) args[1];
          Object key = args[2];
+         //hack - for seting an node as resident we won't create a an eviction event.
+         //this is an implementation restriction, as this method(i.e. isEvited) is intensively called internally
+         //by the eviction code, which would result in events being produced -> that when consumed would produce
+         // other events (due to call to Node.isResident). The isResidentDirect call cannot be used here
+         //the resident information is stored in the node attributes map directly
+         if (MetadataNodeWrapper.JBOSSCACHE_INTERNAL_RESIDENT.equals(key))
+         {
+            return null;
+         }
          if (fqn != null && key != null
                  && !EvictionInterceptor.this.canIgnoreEvent(fqn, NodeEventType.ADD_ELEMENT_EVENT))
          {

Modified: core/trunk/src/main/java/org/jboss/cache/marshall/MethodCall.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/marshall/MethodCall.java	2007-09-25 20:55:20 UTC (rev 4506)
+++ core/trunk/src/main/java/org/jboss/cache/marshall/MethodCall.java	2007-09-26 10:15:08 UTC (rev 4507)
@@ -92,4 +92,16 @@
       return ret.toString();
 
    }
+
+
+   public Object invoke(Object object) throws Throwable
+   {
+      return super.invoke(object);    //To change body of overridden methods use File | Settings | File Templates.
+   }
+
+
+   public Object invoke(Object object, Object[] objects) throws Throwable
+   {
+      return super.invoke(object, objects);    //To change body of overridden methods use File | Settings | File Templates.
+   }
 }

Modified: core/trunk/src/main/resources/META-INF/local-lru-eviction-service.xml
===================================================================
--- core/trunk/src/main/resources/META-INF/local-lru-eviction-service.xml	2007-09-25 20:55:20 UTC (rev 4506)
+++ core/trunk/src/main/resources/META-INF/local-lru-eviction-service.xml	2007-09-26 10:15:08 UTC (rev 4507)
@@ -106,6 +106,11 @@
                <attribute name="timeToLiveSeconds">8</attribute>
                <attribute name="maxAgeSeconds">10</attribute>
             </region>
+            <region name="/residentNodesTest">
+               <attribute name="maxNodes">3</attribute>
+               <attribute name="timeToLiveSeconds">800000</attribute>
+               <attribute name="maxAgeSeconds">100000</attribute>
+            </region>
          </config>
       </attribute>
    </mbean>

Added: core/trunk/src/test/java/org/jboss/cache/api/ResidentNodesTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/api/ResidentNodesTest.java	                        (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/api/ResidentNodesTest.java	2007-09-26 10:15:08 UTC (rev 4507)
@@ -0,0 +1,219 @@
+package org.jboss.cache.api;
+
+import org.testng.annotations.Test;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.AfterMethod;
+import static org.testng.Assert.*;
+import static org.testng.Assert.assertNotNull;
+import org.jboss.cache.*;
+import org.jboss.cache.misc.TestingUtil;
+import org.jboss.cache.factories.UnitTestCacheConfigurationFactory;
+import org.jboss.cache.config.CacheLoaderConfig;
+import org.jboss.cache.config.Configuration;
+import org.jboss.cache.loader.DummyInMemoryCacheLoader;
+import org.jboss.cache.loader.DummyCacheLoader;
+import org.jboss.cache.lock.IsolationLevel;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Tester class for Node.isResident functionality.
+ *
+ * @author <a href="mailto:mircea.markus at jboss.com">Mircea Markus</a>
+ * @since 2.1.0
+ */
+ at Test(groups = {"functional"})
+public class ResidentNodesTest
+{
+
+   private CacheImpl<Object, Object> cache;
+
+   private final String TEST_NODES_ROOT = "residentNodesTest";
+
+   @BeforeMethod(alwaysRun = true)
+   public void setUp()
+   {
+      cache = (CacheImpl<Object, Object>) DefaultCacheFactory.getInstance().createCache("META-INF/local-lru-eviction-service.xml", false);
+      cache.getConfiguration().getEvictionConfig().setWakeupIntervalSeconds(1);
+      cache.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup");
+      CacheLoaderConfig.IndividualCacheLoaderConfig indCLConfig = new CacheLoaderConfig.IndividualCacheLoaderConfig();
+      indCLConfig.setClassName(DummyInMemoryCacheLoader.class.getName());
+      List<CacheLoaderConfig.IndividualCacheLoaderConfig> indCLConfigs = new ArrayList<CacheLoaderConfig.IndividualCacheLoaderConfig>();
+      indCLConfigs.add(indCLConfig);
+      CacheLoaderConfig cacheLoaderConfig = new CacheLoaderConfig();
+      cacheLoaderConfig.setIndividualCacheLoaderConfigs(indCLConfigs);
+      cache.getConfiguration().setCacheLoaderConfig(cacheLoaderConfig);
+      cache.start();
+   }
+
+   @AfterMethod(alwaysRun = true)
+   public void tearDown() throws Exception
+   {
+      cache.stop();
+   }
+
+   /**
+    * Mark some nodes as resident and show that they won't get evicted,
+    * even if normally scenario they would
+    */
+   public void testHappyFlow() throws InterruptedException
+   {
+      cache.put(getSubFqn("/a"), "k_a", "v_a");
+      cache.get(getSubFqn("/a")).setResident(true);
+      cache.put(getSubFqn("/b"), "k_b", "v_b");
+      cache.get(getSubFqn("/b")).setResident(true);
+      cache.put(getSubFqn("/c"), "k_c", "v_c");
+      cache.put(getSubFqn("/d"), "k_d", "v_d");
+      cache.put(getSubFqn("/e"), "k_e", "v_e");
+      cache.put(getSubFqn("/f"), "k_f", "v_f");
+      cache.put(getSubFqn("/g"), "k_g", "v_g");
+      cache.put(getSubFqn("/h"), "k_h", "v_h");
+      cache.put(getSubFqn("/i"), "k_i", "v_i");
+
+      Thread.sleep(3000);//so that eviction is activated
+
+      assertTrue(cache.exists(getSubFqn("/a")));
+      assertTrue(cache.exists(getSubFqn("/b")));
+      assertFalse(cache.exists(getSubFqn("/c")));
+      assertFalse(cache.exists(getSubFqn("/d")));
+      assertFalse(cache.exists(getSubFqn("/e")));
+      assertFalse(cache.exists(getSubFqn("/f")));
+
+      //only last three used are not evicted
+      assertTrue(cache.exists(getSubFqn("/g")));
+      assertTrue(cache.exists(getSubFqn("/h")));
+      assertTrue(cache.exists(getSubFqn("/i")));
+
+   }
+
+   /**
+    * The 'resident' attribute of the node is kept within node's attribute map.
+    * This information won't be visible to the ouside clients if they are not using the
+    * resident attribute. This would ensure a 100% backward comatibility. New clients would need to be aware of this
+    * extra attribute, though.
+    */
+   public void testInternalStateNotVisibleOutside()
+   {
+      cache.put(getSubFqn("/a"), "k_a", "v_a");
+      cache.get(getSubFqn("/a")).setResident(true);
+      assertEquals(cache.getRoot().getChild(getSubFqn("/a")).getData().keySet().size(), 1);
+      assertEquals(cache.getRoot().getChild(getSubFqn("/a")).getKeys().size(), 1);
+      assertTrue(cache.getRoot().getChild(getSubFqn("/a")).isResident());
+
+      cache.get(getSubFqn("/a")).setResident(false);
+      assertFalse(cache.getRoot().getChild(getSubFqn("/a")).isResident());
+      assertEquals(cache.getRoot().getChild(TEST_NODES_ROOT).getChild("a").getData().keySet().size(), 1);
+      assertEquals(cache.getRoot().getChild(TEST_NODES_ROOT).getChild("a").getKeys().size(), 1);
+
+      cache.remove(getSubFqn("/a"), "k_a");
+      assertEquals(cache.get(getSubFqn("/a")).getData().keySet().size(), 0);
+      assertFalse(cache.getRoot().getChild(TEST_NODES_ROOT).getChild("a").isResident());
+
+      cache.get(getSubFqn("/a")).setResident(true);
+      assertEquals(cache.getRoot().getChild(TEST_NODES_ROOT).getChild("a").getData().keySet().size(), 0);
+      assertEquals(cache.getRoot().getChild(TEST_NODES_ROOT).getChild("a").getKeys().size(), 0);
+      assertTrue(cache.getRoot().getChild(TEST_NODES_ROOT).getChild("a").isResident());
+   }
+
+   /**
+    * When replication is on, we want to make sure that the operation will subscribe to the global
+    * replication strategy.
+    */
+   public void testNodeResidencyInformationIsReplicated()
+   {
+      Cache first = DefaultCacheFactory.getInstance().createCache(UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.REPL_SYNC));
+      Cache second = DefaultCacheFactory.getInstance().createCache(UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.REPL_SYNC));
+      Cache[] caches = {first, second};
+      TestingUtil.blockUntilViewsReceived(caches, 5000);
+
+      System.out.println("Caches started!");
+
+      Fqn fqn = Fqn.fromString("/a/b");
+      first.put(fqn, "key", "value");
+      assertNotNull(second.get(fqn, "key"));
+
+      first.getRoot().getChild(fqn).setResident(true);
+      assertTrue(second.getRoot().getChild(fqn).isResident());
+
+      second.getRoot().getChild(fqn).setResident(false);
+      assertFalse(first.getRoot().getChild(fqn).isResident());
+
+      for (Cache c : caches)
+      {
+         c.stop();
+      }
+   }
+
+   /**
+    * If a node is marked as resident, and a get is made on that given node then an VISITED event would normally be
+    * added to the eviction queue. In a LRU scenario, this will cause another node to be evicted given that the size of
+    * the eviction queue is bounded. This test makes sure that this scenario will not hapen.
+    */
+   public void testNoEvictionEventsForResidentNodes() throws InterruptedException
+   {
+      cache.put(getSubFqn("/a"), "k_a", "v_a");
+      cache.put(getSubFqn("/b"), "k_b", "v_b");
+
+      //cache node reference here as getting a node will trigger an read event
+      cache.get(getSubFqn("/a")).setResident(true);
+      cache.get(getSubFqn("/b")).setResident(true);
+
+      cache.put(getSubFqn("/c"), "k_c", "v_c");
+      cache.put(getSubFqn("/d"), "k_d", "v_d");
+      cache.put(getSubFqn("/e"), "k_e", "v_e");
+      cache.put(getSubFqn("/f"), "k_f", "v_f");
+      cache.put(getSubFqn("/g"), "k_g", "v_g");
+      cache.put(getSubFqn("/h"), "k_h", "v_h");
+
+      //at this point the oldest nodes are /a and /b so. There are eviction events in the queue corresponding
+      // to those nodes
+      cache.get(getSubFqn("/a"));
+      cache.get(getSubFqn("/b"));
+
+      Thread.sleep(3000);//so that eviction is activated
+
+      //a and b should exist as those were marked resident. Also they shouldn't be caunted as nodes in the eviction
+      // queue
+      assertTrue(cache.exists(getSubFqn("/a")));
+      assertTrue(cache.exists(getSubFqn("/b")));
+
+      // c, d and e were the first accessed, they should be evicted
+      assertFalse(cache.exists(getSubFqn("/c")));
+      assertFalse(cache.exists(getSubFqn("/d")));
+      assertFalse(cache.exists(getSubFqn("/e")));
+
+      //all of them should be there - even if we re-retrieved a and b at a prev step (cache.get(getSubFqn("/a"))) this
+      //call shouldn't create an eviction event.
+      assertTrue(cache.exists(getSubFqn("/f")));
+      assertTrue(cache.exists(getSubFqn("/g")));
+      assertTrue(cache.exists(getSubFqn("/h")));
+   }
+
+   /**
+    * Underlying metadata information is held as Strings. As Node is parametrized class with keys and values, and there
+    * is an explicit downcast to String in code, this is just a check to make sure no cast exceptions are being thrown.
+    * Note: this is rather a paranoia check as type info is lost during type erasure and underlying map supports
+    * objects - superclass of Strings
+    */
+   public void testNoStringCache()
+   {
+      CacheFactory<Integer, Float> cacheFactory = DefaultCacheFactory.getInstance();
+      Cache<Integer, Float> cache = cacheFactory.createCache("META-INF/local-lru-eviction-service.xml", true);
+      cache.put(getSubFqn("/a"), 1, 5.3f);
+      cache.put(getSubFqn("/b"), 1, 2.3f);
+      cache.put(getSubFqn("/c"), 1, 7.3f);
+      cache.getRoot().getChild(getSubFqn("/a")).setResident(true);
+      cache.getRoot().getChild(getSubFqn("/b")).setResident(false);
+      cache.getRoot().getChild(getSubFqn("/c")).setResident(true);
+      assertTrue(cache.getRoot().getChild(getSubFqn("/a")).isResident());
+      assertFalse(cache.getRoot().getChild(getSubFqn("/b")).isResident());
+      assertTrue(cache.getRoot().getChild(getSubFqn("/c")).isResident());
+      cache.stop();
+   }
+
+   private Fqn getSubFqn(String str)
+   {
+      return Fqn.fromString("/" + TEST_NODES_ROOT + str);
+   }
+}




More information about the jbosscache-commits mailing list