Index: modeshape-jcr/src/main/java/org/modeshape/jcr/AbstractJcrNode.java =================================================================== --- modeshape-jcr/src/main/java/org/modeshape/jcr/AbstractJcrNode.java (revision 1893) +++ modeshape-jcr/src/main/java/org/modeshape/jcr/AbstractJcrNode.java (working copy) @@ -1734,19 +1734,6 @@ abstract class AbstractJcrNode extends AbstractJcrItem implements javax.jcr.Node } /** - * Throw an {@link UnsupportedRepositoryOperationException} if this node is not versionable (i.e., - * isNodeType(JcrMixLexicon.VERSIONABLE) == false). - * - * @throws UnsupportedRepositoryOperationException if !isNodeType({@link JcrMixLexicon#VERSIONABLE}) - * @throws RepositoryException if an error occurs reading the node types for this node - */ - private void checkVersionable() throws UnsupportedRepositoryOperationException, RepositoryException { - if (!isNodeType(JcrMixLexicon.VERSIONABLE)) { - throw new UnsupportedRepositoryOperationException(JcrI18n.requiresVersionable.text()); - } - } - - /** * Throw a {@link ConstraintViolationException} if this node is protected (based on the its node definition). * * @throws ConstraintViolationException if this node's definition indicates that the node is protected @@ -1779,9 +1766,6 @@ abstract class AbstractJcrNode extends AbstractJcrItem implements javax.jcr.Node * @see javax.jcr.Node#checkin() */ public final Version checkin() throws RepositoryException { - checkSession(); - checkVersionable(); - return versionManager().checkin(this); } @@ -1791,9 +1775,6 @@ abstract class AbstractJcrNode extends AbstractJcrItem implements javax.jcr.Node * @see javax.jcr.Node#checkout() */ public final void checkout() throws UnsupportedRepositoryOperationException, LockException, RepositoryException { - checkSession(); - checkVersionable(); - versionManager().checkout(this); } @@ -1806,10 +1787,9 @@ abstract class AbstractJcrNode extends AbstractJcrItem implements javax.jcr.Node boolean bestEffort ) throws ConstraintViolationException, RepositoryException { CheckArg.isNotNull(srcWorkspace, "source workspace name"); - checkSession(); checkNotProtected(); - return versionManager().merge(this, srcWorkspace, bestEffort); + return versionManager().merge(this, srcWorkspace, bestEffort, false); } /** @@ -1818,9 +1798,6 @@ abstract class AbstractJcrNode extends AbstractJcrItem implements javax.jcr.Node * @see javax.jcr.Node#cancelMerge(javax.jcr.version.Version) */ public final void cancelMerge( Version version ) throws RepositoryException { - checkSession(); - checkVersionable(); - versionManager().cancelMerge(this, version); } @@ -1830,9 +1807,6 @@ abstract class AbstractJcrNode extends AbstractJcrItem implements javax.jcr.Node * @see javax.jcr.Node#doneMerge(javax.jcr.version.Version) */ public final void doneMerge( Version version ) throws RepositoryException { - checkSession(); - checkVersionable(); - versionManager().doneMerge(this, version); } @@ -1842,9 +1816,6 @@ abstract class AbstractJcrNode extends AbstractJcrItem implements javax.jcr.Node * @see javax.jcr.Node#getVersionHistory() */ public final JcrVersionHistoryNode getVersionHistory() throws RepositoryException { - checkSession(); - checkVersionable(); - return versionManager().getVersionHistory(this); } @@ -1855,7 +1826,6 @@ abstract class AbstractJcrNode extends AbstractJcrItem implements javax.jcr.Node */ public final JcrVersionNode getBaseVersion() throws RepositoryException { checkSession(); - checkVersionable(); // This can happen if the versionable type was added to the node, but it hasn't been saved yet if (!hasProperty(JcrLexicon.BASE_VERSION)) { @@ -1882,7 +1852,6 @@ abstract class AbstractJcrNode extends AbstractJcrItem implements javax.jcr.Node */ public final void restore( Version version, boolean removeExisting ) throws RepositoryException { - checkSession(); try { checkNotProtected(); } catch (ConstraintViolationException cve) { @@ -1899,7 +1868,6 @@ abstract class AbstractJcrNode extends AbstractJcrItem implements javax.jcr.Node public final void restore( Version version, String relPath, boolean removeExisting ) throws RepositoryException { - checkSession(); checkNotProtected(); PathFactory pathFactory = context().getValueFactories().getPathFactory(); Index: modeshape-jcr/src/main/java/org/modeshape/jcr/JcrLexicon.java =================================================================== --- modeshape-jcr/src/main/java/org/modeshape/jcr/JcrLexicon.java (revision 1893) +++ modeshape-jcr/src/main/java/org/modeshape/jcr/JcrLexicon.java (working copy) @@ -36,6 +36,7 @@ public class JcrLexicon extends org.modeshape.graph.JcrLexicon { public static final Name BASE_VERSION = new BasicName(Namespace.URI, "baseVersion"); public static final Name CHILD_VERSION_HISTORY = new BasicName(Namespace.URI, "childVersionHistory"); public static final Name CONTENT = new BasicName(Namespace.URI, "content"); + public static final Name COPIED_FROM = new BasicName(Namespace.URI, "copiedFrom"); public static final Name CREATED = new BasicName(Namespace.URI, "created"); public static final Name DATA = new BasicName(Namespace.URI, "data"); public static final Name ENCODING = new BasicName(Namespace.URI, "encoding"); Index: modeshape-jcr/src/main/java/org/modeshape/jcr/JcrRepository.java =================================================================== --- modeshape-jcr/src/main/java/org/modeshape/jcr/JcrRepository.java (revision 1893) +++ modeshape-jcr/src/main/java/org/modeshape/jcr/JcrRepository.java (working copy) @@ -1257,7 +1257,6 @@ public class JcrRepository implements Repository { repoDescriptors.put(Repository.OPTION_JOURNALED_OBSERVATION_SUPPORTED, valueFor(factories, false)); repoDescriptors.put(Repository.OPTION_RETENTION_SUPPORTED, valueFor(factories, false)); repoDescriptors.put(Repository.OPTION_LIFECYCLE_SUPPORTED, valueFor(factories, false)); - repoDescriptors.put(Repository.OPTION_TRANSACTIONS_SUPPORTED, valueFor(factories, false)); repoDescriptors.put(Repository.OPTION_WORKSPACE_MANAGEMENT_SUPPORTED, valueFor(factories, false)); repoDescriptors.put(Repository.OPTION_NODE_AND_PROPERTY_WITH_SAME_NAME_SUPPORTED, valueFor(factories, true)); repoDescriptors.put(Repository.OPTION_UPDATE_PRIMARY_NODE_TYPE_SUPPORTED, valueFor(factories, false)); @@ -1284,7 +1283,7 @@ public class JcrRepository implements Repository { repoDescriptors.put(Repository.QUERY_FULL_TEXT_SEARCH_SUPPORTED, valueFor(factories, true)); repoDescriptors.put(Repository.QUERY_JOINS, valueFor(factories, Repository.QUERY_JOINS_INNER_OUTER)); repoDescriptors.put(Repository.SPEC_NAME_DESC, valueFor(factories, JcrI18n.SPEC_NAME_DESC.text())); - repoDescriptors.put(Repository.SPEC_VERSION_DESC, valueFor(factories, "1.0")); + repoDescriptors.put(Repository.SPEC_VERSION_DESC, valueFor(factories, "2.0")); if (!repoDescriptors.containsKey(Repository.REP_NAME_DESC)) { repoDescriptors.put(Repository.REP_NAME_DESC, Index: modeshape-jcr/src/main/java/org/modeshape/jcr/JcrSession.java =================================================================== --- modeshape-jcr/src/main/java/org/modeshape/jcr/JcrSession.java (revision 1893) +++ modeshape-jcr/src/main/java/org/modeshape/jcr/JcrSession.java (working copy) @@ -264,6 +264,16 @@ class JcrSession implements Session { return this.repository.getRepositorySourceName(); } + Path pathFor( String path, + String parameterName ) throws RepositoryException { + try { + return this.executionContext.getValueFactories().getPathFactory().create(path); + + } catch (org.modeshape.graph.property.ValueFormatException vfe) { + throw new RepositoryException(JcrI18n.invalidPathParameter.text(path, parameterName), vfe); + } + } + /** * {@inheritDoc} * @@ -774,7 +784,7 @@ class JcrSession implements Session { * @see javax.jcr.Session#getNodeByIdentifier(java.lang.String) */ @Override - public Node getNodeByIdentifier( String id ) throws ItemNotFoundException, RepositoryException { + public AbstractJcrNode getNodeByIdentifier( String id ) throws ItemNotFoundException, RepositoryException { // Attempt to create a UUID from the identifier ... try { return cache.findJcrNode(Location.create(UUID.fromString(id))); Index: modeshape-jcr/src/main/java/org/modeshape/jcr/JcrVersionHistoryNode.java =================================================================== --- modeshape-jcr/src/main/java/org/modeshape/jcr/JcrVersionHistoryNode.java (revision 1893) +++ modeshape-jcr/src/main/java/org/modeshape/jcr/JcrVersionHistoryNode.java (working copy) @@ -24,10 +24,12 @@ package org.modeshape.jcr; import java.util.Collection; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.NoSuchElementException; import javax.jcr.AccessDeniedException; +import javax.jcr.Node; import javax.jcr.NodeIterator; import javax.jcr.PathNotFoundException; import javax.jcr.Property; @@ -324,22 +326,36 @@ class JcrVersionHistoryNode extends JcrNode implements VersionHistory { @Override public NodeIterator getAllFrozenNodes() throws RepositoryException { - throw new UnsupportedRepositoryOperationException(); + return new FrozenNodeIterator(getAllVersions()); } @Override public NodeIterator getAllLinearFrozenNodes() throws RepositoryException { - throw new UnsupportedRepositoryOperationException(); + return new FrozenNodeIterator(getAllLinearVersions()); } @Override public VersionIterator getAllLinearVersions() throws RepositoryException { - throw new UnsupportedRepositoryOperationException(); + AbstractJcrNode existingNode = session().getNodeByIdentifier(getVersionableIdentifier()); + if (existingNode == null) return getAllVersions(); + + assert existingNode.isNodeType(JcrMixLexicon.VERSIONABLE); + + LinkedList versions = new LinkedList(); + JcrVersionNode baseVersion = existingNode.getBaseVersion(); + + while (baseVersion != null) { + versions.addFirst(baseVersion); + baseVersion = baseVersion.getLinearPredecessor(); + } + + return new LinearVersionIterator(versions, versions.size()); } @Override public String getVersionableIdentifier() throws RepositoryException { - throw new UnsupportedRepositoryOperationException(); + // ModeShape uses a node's UUID as it's identifier + return getVersionableUUID(); } /** @@ -457,4 +473,110 @@ class JcrVersionHistoryNode extends JcrNode implements VersionHistory { throw new UnsupportedOperationException(); } } + + /** + * An implementation of {@link VersionIterator} that iterates over a given set of versions. This differs from + * {@link JcrVersionIterator} in that it expects an exact list of versions to iterate over whereas {@code JcrVersionIterator} + * expects list of children for a {@code nt:versionHistory} node and filters out the label child. + */ + class LinearVersionIterator implements VersionIterator { + + private final Iterator versions; + private final int size; + private int pos; + + protected LinearVersionIterator( Iterable versions, + int size ) { + this.versions = versions.iterator(); + this.size = size; + this.pos = 0; + } + + @Override + public long getPosition() { + return pos; + } + + @Override + public long getSize() { + return this.size; + } + + @Override + public void skip( long skipNum ) { + while (skipNum-- > 0 && versions.hasNext()) { + versions.next(); + pos++; + } + + } + + @Override + public Version nextVersion() { + return versions.next(); + } + + @Override + public boolean hasNext() { + return versions.hasNext(); + } + + @Override + public Object next() { + return nextVersion(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + + class FrozenNodeIterator implements NodeIterator { + private final VersionIterator versions; + + FrozenNodeIterator( VersionIterator versionIter ) { + this.versions = versionIter; + } + + @Override + public boolean hasNext() { + return versions.hasNext(); + } + + @Override + public Object next() { + return nextNode(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + @Override + public Node nextNode() { + try { + return versions.nextVersion().getFrozenNode(); + } catch (RepositoryException re) { + // ModeShape doesn't throw a RepositoryException on getFrozenNode() from a valid version node + throw new IllegalStateException(re); + } + } + + @Override + public long getPosition() { + return versions.getPosition(); + } + + @Override + public long getSize() { + return versions.getSize(); + } + + @Override + public void skip( long skipNum ) { + versions.skip(skipNum); + } + } } Index: modeshape-jcr/src/main/java/org/modeshape/jcr/JcrVersionManager.java =================================================================== --- modeshape-jcr/src/main/java/org/modeshape/jcr/JcrVersionManager.java (revision 1893) +++ modeshape-jcr/src/main/java/org/modeshape/jcr/JcrVersionManager.java (working copy) @@ -64,6 +64,7 @@ import net.jcip.annotations.NotThreadSafe; import org.modeshape.common.i18n.I18n; import org.modeshape.common.text.Jsr283Encoder; import org.modeshape.common.text.TextEncoder; +import org.modeshape.common.util.CheckArg; import org.modeshape.common.util.Logger; import org.modeshape.graph.ExecutionContext; import org.modeshape.graph.Graph; @@ -196,6 +197,8 @@ final class JcrVersionManager implements VersionManager { */ JcrVersionHistoryNode getVersionHistory( AbstractJcrNode node ) throws RepositoryException { session.checkLive(); + checkVersionable(node); + Location historyLocation = Location.create(versionHistoryPathFor(node.uuid())); try { return (JcrVersionHistoryNode)cache().findJcrNode(historyLocation); @@ -234,6 +237,9 @@ final class JcrVersionManager implements VersionManager { */ JcrVersionNode checkin( AbstractJcrNode node ) throws RepositoryException { + session.checkLive(); + checkVersionable(node); + if (node.isNew() || node.isModified()) { throw new InvalidItemStateException(JcrI18n.noPendingChangesAllowed.text()); } @@ -269,20 +275,13 @@ final class JcrVersionManager implements VersionManager { Path versionPath = path(historyPath, name(NODE_ENCODER.encode(now.getString()))); AbstractJcrProperty predecessorsProp = node.getProperty(JcrLexicon.PREDECESSORS); - systemBatch.create(versionPath) - .with(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.VERSION) - .and(JcrLexicon.CREATED, now) - .and(JcrLexicon.UUID, versionUuid) - .and(predecessorsProp.property()) - .and(); + systemBatch.create(versionPath).with(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.VERSION).and(JcrLexicon.CREATED, now).and(JcrLexicon.UUID, + versionUuid).and(predecessorsProp.property()).and(); Path frozenVersionPath = path(versionPath, JcrLexicon.FROZEN_NODE); - systemBatch.create(frozenVersionPath) - .with(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.FROZEN_NODE) - .and(JcrLexicon.FROZEN_UUID, jcrUuid) - .and(JcrLexicon.FROZEN_PRIMARY_TYPE, primaryTypeName) - .and(JcrLexicon.FROZEN_MIXIN_TYPES, mixinTypeNames) - .and(versionedPropertiesFor(node)) - .and(); + systemBatch.create(frozenVersionPath).with(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.FROZEN_NODE).and(JcrLexicon.FROZEN_UUID, + jcrUuid).and(JcrLexicon.FROZEN_PRIMARY_TYPE, + primaryTypeName).and(JcrLexicon.FROZEN_MIXIN_TYPES, + mixinTypeNames).and(versionedPropertiesFor(node)).and(); int onParentVersion = node.getDefinition().getOnParentVersion(); for (NodeIterator childNodes = node.getNodes(); childNodes.hasNext();) { @@ -295,21 +294,24 @@ final class JcrVersionManager implements VersionManager { for (Object ob : predecessorsProp.property()) { UUID predUuid = uuid(ob); - org.modeshape.graph.property.Property successorsProp = systemGraph.getNodeAt(predUuid) - .getProperty(JcrLexicon.SUCCESSORS); + org.modeshape.graph.property.Property successorsProp = systemGraph.getNodeAt(predUuid).getProperty(JcrLexicon.SUCCESSORS); List newSuccessors = new LinkedList(); + boolean alreadySuccessor = false; if (successorsProp != null) { for (Object successor : successorsProp) { newSuccessors.add(successor); + if (uuid(successor).equals(predUuid)) alreadySuccessor = true; } } - newSuccessors.add(versionUuid); + if (!alreadySuccessor) { + newSuccessors.add(versionUuid); - org.modeshape.graph.property.Property newSuccessorsProp = propFactory.create(JcrLexicon.SUCCESSORS, - newSuccessors.toArray()); - systemBatch.set(newSuccessorsProp).on(predUuid).and(); + org.modeshape.graph.property.Property newSuccessorsProp = propFactory.create(JcrLexicon.SUCCESSORS, + newSuccessors.toArray()); + systemBatch.set(newSuccessorsProp).on(predUuid).and(); + } } systemBatch.execute(); @@ -354,29 +356,24 @@ final class JcrVersionManager implements VersionManager { switch (onParentVersionAction) { case OnParentVersionAction.ABORT: - throw new VersionException(JcrI18n.cannotCheckinNodeWithAbortChildNode.text(node.getName(), node.getParent() - .getName())); + throw new VersionException(JcrI18n.cannotCheckinNodeWithAbortChildNode.text(node.getName(), + node.getParent().getName())); case OnParentVersionAction.VERSION: if (node.isNodeType(JcrMixLexicon.VERSIONABLE)) { JcrVersionHistoryNode history = node.getVersionHistory(); UUID historyUuid = history.uuid(); - batch.create(childPath) - .with(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.VERSIONED_CHILD) - .with(JcrLexicon.CHILD_VERSION_HISTORY, historyUuid) - .and(); + batch.create(childPath).with(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.VERSIONED_CHILD).with(JcrLexicon.CHILD_VERSION_HISTORY, + historyUuid).and(); break; } // Otherwise, treat it as a copy, as per 8.2.11.2 in the 1.0.1 Spec case OnParentVersionAction.COPY: - batch.create(childPath) - .with(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.FROZEN_NODE) - .and(JcrLexicon.FROZEN_PRIMARY_TYPE, primaryTypeName) - .and(JcrLexicon.FROZEN_MIXIN_TYPES, mixinTypeNames) - .and(JcrLexicon.FROZEN_UUID, uuid) - .and(versionedPropertiesFor(node)) - .and(); + batch.create(childPath).with(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.FROZEN_NODE).and(JcrLexicon.FROZEN_PRIMARY_TYPE, + primaryTypeName).and(JcrLexicon.FROZEN_MIXIN_TYPES, + mixinTypeNames).and(JcrLexicon.FROZEN_UUID, + uuid).and(versionedPropertiesFor(node)).and(); break; case OnParentVersionAction.INITIALIZE: case OnParentVersionAction.COMPUTE: @@ -438,6 +435,9 @@ final class JcrVersionManager implements VersionManager { * description of the possible error conditions. */ void checkout( AbstractJcrNode node ) throws LockException, RepositoryException { + session.checkLive(); + checkVersionable(node); + // Check this separately since it throws a different type of exception if (node.isLocked() && !node.holdsLock()) { throw new LockException(JcrI18n.lockTokenNotHeld.text(node.getPath())); @@ -504,7 +504,12 @@ final class JcrVersionManager implements VersionManager { */ @Override public void restore( Version[] versions, - boolean removeExisting ) throws RepositoryException { + boolean removeExisting ) throws RepositoryException { + session.checkLive(); + if (session.hasPendingChanges()) { + throw new InvalidItemStateException(JcrI18n.noPendingChangesAllowed.text()); + } + Map existingVersions = new HashMap(versions.length); Set versionRootPaths = new HashSet(versions.length); List nonExistingVersions = new ArrayList(versions.length); @@ -517,7 +522,7 @@ final class JcrVersionManager implements VersionManager { } try { - AbstractJcrNode existingNode = session.getNodeByUUID(history.getVersionableUUID()); + AbstractJcrNode existingNode = session.getNodeByIdentifier(history.getVersionableIdentifier()); existingVersions.put(versions[i], existingNode); versionRootPaths.add(existingNode.path()); } catch (ItemNotFoundException infe) { @@ -532,6 +537,7 @@ final class JcrVersionManager implements VersionManager { RestoreCommand op = new RestoreCommand(existingVersions, versionRootPaths, nonExistingVersions, null, removeExisting); op.execute(); + session.save(); } /** @@ -550,6 +556,8 @@ final class JcrVersionManager implements VersionManager { Version version, String labelToRestore, boolean removeExisting ) throws RepositoryException { + session.checkLive(); + if (session().hasPendingChanges()) { throw new InvalidItemStateException(JcrI18n.noPendingChangesAllowed.text()); } @@ -572,15 +580,15 @@ final class JcrVersionManager implements VersionManager { } if (!versionHistory.isSame(existingNode.getVersionHistory())) { - throw new VersionException(JcrI18n.invalidVersion.text(version.getPath(), existingNode.getVersionHistory() - .getPath())); + throw new VersionException(JcrI18n.invalidVersion.text(version.getPath(), + existingNode.getVersionHistory().getPath())); } if (jcrVersion.isSame(versionHistory.getRootVersion())) { throw new VersionException(JcrI18n.cannotRestoreRootVersion.text(existingNode.getPath())); } - } catch (PathNotFoundException pnfe) { + } catch (ItemNotFoundException pnfe) { // This is allowable, but the node needs to be checked out if (!parentNode.isCheckedOut()) { String parentPath = path.getString(context().getNamespaceRegistry()); @@ -625,6 +633,9 @@ final class JcrVersionManager implements VersionManager { void doneMerge( AbstractJcrNode targetNode, Version version ) throws RepositoryException { + session.checkLive(); + checkVersionable(targetNode); + if (targetNode.isNew() || targetNode.isModified()) { throw new InvalidItemStateException(JcrI18n.noPendingChangesAllowedForNode.text()); } @@ -649,12 +660,15 @@ final class JcrVersionManager implements VersionManager { void cancelMerge( AbstractJcrNode targetNode, Version version ) throws RepositoryException { + session.checkLive(); + checkVersionable(targetNode); + if (targetNode.isNew() || targetNode.isModified()) { throw new InvalidItemStateException(JcrI18n.noPendingChangesAllowedForNode.text()); } if (!targetNode.isNodeType(JcrMixLexicon.VERSIONABLE)) { - throw new VersionException(JcrI18n.requiresVersionable.text()); + throw new UnsupportedRepositoryOperationException(JcrI18n.requiresVersionable.text()); } removeVersionFromMergeFailedProperty(targetNode, version); @@ -706,7 +720,10 @@ final class JcrVersionManager implements VersionManager { NodeIterator merge( AbstractJcrNode targetNode, String srcWorkspace, - boolean bestEffort ) throws RepositoryException { + boolean bestEffort, + boolean isShallow ) throws RepositoryException { + session.checkLive(); + if (session().hasPendingChanges()) { throw new InvalidItemStateException(JcrI18n.noPendingChangesAllowed.text()); } @@ -719,7 +736,7 @@ final class JcrVersionManager implements VersionManager { } JcrSession sourceSession = session().with(srcWorkspace); - MergeCommand op = new MergeCommand(targetNode, sourceSession, bestEffort); + MergeCommand op = new MergeCommand(targetNode, sourceSession, bestEffort, isShallow); op.execute(); session.save(); @@ -749,15 +766,22 @@ final class JcrVersionManager implements VersionManager { } void initializeVersionHistoryFor( AbstractJcrNode node ) throws RepositoryException { + initializeVersionHistoryFor(node, null); + } + + void initializeVersionHistoryFor( AbstractJcrNode node, + UUID originalVersionUuid ) throws RepositoryException { Batch batch = session().createBatch(); - initializeVersionHistoryFor(batch, node.nodeInfo(), true); + initializeVersionHistoryFor(batch, node.nodeInfo(), originalVersionUuid, true); batch.execute(); } + void initializeVersionHistoryFor( Graph.Batch batch, Node node, + UUID originalVersionUuid, boolean forceWrite ) throws RepositoryException { if (!cache().isVersionable(node)) return; @@ -773,16 +797,11 @@ final class JcrVersionManager implements VersionManager { UUID historyUuid = UUID.randomUUID(); UUID versionUuid = UUID.randomUUID(); - initializeVersionStorageFor(node, historyUuid, versionUuid); + initializeVersionStorageFor(node, historyUuid, originalVersionUuid, versionUuid); PropertyInfo jcrUuidProp = node.getProperty(JcrLexicon.UUID); - UUID jcrUuid = uuid(jcrUuidProp.getProperty().getFirstValue()); - - Name nameSegment = name(jcrUuid.toString()); - Path historyPath = factories().getPathFactory().createAbsolutePath(JcrLexicon.SYSTEM, - JcrLexicon.VERSION_STORAGE, - nameSegment); + Path historyPath = versionHistoryPathFor(jcrUuid); ValueFactory refFactory = context().getValueFactories().getReferenceFactory(); org.modeshape.graph.property.Property isCheckedOut = propertyFactory().create(JcrLexicon.IS_CHECKED_OUT, true); @@ -804,6 +823,7 @@ final class JcrVersionManager implements VersionManager { void initializeVersionStorageFor( Node node, UUID historyUuid, + UUID originalVersionUuid, UUID versionUuid ) { JcrNodePayload payload = node.getPayload(); @@ -818,30 +838,27 @@ final class JcrVersionManager implements VersionManager { UUID jcrUuid = uuid(jcrUuidProp.getProperty().getFirstValue()); Path historyPath = versionHistoryPathFor(jcrUuid); - systemBatch.create(historyPath) - .with(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.VERSION_HISTORY) - .and(JcrLexicon.VERSIONABLE_UUID, jcrUuid) - .and(JcrLexicon.UUID, historyUuid) - .and(); + systemBatch.create(historyPath).with(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.VERSION_HISTORY).and(JcrLexicon.VERSIONABLE_UUID, + jcrUuid).and(JcrLexicon.UUID, + historyUuid).and(); + + if (originalVersionUuid != null) { + systemBatch.set(JcrLexicon.COPIED_FROM).on(historyPath).to(originalVersionUuid).and(); + } Path versionLabelsPath = path(historyPath, JcrLexicon.VERSION_LABELS); systemBatch.create(versionLabelsPath).with(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.VERSION_LABELS).and(); Path rootVersionPath = path(historyPath, JcrLexicon.ROOT_VERSION); DateTime now = context().getValueFactories().getDateFactory().create(); - systemBatch.create(rootVersionPath) - .with(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.VERSION) - .and(JcrLexicon.CREATED, now) - .and(JcrLexicon.UUID, versionUuid) - .and(); + systemBatch.create(rootVersionPath).with(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.VERSION).and(JcrLexicon.CREATED, now).and(JcrLexicon.UUID, + versionUuid).and(); Path frozenVersionPath = path(rootVersionPath, JcrLexicon.FROZEN_NODE); - systemBatch.create(frozenVersionPath) - .with(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.FROZEN_NODE) - .and(JcrLexicon.FROZEN_UUID, jcrUuid) - .and(JcrLexicon.FROZEN_PRIMARY_TYPE, primaryTypeName) - .and(JcrLexicon.FROZEN_MIXIN_TYPES, mixinTypeNames) - .and(); + systemBatch.create(frozenVersionPath).with(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.FROZEN_NODE).and(JcrLexicon.FROZEN_UUID, + jcrUuid).and(JcrLexicon.FROZEN_PRIMARY_TYPE, + primaryTypeName).and(JcrLexicon.FROZEN_MIXIN_TYPES, + mixinTypeNames).and(); systemBatch.execute(); @@ -883,6 +900,7 @@ final class JcrVersionManager implements VersionManager { // This updates the changedNodes and nonExistingVersions fields as a side effect restoreNodeMixins(frozenNodeFor(version), root); restoreNode(frozenNodeFor(version), root, dateTime(version.getCreated())); + clearCheckoutStatus(frozenNodeFor(version), root); } if (!nonExistingVersions.isEmpty()) { @@ -925,6 +943,8 @@ final class JcrVersionManager implements VersionManager { Node targetNodeInfo = targetNode.nodeInfo(); Node sourceNodeInfo = sourceNode.nodeInfo(); + Set> versionedChildrenThatShouldNotBeRestored = new HashSet>(); + // Try to match the existing nodes with nodes from the version to be restored Map, Node> presentInBoth = new HashMap, Node>(); @@ -937,11 +957,25 @@ final class JcrVersionManager implements VersionManager { // Map the source children to existing target children where possible for (Node sourceChild : sourceNodeInfo.getChildren()) { + boolean isVersionedChild = JcrNtLexicon.VERSIONED_CHILD.equals(name(sourceChild.getProperty(JcrLexicon.PRIMARY_TYPE).getProperty().getFirstValue())); Node resolvedNode = resolveSourceNode(sourceChild, checkinTime); Node match = findMatchFor(resolvedNode); + if (match != null) { + if (isVersionedChild) { + if (!removeExisting) { + Object rawUuid = match.getProperty(JcrLexicon.UUID).getProperty().getFirstValue(); + String uuid = rawUuid == null ? null : rawUuid.toString(); + throw new ItemExistsException(JcrI18n.itemAlreadyExistsWithUuid.text(uuid, + workspace().getName(), + match.getPath())); + } + // use match directly + versionedChildrenThatShouldNotBeRestored.add(match); + } inTargetOnly.remove(match); presentInBoth.put(sourceChild, match); + } else { inSourceOnly.put(sourceChild, resolvedNode); } @@ -979,6 +1013,8 @@ final class JcrVersionManager implements VersionManager { AbstractJcrNode sourceChildNode; AbstractJcrNode targetChildNode; + boolean shouldRestore = !versionedChildrenThatShouldNotBeRestored.contains(targetChild); + if (targetChild != null) { // Reorder if necessary resolvedChild = resolveSourceNode(sourceChild, checkinTime); @@ -991,32 +1027,39 @@ final class JcrVersionManager implements VersionManager { resolvedChild = inSourceOnly.get(sourceChild); sourceChildNode = cache().findJcrNode(resolvedChild.getNodeId(), resolvedChild.getPath()); - Name primaryTypeName = name(resolvedChild.getProperty(JcrLexicon.FROZEN_PRIMARY_TYPE) - .getProperty() - .getFirstValue()); + Name primaryTypeName = name(resolvedChild.getProperty(JcrLexicon.FROZEN_PRIMARY_TYPE).getProperty().getFirstValue()); PropertyInfo uuidProp = resolvedChild.getProperty(JcrLexicon.FROZEN_UUID); UUID desiredUuid = uuid(uuidProp.getProperty().getFirstValue()); targetChildNode = targetEditor.createChild(sourceChild.getName(), desiredUuid, primaryTypeName); - } - // Have to do this first, as the properties below only exist for mix:versionable nodes - restoreNodeMixins(sourceChildNode, targetChildNode); + assert shouldRestore == true; + } - if (sourceChildNode.getParent().isNodeType(JcrNtLexicon.VERSION)) { + if (shouldRestore) { + // Have to do this first, as the properties below only exist for mix:versionable nodes + restoreNodeMixins(sourceChildNode, targetChildNode); - NodeEditor editor = targetChildNode.editor(); - editor.setProperty(JcrLexicon.IS_CHECKED_OUT, targetChildNode.valueFrom(PropertyType.BOOLEAN, false), false); - editor.setProperty(JcrLexicon.BASE_VERSION, targetChildNode.valueFrom(sourceChildNode.getParent()), false); + if (sourceChildNode.getParent().isNodeType(JcrNtLexicon.VERSION)) { + clearCheckoutStatus(sourceChildNode, targetChildNode); + } + restoreNode(sourceChildNode, targetChildNode, checkinTime); } orderBefore(sourceChild, prevChild, targetEditor); - - restoreNode(sourceChildNode, targetChildNode, checkinTime); prevChild = sourceChild; } } + private void clearCheckoutStatus( AbstractJcrNode sourceChildNode, + AbstractJcrNode targetChildNode ) throws RepositoryException { + + NodeEditor editor = targetChildNode.editor(); + editor.setProperty(JcrLexicon.IS_CHECKED_OUT, targetChildNode.valueFrom(PropertyType.BOOLEAN, false), false); + editor.setProperty(JcrLexicon.BASE_VERSION, targetChildNode.valueFrom(sourceChildNode.getParent()), false); + + } + /** * Moves {@code targetNode} immediately before {@code beforeNode} under their shared parent. This version is very * inefficient in that it always tries to move the node, regardless of whether a move is actually required. @@ -1088,8 +1131,7 @@ final class JcrVersionManager implements VersionManager { } Collection> targetProps = new ArrayList>( - targetNode.nodeInfo() - .getProperties()); + targetNode.nodeInfo().getProperties()); for (PropertyInfo propInfo : targetProps) { Name propName = propInfo.getName(); @@ -1151,7 +1193,7 @@ final class JcrVersionManager implements VersionManager { * First try to find a match among the rootless versions in this restore operation */ for (Version version : nonExistingVersions) { - if (uuidString.equals(version.getContainingHistory().getUUID())) { + if (uuidString.equals(version.getContainingHistory().getIdentifier())) { JcrVersionNode versionNode = (JcrVersionNode)version; nonExistingVersions.remove(version); return versionNode.getFrozenNode().nodeInfo(); @@ -1162,7 +1204,7 @@ final class JcrVersionManager implements VersionManager { * Then check the rooted versions in this restore operation */ for (Version version : existingVersions.keySet()) { - if (uuidString.equals(version.getContainingHistory().getUUID())) { + if (uuidString.equals(version.getContainingHistory().getIdentifier())) { JcrVersionNode versionNode = (JcrVersionNode)version; existingVersions.remove(version); return versionNode.getFrozenNode().nodeInfo(); @@ -1308,16 +1350,19 @@ final class JcrVersionManager implements VersionManager { private final Collection failures; private final AbstractJcrNode targetNode; private final boolean bestEffort; + private final boolean isShallow; private final JcrSession sourceSession; private final String workspaceName; public MergeCommand( AbstractJcrNode targetNode, JcrSession sourceSession, - boolean bestEffort ) { + boolean bestEffort, + boolean isShallow ) { super(); this.targetNode = targetNode; this.sourceSession = sourceSession; this.bestEffort = bestEffort; + this.isShallow = isShallow; this.workspaceName = sourceSession.getWorkspace().getName(); this.failures = new LinkedList(); @@ -1339,11 +1384,13 @@ final class JcrVersionManager implements VersionManager { else if n' is not versionable doleave(n). let v be base version of n. let v' be base version of n'. - if v' is a successor of v and n is not checked-in doupdate(n, n'). - else if v is equal to or a predecessor of v' doleave(n). + if v' is an eventual successor of v and n is not checked-in doupdate(n, n'). + else if v is equal to or an eventual predecessor of v' doleave(n). else dofail(n, v'). */ private void doMerge( AbstractJcrNode targetNode ) throws RepositoryException { + // n is targetNode + // n' is sourceNode Path sourcePath = targetNode.correspondingNodePath(workspaceName); AbstractJcrNode sourceNode; @@ -1379,11 +1426,15 @@ final class JcrVersionManager implements VersionManager { } /* - for each child node c of n domerge(c). + if isShallow = false + for each child node c of n domerge(c). */ private void doLeave( AbstractJcrNode targetNode ) throws RepositoryException { - for (NodeIterator iter = targetNode.getNodes(); iter.hasNext();) { - doMerge((AbstractJcrNode)iter.nextNode()); + if (isShallow == false) { + + for (NodeIterator iter = targetNode.getNodes(); iter.hasNext();) { + doMerge((AbstractJcrNode)iter.nextNode()); + } } } @@ -1428,6 +1479,7 @@ final class JcrVersionManager implements VersionManager { Map presentInBoth = new HashMap(targetNodes); presentInBoth.keySet().retainAll(sourceNodes.keySet()); for (AbstractJcrNode node : presentInBoth.values()) { + if (isShallow && node.isNodeType(JcrMixLexicon.VERSIONABLE)) continue; doMerge(node); } } @@ -1445,10 +1497,11 @@ final class JcrVersionManager implements VersionManager { /* if bestEffort = false throw MergeException. - else add UUID of v' (if not already present) to the + else add identifier of v' (if not already present) to the jcr:mergeFailed property of n, - add UUID of n to failedset, - doleave(n). + add identifier of n to failedset, + if isShallow = false + for each versionable child node c of n domerge(c) */ private void doFail( AbstractJcrNode targetNode, @@ -1482,7 +1535,15 @@ final class JcrVersionManager implements VersionManager { } failures.add(targetNode); - doLeave(targetNode); + if (isShallow == false) { + for (NodeIterator iter = targetNode.getNodes(); iter.hasNext();) { + AbstractJcrNode childNode = (AbstractJcrNode)iter.nextNode(); + + if (childNode.isNodeType(JcrMixLexicon.VERSIONABLE)) { + doMerge(childNode); + } + } + } } /** @@ -1504,8 +1565,7 @@ final class JcrVersionManager implements VersionManager { } Collection> targetProps = new ArrayList>( - targetNode.nodeInfo() - .getProperties()); + targetNode.nodeInfo().getProperties()); for (PropertyInfo propInfo : targetProps) { Name propName = propInfo.getName(); @@ -1541,46 +1601,47 @@ final class JcrVersionManager implements VersionManager { @Override public void cancelMerge( String absPath, - Version version ) - throws VersionException, InvalidItemStateException, UnsupportedRepositoryOperationException, RepositoryException { - throw new UnsupportedRepositoryOperationException(); + Version version ) throws VersionException, InvalidItemStateException, RepositoryException { + cancelMerge(session.getNode(absPath), version); } - @Override - public Version checkin( String absPath ) - throws VersionException, UnsupportedRepositoryOperationException, InvalidItemStateException, LockException, - RepositoryException { - throw new UnsupportedRepositoryOperationException(); - } - - @Override - public void checkout( String absPath ) throws UnsupportedRepositoryOperationException, LockException, RepositoryException { - throw new UnsupportedRepositoryOperationException(); + /** + * Throw an {@link UnsupportedRepositoryOperationException} if the node is not versionable (i.e., + * isNodeType(JcrMixLexicon.VERSIONABLE) == false). + * + * @param node the node to check + * @throws UnsupportedRepositoryOperationException if !isNodeType({@link JcrMixLexicon#VERSIONABLE}) + * @throws RepositoryException if an error occurs reading the node types for this node + */ + private void checkVersionable( AbstractJcrNode node ) throws UnsupportedRepositoryOperationException, RepositoryException { + if (!node.isNodeType(JcrMixLexicon.VERSIONABLE)) { + throw new UnsupportedRepositoryOperationException(JcrI18n.requiresVersionable.text()); + } } @Override - public Version checkpoint( String absPath ) - throws VersionException, UnsupportedRepositoryOperationException, InvalidItemStateException, LockException, - RepositoryException { - throw new UnsupportedRepositoryOperationException(); + public Version checkin( String absPath ) + throws VersionException, InvalidItemStateException, LockException, RepositoryException { + return checkin(session.getNode(absPath)); } @Override - public javax.jcr.Node createActivity( String title ) throws UnsupportedRepositoryOperationException, RepositoryException { - throw new UnsupportedRepositoryOperationException(); + public void checkout( String absPath ) throws LockException, RepositoryException { + checkout(session.getNode(absPath)); } @Override - public javax.jcr.Node createConfiguration( String absPath ) - throws UnsupportedRepositoryOperationException, RepositoryException { - throw new UnsupportedRepositoryOperationException(); + public Version checkpoint( String absPath ) + throws VersionException, InvalidItemStateException, LockException, RepositoryException { + Version version = checkin(absPath); + checkout(absPath); + return version; } @Override public void doneMerge( String absPath, - Version version ) - throws VersionException, InvalidItemStateException, UnsupportedRepositoryOperationException, RepositoryException { - throw new UnsupportedRepositoryOperationException(); + Version version ) throws VersionException, InvalidItemStateException, RepositoryException { + doneMerge(session.getNode(absPath), version); } @Override @@ -1589,18 +1650,19 @@ final class JcrVersionManager implements VersionManager { } @Override - public Version getBaseVersion( String absPath ) throws UnsupportedRepositoryOperationException, RepositoryException { - throw new UnsupportedRepositoryOperationException(); + public Version getBaseVersion( String absPath ) throws RepositoryException { + return session.getNode(absPath).getBaseVersion(); } @Override public VersionHistory getVersionHistory( String absPath ) throws UnsupportedRepositoryOperationException, RepositoryException { - throw new UnsupportedRepositoryOperationException(); + return session.getNode(absPath).getVersionHistory(); } @Override public boolean isCheckedOut( String absPath ) throws RepositoryException { - throw new UnsupportedRepositoryOperationException(); + AbstractJcrNode node = session.getNode(absPath); + return node.isCheckedOut(); } @Override @@ -1617,7 +1679,11 @@ final class JcrVersionManager implements VersionManager { boolean isShallow ) throws NoSuchWorkspaceException, AccessDeniedException, MergeException, LockException, InvalidItemStateException, RepositoryException { - throw new UnsupportedRepositoryOperationException(); + CheckArg.isNotNull(srcWorkspace, "source workspace name"); + + AbstractJcrNode node = session.getNode(absPath); + + return merge(node, srcWorkspace, bestEffort, isShallow); } @Override @@ -1626,48 +1692,62 @@ final class JcrVersionManager implements VersionManager { boolean bestEffort ) throws NoSuchWorkspaceException, AccessDeniedException, MergeException, LockException, InvalidItemStateException, RepositoryException { - throw new UnsupportedRepositoryOperationException(); - } - - @Override - public void removeActivity( javax.jcr.Node activityNode ) - throws UnsupportedRepositoryOperationException, VersionException, RepositoryException { - throw new UnsupportedRepositoryOperationException(); + return merge(absPath, srcWorkspace, bestEffort, false); } @Override public void restore( String absPath, String versionName, boolean removeExisting ) - throws VersionException, ItemExistsException, UnsupportedRepositoryOperationException, LockException, - InvalidItemStateException, RepositoryException { - throw new UnsupportedRepositoryOperationException(); + throws VersionException, ItemExistsException, LockException, InvalidItemStateException, RepositoryException { + + Version version = null; + + // See if the node at absPath exists and has version storage. + Path path = session.pathFor(absPath, "absPath"); + + AbstractJcrNode existingNode = session.getNode(path); + VersionHistory historyNode = existingNode.getVersionHistory(); + if (historyNode != null) { + version = historyNode.getVersion(versionName); + } + + assert version != null; + + // AbstractJcrNode versionStorage = session.getRootNode().getNode(JcrLexicon.SYSTEM).getNode(JcrLexicon.VERSION_STORAGE); + // assert versionStorage != null; + + + restore(path, version, null, removeExisting); } @Override public void restore( String absPath, Version version, boolean removeExisting ) - throws PathNotFoundException, ItemExistsException, VersionException, ConstraintViolationException, - UnsupportedRepositoryOperationException, LockException, InvalidItemStateException, RepositoryException { - throw new UnsupportedRepositoryOperationException(); + throws PathNotFoundException, ItemExistsException, VersionException, ConstraintViolationException, LockException, + InvalidItemStateException, RepositoryException { + Path path = session.pathFor(absPath, "absPath"); + + restore(path, version, null, removeExisting); } @Override public void restore( Version version, boolean removeExisting ) - throws VersionException, ItemExistsException, InvalidItemStateException, UnsupportedRepositoryOperationException, - LockException, RepositoryException { - throw new UnsupportedRepositoryOperationException(); + throws VersionException, ItemExistsException, InvalidItemStateException, LockException, RepositoryException { + AbstractJcrNode node = session.getNodeByIdentifier(version.getContainingHistory().getVersionableIdentifier()); + Path path = node.path(); + + restore(path, version, null, removeExisting); } @Override public void restoreByLabel( String absPath, String versionLabel, boolean removeExisting ) - throws VersionException, ItemExistsException, UnsupportedRepositoryOperationException, LockException, - InvalidItemStateException, RepositoryException { - throw new UnsupportedRepositoryOperationException(); + throws VersionException, ItemExistsException, LockException, InvalidItemStateException, RepositoryException { + session.getNode(absPath).restoreByLabel(versionLabel, removeExisting); } @Override @@ -1676,4 +1756,21 @@ final class JcrVersionManager implements VersionManager { throw new UnsupportedRepositoryOperationException(); } + @Override + public void removeActivity( javax.jcr.Node activityNode ) + throws UnsupportedRepositoryOperationException, VersionException, RepositoryException { + throw new UnsupportedRepositoryOperationException(); + } + + @Override + public javax.jcr.Node createActivity( String title ) throws UnsupportedRepositoryOperationException, RepositoryException { + throw new UnsupportedRepositoryOperationException(); + } + + @Override + public javax.jcr.Node createConfiguration( String absPath ) + throws UnsupportedRepositoryOperationException, RepositoryException { + throw new UnsupportedRepositoryOperationException(); + } + } Index: modeshape-jcr/src/main/java/org/modeshape/jcr/JcrVersionNode.java =================================================================== --- modeshape-jcr/src/main/java/org/modeshape/jcr/JcrVersionNode.java (revision 1893) +++ modeshape-jcr/src/main/java/org/modeshape/jcr/JcrVersionNode.java (working copy) @@ -29,7 +29,6 @@ import java.util.List; import javax.jcr.ItemNotFoundException; import javax.jcr.Property; import javax.jcr.RepositoryException; -import javax.jcr.UnsupportedRepositoryOperationException; import javax.jcr.Value; import javax.jcr.version.Version; import org.modeshape.graph.property.Name; @@ -114,6 +113,22 @@ class JcrVersionNode extends JcrNode implements Version { } + private final JcrVersionNode getFirstNodeForProperty( Name propertyName ) throws RepositoryException { + assert JcrLexicon.SUCCESSORS.equals(propertyName) || JcrLexicon.PREDECESSORS.equals(propertyName); + + Property references = getProperty(propertyName); + + if (references == null) return null; + + Value[] values = references.getValues(); + if (values.length == 0) return null; + + String uuid = values[0].getString(); + AbstractJcrNode node = session().getNodeByUUID(uuid); + + return (JcrVersionNode)node; + } + boolean isSuccessorOf( JcrVersionNode other ) throws RepositoryException { if (!other.hasProperty(JcrLexicon.SUCCESSORS)) return false; @@ -130,13 +145,13 @@ class JcrVersionNode extends JcrNode implements Version { } @Override - public Version getLinearPredecessor() throws RepositoryException { - throw new UnsupportedRepositoryOperationException(); + public JcrVersionNode getLinearPredecessor() throws RepositoryException { + return getFirstNodeForProperty(JcrLexicon.PREDECESSORS); } @Override - public Version getLinearSuccessor() throws RepositoryException { - throw new UnsupportedRepositoryOperationException(); + public JcrVersionNode getLinearSuccessor() throws RepositoryException { + return getFirstNodeForProperty(JcrLexicon.SUCCESSORS); } } Index: modeshape-jcr/src/main/java/org/modeshape/jcr/JcrWorkspace.java =================================================================== --- modeshape-jcr/src/main/java/org/modeshape/jcr/JcrWorkspace.java (revision 1893) +++ modeshape-jcr/src/main/java/org/modeshape/jcr/JcrWorkspace.java (working copy) @@ -33,7 +33,6 @@ import java.util.Map; import java.util.Set; import java.util.UUID; import javax.jcr.AccessDeniedException; -import javax.jcr.InvalidItemStateException; import javax.jcr.InvalidSerializedDataException; import javax.jcr.ItemExistsException; import javax.jcr.ItemNotFoundException; @@ -572,8 +571,16 @@ class JcrWorkspace implements Workspace { while (!nodesToCheck.isEmpty()) { AbstractJcrNode node = nodesToCheck.remove(0); - // This returns silently if the node is not versionable - versionManager.initializeVersionHistoryFor(node); + if (node.isNodeType(JcrMixLexicon.VERSIONABLE)) { + // Find the node that this was copied from + Path nodeDestPath = node.path().relativeTo(destPath); + Path nodeSourcePath = nodeDestPath.resolveAgainst(srcPath); + + AbstractJcrNode fromNode = cache.findJcrNode(Location.create(nodeSourcePath)); + UUID originalVersion = fromNode.getBaseVersion().uuid(); + + versionManager.initializeVersionHistoryFor(node, originalVersion); + } for (NodeIterator iter = node.getNodes(); iter.hasNext();) { nodesToCheck.add((AbstractJcrNode)iter.nextNode()); @@ -740,11 +747,6 @@ class JcrWorkspace implements Workspace { */ public void restore( Version[] versions, boolean removeExisting ) throws RepositoryException { - session.checkLive(); - if (session.hasPendingChanges()) { - throw new InvalidItemStateException(JcrI18n.noPendingChangesAllowed.text()); - } - versionManager().restore(versions, removeExisting); } Index: modeshape-jcr/src/main/java/org/modeshape/jcr/SessionCache.java =================================================================== --- modeshape-jcr/src/main/java/org/modeshape/jcr/SessionCache.java (revision 1893) +++ modeshape-jcr/src/main/java/org/modeshape/jcr/SessionCache.java (working copy) @@ -2678,7 +2678,7 @@ class SessionCache { // Some tests don't set this up. if (workspace != null) { - workspace.versionManager().initializeVersionHistoryFor(batch, node, false); + workspace.versionManager().initializeVersionHistoryFor(batch, node, null, false); } } catch (RepositoryException re) { throw new IllegalStateException(re); Index: modeshape-jcr/src/test/java/org/modeshape/jcr/AbstractJcrNodeTest.java =================================================================== --- modeshape-jcr/src/test/java/org/modeshape/jcr/AbstractJcrNodeTest.java (revision 1893) +++ modeshape-jcr/src/test/java/org/modeshape/jcr/AbstractJcrNodeTest.java (working copy) @@ -44,7 +44,6 @@ import javax.jcr.Property; import javax.jcr.PropertyIterator; import javax.jcr.PropertyType; import javax.jcr.Repository; -import javax.jcr.UnsupportedRepositoryOperationException; import javax.jcr.Workspace; import javax.jcr.lock.LockException; import javax.jcr.nodetype.NoSuchNodeTypeException; @@ -540,31 +539,6 @@ public class AbstractJcrNodeTest extends AbstractJcrTest { * and are in MixinTest */ - @Test( expected = UnsupportedRepositoryOperationException.class ) - public void shouldNotAllowCancelMergeOfNonVersionableNode() throws Exception { - hybrid.cancelMerge(null); - } - - @Test( expected = UnsupportedRepositoryOperationException.class ) - public void shouldNotAllowCheckinOfNonVersionableNode() throws Exception { - hybrid.checkin(); - } - - @Test( expected = UnsupportedRepositoryOperationException.class ) - public void shouldNotAllowCheckoutOfNonVersionableNode() throws Exception { - hybrid.checkout(); - } - - @Test( expected = UnsupportedRepositoryOperationException.class ) - public void shouldNotAllowDoneMergeOfNonVersionableNode() throws Exception { - hybrid.doneMerge(null); - } - - @Test( expected = UnsupportedRepositoryOperationException.class ) - public void shouldNotAllowGetBaseVersionOfNonVersionableNode() throws Exception { - hybrid.getBaseVersion(); - } - @Test( expected = LockException.class ) public void shouldNotAllowGetLockIfNoLock() throws Exception { hybrid.getLock(); @@ -648,11 +622,6 @@ public class AbstractJcrNodeTest extends AbstractJcrTest { assertThat((JcrSession)prius.getSession(), is(jcrSession)); } - @Test( expected = UnsupportedRepositoryOperationException.class ) - public void shouldNotAllowGetVersionHistory() throws Exception { - cars.getVersionHistory(); - } - /* * Same-ness */ Index: modeshape-jcr/src/test/java/org/modeshape/jcr/JcrRepositoryTest.java =================================================================== --- modeshape-jcr/src/test/java/org/modeshape/jcr/JcrRepositoryTest.java (revision 1893) +++ modeshape-jcr/src/test/java/org/modeshape/jcr/JcrRepositoryTest.java (working copy) @@ -503,7 +503,7 @@ public class JcrRepositoryTest { assertThat(repository.getDescriptor(Repository.REP_VERSION_DESC), is(notNullValue())); // assertThat(repository.getDescriptor(Repository.REP_VERSION_DESC), is("1.1-SNAPSHOT")); assertThat(repository.getDescriptor(Repository.SPEC_NAME_DESC), is(JcrI18n.SPEC_NAME_DESC.text())); - assertThat(repository.getDescriptor(Repository.SPEC_VERSION_DESC), is("1.0")); + assertThat(repository.getDescriptor(Repository.SPEC_VERSION_DESC), is("2.0")); } @Ignore( "GC behavior is non-deterministic from the application's POV - this test _will_ occasionally fail" ) Index: modeshape-jcr/src/test/java/org/modeshape/jcr/JcrTckTest.java =================================================================== --- modeshape-jcr/src/test/java/org/modeshape/jcr/JcrTckTest.java (revision 1893) +++ modeshape-jcr/src/test/java/org/modeshape/jcr/JcrTckTest.java (working copy) @@ -179,6 +179,38 @@ import org.apache.jackrabbit.test.api.query.XPathDocOrderTest; import org.apache.jackrabbit.test.api.query.XPathJcrPathTest; import org.apache.jackrabbit.test.api.query.XPathPosIndexTest; import org.apache.jackrabbit.test.api.query.XPathQueryLevel2Test; +import org.apache.jackrabbit.test.api.version.ActivitiesTest; +import org.apache.jackrabbit.test.api.version.CheckinTest; +import org.apache.jackrabbit.test.api.version.CheckoutTest; +import org.apache.jackrabbit.test.api.version.ConfigurationsTest; +import org.apache.jackrabbit.test.api.version.CopyTest; +import org.apache.jackrabbit.test.api.version.GetContainingHistoryTest; +import org.apache.jackrabbit.test.api.version.GetCreatedTest; +import org.apache.jackrabbit.test.api.version.GetPredecessorsTest; +import org.apache.jackrabbit.test.api.version.GetReferencesNodeTest; +import org.apache.jackrabbit.test.api.version.GetVersionableUUIDTest; +import org.apache.jackrabbit.test.api.version.MergeCancelMergeTest; +import org.apache.jackrabbit.test.api.version.MergeCheckedoutSubNodeTest; +import org.apache.jackrabbit.test.api.version.MergeDoneMergeTest; +import org.apache.jackrabbit.test.api.version.MergeNodeIteratorTest; +import org.apache.jackrabbit.test.api.version.MergeNodeTest; +import org.apache.jackrabbit.test.api.version.MergeNonVersionableSubNodeTest; +import org.apache.jackrabbit.test.api.version.MergeShallowTest; +import org.apache.jackrabbit.test.api.version.MergeSubNodeTest; +import org.apache.jackrabbit.test.api.version.OnParentVersionAbortTest; +import org.apache.jackrabbit.test.api.version.OnParentVersionComputeTest; +import org.apache.jackrabbit.test.api.version.OnParentVersionCopyTest; +import org.apache.jackrabbit.test.api.version.OnParentVersionIgnoreTest; +import org.apache.jackrabbit.test.api.version.OnParentVersionInitializeTest; +import org.apache.jackrabbit.test.api.version.RemoveVersionTest; +import org.apache.jackrabbit.test.api.version.SessionMoveVersionExceptionTest; +import org.apache.jackrabbit.test.api.version.VersionGraphTest; +import org.apache.jackrabbit.test.api.version.VersionHistoryTest; +import org.apache.jackrabbit.test.api.version.VersionLabelTest; +import org.apache.jackrabbit.test.api.version.VersionStorageTest; +import org.apache.jackrabbit.test.api.version.VersionTest; +import org.apache.jackrabbit.test.api.version.WorkspaceMoveVersionExceptionTest; +import org.apache.jackrabbit.test.api.version.WorkspaceRestoreTest; /** * Test suite to wrap Apache Jackrabbit JCR technology compatibility kit (TCK) unit tests. Note that technically these are not the @@ -207,7 +239,6 @@ public class JcrTckTest { suite.addTest(levelOneSuite()); suite.addTest(levelTwoSuite()); suite.addTest(new OptionalFeatureTests()); - return suite; } @@ -411,6 +442,7 @@ public class JcrTckTest { // addTest(org.apache.jackrabbit.test.api.observation.TestAll.suite()); // addTest(org.apache.jackrabbit.test.api.version.TestAll.suite()); + addTest(new FullVersioningTests()); addTest(org.apache.jackrabbit.test.api.lock.TestAll.suite()); addTest(org.apache.jackrabbit.test.api.util.TestAll.suite()); // addTest(org.apache.jackrabbit.test.api.query.TestAll.suite()); @@ -486,4 +518,49 @@ public class JcrTckTest { // addTestSuite(WorkspaceOperationTest.class); } } + + private static class FullVersioningTests extends TestSuite { + protected FullVersioningTests() { + super("JCR Full Versioning Tests"); + + addTestSuite(VersionTest.class); + addTestSuite(VersionHistoryTest.class); + addTestSuite(VersionStorageTest.class); + addTestSuite(VersionLabelTest.class); + addTestSuite(CheckoutTest.class); + addTestSuite(CheckinTest.class); + addTestSuite(CopyTest.class); + addTestSuite(VersionGraphTest.class); + addTestSuite(RemoveVersionTest.class); + // addTestSuite(RestoreTest.class); + addTestSuite(WorkspaceRestoreTest.class); + addTestSuite(OnParentVersionAbortTest.class); + addTestSuite(OnParentVersionComputeTest.class); + addTestSuite(OnParentVersionCopyTest.class); + addTestSuite(OnParentVersionIgnoreTest.class); + addTestSuite(OnParentVersionInitializeTest.class); + addTestSuite(GetReferencesNodeTest.class); + addTestSuite(GetPredecessorsTest.class); + addTestSuite(GetCreatedTest.class); + addTestSuite(GetContainingHistoryTest.class); + addTestSuite(GetVersionableUUIDTest.class); + addTestSuite(SessionMoveVersionExceptionTest.class); + addTestSuite(WorkspaceMoveVersionExceptionTest.class); + addTestSuite(MergeCancelMergeTest.class); + addTestSuite(MergeCheckedoutSubNodeTest.class); + addTestSuite(MergeDoneMergeTest.class); + addTestSuite(MergeNodeIteratorTest.class); + addTestSuite(MergeNodeTest.class); + addTestSuite(MergeShallowTest.class); + // addTestSuite(MergeActivityTest.class); + addTestSuite(MergeNonVersionableSubNodeTest.class); + addTestSuite(MergeSubNodeTest.class); + + // JCR 2.0 + + addTestSuite(ActivitiesTest.class); + addTestSuite(ConfigurationsTest.class); + } + } + } Index: modeshape-jcr/src/test/resources/repositoryStubImpl.properties =================================================================== --- modeshape-jcr/src/test/resources/repositoryStubImpl.properties (revision 1893) +++ modeshape-jcr/src/test/resources/repositoryStubImpl.properties (working copy) @@ -8,6 +8,9 @@ javax.jcr.tck.nodename4=node4 javax.jcr.tck.propertyname1=prop1 javax.jcr.tck.propertyname2=prop2 javax.jcr.tck.propertyname3=prop3 +javax.jcr.tck.propertyValue1=value1 +javax.jcr.tck.propertyValue2=value2 +javax.jcr.tck.destination=/testroot/workarea/destinationNode javax.jcr.tck.workspacename=otherWorkspace javax.jcr.tck.nodetype=nt\:unstructured javax.jcr.tck.nodetype2=modetest\:referenceableUnstructured