You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by md...@apache.org on 2012/05/21 19:37:45 UTC

svn commit: r1341123 - in /jackrabbit/oak/trunk: oak-core/src/main/java/org/apache/jackrabbit/oak/api/ oak-core/src/main/java/org/apache/jackrabbit/oak/core/ oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/ oak-jcr/src/main/java/org/apache/jack...

Author: mduerig
Date: Mon May 21 17:37:44 2012
New Revision: 1341123

URL: http://svn.apache.org/viewvc?rev=1341123&view=rev
Log:
OAK-106: Use NodeStateBuilder instances to record changes in TreeImpl

Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/Root.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/RootImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/TreeImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/NodeDelegate.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/PropertyDelegate.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionDelegate.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/Root.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/Root.java?rev=1341123&r1=1341122&r2=1341123&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/Root.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/Root.java Mon May 21 17:37:44 2012
@@ -64,9 +64,9 @@ public interface Root {
     void rebase();
 
     /**
-     * Clear all changes made to this root
+     * Reverts all changes made to this root.
      */
-    void clear();
+    void revert();
 
     /**
      * Atomically apply all changes made to the tree beneath this root to the

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/RootImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/RootImpl.java?rev=1341123&r1=1341122&r2=1341123&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/RootImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/RootImpl.java Mon May 21 17:37:44 2012
@@ -42,6 +42,12 @@ import static org.apache.jackrabbit.oak.
 public class RootImpl implements Root {
     static final Logger log = LoggerFactory.getLogger(RootImpl.class);
 
+    /**
+     * Number of {@link #setWorkspaceRootState(NodeState)} calls for which changes
+     * are kept in memory.
+     */
+    private static final int PURGE_LIMIT = 100;
+
     /** The underlying store to which this root belongs */
     private final NodeStore store;
 
@@ -54,6 +60,35 @@ public class RootImpl implements Root {
     /** Current branch this root operates on */
     private NodeStoreBranch branch;
 
+    /** Lazily initialised builder for the root node state of this workspace */
+    private NodeStateBuilder workspaceBuilder;
+
+    /**
+     * Number of {@link #setWorkspaceRootState(NodeState)} occurred so since the lase
+     * purge.
+     */
+    private int modCount;
+
+    /**
+     * Listeners which needs to be modified as soon as {@link #purgePendingChanges()}
+     * is called. Listeners are removed from this list after being called. If further
+     * notifications are required, they need to explicitly re-register.
+     *
+     * The {@link TreeImpl} instances us this mechanism to dispose of its associated
+     * {@link NodeStateBuilder} on purge. Keeping a reference on those {@code TreeImpl}
+     * instances {@code NodeStateBuilder} (i.e. those which are modified) prevents them
+     * from being prematurely garbage collected.
+     */
+    private List<PurgeListener> purgePurgeListeners = new ArrayList<PurgeListener>();
+
+    /**
+     * Purge listener.
+     * @see #purgePurgeListeners
+     */
+    public interface PurgeListener {
+        void purged();
+    }
+
     /**
      * New instance bases on a given {@link NodeStore} and a workspace
      * @param store  node store
@@ -68,6 +103,7 @@ public class RootImpl implements Root {
 
     @Override
     public boolean move(String sourcePath, String destPath) {
+        purgePendingChanges();
         TreeImpl source = getChild(sourcePath);
         if (source == null) {
             return false;
@@ -92,6 +128,7 @@ public class RootImpl implements Root {
 
     @Override
     public boolean copy(String sourcePath, String destPath) {
+        purgePendingChanges();
         return branch.copy(
             PathUtils.concat(workspaceName, sourcePath),
             PathUtils.concat(workspaceName, destPath));
@@ -104,7 +141,7 @@ public class RootImpl implements Root {
 
     @Override
     public void rebase() {
-        root.clear();
+        purgePendingChanges();
         NodeState base = getWorkspaceBaseState();
         NodeState head = getWorkspaceRootState();
         branch = store.branch();
@@ -112,21 +149,35 @@ public class RootImpl implements Root {
     }
 
     @Override
-    public void clear() {
-        root.clear();
-        branch = store.branch();
+    public void revert() {
+        workspaceBuilder = null;
+        root.revert();
     }
 
     @Override
     public void commit() throws CommitFailedException {
+        purgePendingChanges();
+        NodeState base = getWorkspaceRootState();
         branch.merge();
-        root.clear();
         branch = store.branch();
+        root.saved();
+        NodeState head = getWorkspaceRootState();
+        merge(base, head, getRoot());
     }
 
     @Override
     public boolean hasPendingChanges() {
-        return !branch.getBase().equals(branch.getRoot());
+        return !getWorkspaceBaseState().equals(getWorkspaceRootState());
+    }
+
+    /**
+     * Add a {@code PurgeListener} to this instance. Listeners are automatically
+     * unregistered after having been called. If further notifications are required,
+     * they need to explicitly re-register.
+     * @param purgeListener  listener
+     */
+    public void addListener(PurgeListener purgeListener) {
+        purgePurgeListeners.add(purgeListener);
     }
 
     /**
@@ -134,7 +185,9 @@ public class RootImpl implements Root {
      * @return root node state
      */
     public NodeState getWorkspaceRootState() {
-        return branch.getRoot().getChildNode(workspaceName);
+        return workspaceBuilder == null
+            ? branch.getRoot().getChildNode(workspaceName)
+            : workspaceBuilder.getNodeState();
     }
 
     /**
@@ -164,9 +217,23 @@ public class RootImpl implements Root {
      * @param nodeState  node state representing the modified workspace
      */
     public void setWorkspaceRootState(NodeState nodeState) {
-        NodeStateBuilder builder = getBuilder(branch.getRoot());
-        builder.setNode(workspaceName, nodeState);
-        branch.setRoot(builder.getNodeState());
+        getWorkspaceBuilder().setNode(workspaceName, nodeState);
+        modCount++;
+        if (needsPurging()) {
+            purgePendingChanges();
+        }
+    }
+
+    /**
+     * Purge all pending changes to the underlying {@link NodeStoreBranch}.
+     * All registered {@link PurgeListener}s are notified.
+     */
+    public void purgePendingChanges() {
+        if (workspaceBuilder != null) {
+            branch.setRoot(workspaceBuilder.getNodeState());
+            workspaceBuilder = null;
+            notifyListeners();
+        }
     }
 
     /**
@@ -184,6 +251,38 @@ public class RootImpl implements Root {
 
     //------------------------------------------------------------< private >---
 
+    // TODO better way to determine purge limit
+    private boolean needsPurging() {
+        if (modCount > PURGE_LIMIT) {
+            modCount = 0;
+            return true;
+        }
+        else {
+            return false;
+        }
+    }
+
+    /**
+     * Lazily initialise the {@code NodeStateBuilder} associated with the
+     * root node of the current workspace.
+     * @return
+     */
+    private synchronized NodeStateBuilder getWorkspaceBuilder() {
+        if (workspaceBuilder == null) {
+            workspaceBuilder = getBuilder(branch.getRoot());
+        }
+        return workspaceBuilder;
+    }
+
+    private void notifyListeners() {
+        List<PurgeListener> purgeListeners = this.purgePurgeListeners;
+        this.purgePurgeListeners = new ArrayList<PurgeListener>();
+
+        for (PurgeListener purgeListener : purgeListeners) {
+            purgeListener.purged();
+        }
+    }
+
     private TreeImpl getRoot() {
         return root;
     }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/TreeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/TreeImpl.java?rev=1341123&r1=1341122&r2=1341123&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/TreeImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/TreeImpl.java Mon May 21 17:37:44 2012
@@ -22,6 +22,7 @@ import org.apache.commons.collections.ma
 import org.apache.jackrabbit.oak.api.CoreValue;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.core.RootImpl.PurgeListener;
 import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.jackrabbit.oak.spi.state.NodeStateBuilder;
@@ -38,7 +39,7 @@ import java.util.concurrent.locks.Reentr
 
 import static org.apache.jackrabbit.oak.plugins.memory.MemoryNodeState.EMPTY_NODE;
 
-public class TreeImpl implements Tree {
+public class TreeImpl implements Tree, PurgeListener {
 
     /** Underlying {@code Root} of this {@code Tree} instance */
     private final RootImpl root;
@@ -49,6 +50,9 @@ public class TreeImpl implements Tree {
     /** Name of this tree */
     private String name;
 
+    /** Lazily initialised {@code NodeStateBuilder} for the underyling node state */
+    protected NodeStateBuilder nodeStateBuilder;
+
     private final Children children = new Children();
 
     private TreeImpl(RootImpl root, TreeImpl parent, String name) {
@@ -60,13 +64,15 @@ public class TreeImpl implements Tree {
     static TreeImpl createRoot(final RootImpl root) {
         return new TreeImpl(root, null, "") {
             @Override
-            protected NodeState getNodeState() {
-                return root.getWorkspaceRootState();
+            protected NodeState getBaseState() {
+                return root.getWorkspaceBaseState();
             }
 
             @Override
-            protected NodeState getBaseState() {
-                return root.getWorkspaceBaseState();
+            protected NodeState getNodeState() {
+                return nodeStateBuilder == null
+                    ? root.getWorkspaceRootState()
+                    : nodeStateBuilder.getNodeState();
             }
 
             @Override
@@ -322,18 +328,49 @@ public class TreeImpl implements Tree {
         return true;
     }
 
-    //------------------------------------------------------------< protected >---
+    //------------------------------------------------------------< internal >---
 
-    protected NodeState getNodeState() {
-        return parent.getNodeState().getChildNode(name);
+    /**
+     * Revert all changes to this instance and its child instances.
+     */
+    public void revert() {
+        for (TreeImpl child : children) {
+            // TODO revert changes done to this instance
+            child.revert();
+        }
+        nodeStateBuilder = null;
+        children.clear();
+    }
+
+    /**
+     * Remove revert info for this instance and all its child instances.
+     */
+    public void saved() {
+        for (TreeImpl child : children) {
+            // TODO clear records of changes done to this instance
+            child.saved();
+        }
+        nodeStateBuilder = null;
+        children.clear();
     }
 
+    //------------------------------------------------------------< RootImpl.Listener >---
+
+    @Override
+    public void purged() {
+        nodeStateBuilder = null;
+    }
+
+    //------------------------------------------------------------< protected >---
+
     protected NodeState getBaseState() {
         return parent.getBaseState().getChildNode(name);
     }
 
-    protected NodeStateBuilder getNodeStateBuilder() {
-        return root.getBuilder(getNodeState());
+    protected NodeState getNodeState() {
+        return nodeStateBuilder == null
+            ? parent.getNodeState().getChildNode(name)
+            : nodeStateBuilder.getNodeState();
     }
 
     protected void updateParentState(NodeState childState) {
@@ -342,14 +379,16 @@ public class TreeImpl implements Tree {
         parent.updateParentState(parentBuilder.getNodeState());
     }
 
-    //------------------------------------------------------------< internal >---
+    //------------------------------------------------------------< private >---
 
-    void clear() {
-        children.clear();
+    private synchronized NodeStateBuilder getNodeStateBuilder() {
+        if (nodeStateBuilder == null) {
+            nodeStateBuilder = root.getBuilder(getNodeState());
+            root.addListener(this);
+        }
+        return nodeStateBuilder;
     }
 
-    //------------------------------------------------------------< private >---
-
     private void buildPath(StringBuilder sb) {
         if (parent != null) {
             parent.buildPath(sb);
@@ -397,7 +436,7 @@ public class TreeImpl implements Tree {
         return !isDirty[0];
     }
 
-    private static class Children {
+    private static class Children implements Iterable<TreeImpl> {
         @SuppressWarnings("unchecked")
         private final Map<String, TreeImpl> children = new ReferenceMap();
         private final Lock readLock;
@@ -437,11 +476,13 @@ public class TreeImpl implements Tree {
         }
 
         public void clear() {
-            for (TreeImpl child : children.values()) {
-                child.clear();
-            }
             children.clear();
         }
+
+        @Override
+        public Iterator<TreeImpl> iterator() {
+            return children.values().iterator();
+        }
     }
 
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java?rev=1341123&r1=1341122&r2=1341123&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java Mon May 21 17:37:44 2012
@@ -22,11 +22,6 @@ import static org.apache.jackrabbit.oak.
  */
 class KernelNodeStoreBranch implements NodeStoreBranch {
 
-    /**
-     * Number of {@link #setRoot(NodeState)} for which changes are kept in memory.
-     */
-    private static final int PURGE_LIMIT = 100;
-
     /** The underlying store to which this branch belongs */
     private final KernelNodeStore store;
 
@@ -43,9 +38,6 @@ class KernelNodeStoreBranch implements N
     /** Last state which was committed to this branch */
     private NodeState committed;
 
-    /** Number of {@link #setRoot(NodeState)} occurred so since the lase purge */
-    private int modCount;
-
     KernelNodeStoreBranch(KernelNodeStore store) {
         this.store = store;
 
@@ -69,10 +61,7 @@ class KernelNodeStoreBranch implements N
     @Override
     public void setRoot(NodeState newRoot) {
         currentRoot = newRoot;
-        modCount++;
-        if (needsPurging()) {
-            purge(buildJsop());
-        }
+        save(buildJsop());
     }
 
     @Override
@@ -91,7 +80,7 @@ class KernelNodeStoreBranch implements N
             return false;
         }
 
-        purge(buildJsop() + ">\"" + source + "\":\"" + target + '"');
+        save(buildJsop() + ">\"" + source + "\":\"" + target + '"');
         return true;
     }
 
@@ -111,13 +100,13 @@ class KernelNodeStoreBranch implements N
             return false;
         }
 
-        purge(buildJsop() + "*\"" + source + "\":\"" + target + '"');
+        save(buildJsop() + "*\"" + source + "\":\"" + target + '"');
         return true;
     }
 
     @Override
     public KernelNodeState merge() throws CommitFailedException {
-        purge(buildJsop());
+        save(buildJsop());
         // TODO rebase, call commitHook (OAK-100)
         MicroKernel kernel = getKernel();
         String mergedRevision;
@@ -155,7 +144,7 @@ class KernelNodeStoreBranch implements N
         return node;
     }
 
-    private void purge(String jsop) {
+    private void save(String jsop) {
         MicroKernel kernel = getKernel();
         branchRevision = kernel.commit("/", jsop, branchRevision, null);
         currentRoot = new KernelNodeState(kernel, getValueFactory(), "/", branchRevision);
@@ -238,15 +227,4 @@ class KernelNodeStoreBranch implements N
             }
         });
     }
-
-    // TODO better way to determine purge limit
-    private boolean needsPurging() {
-        if (modCount > PURGE_LIMIT) {
-            modCount = 0;
-            return true;
-        }
-        else {
-            return false;
-        }
-    }
 }

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/NodeDelegate.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/NodeDelegate.java?rev=1341123&r1=1341122&r2=1341123&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/NodeDelegate.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/NodeDelegate.java Mon May 21 17:37:44 2012
@@ -24,11 +24,10 @@ import org.apache.jackrabbit.oak.commons
 import org.apache.jackrabbit.oak.util.Function1;
 import org.apache.jackrabbit.oak.util.Iterators;
 
-import java.util.Iterator;
-import java.util.List;
-
 import javax.annotation.CheckForNull;
 import javax.annotation.Nonnull;
+import java.util.Iterator;
+import java.util.List;
 
 /**
  * {@code NodeDelegate} serve as internal representations of {@code Node}s.
@@ -234,6 +233,7 @@ public class NodeDelegate extends ItemDe
     }
 
     private synchronized Tree getTree() {
+        // TODO: this should not be necessary anymore once TreeImpl.revert and TreeImpl.saved are implemented
         return tree = sessionDelegate.getTree(tree.getPath());
     }
 

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/PropertyDelegate.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/PropertyDelegate.java?rev=1341123&r1=1341122&r2=1341123&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/PropertyDelegate.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/PropertyDelegate.java Mon May 21 17:37:44 2012
@@ -242,6 +242,7 @@ public class PropertyDelegate extends It
     }
 
     private synchronized void resolve() {
+        // TODO: this should not be necessary anymore once TreeImpl.revert and TreeImpl.saved are implemented
         parent = sessionDelegate.getTree(parent.getPath());
 
         if (parent == null) {

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionDelegate.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionDelegate.java?rev=1341123&r1=1341122&r2=1341123&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionDelegate.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionDelegate.java Mon May 21 17:37:44 2012
@@ -148,12 +148,10 @@ public class SessionDelegate {
     }
 
     public void refresh(boolean keepChanges) {
-        if (keepChanges) {
-            root.rebase();
-        }
-        else {
-            root.clear();
+        if (!keepChanges) {
+            root.revert();
         }
+        root.rebase();
     }
 
     /**