Return-Path: Delivered-To: apmail-jackrabbit-commits-archive@www.apache.org Received: (qmail 97945 invoked from network); 1 May 2006 12:55:19 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 1 May 2006 12:55:19 -0000 Received: (qmail 68300 invoked by uid 500); 1 May 2006 12:55:19 -0000 Delivered-To: apmail-jackrabbit-commits-archive@jackrabbit.apache.org Received: (qmail 68250 invoked by uid 500); 1 May 2006 12:55:18 -0000 Mailing-List: contact commits-help@jackrabbit.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@jackrabbit.apache.org Delivered-To: mailing list commits@jackrabbit.apache.org Received: (qmail 68236 invoked by uid 99); 1 May 2006 12:55:18 -0000 Received: from asf.osuosl.org (HELO asf.osuosl.org) (140.211.166.49) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 01 May 2006 05:55:18 -0700 X-ASF-Spam-Status: No, hits=-9.4 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received: from [209.237.227.194] (HELO minotaur.apache.org) (209.237.227.194) by apache.org (qpsmtpd/0.29) with SMTP; Mon, 01 May 2006 05:55:16 -0700 Received: (qmail 97826 invoked by uid 65534); 1 May 2006 12:54:56 -0000 Message-ID: <20060501125456.97825.qmail@minotaur.apache.org> Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r398589 - in /jackrabbit/trunk/jackrabbit/src: main/java/org/apache/jackrabbit/core/version/ test/java/org/apache/jackrabbit/core/ Date: Mon, 01 May 2006 12:54:55 -0000 To: commits@jackrabbit.apache.org From: tripod@apache.org X-Mailer: svnmailer-1.0.8 X-Virus-Checked: Checked by ClamAV on apache.org X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N 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 InternalVersion @@ -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. *

@@ -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; iXATest 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.