Index: C:/dev/jackrabbit/src/test/java/org/apache/jackrabbit/test/api/ReferencesTest.java =================================================================== --- C:/dev/jackrabbit/src/test/java/org/apache/jackrabbit/test/api/ReferencesTest.java (revision 481177) +++ C:/dev/jackrabbit/src/test/java/org/apache/jackrabbit/test/api/ReferencesTest.java (working copy) @@ -163,4 +163,34 @@ fail("too many referers: " + iter.nextProperty().getPath()); } } + + /** + * Tests getNodeReferences before saving + */ + public void testGetReferences() throws RepositoryException { + Node n1 = testRootNode.addNode(nodeName1, testNodeType); + n1.addMixin(mixReferenceable); + Node n2 = testRootNode.addNode(nodeName2, testNodeType); + n2.addMixin(mixReferenceable); + // with some impls. the mixin type has only affect upon save + testRootNode.save(); + // create references: n3.p1 -> n1 + Node n3 = testRootNode.addNode(nodeName3, testNodeType); + n3.getSession().save(); + n3.setProperty(propertyName1, n1); + n3.save(); + PropertyIterator it = n1.getReferences(); + boolean found = false; + while (it.hasNext()) { + Property p = it.nextProperty(); + if (p.getName().equals(propertyName1)) { + found = true; + } + } + assertTrue("Unsaved property found in getReferences", found); + it = n2.getReferences(); + assertFalse(it.hasNext()); + n3.getSession().save(); + } + } Index: C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/persistence/AbstractPersistenceManager.java =================================================================== --- C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/persistence/AbstractPersistenceManager.java (revision 481177) +++ C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/persistence/AbstractPersistenceManager.java (working copy) @@ -21,7 +21,10 @@ import org.apache.jackrabbit.core.state.ChangeLog; import org.apache.jackrabbit.core.state.ItemState; import org.apache.jackrabbit.core.state.ItemStateException; +import org.apache.jackrabbit.core.state.NoSuchItemStateException; import org.apache.jackrabbit.core.state.NodeReferences; +import org.apache.jackrabbit.core.state.NodeReferencesChanges; +import org.apache.jackrabbit.core.state.NodeReferencesId; import org.apache.jackrabbit.core.state.NodeState; import org.apache.jackrabbit.core.state.PropertyState; @@ -86,7 +89,22 @@ } iter = changeLog.modifiedRefs(); while (iter.hasNext()) { - NodeReferences refs = (NodeReferences) iter.next(); + NodeReferencesChanges changes = (NodeReferencesChanges) iter.next(); + NodeReferencesId id = changes.getId(); + // load the list of references + // TODO the persistence manager should be able to merge changes itself + NodeReferences refs; + try { + refs = load(id); + } catch (NoSuchItemStateException e) { + // actually, it would be better if there is a method 'loadIfExists' + // that returns null if it does not exist + // calling a has... method first would maybe be another network + // roundtrip + refs = new NodeReferences(id); + } + // merge the changes + refs = refs.merge(changes); if (refs.hasReferences()) { store(refs); } else { Index: C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/virtual/VirtualItemStateProvider.java =================================================================== --- C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/virtual/VirtualItemStateProvider.java (revision 481177) +++ C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/virtual/VirtualItemStateProvider.java (working copy) @@ -18,9 +18,10 @@ import org.apache.jackrabbit.core.ItemId; import org.apache.jackrabbit.core.NodeId; +import org.apache.jackrabbit.core.PropertyId; import org.apache.jackrabbit.core.state.ItemStateManager; -import org.apache.jackrabbit.core.state.NodeReferences; import org.apache.jackrabbit.core.state.ItemStateListener; +import org.apache.jackrabbit.core.state.NodeReferencesId; import org.apache.jackrabbit.name.QName; import javax.jcr.RepositoryException; @@ -75,14 +76,20 @@ throws RepositoryException; /** - * Informs this provider that the node references to one of its states has - * changed. + * Informs this provider that a node references has been added. * - * @param refs - * @return true if the reference target is one of its items. + * @param refId + * @param sourceId */ - boolean setNodeReferences(NodeReferences refs); + void addReference(NodeReferencesId refsId, PropertyId sourceId); + /** + * Informs this provider that a node references has been deleted. + * + * @param refId + * @param sourceId + */ + void deleteReference(NodeReferencesId refId, PropertyId sourceId); /** * Add an ItemStateListener Index: C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/LocalItemStateManager.java =================================================================== --- C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/LocalItemStateManager.java (revision 481177) +++ C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/LocalItemStateManager.java (working copy) @@ -185,20 +185,18 @@ */ public NodeReferences getNodeReferences(NodeReferencesId id) throws NoSuchItemStateException, ItemStateException { - - // check change log - NodeReferences refs = changeLog.get(id); - if (refs != null) { - return refs; + NodeReferences references = sharedStateMgr.getNodeReferences(id); + NodeReferencesChanges changes = changeLog.get(id); + if (changes != null) { + references = references.merge(changes); } - return sharedStateMgr.getNodeReferences(id); + return references; } /** * {@inheritDoc} */ public boolean hasNodeReferences(NodeReferencesId id) { - // check change log if (changeLog.get(id) != null) { return true; } Index: C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java =================================================================== --- C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java (revision 481177) +++ C:/dev/jackrabbit/src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java (working copy) @@ -18,6 +18,7 @@ import EDU.oswego.cs.dl.util.concurrent.ReadWriteLock; import EDU.oswego.cs.dl.util.concurrent.ReentrantWriterPreferenceReadWriteLock; + import org.apache.jackrabbit.core.ItemId; import org.apache.jackrabbit.core.NodeId; import org.apache.jackrabbit.core.PropertyId; @@ -304,9 +305,16 @@ */ public NodeReferences getNodeReferences(NodeReferencesId id) throws NoSuchItemStateException, ItemStateException { + NodeReferences result = getNodeReferencesIfExists(id); + if (result == null) { + throw new NoSuchItemStateException(id.toString()); + } + return result; + } + private NodeReferences getNodeReferencesIfExists(NodeReferencesId id) + throws NoSuchItemStateException, ItemStateException { acquireReadLock(); - try { // check persistence manager try { @@ -322,12 +330,10 @@ // ignore } } + return null; } finally { rwLock.readLock().release(); } - - // throw - throw new NoSuchItemStateException(id.toString()); } /** @@ -612,9 +618,9 @@ // filter out virtual node references for later processing // (see comment above) for (Iterator iter = local.modifiedRefs(); iter.hasNext();) { - NodeReferences refs = (NodeReferences) iter.next(); + NodeReferencesChanges refChanges = (NodeReferencesChanges) iter.next(); boolean virtual = false; - NodeId id = refs.getId().getTargetId(); + NodeId id = refChanges.getId().getTargetId(); for (int i = 0; i < virtualProviders.length; i++) { if (virtualProviders[i].hasItemState(id)) { List virtualRefs = virtualNodeReferences[i]; @@ -622,7 +628,7 @@ virtualRefs = new LinkedList(); virtualNodeReferences[i] = virtualRefs; } - virtualRefs.add(refs); + virtualRefs.add(refChanges); virtual = true; break; } @@ -630,7 +636,14 @@ if (!virtual) { // if target of node reference does not lie in a virtual // space, add to modified set of normal provider. - shared.modified(refs); + for (Iterator it = refChanges.getAdded(); it.hasNext();) { + PropertyId propId = (PropertyId) it.next(); + shared.referenceAdded(refChanges.getId(), propId); + } + for (Iterator it = refChanges.getDeleted(); it.hasNext();) { + PropertyId propId = (PropertyId) it.next(); + shared.referenceDeleted(refChanges.getId(), propId); + } } } @@ -695,8 +708,18 @@ List virtualRefs = virtualNodeReferences[i]; if (virtualRefs != null) { for (Iterator iter = virtualRefs.iterator(); iter.hasNext();) { - NodeReferences refs = (NodeReferences) iter.next(); - virtualProviders[i].setNodeReferences(refs); + NodeReferencesChanges refChanges = (NodeReferencesChanges) iter.next(); + NodeReferencesId refId = refChanges.getId(); + Iterator it = refChanges.getAdded(); + while (it.hasNext()) { + PropertyId prop = (PropertyId) it.next(); + virtualProviders[i].addReference(refId, prop); + } + it = refChanges.getDeleted(); + while (it.hasNext()) { + PropertyId prop = (PropertyId) it.next(); + virtualProviders[i].deleteReference(refId, prop); + } } } } @@ -1159,12 +1182,8 @@ && virtualProvider.hasNodeReferences(refsId)) { continue; } - NodeReferences refs = - getOrCreateNodeReferences(refsId, changes); - // add reference - refs.addReference(prop.getPropertyId()); // update change log - changes.modified(refs); + changes.referenceAdded(refsId, prop.getPropertyId()); } } } @@ -1189,16 +1208,8 @@ && virtualProvider.hasNodeReferences(refsId)) { continue; } - // either get node references from change log or load from - // persistence manager - NodeReferences refs = changes.get(refsId); - if (refs == null) { - refs = getNodeReferences(refsId); - } - // remove reference - refs.removeReference(oldProp.getPropertyId()); // update change log - changes.modified(refs); + changes.referenceDeleted(refsId, oldProp.getPropertyId()); } } // check new type @@ -1213,12 +1224,8 @@ && virtualProvider.hasNodeReferences(refsId)) { continue; } - NodeReferences refs = - getOrCreateNodeReferences(refsId, changes); - // add reference - refs.addReference(newProp.getPropertyId()); // update change log - changes.modified(refs); + changes.referenceAdded(refsId, newProp.getPropertyId()); } } } @@ -1240,16 +1247,8 @@ && virtualProvider.hasNodeReferences(refsId)) { continue; } - // either get node references from change log or - // load from persistence manager - NodeReferences refs = changes.get(refsId); - if (refs == null) { - refs = getNodeReferences(refsId); - } - // remove reference - refs.removeReference(prop.getPropertyId()); // update change log - changes.modified(refs); + changes.referenceDeleted(refsId, prop.getPropertyId()); } } } @@ -1257,37 +1256,6 @@ } /** - * Returns a node references object using the following rules:

- *

- * - * @param refsId node references id - * @param changes change log - * @return a node references object - * @throws ItemStateException if an error occurs - */ - private NodeReferences getOrCreateNodeReferences(NodeReferencesId refsId, - ChangeLog changes) - throws ItemStateException { - // check change log - NodeReferences refs = changes.get(refsId); - if (refs == null) { - // not yet in change log: - // either load existing or create new - if (hasNodeReferences(refsId)) { - refs = getNodeReferences(refsId); - } else { - refs = new NodeReferences(refsId); - } - } - return refs; - } - - /** * Verifies that *