jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mdue...@apache.org
Subject svn commit: r771280 - in /jackrabbit/trunk/jackrabbit-jcr2spi/src: main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ main/java/org/apache/jackrabbit/jcr2spi/state/ test/java/org/apache/jackrabbit/jcr2spi/
Date Mon, 04 May 2009 12:06:58 GMT
Author: mduerig
Date: Mon May  4 12:06:58 2009
New Revision: 771280

URL: http://svn.apache.org/viewvc?rev=771280&view=rev
Log:
JCR-2050: Optimize refresh operations

Modified:
    jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeEntries.java
    jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeEntriesImpl.java
    jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/EntryFactory.java
    jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntry.java
    jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntryImpl.java
    jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java
    jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntryImpl.java
    jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemState.java
    jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/GetPropertyTest.java

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeEntries.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeEntries.java?rev=771280&r1=771279&r2=771280&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeEntries.java
(original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeEntries.java
Mon May  4 12:06:58 2009
@@ -16,14 +16,15 @@
  */
 package org.apache.jackrabbit.jcr2spi.hierarchy;
 
-import org.apache.jackrabbit.spi.Name;
-import org.apache.jackrabbit.spi.Path;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
 
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.RepositoryException;
-import java.util.List;
-import java.util.NoSuchElementException;
-import java.util.Iterator;
+
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.Path;
 
 /**
  * <code>ChildNodeEntries</code> represents a collection of <code>NodeEntry</code>s
that
@@ -39,12 +40,6 @@
     boolean isComplete();
 
     /**
-     * Mark <code>ChildNodeEntries</code> in order to force reloading the
-     * entries.
-     */
-    void invalidate();
-
-    /**
      * Reloads this <code>ChildNodeEntries</code> object.
      *
      * @throws ItemNotFoundException

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeEntriesImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeEntriesImpl.java?rev=771280&r1=771279&r2=771280&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeEntriesImpl.java
(original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/ChildNodeEntriesImpl.java
Mon May  4 12:06:58 2009
@@ -16,27 +16,28 @@
  */
 package org.apache.jackrabbit.jcr2spi.hierarchy;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.apache.jackrabbit.spi.Name;
-import org.apache.jackrabbit.spi.Path;
-import org.apache.jackrabbit.spi.ChildInfo;
-import org.apache.jackrabbit.spi.NodeId;
-import org.apache.jackrabbit.jcr2spi.state.Status;
-import org.apache.commons.collections.list.AbstractLinkedList;
-
-import javax.jcr.RepositoryException;
-import javax.jcr.ItemNotFoundException;
-import java.util.Iterator;
-import java.util.List;
-import java.util.NoSuchElementException;
+import java.lang.ref.Reference;
+import java.lang.ref.SoftReference;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.ConcurrentModificationException;
-import java.util.Map;
 import java.util.HashMap;
-import java.util.Collections;
-import java.lang.ref.Reference;
-import java.lang.ref.SoftReference;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.RepositoryException;
+
+import org.apache.commons.collections.list.AbstractLinkedList;
+import org.apache.jackrabbit.jcr2spi.state.Status;
+import org.apache.jackrabbit.spi.ChildInfo;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.NodeId;
+import org.apache.jackrabbit.spi.Path;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * <code>ChildNodeEntriesImpl</code> implements a memory sensitive implementation
@@ -49,7 +50,6 @@
     private static final int STATUS_OK = 0;
     private static final int STATUS_INVALIDATED = 1;
 
-    private int status = STATUS_OK;
     private boolean complete = false;
 
     /**
@@ -122,19 +122,12 @@
      * @see ChildNodeEntries#isComplete()
      */
     public boolean isComplete() {
-        return (status == STATUS_OK && complete) ||
+        return (parent.getStatus() != Status.INVALIDATED && complete) ||
                 parent.getStatus() == Status.NEW ||
                 Status.isTerminal(parent.getStatus());
     }
 
     /**
-     * @see ChildNodeEntries#invalidate()
-     */
-    public void invalidate() {
-        this.status = STATUS_INVALIDATED;
-    }
-
-    /**
      * @see ChildNodeEntries#reload()
      */
     public synchronized void reload() throws ItemNotFoundException, RepositoryException {
@@ -189,7 +182,6 @@
             prevLN = ln;
         }
         // finally reset the status
-        status = STATUS_OK;
         complete = true;
     }
 

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/EntryFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/EntryFactory.java?rev=771280&r1=771279&r2=771280&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/EntryFactory.java
(original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/EntryFactory.java
Mon May  4 12:06:58 2009
@@ -16,15 +16,15 @@
  */
 package org.apache.jackrabbit.jcr2spi.hierarchy;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.apache.jackrabbit.jcr2spi.state.TransientItemStateFactory;
 import org.apache.jackrabbit.jcr2spi.util.LogUtil;
 import org.apache.jackrabbit.spi.IdFactory;
 import org.apache.jackrabbit.spi.Name;
-import org.apache.jackrabbit.spi.PathFactory;
 import org.apache.jackrabbit.spi.Path;
+import org.apache.jackrabbit.spi.PathFactory;
 import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * <code>EntryFactory</code>...
@@ -58,6 +58,11 @@
     private NamePathResolver resolver;
 
     /**
+     * Strategy used for item state invalidation (refresh)
+     */
+    private final InvalidationStrategy invalidationStrategy;
+
+    /**
      * Create a new instance of the <code>EntryFactory</code>.
      *
      * @param isf
@@ -71,6 +76,10 @@
         this.pathFactory = pathFactory;
         this.isf = isf;
         this.listener = listener;
+
+        // todo: make this configurable if necessary
+        // this.invalidationStrategy = new NodeEntryImpl.EagerInvalidation();
+        this.invalidationStrategy = new NodeEntryImpl.LazyInvalidation();
         this.rootEntry = NodeEntryImpl.createRootEntry(this);
     }
 
@@ -115,6 +124,13 @@
         listener.uniqueIdChanged(entry, previousUniqueID);
     }
 
+    /**
+     * @return  the strategy used for item state invalidation (refresh)
+     */
+    public InvalidationStrategy getInvalidationStrategy() {
+        return invalidationStrategy;
+    }
+
     //--------------------------------------------------------------------------
     /**
      * @param resolver
@@ -134,12 +150,40 @@
             return LogUtil.safeGetJCRPath(path, resolver);
         }
     }
-    
-    //--------------------------------------------------------------------------
+
+    //--------------------------------------------------< NodeEntryListener >---
     public interface NodeEntryListener {
 
         public void entryCreated(NodeEntry entry);
 
         public void uniqueIdChanged (NodeEntry entry, String previousUniqueID);
     }
-}
\ No newline at end of file
+
+    // ----------------------------------------------< InvalidationStrategy >---
+    /**
+     * Strategy for invalidating item states
+     */
+    public interface InvalidationStrategy {
+
+        /**
+         * Invalidate underlying {@link org.apache.jackrabbit.jcr2spi.state.ItemState} of
this
+         * <code>entry</code>. Implementors may choose to delay the actual call
to
+         * {@link org.apache.jackrabbit.jcr2spi.state.ItemState#invalidate()} for this
+         * <code>entry</code> and for any of its child entries. They need to
ensure however that
+         * {@link #applyPending(NodeEntry)} properly invalidates the respective state when
called.
+         *
+         * @param entry The <code>NodeEntry</code> to invalidate.
+         * @param recursive Invalidate state of child entries if <code>true</code>.
+         */
+        public void invalidate(NodeEntry entry, boolean recursive);
+
+        /**
+         * Apply any pending {@link org.apache.jackrabbit.jcr2spi.state.ItemState#invalidate()
+         * invalidation} of the underyling {@link org.apache.jackrabbit.jcr2spi.state.ItemState}
of
+         * this <code>entry</code>.
+         *
+         * @param entry The affected <code>NodeEntry</code>.
+         */
+        public void applyPending(NodeEntry entry);
+    }
+}

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntry.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntry.java?rev=771280&r1=771279&r2=771280&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntry.java
(original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntry.java
Mon May  4 12:06:58 2009
@@ -16,15 +16,15 @@
  */
 package org.apache.jackrabbit.jcr2spi.hierarchy;
 
-import org.apache.jackrabbit.spi.Name;
-import org.apache.jackrabbit.spi.Path;
-import org.apache.jackrabbit.jcr2spi.state.ItemState;
-import org.apache.jackrabbit.jcr2spi.state.Status;
-import org.apache.jackrabbit.jcr2spi.operation.Operation;
-
-import javax.jcr.RepositoryException;
 import javax.jcr.InvalidItemStateException;
 import javax.jcr.ItemNotFoundException;
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.jcr2spi.operation.Operation;
+import org.apache.jackrabbit.jcr2spi.state.ItemState;
+import org.apache.jackrabbit.jcr2spi.state.Status;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.Path;
 
 /**
  * <code>HierarchyEntry</code>...
@@ -112,16 +112,21 @@
     /**
      * Invalidates the underlying <code>ItemState</code> if available and if
it
      * is not transiently modified. If the <code>recursive</code> flag is true,
-     * the hierarchy is traverses and {@link #invalidate(boolean)} is called on
-     * all child entries.<br>
+     * also invalidates the child entries recursively.<br>
      * Note, that in contrast to {@link HierarchyEntry#reload(boolean)}
      * this method only sets the status of this item state to {@link
-     * Status#INVALIDATED} and does not acutally update it with the persistent
+     * Status#INVALIDATED} and does not actually update it with the persistent
      * state in the repository.
      */
     public void invalidate(boolean recursive);
 
     /**
+     * Calculates the status of the underlying <code>ItemState</code>: any pending
+     * changes to the underlying <code>ItemState</code> are applied.
+     */
+    public void calculateStatus();
+
+    /**
      * Traverses the hierarchy and reverts all transient modifications such as
      * adding, modifying or removing item states. 'Existing' item states
      * are reverted to their initial state and their status is reset to {@link Status#EXISTING}.
@@ -167,4 +172,5 @@
      * @param transientOperation
      */
     public void complete(Operation transientOperation) throws RepositoryException;
-}
\ No newline at end of file
+
+}

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntryImpl.java?rev=771280&r1=771279&r2=771280&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntryImpl.java
(original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/HierarchyEntryImpl.java
Mon May  4 12:06:58 2009
@@ -26,9 +26,12 @@
 import org.apache.jackrabbit.jcr2spi.state.ItemState;
 import org.apache.jackrabbit.jcr2spi.state.ItemStateFactory;
 import org.apache.jackrabbit.jcr2spi.state.Status;
+import org.apache.jackrabbit.jcr2spi.state.TransientItemStateFactory;
 import org.apache.jackrabbit.jcr2spi.state.ItemState.MergeResult;
+import org.apache.jackrabbit.spi.IdFactory;
 import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.Path;
+import org.apache.jackrabbit.spi.PathFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -76,6 +79,30 @@
     }
 
     /**
+     * Shortcut for {@link EntryFactory#getItemStateFactory()}
+     * @return
+     */
+    protected TransientItemStateFactory getItemStateFactory() {
+        return factory.getItemStateFactory();
+    }
+
+    /**
+     * Shortcut for {@link EntryFactory#getPathFactory()}
+     * @return
+     */
+    protected PathFactory getPathFactory() {
+        return factory.getPathFactory();
+    }
+
+    /**
+     * Shortcut for {@link EntryFactory#getIdFactory()}
+     * @return
+     */
+    protected IdFactory getIdFactory() {
+        return factory.getIdFactory();
+    }
+
+    /**
      * Resolves this <code>HierarchyEntryImpl</code> and returns the target
      * <code>ItemState</code> of this reference. This method may return a
      * cached <code>ItemState</code> if this method was called before already
@@ -239,11 +266,11 @@
      * @see HierarchyEntry#invalidate(boolean)
      */
     public void invalidate(boolean recursive) {
-        if (getStatus() == Status.EXISTING) {
-            ItemState state = internalGetItemState();
-            state.setStatus(Status.INVALIDATED);
+        ItemState state = internalGetItemState();
+        if (state == null) {
+            log.debug("Skip invalidation for unresolved HierarchyEntry " + name);
         } else {
-            log.debug("Skip invalidation for HierarchyEntry " + name + " with status " +
Status.getName(getStatus()));
+            state.invalidate();
         }
     }
 
@@ -314,7 +341,7 @@
          * then be merged into the current state.
          */
         try {
-            ItemStateFactory isf = factory.getItemStateFactory();
+            ItemStateFactory isf = getItemStateFactory();
             if (denotesNode()) {
                 NodeEntry ne = (NodeEntry) this;
                 isf.createNodeState(ne.getWorkspaceId(), ne);

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java?rev=771280&r1=771279&r2=771280&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java
(original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/NodeEntryImpl.java
Mon May  4 12:06:58 2009
@@ -16,6 +16,22 @@
  */
 package org.apache.jackrabbit.jcr2spi.hierarchy;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.jcr.InvalidItemStateException;
+import javax.jcr.ItemExistsException;
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.RepositoryException;
+
 import org.apache.commons.collections.iterators.IteratorChain;
 import org.apache.jackrabbit.commons.iterator.RangeIteratorAdapter;
 import org.apache.jackrabbit.jcr2spi.operation.AddNode;
@@ -47,21 +63,6 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.jcr.InvalidItemStateException;
-import javax.jcr.ItemExistsException;
-import javax.jcr.ItemNotFoundException;
-import javax.jcr.PathNotFoundException;
-import javax.jcr.RepositoryException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
 /**
  * <code>NodeEntryImpl</code> implements common functionality for child
  * node entry implementations.
@@ -114,6 +115,12 @@
     private RevertInfo revertInfo;
 
     /**
+     * Information regarding the invalidation status of the underyling {@link ItemState}
+     * of this entry. The semantics depend on the {@link EntryFactory.InvalidationStrategy}.
+     */
+    private long invalidationStatus;
+
+    /**
      * Creates a new <code>NodeEntryImpl</code>
      *
      * @param parent    the <code>NodeEntry</code> that owns this child item
@@ -164,31 +171,18 @@
         return true;
     }
 
-    /**
-     * @inheritDoc
-     * @see HierarchyEntry#invalidate(boolean)
-     */
     public void invalidate(boolean recursive) {
-        if (recursive) {
-            // invalidate all child entries including properties present in the
-            // attic (removed props shadowed by a new property with the same name).
-            for (Iterator it = getAllChildEntries(true); it.hasNext();) {
-                HierarchyEntry ce = (HierarchyEntry) it.next();
-                ce.invalidate(recursive);
-            }
-        }
-        // invalidate 'childNodeEntries'
-        if (getStatus() != Status.NEW) {
-            childNodeEntries.invalidate();
-        }
-        // ... and invalidate the resolved state (if available)
-        super.invalidate(recursive);
+        getInvalidationStrategy().invalidate(this, recursive);
+    }
+
+    public void calculateStatus() {
+        getInvalidationStrategy().applyPending(this);
     }
 
     /**
      * If 'recursive' is true, the complete hierarchy below this entry is
      * traversed and reloaded. Otherwise only this entry and the direct
-     * decendants are reloaded.
+     * descendants are reloaded.
      *
      * @see HierarchyEntry#reload(boolean)
      */
@@ -199,7 +193,7 @@
         // reload all children unless 'recursive' is false and the reload above
         // did not cause this entry to be removed -> therefore check status.
         if (recursive && !Status.isTerminal(getStatus())) {
-            // recursivly reload all entries including props that are in the attic.
+            // recursively reload all entries including props that are in the attic.
             for (Iterator it = getAllChildEntries(true); it.hasNext();) {
                 HierarchyEntry ce = (HierarchyEntry) it.next();
                 ce.reload(recursive);
@@ -300,11 +294,11 @@
      * @see NodeEntry#getId()
      */
     public NodeId getId() throws InvalidItemStateException, RepositoryException {
-        IdFactory idFactory = factory.getIdFactory();
+        IdFactory idFactory = getIdFactory();
         if (uniqueID != null) {
             return idFactory.createNodeId(uniqueID);
         } else {
-            PathFactory pf = factory.getPathFactory();
+            PathFactory pf = getPathFactory();
             if (parent == null) {
                 // root node
                 return idFactory.createNodeId((String) null, pf.getRootPath());
@@ -319,14 +313,15 @@
      * @see NodeEntry#getWorkspaceId()
      */
     public NodeId getWorkspaceId() throws InvalidItemStateException, RepositoryException
{
-        IdFactory idFactory = factory.getIdFactory();
         if (uniqueID != null || parent == null) {
             // uniqueID and root-node -> internal id is always the same as getId().
             return getId();
         } else {
-            PathFactory pf = factory.getPathFactory();
-            NodeId parentId = (revertInfo != null) ? revertInfo.oldParent.getWorkspaceId()
: parent.getWorkspaceId();
-            return idFactory.createNodeId(parentId, pf.create(getName(true), getIndex(true)));
+            NodeId parentId = (revertInfo != null)
+                ? revertInfo.oldParent.getWorkspaceId()
+                : parent.getWorkspaceId();
+            return getIdFactory().createNodeId(parentId,
+                    getPathFactory().create(getName(true), getIndex(true)));
         }
     }
 
@@ -375,7 +370,7 @@
         NodeEntryImpl entry = this;
         Path.Element[] elems = path.getElements();
         for (int i = 0; i < elems.length; i++) {
-            Path.Element elem = (Path.Element) elems[i];
+            Path.Element elem = elems[i];
             // check for root element
             if (elem.denotesRoot()) {
                 if (entry.getParent() != null) {
@@ -421,7 +416,7 @@
                 * 2) if the NameElement does not have SNS-index => try Property
                 * 3) else throw
                 */
-                PathBuilder pb = new PathBuilder(factory.getPathFactory());
+                PathBuilder pb = new PathBuilder(getPathFactory());
                 for (int j = i; j < elems.length; j++) {
                     pb.addLast(elems[j]);
                 }
@@ -450,7 +445,7 @@
         Path.Element[] elems = path.getElements();
         int i = 0;
         for (; i < elems.length-1; i++) {
-            Path.Element elem = (Path.Element) elems[i];
+            Path.Element elem = elems[i];
             if (elems[i].denotesRoot()) {
                 if (entry.getParent() != null) {
                     throw new RepositoryException("NodeEntry out of 'hierarchy' " + path.toString());
@@ -499,13 +494,13 @@
             * PropertyState (including building the itermediate entries. If that
             * fails ItemNotFoundException is thrown.
             */
-            PathBuilder pb = new PathBuilder(factory.getPathFactory());
+            PathBuilder pb = new PathBuilder(getPathFactory());
             for (int j = i; j < elems.length; j++) {
                 pb.addLast(elems[j]);
             }
             Path remainingPath = pb.getPath();
 
-            IdFactory idFactory = factory.getIdFactory();
+            IdFactory idFactory = getIdFactory();
             NodeId parentId = entry.getWorkspaceId();
             parentId = (remainingPath.getLength() == 1) ? parentId : idFactory.createNodeId(parentId,
remainingPath.getAncestor(1));
             PropertyId propId = idFactory.createPropertyId(parentId, remainingPath.getNameElement().getName());
@@ -606,8 +601,9 @@
         if (cne == null && loadIfNotFound
                 && !containsAtticChild(entries, nodeName, index)
                 && !childNodeEntries.isComplete()) {
-            PathFactory pf = factory.getPathFactory();
-            NodeId cId = factory.getIdFactory().createNodeId(getWorkspaceId(), pf.create(nodeName,
index));
+
+            NodeId cId = getIdFactory().createNodeId(getWorkspaceId(),
+                    getPathFactory().create(nodeName, index));
             cne = loadNodeEntry(cId);
         }
         return cne;
@@ -687,7 +683,7 @@
     public NodeEntry addNewNodeEntry(Name nodeName, String uniqueID,
                                      Name primaryNodeType, QNodeDefinition definition) throws
RepositoryException {
         NodeEntry entry = internalAddNodeEntry(nodeName, uniqueID, Path.INDEX_UNDEFINED);
-        NodeState state = factory.getItemStateFactory().createNewNodeState(entry, primaryNodeType,
definition);
+        NodeState state = getItemStateFactory().createNewNodeState(entry, primaryNodeType,
definition);
         entry.setItemState(state);
         return entry;
     }
@@ -826,9 +822,8 @@
             }
         }
 
-        // add the property entry
         PropertyEntry entry = factory.createPropertyEntry(this, propName);
-        PropertyState state = factory.getItemStateFactory().createNewPropertyState(entry,
definition, values, propertyType);
+        PropertyState state = getItemStateFactory().createNewPropertyState(entry, definition,
values, propertyType);
         entry.setItemState(state);
 
         // add the property entry if creating the new state was successful
@@ -984,14 +979,14 @@
      * Returns a <code>NodeState</code>.
      */
     ItemState doResolve() throws ItemNotFoundException, RepositoryException {
-        return factory.getItemStateFactory().createNodeState(getWorkspaceId(), this);
+        return getItemStateFactory().createNodeState(getWorkspaceId(), this);
     }
 
     /**
      * @see HierarchyEntryImpl#buildPath(boolean)
      */
     Path buildPath(boolean wspPath) throws RepositoryException {
-        PathFactory pf = factory.getPathFactory();
+        PathFactory pf = getPathFactory();
         // shortcut for root state
         if (parent == null) {
             return pf.getRootPath();
@@ -1084,6 +1079,27 @@
         }
     }
 
+    private EntryFactory.InvalidationStrategy getInvalidationStrategy() {
+        return factory.getInvalidationStrategy();
+    }
+
+    /**
+     * Invalidates the underlying {@link ItemState}. If <code>recursive</code>
is
+     * true also invalidates the underlying item states of all child entries.
+     * @param recursive
+     */
+    private void invalidateInternal(boolean recursive) {
+        if (recursive) {
+            // invalidate all child entries including properties present in the
+            // attic (removed props shadowed by a new property with the same name).
+            for (Iterator it = getAllChildEntries(true); it.hasNext();) {
+                HierarchyEntry ce = (HierarchyEntry) it.next();
+                ce.invalidate(true);
+            }
+        }
+        super.invalidate(true);
+    }
+
     /**
      * @param oldName
      * @param oldIndex
@@ -1143,7 +1159,7 @@
      */
     private NodeEntry loadNodeEntry(NodeId childId) throws RepositoryException {
         try {
-            NodeState state = factory.getItemStateFactory().createDeepNodeState(childId,
this);
+            NodeState state = getItemStateFactory().createDeepNodeState(childId, this);
             return state.getNodeEntry();
         } catch (ItemNotFoundException e) {
             return null;
@@ -1159,7 +1175,7 @@
      */
     private PropertyEntry loadPropertyEntry(PropertyId childId) throws RepositoryException
{
         try {
-            PropertyState state = factory.getItemStateFactory().createDeepPropertyState(childId,
this);
+            PropertyState state = getItemStateFactory().createDeepPropertyState(childId,
this);
             return (PropertyEntry) state.getHierarchyEntry();
         } catch (ItemNotFoundException e) {
             return null;
@@ -1479,6 +1495,7 @@
                     } // else: propEntry has never been moved to the attic (see 'addPropertyEntry')
                 }
                 rmEntry.revert();
+                break;
             default: // ignore
         }
 
@@ -1620,4 +1637,134 @@
             revertInfo = null;
         }
     }
+
+    // ----------------------------------------------< InvalidationStrategy >---
+    /**
+     * An implementation of <code>InvalidationStrategy</code> which lazily invalidates
+     * the underlying {@link ItemState}s.
+     */
+    static class LazyInvalidation implements EntryFactory.InvalidationStrategy {
+
+        /**
+         * Marker for entries with a pending recursive invalidation.
+         */
+        private static long INVALIDATION_PENDING = -1;
+
+        /**
+         * Time stamp of the last time a recursive invalidation occurred.
+         */
+        private long lastInvalidation;
+
+        /**
+         * A recursive invalidation is being processed if <code>true</code>.
+         * This flag is for preventing re-entrance.
+         */
+        private boolean invalidating;
+
+        /**
+         * Actual time stamp
+         */
+        private long timeStamp;
+
+        /**
+         * @return  time stamp used to mark entries
+         */
+        private long getTimeStamp() {
+            return timeStamp++;
+        }
+
+        /**
+         * Records a pending recursive {@link ItemState#invalidate() invalidation} for
+         * <code>entry</code> if <code>recursive</code> is <code>true</code>.
Otherwise
+         * invalidates the entry right away.
+         * {@inheritDoc}
+         */
+        public void invalidate(NodeEntry entry, boolean recursive) {
+            if (recursive) {
+                ((NodeEntryImpl)entry).invalidationStatus = INVALIDATION_PENDING;
+                if (!invalidating) {
+                    lastInvalidation = getTimeStamp();
+                }
+            } else {
+                ((NodeEntryImpl)entry).invalidateInternal(false);
+            }
+        }
+
+        /**
+         * Checks whether <code>entry</code> itself has a invalidation pending.
+         * If so, the <code>entry</code> is invalidated. Otherwise check
+         * whether an invalidation occurred after the entry has last been
+         * invalidated. If so, search the path to the root for an originator of
+         * the pending invalidation.
+         * If such an originator is found, invalidate each entry on the path.
+         * Otherwise this method does nothing.
+         * {@inheritDoc}
+         */
+        public void applyPending(NodeEntry entry) {
+            if (!invalidating) {
+                invalidating = true;
+                try {
+                    NodeEntryImpl ne = (NodeEntryImpl) entry;
+                    if (ne.invalidationStatus == INVALIDATION_PENDING) {
+                        ne.invalidateInternal(true);
+                        ne.invalidationStatus = getTimeStamp();
+                    } else if (ne.invalidationStatus <= lastInvalidation) {
+                        resolvePendingInvalidation(ne);
+                    }
+                } finally {
+                    invalidating = false;
+                }
+            }
+        }
+
+        /**
+         * Search the path to the root for an originator of a pending invalidation of
+         * this <code>entry</code>. If such an originator is found, invalidate
each
+         * entry on the path. Otherwise do nothing.
+         *
+         * @param entry
+         */
+        private void resolvePendingInvalidation(NodeEntryImpl entry) {
+            if (entry != null) {
+
+                // First recursively travel up to the first parent node
+                // which has invalidation pending or to the root node if
+                // no such node exists.
+                if (entry.invalidationStatus != INVALIDATION_PENDING) {
+                    resolvePendingInvalidation(entry.parent);
+                }
+
+                // Then travel the path backwards invalidating as required
+                if (entry.invalidationStatus == INVALIDATION_PENDING) {
+                    entry.invalidateInternal(true);
+                }
+                entry.invalidationStatus = getTimeStamp();
+            }
+        }
+    }
+
+    /**
+     * An implementation of <code>InvalidationStrategy</code> which eagerly invalidates
+     * the underlying {@link ItemState}s.
+     */
+    static class EagerInvalidation implements EntryFactory.InvalidationStrategy {
+
+        /**
+         * Calls {@link ItemState#invalidate()} for the underlying item state of this
+         * <code>entry</code> and - if <code>recursive</code> is
<code>true</code> -
+         * recursively for all item states of all child entries
+         * {@inheritDoc}
+         */
+        public void invalidate(NodeEntry entry, boolean recursive) {
+            ((NodeEntryImpl) entry).invalidateInternal(recursive);
+        }
+
+        /**
+         * Does nothing since invalidation has occurred already.
+         * {@inheritDoc}
+         */
+        public void applyPending(NodeEntry entry) {
+            // Empty
+        }
+    }
 }

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntryImpl.java?rev=771280&r1=771279&r2=771280&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntryImpl.java
(original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/hierarchy/PropertyEntryImpl.java
Mon May  4 12:06:58 2009
@@ -16,18 +16,18 @@
  */
 package org.apache.jackrabbit.jcr2spi.hierarchy;
 
+import javax.jcr.InvalidItemStateException;
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.jcr2spi.operation.Operation;
+import org.apache.jackrabbit.jcr2spi.operation.SetPropertyValue;
+import org.apache.jackrabbit.jcr2spi.state.ItemState;
+import org.apache.jackrabbit.jcr2spi.state.PropertyState;
+import org.apache.jackrabbit.jcr2spi.state.Status;
 import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.Path;
 import org.apache.jackrabbit.spi.PropertyId;
-import org.apache.jackrabbit.jcr2spi.state.PropertyState;
-import org.apache.jackrabbit.jcr2spi.state.ItemState;
-import org.apache.jackrabbit.jcr2spi.state.Status;
-import org.apache.jackrabbit.jcr2spi.operation.Operation;
-import org.apache.jackrabbit.jcr2spi.operation.SetPropertyValue;
-
-import javax.jcr.RepositoryException;
-import javax.jcr.ItemNotFoundException;
-import javax.jcr.InvalidItemStateException;
 
 /**
  * <code>PropertyEntryImpl</code> implements a reference to a property state.
@@ -66,7 +66,7 @@
      * Returns a <code>PropertyState</code>.
      */
     ItemState doResolve() throws ItemNotFoundException, RepositoryException {
-        return factory.getItemStateFactory().createPropertyState(getWorkspaceId(), this);
+        return getItemStateFactory().createPropertyState(getWorkspaceId(), this);
     }
 
     /**
@@ -74,7 +74,7 @@
      */
     Path buildPath(boolean workspacePath) throws RepositoryException {
         Path parentPath = parent.buildPath(workspacePath);
-        return factory.getPathFactory().create(parentPath, getName(), true);
+        return getPathFactory().create(parentPath, getName(), true);
     }
 
     //------------------------------------------------------< PropertyEntry >---
@@ -82,14 +82,14 @@
      * @see PropertyEntry#getId()
      */
     public PropertyId getId() throws InvalidItemStateException, RepositoryException {
-        return factory.getIdFactory().createPropertyId(parent.getId(), getName());
+        return getIdFactory().createPropertyId(parent.getId(), getName());
     }
 
     /**
      * @see PropertyEntry#getWorkspaceId()
      */
     public PropertyId getWorkspaceId() throws InvalidItemStateException, RepositoryException
{
-        return factory.getIdFactory().createPropertyId(parent.getWorkspaceId(), getName());
+        return getIdFactory().createPropertyId(parent.getWorkspaceId(), getName());
     }
 
     /**
@@ -136,4 +136,8 @@
                 // ignore
         }
     }
+
+    public void calculateStatus() {
+        parent.calculateStatus();
+    }
 }

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemState.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemState.java?rev=771280&r1=771279&r2=771280&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemState.java
(original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemState.java
Mon May  4 12:06:58 2009
@@ -215,6 +215,9 @@
      * @return the status of this item.
      */
     public final int getStatus() {
+        // Call calculateStatus to apply a possible pending invalidation
+        // in the entry hierarchy.
+        getHierarchyEntry().calculateStatus();
         return status;
     }
 
@@ -309,6 +312,18 @@
     }
 
     /**
+     * Invalidates this state: set its {@link Status} to {@link Status#INVALIDATED}
+     * if the current status is {@link Status#EXISTING}. Does nothing otherwise.
+     */
+    public void invalidate() {
+        if (status == Status.EXISTING) {
+            setStatus(Status.INVALIDATED);
+        } else {
+            log.debug("Skip invalidation for item {} with status {}", getName(), Status.getName(status));
+        }
+    }
+
+    /**
      * Marks this item state as modified.
      */
     void markModified() throws InvalidItemStateException {

Modified: jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/GetPropertyTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/GetPropertyTest.java?rev=771280&r1=771279&r2=771280&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/GetPropertyTest.java
(original)
+++ jackrabbit/trunk/jackrabbit-jcr2spi/src/test/java/org/apache/jackrabbit/jcr2spi/GetPropertyTest.java
Mon May  4 12:06:58 2009
@@ -16,19 +16,20 @@
  */
 package org.apache.jackrabbit.jcr2spi;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.apache.jackrabbit.test.AbstractJCRTest;
-import org.apache.jackrabbit.test.NotExecutableException;
-import org.apache.jackrabbit.util.Text;
-
+import javax.jcr.InvalidItemStateException;
+import javax.jcr.Item;
 import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
 import javax.jcr.Property;
-import javax.jcr.Session;
-import javax.jcr.RepositoryException;
 import javax.jcr.PropertyIterator;
-import javax.jcr.InvalidItemStateException;
-import javax.jcr.PathNotFoundException;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.jackrabbit.test.AbstractJCRTest;
+import org.apache.jackrabbit.test.NotExecutableException;
+import org.apache.jackrabbit.util.Text;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /** <code>GetPropertyTest</code>... */
 public class GetPropertyTest extends AbstractJCRTest {
@@ -122,6 +123,61 @@
         assertTrue(readOnly.itemExists(p3.getPath()));
     }
 
+    public void testGetExternallyChangedNode() throws RepositoryException {
+        // Access node1 through session 1
+        Node node1 = (Node) readOnly.getItem(node1Path);
+
+        // Add node and property through session 2
+        Node n2 = testRootNode.getNode(nodeName1).addNode(nodeName2);
+        Property p3 = n2.setProperty(propertyName1, "test");
+        testRootNode.save();
+
+        // Assert added nodes are visible in session 1 after refresh
+        node1.refresh(false);
+        assertTrue(readOnly.itemExists(n2.getPath()));
+        assertTrue(readOnly.itemExists(p3.getPath()));
+
+        Item m2 = readOnly.getItem(n2.getPath());
+        assertTrue(m2.isNode());
+        assertTrue(((Node) m2).hasProperty(propertyName1));
+
+        // Remove property through session 2
+        p3.remove();
+        testRootNode.save();
+
+        // Assert removal is visible through session 1
+        node1.refresh(false);
+        assertFalse(((Node) m2).hasProperty(propertyName1));
+    }
+
+    public void testGetExternallyChangedProperty() throws RepositoryException {
+        // Access node1 through session 1
+        Node node1 = (Node) readOnly.getItem(node1Path);
+
+        // Add node and property through session 2
+        Node n2 = testRootNode.getNode(nodeName1).addNode(nodeName2);
+        Property p3 = n2.setProperty(propertyName1, "test");
+        p3.setValue("v3");
+        testRootNode.save();
+
+        // Assert added nodes are visible in session 1 after refresh
+        node1.refresh(false);
+        assertTrue(readOnly.itemExists(n2.getPath()));
+        assertTrue(readOnly.itemExists(p3.getPath()));
+
+        Item q3 = readOnly.getItem(p3.getPath());
+        assertFalse(q3.isNode());
+        assertTrue("v3".equals(((Property) q3).getString()));
+
+        // Change property value through session 2
+        p3.setValue("v3_modified");
+        testRootNode.save();
+
+        // Assert modification is visible through session 1
+        node1.refresh(false);
+        assertTrue("v3_modified".equals(((Property) q3).getString()));
+    }
+
     public void testGetDeepSNSProperties() throws RepositoryException, NotExecutableException
{
         Node n = testRootNode.getNode(nodeName1);
         if (!n.getDefinition().allowsSameNameSiblings()) {
@@ -212,4 +268,4 @@
             rw.logout();
         }
     }
-}
\ No newline at end of file
+}



Mime
View raw message