You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by tr...@apache.org on 2006/05/01 14:54:55 UTC

svn commit: r398589 - in /jackrabbit/trunk/jackrabbit/src: main/java/org/apache/jackrabbit/core/version/ test/java/org/apache/jackrabbit/core/

Author: tripod
Date: Mon May  1 05:54:53 2006
New Revision: 398589

URL: http://svn.apache.org/viewcvs?rev=398589&view=rev
Log:
JCR-414 jcr:successors property not persisted correctly within a transaction

Modified:
    jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/InternalVersionHistoryImpl.java
    jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/InternalVersionImpl.java
    jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/VersionItemStateProvider.java
    jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java
    jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/XAVersionManager.java
    jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/core/XATest.java

Modified: jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/InternalVersionHistoryImpl.java
URL: http://svn.apache.org/viewcvs/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/InternalVersionHistoryImpl.java?rev=398589&r1=398588&r2=398589&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/InternalVersionHistoryImpl.java (original)
+++ jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/InternalVersionHistoryImpl.java Mon May  1 05:54:53 2006
@@ -133,11 +133,14 @@
             vMgr.versionCreated(v);
         }
 
-        // resolve successors and predecessors
-        Iterator iter = versionCache.values().iterator();
-        while (iter.hasNext()) {
-            InternalVersionImpl v = (InternalVersionImpl) iter.next();
-            v.resolvePredecessors();
+        // check for legacy version nodes that had 'virtual' jcr:successor property
+        if (rootVersion.getSuccessors().length==0 && versionCache.size()>1) {
+            // resolve successors and predecessors
+            Iterator iter = versionCache.values().iterator();
+            while (iter.hasNext()) {
+                InternalVersionImpl v = (InternalVersionImpl) iter.next();
+                v.legacyResolveSuccessors();
+            }
         }
 
         try {
@@ -183,8 +186,7 @@
         InternalVersionImpl v = (InternalVersionImpl) tempVersionCache.remove(child.getNodeId());
         if (v != null) {
             v.clear();
-        }
-        if (v == null) {
+        } else {
             v = new InternalVersionImpl(this, child, child.getName());
         }
         return v;
@@ -323,9 +325,6 @@
             throw new ReferentialIntegrityException("Unable to remove version. At least once referenced.");
         }
 
-        // remove from persistance state
-        node.removeNode(v.getName());
-
         // unregister from labels
         QName[] labels = v.internalGetLabels();
         for (int i = 0; i < labels.length; i++) {
@@ -335,6 +334,9 @@
         // detach from the version graph
         v.internalDetach();
 
+        // remove from persistance state
+        node.removeNode(v.getName());
+
         // and remove from history
         versionCache.remove(v.getId());
         vMgr.versionDestroyed(v);
@@ -430,11 +432,9 @@
         NodeId versionId = new NodeId(UUID.randomUUID());
         NodeStateEx vNode = node.addNode(name, QName.NT_VERSION, versionId, true);
 
-        // initialize 'created' and 'predecessors'
+        // initialize 'created', 'predecessors' and 'successors'
         vNode.setPropertyValue(QName.JCR_CREATED, InternalValue.create(Calendar.getInstance()));
         vNode.setPropertyValues(QName.JCR_PREDECESSORS, PropertyType.REFERENCE, predecessors);
-
-        // initialize 'empty' successors; their values are dynamically resolved
         vNode.setPropertyValues(QName.JCR_SUCCESSORS, PropertyType.REFERENCE, InternalValue.EMPTY_ARRAY);
 
         // checkin source node
@@ -442,11 +442,12 @@
 
         // update version graph
         InternalVersionImpl version = new InternalVersionImpl(this, vNode, name);
-        version.resolvePredecessors();
-        vMgr.versionCreated(version);
+        version.internalAttach();
 
         // and store
         node.store();
+
+        vMgr.versionCreated(version);
 
         // update cache
         versionCache.put(version.getId(), version);

Modified: jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/InternalVersionImpl.java
URL: http://svn.apache.org/viewcvs/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/InternalVersionImpl.java?rev=398589&r1=398588&r2=398589&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/InternalVersionImpl.java (original)
+++ jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/InternalVersionImpl.java Mon May  1 05:54:53 2006
@@ -27,6 +27,7 @@
 import java.util.Arrays;
 import java.util.Calendar;
 import java.util.HashSet;
+import java.util.List;
 
 /**
  * Implements a <code>InternalVersion</code>
@@ -35,16 +36,6 @@
         implements InternalVersion {
 
     /**
-     * the list/cache of predecessors (values == InternalVersion)
-     */
-    private ArrayList predecessors = new ArrayList();
-
-    /**
-     * the list of successors (values == InternalVersion)
-     */
-    private ArrayList successors = new ArrayList();
-
-    /**
      * the underlying persistance node of this version
      */
     private NodeStateEx node;
@@ -143,22 +134,43 @@
      * {@inheritDoc}
      */
     public InternalVersion[] getSuccessors() {
-        return (InternalVersionImpl[]) successors.toArray(new InternalVersionImpl[successors.size()]);
+        InternalValue[] values = node.getPropertyValues(QName.JCR_SUCCESSORS);
+        if (values != null) {
+            InternalVersion[] versions = new InternalVersion[values.length];
+            for (int i = 0; i < values.length; i++) {
+                NodeId vId = new NodeId((UUID) values[i].internalValue());
+                versions[i] = versionHistory.getVersion(vId);
+            }
+            return versions;
+        } else {
+            return new InternalVersion[0];
+        }
     }
 
     /**
      * {@inheritDoc}
      */
     public InternalVersion[] getPredecessors() {
-        return (InternalVersionImpl[]) predecessors.toArray(new InternalVersionImpl[predecessors.size()]);
+        InternalValue[] values = node.getPropertyValues(QName.JCR_PREDECESSORS);
+        if (values != null) {
+            InternalVersion[] versions = new InternalVersion[values.length];
+            for (int i = 0; i < values.length; i++) {
+                NodeId vId = new NodeId((UUID) values[i].internalValue());
+                versions[i] = versionHistory.getVersion(vId);
+            }
+            return versions;
+        } else {
+            return new InternalVersion[0];
+        }
     }
 
     /**
      * {@inheritDoc}
      */
     public boolean isMoreRecent(InternalVersion v) {
-        for (int i = 0; i < predecessors.size(); i++) {
-            InternalVersion pred = (InternalVersion) predecessors.get(i);
+        InternalVersion[] preds = getPredecessors();
+        for (int i = 0; i < preds.length; i++) {
+            InternalVersion pred = preds[i];
             if (pred.equals(v) || pred.isMoreRecent(v)) {
                 return true;
             }
@@ -195,51 +207,28 @@
     }
 
     /**
-     * resolves the predecessors property and indirectly adds it self to their
-     * successor list.
-     */
-    void resolvePredecessors() {
-        InternalValue[] values = node.getPropertyValues(QName.JCR_PREDECESSORS);
-        if (values != null) {
-            for (int i = 0; i < values.length; i++) {
-                NodeId vId = new NodeId((UUID) values[i].internalValue());
-                InternalVersionImpl v = (InternalVersionImpl) versionHistory.getVersion(vId);
-                predecessors.add(v);
-                v.addSuccessor(this);
-            }
-        }
-    }
-
-    /**
      * Clear the list of predecessors/successors and the label cache.
      */
     void clear() {
-        successors.clear();
-        predecessors.clear();
         labelCache = null;
     }
 
     /**
-     * adds a successor version to the internal cache
-     *
-     * @param successor
-     */
-    private void addSuccessor(InternalVersion successor) {
-        successors.add(successor);
-    }
-
-    /**
-     * stores the internal predecessor cache to the persistance node
+     * stores the given successors or predecessors to the persistance node
      *
      * @throws RepositoryException
      */
-    private void storePredecessors() throws RepositoryException {
-        InternalValue[] values = new InternalValue[predecessors.size()];
+    private void storeXCessors(List cessors, QName propname, boolean store)
+            throws RepositoryException {
+        InternalValue[] values = new InternalValue[cessors.size()];
         for (int i = 0; i < values.length; i++) {
             values[i] = InternalValue.create(
-                    ((InternalVersion) predecessors.get(i)).getId().getUUID());
+                    ((InternalVersion) cessors.get(i)).getId().getUUID());
+        }
+        node.setPropertyValues(propname, PropertyType.STRING, values);
+        if (store) {
+            node.store();
         }
-        node.setPropertyValues(QName.JCR_PREDECESSORS, PropertyType.STRING, values);
     }
 
     /**
@@ -249,15 +238,15 @@
      */
     void internalDetach() throws RepositoryException {
         // detach this from all successors
-        InternalVersionImpl[] succ = (InternalVersionImpl[]) getSuccessors();
+        InternalVersion[] succ = getSuccessors();
         for (int i = 0; i < succ.length; i++) {
-            succ[i].internalDetachPredecessor(this);
+            ((InternalVersionImpl) succ[i]).internalDetachPredecessor(this, true);
         }
 
         // detach cached successors from preds
-        InternalVersionImpl[] preds = (InternalVersionImpl[]) getPredecessors();
+        InternalVersion[] preds = getPredecessors();
         for (int i = 0; i < preds.length; i++) {
-            preds[i].internalDetachSuccessor(this);
+            ((InternalVersionImpl) preds[i]).internalDetachSuccessor(this, true);
         }
 
         // clear properties
@@ -265,6 +254,35 @@
     }
 
     /**
+     * Attaches this version as successor to all predecessors. assuming that the
+     * predecessors are already set.
+     *
+     * @throws RepositoryException
+     */
+    void internalAttach() throws RepositoryException {
+        InternalVersion[] preds = getPredecessors();
+        for (int i = 0; i < preds.length; i++) {
+            ((InternalVersionImpl) preds[i]).internalAddSuccessor(this, true);
+        }
+    }
+
+    /**
+     * Adds a version to the set of successors.
+     *
+     * @param succ
+     * @param store
+     * @throws RepositoryException
+     */
+    private void internalAddSuccessor(InternalVersionImpl succ, boolean store)
+            throws RepositoryException {
+        List l = new ArrayList(Arrays.asList(getSuccessors()));
+        if (!l.contains(succ)) {
+            l.add(succ);
+            storeXCessors(l, QName.JCR_SUCCESSORS, store);
+        }
+    }
+
+    /**
      * Removes the predecessor V of this predecessors list and adds all of Vs
      * predecessors to it.
      * <p/>
@@ -272,18 +290,15 @@
      *
      * @param v the successor to detach
      */
-    private void internalDetachPredecessor(InternalVersionImpl v) throws RepositoryException {
+    private void internalDetachPredecessor(InternalVersionImpl v, boolean store)
+            throws RepositoryException {
         // remove 'v' from predecessor list
-        for (int i = 0; i < predecessors.size(); i++) {
-            if (predecessors.get(i).equals(v)) {
-                predecessors.remove(i);
-                break;
-            }
-        }
+        List l = new ArrayList(Arrays.asList(getPredecessors()));
+        l.remove(v);
+
         // attach v's predecessors
-        predecessors.addAll(Arrays.asList(v.getPredecessors()));
-        storePredecessors();
-        node.store();
+        l.addAll(Arrays.asList(v.getPredecessors()));
+        storeXCessors(l, QName.JCR_PREDECESSORS, store);
     }
 
     /**
@@ -294,16 +309,15 @@
      *
      * @param v the successor to detach
      */
-    private void internalDetachSuccessor(InternalVersionImpl v) {
+    private void internalDetachSuccessor(InternalVersionImpl v, boolean store)
+            throws RepositoryException {
         // remove 'v' from successors list
-        for (int i = 0; i < successors.size(); i++) {
-            if (successors.get(i).equals(v)) {
-                successors.remove(i);
-                break;
-            }
-        }
+        List l = new ArrayList(Arrays.asList(getSuccessors()));
+        l.remove(v);
+
         // attach v's successors
-        successors.addAll(Arrays.asList(v.getSuccessors()));
+        l.addAll(Arrays.asList(v.getSuccessors()));
+        storeXCessors(l, QName.JCR_SUCCESSORS, store);
     }
 
     /**
@@ -365,5 +379,42 @@
      */
     void invalidate() {
         node.getState().discard();
+    }
+
+    /**
+     * Resolves jcr:successor properties that are missing.
+     *
+     * @throws RepositoryException
+     */
+    void legacyResolveSuccessors() throws RepositoryException {
+        InternalValue[] values = node.getPropertyValues(QName.JCR_PREDECESSORS);
+        if (values != null) {
+            for (int i = 0; i < values.length; i++) {
+                NodeId vId = new NodeId((UUID) values[i].internalValue());
+                InternalVersionImpl v = (InternalVersionImpl) versionHistory.getVersion(vId);
+                v.internalAddSuccessor(this, false);
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof InternalVersionImpl) {
+            InternalVersionImpl v = (InternalVersionImpl) obj;
+            return v.getId().equals(getId());
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int hashCode() {
+        return getId().hashCode();
     }
 }

Modified: jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/VersionItemStateProvider.java
URL: http://svn.apache.org/viewcvs/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/VersionItemStateProvider.java?rev=398589&r1=398588&r2=398589&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/VersionItemStateProvider.java (original)
+++ jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/VersionItemStateProvider.java Mon May  1 05:54:53 2006
@@ -18,16 +18,13 @@
 import org.apache.commons.collections.map.ReferenceMap;
 import org.apache.jackrabbit.core.ItemId;
 import org.apache.jackrabbit.core.NodeId;
-import org.apache.jackrabbit.core.PropertyId;
 import org.apache.jackrabbit.core.state.ItemState;
 import org.apache.jackrabbit.core.state.ItemStateException;
 import org.apache.jackrabbit.core.state.ItemStateListener;
 import org.apache.jackrabbit.core.state.NoSuchItemStateException;
 import org.apache.jackrabbit.core.state.NodeReferences;
 import org.apache.jackrabbit.core.state.NodeReferencesId;
-import org.apache.jackrabbit.core.state.PropertyState;
 import org.apache.jackrabbit.core.state.SharedItemStateManager;
-import org.apache.jackrabbit.core.value.InternalValue;
 import org.apache.jackrabbit.core.virtual.VirtualItemStateProvider;
 import org.apache.jackrabbit.core.virtual.VirtualNodeState;
 import org.apache.jackrabbit.core.virtual.VirtualPropertyState;
@@ -122,41 +119,8 @@
 
             // attach us as listener
             item.addListener(this);
-
-            // special check for successors
-            if (item instanceof PropertyState) {
-                PropertyState prop = (PropertyState) item;
-                if (prop.getName().equals(QName.JCR_SUCCESSORS)) {
-                    try {
-                        InternalVersion v = vMgr.getVersion(prop.getParentId());
-                        if (v != null) {
-                            InternalVersion[] succs = v.getSuccessors();
-                            InternalValue[] succV = new InternalValue[succs.length];
-                            for (int i = 0; i < succs.length; i++) {
-                                succV[i] = InternalValue.create(succs[i].getId().getUUID());
-                            }
-                            prop.setValues(succV);
-                        }
-                    } catch (RepositoryException e) {
-                        log.warn("Unable to resolve jcr:successors property for " + id);
-                    }
-                }
-            }
         }
         return item;
-    }
-
-    /**
-     * called by the version manager when a dynamic property needs to be
-     * invalidated.
-     *
-     * @param id
-     */
-    synchronized void onPropertyChanged(PropertyId id) {
-        ItemState item = (ItemState) items.get(id);
-        if (item != null) {
-            item.discard();
-        }
     }
 
     /**

Modified: jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java
URL: http://svn.apache.org/viewcvs/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java?rev=398589&r1=398588&r2=398589&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java (original)
+++ jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java Mon May  1 05:54:53 2006
@@ -267,16 +267,8 @@
                 escFactory.doSourced((SessionImpl) node.getSession(), new SourcedTarget(){
             public Object run() throws RepositoryException {
                 String histUUID = node.getProperty(QName.JCR_VERSIONHISTORY).getString();
-                InternalVersion version = checkin((InternalVersionHistoryImpl)
+                return checkin((InternalVersionHistoryImpl) 
                         getVersionHistory(NodeId.valueOf(histUUID)), node);
-
-                // invalidate predecessors successor properties
-                InternalVersion[] preds = version.getPredecessors();
-                for (int i = 0; i < preds.length; i++) {
-                    PropertyId propId = new PropertyId(preds[i].getId(), QName.JCR_SUCCESSORS);
-                    versProvider.onPropertyChanged(propId);
-                }
-                return version;
             }
         });
 
@@ -301,18 +293,9 @@
 
         escFactory.doSourced((SessionImpl) history.getSession(), new SourcedTarget(){
             public Object run() throws RepositoryException {
-                AbstractVersion version = (AbstractVersion) historyImpl.getNode(name);
-                InternalVersion[] preds = version.getInternalVersion().getPredecessors();
-
                 InternalVersionHistoryImpl vh = (InternalVersionHistoryImpl)
                         historyImpl.getInternalVersionHistory();
                 removeVersion(vh, name);
-
-                // invalidate predecessors successor properties
-                for (int i = 0; i < preds.length; i++) {
-                    PropertyId propId = new PropertyId(preds[i].getId(), QName.JCR_SUCCESSORS);
-                    versProvider.onPropertyChanged(propId);
-                }
                 return null;
             }
         });

Modified: jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/XAVersionManager.java
URL: http://svn.apache.org/viewcvs/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/XAVersionManager.java?rev=398589&r1=398588&r2=398589&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/XAVersionManager.java (original)
+++ jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/version/XAVersionManager.java Mon May  1 05:54:53 2006
@@ -354,6 +354,16 @@
         if (history.getVersionManager() != this) {
             history = makeLocalCopy(history);
             xaItems.put(history.getId(), history);
+            // also put 'successor' and 'predecessor' version items to xaItem sets
+            InternalVersion v = history.getVersion(name);
+            InternalVersion[] vs = v.getSuccessors();
+            for (int i=0; i<vs.length; i++) {
+                xaItems.put(vs[i].getId(), vs[i]);
+            }
+            vs = v.getPredecessors();
+            for (int i=0; i<vs.length; i++) {
+                xaItems.put(vs[i].getId(), vs[i]);
+            }
         }
         super.removeVersion(history, name);
     }

Modified: jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/core/XATest.java
URL: http://svn.apache.org/viewcvs/jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/core/XATest.java?rev=398589&r1=398588&r2=398589&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/core/XATest.java (original)
+++ jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/core/XATest.java Mon May  1 05:54:53 2006
@@ -22,11 +22,15 @@
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.Session;
 import javax.jcr.RepositoryException;
+import javax.jcr.SimpleCredentials;
+import javax.jcr.observation.Event;
 import javax.jcr.version.VersionException;
 import javax.jcr.version.Version;
 import javax.jcr.lock.Lock;
 import javax.transaction.UserTransaction;
 import javax.transaction.RollbackException;
+import java.io.PrintWriter;
+import java.io.PrintStream;
 
 /**
  * <code>XATest</code> contains the test cases for the methods
@@ -925,6 +929,195 @@
             // expected
         }
     }
+
+    /**
+     * Tests a couple of checkin/restore/remove operations on different
+     * workspaces and different transactions.
+     *
+     * @throws Exception
+     */
+    public void testXAVersionsThoroughly() throws Exception {
+        Session s1 = superuser;
+        Session s2 = helper.getSuperuserSession(workspaceName);
+
+        // add node and save
+        Node n1 = testRootNode.addNode(nodeName1, testNodeType);
+        n1.addMixin(mixVersionable);
+        testRootNode.save();
+
+        if (!s2.itemExists(testRootNode.getPath())) {
+            s2.getRootNode().addNode(testRootNode.getName());
+            s2.save();
+        }
+        s2.getWorkspace().clone(s1.getWorkspace().getName(), n1.getPath(), n1.getPath(), true);
+        Node n2 = (Node) s2.getItem(n1.getPath());
+
+        //log.println("---------------------------------------");
+        String phase="init";
+
+        Version v1_1 = n1.getBaseVersion();
+        Version v2_1 = n2.getBaseVersion();
+
+        check(v1_1, phase, "jcr:rootVersion", 0);
+        check(v2_1, phase, "jcr:rootVersion", 0);
+
+        //log.println("--------checkout/checkin n1 (uncommitted)----------");
+        phase="checkin N1 uncomitted.";
+
+        UserTransaction tx = new UserTransactionImpl(s1);
+        tx.begin();
+
+        n1.checkout();
+        n1.checkin();
+
+        Version v1_2 = n1.getBaseVersion();
+
+        check(v1_1, phase, "jcr:rootVersion", 1);
+        check(v2_1, phase, "jcr:rootVersion", 0);
+        check(v1_2, phase, "1.0", 0);
+
+        //log.println("--------checkout/checkin n1 (comitted)----------");
+        phase="checkin N1 committed.";
+
+        tx.commit();
+
+        check(v1_1, phase, "jcr:rootVersion", 1);
+        check(v2_1, phase, "jcr:rootVersion", 1);
+        check(v1_2, phase, "1.0", 0);
+
+        //log.println("--------restore n2 (uncommitted) ----------");
+        phase="restore N2 uncommitted.";
+
+        tx = new UserTransactionImpl(s2);
+        tx.begin();
+
+        n2.restore("1.0", false);
+        Version v2_2 = n2.getBaseVersion();
+
+        check(v1_1, phase, "jcr:rootVersion", 1);
+        check(v2_1, phase, "jcr:rootVersion", 1);
+        check(v1_2, phase, "1.0", 0);
+        check(v2_2, phase, "1.0", 0);
+
+        //log.println("--------restore n2 (comitted) ----------");
+        phase="restore N2 committed.";
+
+        tx.commit();
+
+        check(v1_1, phase, "jcr:rootVersion", 1);
+        check(v2_1, phase, "jcr:rootVersion", 1);
+        check(v1_2, phase, "1.0", 0);
+        check(v2_2, phase, "1.0", 0);
+
+        //log.println("--------checkout/checkin n2 (uncommitted) ----------");
+        phase="checkin N2 uncommitted.";
+
+        tx = new UserTransactionImpl(s2);
+        tx.begin();
+
+        n2.checkout();
+        n2.checkin();
+
+        Version v2_3 = n2.getBaseVersion();
+
+        check(v1_1, phase, "jcr:rootVersion", 1);
+        check(v2_1, phase, "jcr:rootVersion", 1);
+        check(v1_2, phase, "1.0", 0);
+        check(v2_2, phase, "1.0", 1);
+        check(v2_3, phase, "1.1", 0);
+
+        //log.println("--------checkout/checkin n2 (committed) ----------");
+        phase="checkin N2 committed.";
+
+        tx.commit();
+
+        check(v1_1, phase, "jcr:rootVersion", 1);
+        check(v2_1, phase, "jcr:rootVersion", 1);
+        check(v1_2, phase, "1.0", 1);
+        check(v2_2, phase, "1.0", 1);
+        check(v2_3, phase, "1.1", 0);
+
+        //log.println("--------checkout/checkin n1 (uncommitted) ----------");
+        phase="checkin N1 uncommitted.";
+
+        tx = new UserTransactionImpl(s1);
+        tx.begin();
+
+        n1.checkout();
+        n1.checkin();
+
+        Version v1_3 = n1.getBaseVersion();
+
+        check(v1_1, phase, "jcr:rootVersion", 1);
+        check(v2_1, phase, "jcr:rootVersion", 1);
+        check(v1_2, phase, "1.0", 2);
+        check(v2_2, phase, "1.0", 1);
+        check(v2_3, phase, "1.1", 0);
+        check(v1_3, phase, "1.1.1", 0);
+
+        //log.println("--------checkout/checkin n1 (committed) ----------");
+        phase="checkin N1 committed.";
+
+        tx.commit();
+
+        check(v1_1, phase, "jcr:rootVersion", 1);
+        check(v2_1, phase, "jcr:rootVersion", 1);
+        check(v1_2, phase, "1.0", 2);
+        check(v2_2, phase, "1.0", 2);
+        check(v2_3, phase, "1.1", 0);
+        check(v1_3, phase, "1.1.1", 0);
+
+        //log.println("--------remove n1-1.0 (uncommitted) ----------");
+        phase="remove N1 1.0 uncommitted.";
+
+        tx = new UserTransactionImpl(s1);
+        tx.begin();
+
+        n1.getVersionHistory().removeVersion("1.0");
+
+        check(v1_1, phase, "jcr:rootVersion", 2);
+        check(v2_1, phase, "jcr:rootVersion", 1);
+        check(v1_2, phase, "1.0", -1);
+        check(v2_2, phase, "1.0", 2);
+        check(v2_3, phase, "1.1", 0);
+        check(v1_3, phase, "1.1.1", 0);
+
+        //log.println("--------remove n1-1.0  (committed) ----------");
+        phase="remove N1 1.0 committed.";
+
+        tx.commit();
+
+        check(v1_1, phase, "jcr:rootVersion", 2);
+        check(v2_1, phase, "jcr:rootVersion", 2);
+        check(v1_2, phase, "1.0", -1);
+        check(v2_2, phase, "1.0", -1);
+        check(v2_3, phase, "1.1", 0);
+        check(v1_3, phase, "1.1.1", 0);
+
+        //s1.logout();
+        s2.logout();
+
+    }
+
+    /**
+     * helper method for {@link #testXAVersionsThoroughly()}
+     */
+    private void check(Version v, String phase, String name, int numSucc) {
+        String vName;
+        int vSucc = -1;
+        try {
+            vName = v.getName();
+            //vSucc = v.getProperty("jcr:successors").getValues().length;
+            vSucc = v.getSuccessors().length;
+        } catch (RepositoryException e) {
+            // node is invalid after remove
+            vName = name;
+        }
+        assertEquals(phase + " Version Name", name, vName);
+        assertEquals(phase + " Num Successors", numSucc, vSucc);
+    }
+
+
 
     /**
      * Test new version label becomes available to other sessions on commit.