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

jbosscache-commits at lists.jboss.org jbosscache-commits at lists.jboss.org
Fri Sep 21 09:53:59 EDT 2007


Author: manik.surtani at jboss.com
Date: 2007-09-21 09:53:59 -0400 (Fri, 21 Sep 2007)
New Revision: 4497

Added:
   core/trunk/src/main/java/org/jboss/cache/loader/ReadOnlyDelegatingCacheLoader.java
Modified:
   core/trunk/src/main/docbook/userguide/en/modules/transactions.xml
   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/UnversionedNode.java
   core/trunk/src/main/java/org/jboss/cache/interceptors/OptimisticValidatorInterceptor.java
   core/trunk/src/main/java/org/jboss/cache/interceptors/PessimisticLockInterceptor.java
   core/trunk/src/main/java/org/jboss/cache/loader/CacheLoaderManager.java
   core/trunk/src/main/java/org/jboss/cache/optimistic/WorkspaceNodeImpl.java
   core/trunk/src/test/java/org/jboss/cache/loader/CacheLoaderTestsBase.java
   core/trunk/src/test/java/org/jboss/cache/loader/FileCacheLoaderTest.java
   core/trunk/src/test/java/org/jboss/cache/lock/WriteLockOnParentTest.java
Log:
JBCACHE-957 - lockParentForChildInsertRemove now configurable on a per-node basis
JBCACHE-751 - IgnoreModifications to be made available to all cache loaders, not just chained ones.

Modified: core/trunk/src/main/docbook/userguide/en/modules/transactions.xml
===================================================================
--- core/trunk/src/main/docbook/userguide/en/modules/transactions.xml	2007-09-21 03:15:01 UTC (rev 4496)
+++ core/trunk/src/main/docbook/userguide/en/modules/transactions.xml	2007-09-21 13:53:59 UTC (rev 4497)
@@ -160,6 +160,11 @@
                <emphasis>write lock</emphasis>
                on the parent node.
             </para>
+            <para>
+               In addition to the above, in version 2.1.0 and above, JBoss Cache offers the ability to override this
+               configuration on a per-node basis.  See <literal>Node.setLockForChildInsertRemove()</literal> and it's
+               corresponding javadocs for details.
+            </para>
          </section>
       </section>
 

Modified: core/trunk/src/main/java/org/jboss/cache/CacheImpl.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/CacheImpl.java	2007-09-21 03:15:01 UTC (rev 4496)
+++ core/trunk/src/main/java/org/jboss/cache/CacheImpl.java	2007-09-21 13:53:59 UTC (rev 4497)
@@ -864,6 +864,7 @@
    {
       cacheStatus = CacheStatus.DESTROYING;
       regionManager = null;
+      cacheLoaderManager = null;
       notifier = null;
 
       // The rest of these should have already been taken care of in stop,

Modified: core/trunk/src/main/java/org/jboss/cache/Node.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/Node.java	2007-09-21 03:15:01 UTC (rev 4496)
+++ core/trunk/src/main/java/org/jboss/cache/Node.java	2007-09-21 13:53:59 UTC (rev 4497)
@@ -277,4 +277,25 @@
     */
    boolean isValid();
 
+   /**
+    * Tests whether this node is configured to be exclusively locked when inserting or removing children.
+    * <p />
+    * The default
+    * value for this is what is configured in the <tt>LockParentForChildInsertRemove</tt> configuration property,
+    * programatically reachable by querying {@link org.jboss.cache.config.Configuration#isLockParentForChildInsertRemove()}
+    * <p />
+    * This can also be configured on a per-node basis using {@link #setLockForChildInsertRemove(boolean)}
+    * @return true if the node is configured to be exclusively locked for child insertions and removal, false otherwise.
+    * @since 2.1.0
+    */
+   boolean isLockForChildInsertRemove();
+
+   /**
+    * Configures the behaviour of how this node is locked when adding/removing children.
+    * @param lockForChildInsertRemove if true, exclusive locks will be obtained when children are added/removed.  If
+    * false, a shared "read lock" will be obtained instead.
+    * @since 2.1.0
+    */
+   void setLockForChildInsertRemove(boolean lockForChildInsertRemove);
+
 }

Modified: core/trunk/src/main/java/org/jboss/cache/UnversionedNode.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/UnversionedNode.java	2007-09-21 03:15:01 UTC (rev 4496)
+++ core/trunk/src/main/java/org/jboss/cache/UnversionedNode.java	2007-09-21 13:53:59 UTC (rev 4497)
@@ -68,6 +68,8 @@
     */
    private final Map<K, V> data = new HashMap<K, V>();
 
+   private boolean lockForChildInsertRemove;
+
    /**
     * Constructs a new node with an FQN of Root.
     */
@@ -107,6 +109,7 @@
       {
          throw new IllegalArgumentException("Child " + child_name + " must be last part of " + fqn);
       }
+      lockForChildInsertRemove = cache.getConfiguration().isLockParentForChildInsertRemove();
    }
 
    /**
@@ -742,4 +745,14 @@
       // TODO; implement this property, to detect if it has been evicted, removed by another thread, etc.  Method added for now as a dummy so it exists in the API
       return true;
    }
+
+   public boolean isLockForChildInsertRemove()
+   {
+      return lockForChildInsertRemove;
+   }
+
+   public void setLockForChildInsertRemove(boolean lockForChildInsertRemove)
+   {
+      this.lockForChildInsertRemove = lockForChildInsertRemove;
+   }
 }

Modified: core/trunk/src/main/java/org/jboss/cache/interceptors/OptimisticValidatorInterceptor.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/interceptors/OptimisticValidatorInterceptor.java	2007-09-21 03:15:01 UTC (rev 4496)
+++ core/trunk/src/main/java/org/jboss/cache/interceptors/OptimisticValidatorInterceptor.java	2007-09-21 13:53:59 UTC (rev 4497)
@@ -199,7 +199,7 @@
                   underlyingNode.removeChildDirect(child.getLastElement());
                }
 
-               updateVersion = cache.getConfiguration().isLockParentForChildInsertRemove();
+               updateVersion = underlyingNode.isLockForChildInsertRemove();
                // do we need to notify listeners of a modification??  If all we've done is added children then don't
                // notify.
             }

Modified: core/trunk/src/main/java/org/jboss/cache/interceptors/PessimisticLockInterceptor.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/interceptors/PessimisticLockInterceptor.java	2007-09-21 03:15:01 UTC (rev 4496)
+++ core/trunk/src/main/java/org/jboss/cache/interceptors/PessimisticLockInterceptor.java	2007-09-21 13:53:59 UTC (rev 4497)
@@ -320,7 +320,7 @@
          }
          else
          {
-            if (writeLockNeeded(ctx, lock_type, i, treeNodeSize, isEvictionOperation, isDeleteOperation, createIfNotExists, isRemoveDataOperation, fqn, child_node.getFqn()))
+            if (writeLockNeeded(ctx, lock_type, i, treeNodeSize, isEvictionOperation, isDeleteOperation, createIfNotExists, isRemoveDataOperation, fqn, child_node))
             {
                lockTypeRequired = NodeLock.LockType.WRITE;
 
@@ -373,21 +373,21 @@
       n.markAsDeleted(false);
    }
 
-   private boolean writeLockNeeded(InvocationContext ctx, NodeLock.LockType lock_type, int currentNodeIndex, int treeNodeSize, boolean isEvictOperation, boolean isRemoveOperation, boolean isPutOperation, boolean isRemoveDataOperation, Fqn targetFqn, Fqn currentFqn)
+   private boolean writeLockNeeded(InvocationContext ctx, NodeLock.LockType lock_type, int currentNodeIndex, int treeNodeSize, boolean isEvictOperation, boolean isRemoveOperation, boolean isPutOperation, boolean isRemoveDataOperation, Fqn targetFqn, NodeSPI currentNode)
    {
       // write lock forced!!
       boolean isTargetNode = isTargetNode(currentNodeIndex, treeNodeSize);
 
       if (ctx.getOptionOverrides().isForceWriteLock() && isTargetNode) return true;
 
-      if (cache.getConfiguration().isLockParentForChildInsertRemove())
+      if (currentNode.isLockForChildInsertRemove())
       {
          if (isRemoveOperation && currentNodeIndex == treeNodeSize - 2)
          {
             return true;// we're doing a remove and we've reached the PARENT node of the target to be removed.
          }
 
-         if (!isTargetNode && cache.peek(new Fqn(currentFqn, targetFqn.get(currentNodeIndex + 1)), false) == null)
+         if (!isTargetNode && cache.peek(new Fqn(currentNode.getFqn(), targetFqn.get(currentNodeIndex + 1)), false) == null)
          {
             return isPutOperation;// we're at a node in the tree, not yet at the target node, and we need to create the next node.  So we need a WL here.
          }

Modified: core/trunk/src/main/java/org/jboss/cache/loader/CacheLoaderManager.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/loader/CacheLoaderManager.java	2007-09-21 03:15:01 UTC (rev 4496)
+++ core/trunk/src/main/java/org/jboss/cache/loader/CacheLoaderManager.java	2007-09-21 13:53:59 UTC (rev 4497)
@@ -176,6 +176,13 @@
             tmpLoader = asyncDecorator;
          }
 
+         if (cfg.isIgnoreModifications())
+         {
+            AbstractDelegatingCacheLoader readOnlyDecorator;
+            readOnlyDecorator = new ReadOnlyDelegatingCacheLoader(tmpLoader);
+            tmpLoader = readOnlyDecorator;
+         }
+
          // singleton?
          SingletonStoreConfig ssc = cfg.getSingletonStoreConfig();
          if (ssc != null && ssc.isSingletonStoreEnabled())

Added: core/trunk/src/main/java/org/jboss/cache/loader/ReadOnlyDelegatingCacheLoader.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/loader/ReadOnlyDelegatingCacheLoader.java	                        (rev 0)
+++ core/trunk/src/main/java/org/jboss/cache/loader/ReadOnlyDelegatingCacheLoader.java	2007-09-21 13:53:59 UTC (rev 4497)
@@ -0,0 +1,85 @@
+package org.jboss.cache.loader;
+
+import org.jboss.cache.Fqn;
+import org.jboss.cache.Modification;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.Map;
+import java.util.List;
+import java.io.ObjectInputStream;
+
+/**
+ * Provides ignoreModifications features to all cache loaders.
+ *
+ * @author <a href="mailto:manik at jboss.org">Manik Surtani</a>
+ * @since 2.1.0
+ */
+public class ReadOnlyDelegatingCacheLoader extends AbstractDelegatingCacheLoader
+{
+   private static Log log = LogFactory.getLog(ReadOnlyDelegatingCacheLoader.class);
+
+   public ReadOnlyDelegatingCacheLoader(CacheLoader cl)
+   {
+      super(cl);
+   }
+
+   public Object put(Fqn name, Object key, Object value) throws Exception
+   {
+      log.trace("Not delegating write operation to underlying cache loader");
+      Map map = get(name);
+      return (map == null) ? null : map.get(key);
+   }
+
+   public void put(Fqn name, Map attributes) throws Exception
+   {
+      log.trace("Not delegating write operation to underlying cache loader");
+   }
+
+   public void put(List<Modification> modifications) throws Exception
+   {
+      log.trace("Not delegating write operation to underlying cache loader");
+   }
+
+   public Object remove(Fqn fqn, Object key) throws Exception
+   {
+      log.trace("Not delegating write operation to underlying cache loader");
+      Map map = get(fqn);
+      return (map == null) ? null : map.get(key);
+   }
+
+   public void remove(Fqn fqn) throws Exception
+   {
+      log.trace("Not delegating write operation to underlying cache loader");
+   }
+
+   public void removeData(Fqn fqn) throws Exception
+   {
+      log.trace("Not delegating write operation to underlying cache loader");
+   }
+
+   public void prepare(Object tx, List<Modification> modifications, boolean one_phase) throws Exception
+   {
+      log.trace("Not delegating write operation to underlying cache loader");
+   }
+
+   public void commit(Object tx) throws Exception
+   {
+      log.trace("Not delegating write operation to underlying cache loader");
+   }
+
+   public void rollback(Object tx)
+   {
+      log.trace("Not delegating write operation to underlying cache loader");
+   }
+
+   public void storeEntireState(ObjectInputStream is) throws Exception
+   {
+      log.trace("Not delegating write operation to underlying cache loader");
+   }
+
+   public void storeState(Fqn subtree, ObjectInputStream is) throws Exception
+   {
+      log.trace("Not delegating write operation to underlying cache loader");
+   }
+}

Modified: core/trunk/src/main/java/org/jboss/cache/optimistic/WorkspaceNodeImpl.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/optimistic/WorkspaceNodeImpl.java	2007-09-21 03:15:01 UTC (rev 4496)
+++ core/trunk/src/main/java/org/jboss/cache/optimistic/WorkspaceNodeImpl.java	2007-09-21 13:53:59 UTC (rev 4497)
@@ -329,6 +329,16 @@
       throw new UnsupportedOperationException();
    }
 
+   public boolean isLockForChildInsertRemove()
+   {
+      throw new UnsupportedOperationException();
+   }
+
+   public void setLockForChildInsertRemove(boolean lockForChildInsertRemove)
+   {
+      throw new UnsupportedOperationException();
+   }
+
    public NodeSPI<K, V> getChild(Fqn f)
    {
       if (f.size() > 1)

Modified: core/trunk/src/test/java/org/jboss/cache/loader/CacheLoaderTestsBase.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/loader/CacheLoaderTestsBase.java	2007-09-21 03:15:01 UTC (rev 4496)
+++ core/trunk/src/test/java/org/jboss/cache/loader/CacheLoaderTestsBase.java	2007-09-21 13:53:59 UTC (rev 4497)
@@ -39,6 +39,7 @@
 import org.jboss.util.stream.MarshalledValueOutputStream;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
 
 /**
  * Commons tests for all CacheLoaders
@@ -46,6 +47,7 @@
  * @author Bela Ban
  * @version $Id$
  */
+ at Test(groups = {"functional"})
 abstract public class CacheLoaderTestsBase extends AbstractCacheLoaderTestBase
 {
 
@@ -2099,6 +2101,33 @@
 
    }
 
+   public void testIgnoreModifications() throws Exception
+   {
+      Fqn fqn = Fqn.fromString("/a");
+      cache.put(fqn, "k", "v");
+      assert "v".equals(cache.get(fqn, "k"));
+      assert "v".equals(loader.get(fqn).get("k"));
+
+      // now stop the cache
+      cache.stop();
+      cache.destroy();
+      cache.getConfiguration().getCacheLoaderConfig().getIndividualCacheLoaderConfigs().get(0).setIgnoreModifications(true);
+      cache.start();
+      loader = cache.getCacheLoader();
+
+      // test that the cache loader is wrapped by a read-only delegate
+      assert loader instanceof ReadOnlyDelegatingCacheLoader;
+            
+      // old state should be persisted.
+      assert "v".equals(cache.get(fqn, "k"));
+      assert "v".equals(loader.get(fqn).get("k"));
+
+      // the loader should now be read-only
+      cache.put(fqn, "k", "v2");
+      assert "v2".equals(cache.get(fqn, "k"));
+      assert "v".equals(loader.get(fqn).get("k"));
+   }
+
    public void testCacheLoaderThreadSafety() throws Exception
    {
       threadSafetyTest(true);

Modified: core/trunk/src/test/java/org/jboss/cache/loader/FileCacheLoaderTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/loader/FileCacheLoaderTest.java	2007-09-21 03:15:01 UTC (rev 4496)
+++ core/trunk/src/test/java/org/jboss/cache/loader/FileCacheLoaderTest.java	2007-09-21 13:53:59 UTC (rev 4497)
@@ -1,16 +1,12 @@
 package org.jboss.cache.loader;
 
 import static org.testng.AssertJUnit.assertEquals;
+import org.testng.annotations.Test;
 
 import org.jboss.cache.Fqn;
 import org.jboss.cache.misc.TestingUtil;
 
-/**
- * Created by IntelliJ IDEA.
- * User: bela
- * Date: Jun 9, 2004
- * Time: 9:05:19 AM
- */
+ at Test(groups = {"functional"})
 public class FileCacheLoaderTest extends CacheLoaderTestsBase
 {
    protected void configureCache() throws Exception

Modified: core/trunk/src/test/java/org/jboss/cache/lock/WriteLockOnParentTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/lock/WriteLockOnParentTest.java	2007-09-21 03:15:01 UTC (rev 4496)
+++ core/trunk/src/test/java/org/jboss/cache/lock/WriteLockOnParentTest.java	2007-09-21 13:53:59 UTC (rev 4497)
@@ -13,6 +13,7 @@
 import org.jboss.cache.CacheSPI;
 import org.jboss.cache.DefaultCacheFactory;
 import org.jboss.cache.Fqn;
+import org.jboss.cache.Node;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
@@ -175,4 +176,92 @@
       assertNotNull("/a/c should exist", cache.peek(a_c, false));
    }
 
+   public void testPerNodeConfigurationDefaultLock() throws Exception
+   {
+      testPerNodeConfiguration(true);
+   }
+
+   public void testPerNodeConfigurationDefaultNoLock() throws Exception
+   {
+      testPerNodeConfiguration(false);
+   }
+
+   private void testPerNodeConfiguration(boolean defaultLock) throws Exception
+   {
+      cache.getConfiguration().setLockParentForChildInsertRemove(defaultLock);
+
+      cache.put(a, Collections.emptyMap());
+      if (!defaultLock)
+      {
+         // set a per-node lock for a
+         cache.getRoot().getChild(a).setLockForChildInsertRemove(true);
+      }
+
+      cache.put(a_b, Collections.emptyMap());
+      cache.put(a_c, Collections.emptyMap());
+
+      assertNotNull("/a should exist", cache.peek(a, false));
+      assertNotNull("/a/b should exist", cache.peek(a_b, false));
+      assertNotNull("/a/c should exist", cache.peek(a_c, false));
+
+      // concurrent remove of /a/b and /a/c
+      tm.begin();
+      cache.removeNode(a_b);
+      Transaction t1 = tm.suspend();
+
+      tm.begin();
+      try
+      {
+         cache.removeNode(a_c);
+         fail("Should not get here.");
+      }
+      catch (TimeoutException e)
+      {
+         // expected
+      }
+      tm.commit();
+
+      tm.resume(t1);
+      tm.commit();
+
+      assertNotNull("/a should exist", cache.peek(a, false));
+      assertNull("/a/b should not exist", cache.peek(a_b, false));
+      assertNotNull("/a/c should exist", cache.peek(a_c, false));
+
+      Fqn b = Fqn.fromString("/b");
+      Fqn b_b = Fqn.fromString("/b/b");
+      Fqn b_c = Fqn.fromString("/b/c");
+      cache.put(b, Collections.emptyMap());
+
+      if (defaultLock)
+      {
+         // set a per-node locking config for node b
+         cache.getRoot().getChild(b).setLockForChildInsertRemove(false);
+      }
+
+
+      cache.put(b_b, Collections.emptyMap());
+      cache.put(b_c, Collections.emptyMap());
+
+      assertNotNull("/a should exist", cache.peek(b, false));
+      assertNotNull("/a/b should exist", cache.peek(b_b, false));
+      assertNotNull("/a/c should exist", cache.peek(b_c, false));
+
+      // concurrent remove of /a/b and /a/c
+      tm.begin();
+      cache.removeNode(b_b);
+      t1 = tm.suspend();
+
+      tm.begin();
+      cache.removeNode(b_c);
+      tm.commit();
+
+      tm.resume(t1);
+      tm.commit();
+
+      assertNotNull("/b should exist", cache.peek(b, false));
+      assertNull("/b/b should not exist", cache.peek(b_b, false));
+      assertNull("/b/c should not exist", cache.peek(b_c, false));
+   }
+
 }




More information about the jbosscache-commits mailing list