You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by ju...@apache.org on 2008/04/22 02:56:05 UTC

svn commit: r650336 - in /jackrabbit/branches/1.4/jackrabbit-core/src: main/java/org/apache/jackrabbit/core/state/XAItemStateManager.java test/java/org/apache/jackrabbit/core/XATest.java

Author: jukka
Date: Mon Apr 21 17:55:58 2008
New Revision: 650336

URL: http://svn.apache.org/viewvc?rev=650336&view=rev
Log:
1.4: Merged revisions 638363 and 638374 (JCR-1484)

Modified:
    jackrabbit/branches/1.4/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/XAItemStateManager.java
    jackrabbit/branches/1.4/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/XATest.java

Modified: jackrabbit/branches/1.4/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/XAItemStateManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.4/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/XAItemStateManager.java?rev=650336&r1=650335&r2=650336&view=diff
==============================================================================
--- jackrabbit/branches/1.4/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/XAItemStateManager.java (original)
+++ jackrabbit/branches/1.4/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/XAItemStateManager.java Mon Apr 21 17:55:58 2008
@@ -25,6 +25,8 @@
 import org.apache.jackrabbit.core.value.InternalValue;
 import org.apache.jackrabbit.core.virtual.VirtualItemStateProvider;
 import org.apache.jackrabbit.uuid.UUID;
+import org.apache.commons.collections.iterators.FilterIterator;
+import org.apache.commons.collections.Predicate;
 
 import javax.jcr.ReferentialIntegrityException;
 import javax.jcr.PropertyType;
@@ -266,14 +268,7 @@
         if (virtualProvider != null && virtualProvider.hasNodeReferences(id)) {
             return virtualProvider.getNodeReferences(id);
         }
-        ChangeLog changeLog = getChangeLog();
-        if (changeLog != null) {
-            NodeReferences refs = changeLog.get(id);
-            if (refs != null) {
-                return refs;
-            }
-        }
-        return super.getNodeReferences(id);
+        return getReferences(id);
     }
 
     /**
@@ -288,13 +283,11 @@
         if (virtualProvider != null && virtualProvider.hasNodeReferences(id)) {
             return true;
         }
-        ChangeLog changeLog = getChangeLog();
-        if (changeLog != null) {
-            if (changeLog.get(id) != null) {
-                return true;
-            }
+        try {
+            return getReferences(id).hasReferences();
+        } catch (ItemStateException e) {
+            return false;
         }
-        return super.hasNodeReferences(id);
     }
 
     /**
@@ -315,6 +308,108 @@
     }
 
     //-------------------------------------------------------< implementation >
+
+    /**
+     * Returns the node references for the given <code>id</code>.
+     *
+     * @param id the node references id.
+     * @return the node references for the given <code>id</code>.
+     * @throws ItemStateException if an error occurs while reading from the
+     *                            underlying shared item state manager.
+     */
+    private NodeReferences getReferences(NodeReferencesId id)
+            throws ItemStateException {
+        NodeReferences refs;
+        try {
+            refs = super.getNodeReferences(id);
+        } catch (NoSuchItemStateException e) {
+            refs = new NodeReferences(id);
+        }
+        // apply changes from change log
+        ChangeLog changes = getChangeLog();
+        if (changes != null) {
+            UUID uuid = id.getTargetId().getUUID();
+            // check removed reference properties
+            for (Iterator it = filterReferenceProperties(changes.deletedStates());
+                 it.hasNext(); ) {
+                PropertyState prop = (PropertyState) it.next();
+                InternalValue[] values = prop.getValues();
+                for (int i = 0; i < values.length; i++) {
+                    if (values[i].getUUID().equals(uuid)) {
+                        refs.removeReference(prop.getPropertyId());
+                        break;
+                    }
+                }
+            }
+            // check added reference properties
+            for (Iterator it = filterReferenceProperties(changes.addedStates());
+                 it.hasNext(); ) {
+                PropertyState prop = (PropertyState) it.next();
+                InternalValue[] values = prop.getValues();
+                for (int i = 0; i < values.length; i++) {
+                    if (values[i].getUUID().equals(uuid)) {
+                        refs.addReference(prop.getPropertyId());
+                        break;
+                    }
+                }
+            }
+            // check modified properties
+            for (Iterator it = changes.modifiedStates(); it.hasNext(); ) {
+                ItemState state = (ItemState) it.next();
+                if (state.isNode()) {
+                    continue;
+                }
+                try {
+                    PropertyState old = (PropertyState) sharedStateMgr.getItemState(state.getId());
+                    if (old.getType() == PropertyType.REFERENCE) {
+                        // remove if one of the old values references the node
+                        InternalValue[] values = old.getValues();
+                        for (int i = 0; i < values.length; i++) {
+                            if (values[i].getUUID().equals(uuid)) {
+                                refs.removeReference(old.getPropertyId());
+                                break;
+                            }
+                        }
+                    }
+                } catch (NoSuchItemStateException e) {
+                    // property is stale
+                }
+
+                PropertyState prop = (PropertyState) state;
+                if (prop.getType() == PropertyType.REFERENCE) {
+                    // add if modified value references node
+                    InternalValue[] values = prop.getValues();
+                    for (int i = 0; i < values.length; i++) {
+                        if (values[i].getUUID().equals(uuid)) {
+                            refs.addReference(prop.getPropertyId());
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+        return refs;
+    }
+
+    /**
+     * Takes an iterator over {@link ItemState}s and returns a new iterator that
+     * filters out all but REFERENCE {@link PropertyState}s.
+     *
+     * @param itemStates item state source iterator.
+     * @return iterator over reference property states.
+     */
+    private Iterator filterReferenceProperties(Iterator itemStates) {
+        return new FilterIterator(itemStates, new Predicate() {
+            public boolean evaluate(Object object) {
+                ItemState state = (ItemState) object;
+                if (!state.isNode()) {
+                    PropertyState prop = (PropertyState) state;
+                    return prop.getType() == PropertyType.REFERENCE;
+                }
+                return false;
+            }
+        });
+    }
 
     /**
      * Determine all node references whose targets only exist in the view of

Modified: jackrabbit/branches/1.4/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/XATest.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/1.4/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/XATest.java?rev=650336&r1=650335&r2=650336&view=diff
==============================================================================
--- jackrabbit/branches/1.4/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/XATest.java (original)
+++ jackrabbit/branches/1.4/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/XATest.java Mon Apr 21 17:55:58 2008
@@ -589,6 +589,154 @@
         otherSuperuser.logout();
     }
 
+    /**
+     * Checks if getReferences() reflects an added reference property that has
+     * been saved but not yet committed.
+     * <p/>
+     * Spec say:
+     * <p/>
+     * <i>Some level 2 implementations may only return properties that have been
+     * saved (in a transactional setting this includes both those properties
+     * that have been saved but not yet committed, as well as properties that
+     * have been committed). Other level 2 implementations may additionally
+     * return properties that have been added within the current Session but are
+     * not yet saved.</i>
+     * <p/>
+     * Jackrabbit does not support the latter, but at least has to support the
+     * first.
+     */
+    public void testGetReferencesAddedRef() throws Exception {
+        // create one referenceable node
+        Node target = testRootNode.addNode(nodeName1);
+        target.addMixin(mixReferenceable);
+        // second node, which will later reference the target node
+        Node n = testRootNode.addNode(nodeName2);
+        testRootNode.save();
+
+        UserTransactionImpl tx = new UserTransactionImpl(superuser);
+        tx.begin();
+        try {
+            // create reference
+            n.setProperty(propertyName1, target);
+            testRootNode.save();
+            assertTrue("Node.getReferences() must reflect references that have " +
+                    "been saved but not yet committed", target.getReferences().hasNext());
+        } finally {
+            tx.rollback();
+        }
+    }
+
+    /**
+     * Checks if getReferences() reflects a removed reference property that has
+     * been saved but not yet committed.
+     */
+    public void testGetReferencesRemovedRef() throws Exception {
+        // create one referenceable node
+        Node target = testRootNode.addNode(nodeName1);
+        target.addMixin(mixReferenceable);
+        // second node, which reference the target node
+        Node n = testRootNode.addNode(nodeName2);
+        // create reference
+        n.setProperty(propertyName1, target);
+        testRootNode.save();
+
+        UserTransactionImpl tx = new UserTransactionImpl(superuser);
+        tx.begin();
+        try {
+            n.getProperty(propertyName1).remove();
+            testRootNode.save();
+            assertTrue("Node.getReferences() must reflect references that have " +
+                    "been saved but not yet committed", !target.getReferences().hasNext());
+        } finally {
+            tx.rollback();
+        }
+    }
+
+    /**
+     * Checks if getReferences() reflects a modified reference property that has
+     * been saved but not yet committed.
+     */
+    public void testGetReferencesModifiedRef() throws Exception {
+        // create two referenceable node
+        Node target1 = testRootNode.addNode(nodeName1);
+        target1.addMixin(mixReferenceable);
+        // second node, which reference the target1 node
+        Node target2 = testRootNode.addNode(nodeName2);
+        target2.addMixin(mixReferenceable);
+        Node n = testRootNode.addNode(nodeName3);
+        // create reference
+        n.setProperty(propertyName1, target1);
+        testRootNode.save();
+
+        UserTransactionImpl tx = new UserTransactionImpl(superuser);
+        tx.begin();
+        try {
+            // change reference
+            n.setProperty(propertyName1, target2);
+            testRootNode.save();
+            assertTrue("Node.getReferences() must reflect references that have " +
+                    "been saved but not yet committed", !target1.getReferences().hasNext());
+            assertTrue("Node.getReferences() must reflect references that have " +
+                    "been saved but not yet committed", target2.getReferences().hasNext());
+        } finally {
+            tx.rollback();
+        }
+    }
+
+    /**
+     * Checks if getReferences() reflects a modified reference property that has
+     * been saved but not yet committed. The old value is a reference, while
+     * the new value is not.
+     */
+    public void testGetReferencesModifiedRefOldValueReferenceable() throws Exception {
+        // create one referenceable node
+        Node target = testRootNode.addNode(nodeName1);
+        target.addMixin(mixReferenceable);
+        Node n = testRootNode.addNode(nodeName2);
+        // create reference
+        n.setProperty(propertyName1, target);
+        testRootNode.save();
+
+        UserTransactionImpl tx = new UserTransactionImpl(superuser);
+        tx.begin();
+        try {
+            // change reference to a string value
+            n.setProperty(propertyName1, "foo");
+            testRootNode.save();
+            assertTrue("Node.getReferences() must reflect references that have " +
+                    "been saved but not yet committed", !target.getReferences().hasNext());
+        } finally {
+            tx.rollback();
+        }
+    }
+
+    /**
+     * Checks if getReferences() reflects a modified reference property that has
+     * been saved but not yet committed. The new value is a reference, while
+     * the old value wasn't.
+     */
+    public void testGetReferencesModifiedRefNewValueReferenceable() throws Exception {
+        // create one referenceable node
+        Node target = testRootNode.addNode(nodeName1);
+        target.addMixin(mixReferenceable);
+        Node n = testRootNode.addNode(nodeName2);
+        // create string property
+        n.setProperty(propertyName1, "foo");
+        testRootNode.save();
+
+        UserTransactionImpl tx = new UserTransactionImpl(superuser);
+        tx.begin();
+        try {
+            // change string into a reference
+            n.setProperty(propertyName1, target);
+            testRootNode.save();
+            assertTrue("Node.getReferences() must reflect references that have " +
+                    "been saved but not yet committed", target.getReferences().hasNext());
+        } finally {
+            tx.rollback();
+        }
+    }
+
     //--------------------------------------------------------------< locking >
 
     /**