[jbosscache-commits] JBoss Cache SVN: r4646 - in core/trunk/src: main/java/org/jboss/cache/interceptors and 2 other directories.
jbosscache-commits at lists.jboss.org
jbosscache-commits at lists.jboss.org
Thu Oct 18 21:22:34 EDT 2007
Author: mircea.markus
Date: 2007-10-18 21:22:34 -0400 (Thu, 18 Oct 2007)
New Revision: 4646
Added:
core/trunk/src/test/java/org/jboss/cache/api/StructuralNodesTest.java
Modified:
core/trunk/src/main/java/org/jboss/cache/AbstractNode.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/UnversionedNode.java
core/trunk/src/main/java/org/jboss/cache/interceptors/OptimisticCreateIfNotExistsInterceptor.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/optimistic/WorkspaceNodeImpl.java
Log:
JBCACHE-1153 - structural nodes
Modified: core/trunk/src/main/java/org/jboss/cache/AbstractNode.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/AbstractNode.java 2007-10-18 20:53:27 UTC (rev 4645)
+++ core/trunk/src/main/java/org/jboss/cache/AbstractNode.java 2007-10-19 01:22:34 UTC (rev 4646)
@@ -16,6 +16,7 @@
protected Map<Object, Node<K, V>> children;
protected Fqn fqn;
protected boolean resident;
+ protected boolean structural;
public boolean isDeleted()
{
@@ -54,6 +55,10 @@
}
+ public boolean isStructural()
+ {
+ return structural;
+ }
public boolean equals(Object another)
{
Modified: core/trunk/src/main/java/org/jboss/cache/Node.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/Node.java 2007-10-18 20:53:27 UTC (rev 4645)
+++ core/trunk/src/main/java/org/jboss/cache/Node.java 2007-10-19 01:22:34 UTC (rev 4646)
@@ -292,6 +292,17 @@
*/
void setResident(boolean resident);
+ /**
+ * Structural nodes are the nodes created to support other nodes that hold data. E.g. the operation
+ * cache.put("/a/b/c","key","value") the nodes "/a" and "/a/b" are created only for supporting the existence of
+ * "/a/b/c" which holds data -"/a" and "/a/b" are structural nodes.
+ * A node is no longer considered structural when it's attribut map is changed, i.e. attributes are added.
+ * Structural nodes are not considered for viction, i.e. they are marked as resident. When a node moves to
+ * non-structural state it will automatically be marked as non-resident. Structural nodes might be marked as
+ * non-resident and considered for eviction. This property is internally computed (as described bellow) and is not replicated.
+ */
+ boolean isStructural();
+
/**
* Tests whether this node is configured to be exclusively locked when inserting or removing children.
* <p />
Modified: core/trunk/src/main/java/org/jboss/cache/NodeSPI.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/NodeSPI.java 2007-10-18 20:53:27 UTC (rev 4645)
+++ core/trunk/src/main/java/org/jboss/cache/NodeSPI.java 2007-10-19 01:22:34 UTC (rev 4646)
@@ -437,4 +437,11 @@
* @return true if the node has one or more child nodes; false otherwise.
*/
boolean hasChildrenDirect();
+
+ /**
+ * It is internally decided (i.e. wthin the cache, one cannot specify it from the outside)
+ * whether a node is structural or not - so this method fits better here than in Node interface.
+ * @see org.jboss.cache.Node#isStructural()
+ */
+ void setStructural(boolean structural);
}
Modified: core/trunk/src/main/java/org/jboss/cache/UnversionedNode.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/UnversionedNode.java 2007-10-18 20:53:27 UTC (rev 4645)
+++ core/trunk/src/main/java/org/jboss/cache/UnversionedNode.java 2007-10-19 01:22:34 UTC (rev 4646)
@@ -754,4 +754,10 @@
{
this.lockForChildInsertRemove = lockForChildInsertRemove;
}
+
+ public void setStructural(boolean structural)
+ {
+ this.setResident(structural);
+ this.structural = structural;
+ }
}
Modified: core/trunk/src/main/java/org/jboss/cache/interceptors/OptimisticCreateIfNotExistsInterceptor.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/interceptors/OptimisticCreateIfNotExistsInterceptor.java 2007-10-18 20:53:27 UTC (rev 4645)
+++ core/trunk/src/main/java/org/jboss/cache/interceptors/OptimisticCreateIfNotExistsInterceptor.java 2007-10-19 01:22:34 UTC (rev 4646)
@@ -181,6 +181,7 @@
if (isTargetFqn && !workspace.isVersioningImplicit()) versionToPassIn = version;
NodeSPI newUnderlyingChildNode = workspaceNode.createChild(childName, workspaceNode.getNode(), cache, versionToPassIn);
+ newUnderlyingChildNode.setStructural(true);
// now assign "workspaceNode" to the new child created.
workspaceNode = nodeFactory.createWorkspaceNode(newUnderlyingChildNode, workspace);
Modified: core/trunk/src/main/java/org/jboss/cache/interceptors/OptimisticValidatorInterceptor.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/interceptors/OptimisticValidatorInterceptor.java 2007-10-18 20:53:27 UTC (rev 4645)
+++ core/trunk/src/main/java/org/jboss/cache/interceptors/OptimisticValidatorInterceptor.java 2007-10-19 01:22:34 UTC (rev 4646)
@@ -211,6 +211,7 @@
Map mergedData = workspaceNode.getMergedData();
underlyingNode.clearDataDirect();
underlyingNode.putAllDirect(mergedData);
+ underlyingNode.setStructural(workspaceNode.isStructural());
updateVersion = true;
}
Modified: core/trunk/src/main/java/org/jboss/cache/interceptors/PessimisticLockInterceptor.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/interceptors/PessimisticLockInterceptor.java 2007-10-18 20:53:27 UTC (rev 4645)
+++ core/trunk/src/main/java/org/jboss/cache/interceptors/PessimisticLockInterceptor.java 2007-10-19 01:22:34 UTC (rev 4646)
@@ -78,6 +78,7 @@
case MethodDeclarations.putKeyValMethodLocal_id:
log.trace("Creating nodes if necessary");
createNodes((Fqn) args[1], ctx.getGlobalTransaction());
+ markNodeNonStructural(ctx);
break;
}
@@ -194,6 +195,14 @@
log.trace("bypassed locking as method " + m.getName() + "() doesn't require locking");
}
}
+ //this should be executed before calling eviction code/super.invoke()
+ //else, all nodes would be seen as resident by the eviction interceptor
+ if (m.getMethodId() == MethodDeclarations.putDataMethodLocal_id ||
+ m.getMethodId() == MethodDeclarations.putDataEraseMethodLocal_id ||
+ m.getMethodId() == MethodDeclarations.putKeyValMethodLocal_id)
+ {
+ this.markNodeNonStructural(ctx);
+ }
if (m.getMethodId() == MethodDeclarations.lockMethodLocal_id)
{
return null;
@@ -227,6 +236,19 @@
return o;
}
+ private void markNodeNonStructural(InvocationContext ctx)
+ {
+ MethodCall m = ctx.getMethodCall();
+ Object[] args = m.getArgs();
+ Fqn fqn = (Fqn) args[1];
+ NodeSPI nodeSpi = cache.getRoot().getChildDirect(fqn);
+ if (nodeSpi == null)
+ {
+ throw new IllegalStateException("Could not add attribute to a NULL node!");
+ }
+ nodeSpi.setStructural(false);
+ }
+
private long getLockAcquisitionTimeout(InvocationContext ctx)
{
long timeout = lock_acquisition_timeout;
@@ -307,6 +329,7 @@
if (child_node == null && createIfNotExists)
{
child_node = n.addChildDirect(new Fqn(child_name));
+ child_node.setStructural(true);
created = true;
}
@@ -467,7 +490,11 @@
Fqn childFqn = new Fqn(child_name);
NodeSPI child_node = n.getChildDirect(childFqn);
- if (child_node == null) child_node = n.addChildDirect(childFqn);
+ if (child_node == null)
+ {
+ child_node = n.addChildDirect(childFqn);
+ child_node.setStructural(true);
+ }
// test if this node needs to be 'undeleted'
// reverse the "remove" if the node has been previously removed in the same tx, if this operation is a put()
if (gtx != null && needToReverseRemove(child_node, tx_table.get(gtx), NodeLock.LockType.WRITE, false, true))
Modified: core/trunk/src/main/java/org/jboss/cache/optimistic/WorkspaceNodeImpl.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/optimistic/WorkspaceNodeImpl.java 2007-10-18 20:53:27 UTC (rev 4645)
+++ core/trunk/src/main/java/org/jboss/cache/optimistic/WorkspaceNodeImpl.java 2007-10-19 01:22:34 UTC (rev 4646)
@@ -103,6 +103,7 @@
public void putAll(Map<K, V> data)
{
realPut(data, false);
+ this.structural = false;
modified = true;
}
@@ -110,11 +111,13 @@
{
clearData();
putAll(data);
+ this.structural = false;
}
public V put(K key, V value)
{
modified = true;
+ this.structural = false;
return optimisticDataMap.put(key, value);
}
Added: core/trunk/src/test/java/org/jboss/cache/api/StructuralNodesTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/api/StructuralNodesTest.java (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/api/StructuralNodesTest.java 2007-10-19 01:22:34 UTC (rev 4646)
@@ -0,0 +1,143 @@
+package org.jboss.cache.api;
+
+import org.jboss.cache.CacheImpl;
+import org.jboss.cache.DefaultCacheFactory;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.misc.TestingUtil;
+import org.jboss.cache.config.Configuration;
+import org.jboss.cache.factories.UnitTestCacheConfigurationFactory;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+import org.testng.annotations.Test;
+import org.testng.annotations.AfterMethod;
+
+import javax.transaction.TransactionManager;
+import javax.transaction.SystemException;
+import javax.transaction.NotSupportedException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Testser class for strctural nodes functionality.
+ *
+ * @author <a href="mailto:mircea.markus at jboss.com">Mircea Markus</a>
+ * @since 2.1.0
+ */
+ at Test(groups = {"functional"})
+public class StructuralNodesTest
+{
+ private CacheImpl<Object, Object> cache;
+
+
+ @AfterMethod(alwaysRun = true)
+ public void closeCache()
+ {
+ cache.remove("/");
+ cache.stop();
+ }
+
+ /**
+ * Tests that when creaitng a node its parents are marked by default as structural, and are by default resident.
+ * When modifying the attribute map, the node is not structural anymore and also is not resident.
+ */
+ public void testHappyFlowPessimistic()
+ {
+ Configuration cacheConfig = UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, false);
+ cache = (CacheImpl<Object, Object>) DefaultCacheFactory.getInstance().createCache(cacheConfig, true);
+ cache.put("/a/b/c/d/e","k","v");
+
+ assertTrue(cache.getRoot().getChild(Fqn.fromString("/a")).isStructural());
+ assertTrue(cache.getRoot().getChild(Fqn.fromString("/a")).isResident());
+ assertTrue(cache.getRoot().getChild(Fqn.fromString("/a/b")).isStructural());
+ assertTrue(cache.getRoot().getChild(Fqn.fromString("/a/b")).isResident());
+ assertTrue(cache.getRoot().getChild(Fqn.fromString("/a/b/c")).isStructural());
+ assertTrue(cache.getRoot().getChild(Fqn.fromString("a/b/c")).isResident());
+ assertTrue(cache.getRoot().getChild(Fqn.fromString("a/b/c/d")).isStructural());
+ assertTrue(cache.getRoot().getChild(Fqn.fromString("a/b/c/d")).isResident());
+ assertFalse(cache.getRoot().getChild(Fqn.fromString("a/b/c/d/e")).isStructural());
+ assertFalse(cache.getRoot().getChild(Fqn.fromString("a/b/c/d/e")).isResident());
+
+ //now add some attributes to some nodes - this should make them both non-structural and non-resident
+ Map data = new HashMap();
+ cache.put("/a", data);
+ assertFalse(cache.getRoot().getChild(Fqn.fromString("/a")).isStructural());
+ assertFalse(cache.getRoot().getChild(Fqn.fromString("/a")).isResident());
+ }
+
+
+ /**
+ * There is a bunch of method calls that will make the structural nodes not structural.
+ * This tests that for all those method calls the 'structural' flag is set to false.
+ */
+ public void testMakeNonStructural()
+ {
+ Configuration cacheConfig = UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, false);
+ cache = (CacheImpl<Object, Object>) DefaultCacheFactory.getInstance().createCache(cacheConfig, true);
+
+
+
+ cache.put("/put/putIfAbsent/putAll/test","k","v");
+
+ assertTrue(cache.getRoot().getChild(Fqn.fromString("/put")).isStructural());
+ assertTrue(cache.getRoot().getChild(Fqn.fromString("/put/putIfAbsent")).isStructural());
+ assertTrue(cache.getRoot().getChild(Fqn.fromString("/put/putIfAbsent/putAll")).isStructural());
+
+ cache.getRoot().getChild(Fqn.fromString("/put")).put("k","v");
+ assertFalse(cache.getRoot().getChild(Fqn.fromString("/put")).isStructural());
+ assertFalse(cache.getRoot().getChild(Fqn.fromString("/put")).isResident());
+
+ cache.getRoot().getChild(Fqn.fromString("/put/putIfAbsent")).putIfAbsent("k","v");
+ assertFalse(cache.getRoot().getChild(Fqn.fromString("/put/putIfAbsent")).isStructural());
+ assertFalse(cache.getRoot().getChild(Fqn.fromString("/put/putIfAbsent")).isResident());
+
+ cache.getRoot().getChild(Fqn.fromString("/put/putIfAbsent/putAll")).putAll(new HashMap<Object,Object>());
+ assertFalse(cache.getRoot().getChild(Fqn.fromString("/put/putIfAbsent/putAll")).isStructural());
+ assertFalse(cache.getRoot().getChild(Fqn.fromString("/put/putIfAbsent/putAll")).isResident());
+
+ }
+
+ /**
+ * When we use optimisic locking, check that nodes made non-structural during transaction are non-structural after commiting.
+ */
+ public void testOptmisticLockingWithCommit() throws Exception
+ {
+ Configuration config = UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, false);
+ config.setNodeLockingOptimistic(true);
+ cache = (CacheImpl<Object, Object>) DefaultCacheFactory.getInstance().createCache(config, true);
+ cache.put("/a/b/c","k","v");
+
+ TransactionManager txManager = cache.getTransactionManager();
+ txManager.begin();
+ cache.getRoot().getChild(Fqn.fromString("/a/b")).put("key","value");
+ txManager.commit();
+ assertTrue(cache.getRoot().getChild(Fqn.fromString("/a")).isStructural());
+ assertTrue(cache.getRoot().getChild(Fqn.fromString("/a")).isResident());
+ assertFalse(cache.getRoot().getChild(Fqn.fromString("/a/b")).isStructural());
+ assertFalse(cache.getRoot().getChild(Fqn.fromString("/a/b")).isResident());
+ assertFalse(cache.getRoot().getChild(Fqn.fromString("/a/b/c")).isStructural());
+ assertFalse(cache.getRoot().getChild(Fqn.fromString("/a/b/c")).isResident());
+ }
+
+ /**
+ * When we use optimisic locking, check that nodes made non-structural during transaction are structural after rollback.
+ */
+ public void testOptmisticLockingWithRollback() throws Exception
+ {
+ Configuration config = UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, false);
+ config.setNodeLockingOptimistic(true);
+ cache = (CacheImpl<Object, Object>) DefaultCacheFactory.getInstance().createCache(config, true);
+ cache.put("/a/b/c","k","v");
+
+ TransactionManager txManager = cache.getTransactionManager();
+ txManager.begin();
+ cache.getRoot().getChild(Fqn.fromString("/a/b")).put("key","value");
+ txManager.rollback();
+ assertTrue(cache.getRoot().getChild(Fqn.fromString("/a")).isStructural());
+ assertTrue(cache.getRoot().getChild(Fqn.fromString("/a")).isResident());
+ assertTrue(cache.getRoot().getChild(Fqn.fromString("/a/b")).isStructural());
+ assertTrue(cache.getRoot().getChild(Fqn.fromString("/a/b")).isResident());
+ assertFalse(cache.getRoot().getChild(Fqn.fromString("/a/b/c")).isStructural());
+ assertFalse(cache.getRoot().getChild(Fqn.fromString("/a/b/c")).isResident());
+ }
+
+}
More information about the jbosscache-commits
mailing list