jackrabbit-oak-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mreut...@apache.org
Subject svn commit: r1421137 [1/2] - in /jackrabbit/oak/trunk: oak-core/ oak-core/src/main/java/org/apache/jackrabbit/oak/core/ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/ oak-core/src/main/java/org/apache/jackrabbit/oak/version/ oak-jcr/...
Date Thu, 13 Dec 2012 08:44:25 GMT
Author: mreutegg
Date: Thu Dec 13 08:44:19 2012
New Revision: 1421137

URL: http://svn.apache.org/viewvc?rev=1421137&view=rev
Log:
OAK-168: Basic JCR VersionManager support
- initial check in (still lots of TODOs)

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/ReadOnlyVersionManager.java   (with props)
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/ReadWriteVersionManager.java   (with props)
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/SimpleRoot.java   (with props)
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionHook.java   (with props)
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/ReadWriteVersionManager.java   (with props)
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionDelegate.java   (with props)
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionHistoryDelegate.java   (with props)
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionHistoryImpl.java   (with props)
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionManagerDelegate.java   (with props)
Modified:
    jackrabbit/oak/trunk/oak-core/pom.xml
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/ReadOnlyTree.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/version/VersionConstants.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/Jcr.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/NodeDelegate.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/NodeImpl.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/PropertyImpl.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionDelegate.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionImpl.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/WorkspaceImpl.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/lock/LockManagerImpl.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/query/QueryImpl.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionImpl.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionManagerImpl.java

Modified: jackrabbit/oak/trunk/oak-core/pom.xml
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/pom.xml?rev=1421137&r1=1421136&r2=1421137&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/pom.xml (original)
+++ jackrabbit/oak/trunk/oak-core/pom.xml Thu Dec 13 08:44:19 2012
@@ -55,6 +55,7 @@
                 org.apache.jackrabbit.oak.plugins.name,
                 org.apache.jackrabbit.oak.plugins.nodetype,
                 org.apache.jackrabbit.oak.plugins.observation,
+                org.apache.jackrabbit.oak.plugins.version,
                 org.apache.jackrabbit.oak.spi.query,
                 org.apache.jackrabbit.oak.spi.commit,
                 org.apache.jackrabbit.oak.spi.lifecycle,

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/ReadOnlyTree.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/ReadOnlyTree.java?rev=1421137&r1=1421136&r2=1421137&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/ReadOnlyTree.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/ReadOnlyTree.java Thu Dec 13 08:44:19 2012
@@ -24,6 +24,7 @@ import org.apache.jackrabbit.oak.api.Pro
 import org.apache.jackrabbit.oak.api.Tree;
 import org.apache.jackrabbit.oak.api.TreeLocation;
 import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 
@@ -132,8 +133,7 @@ public class ReadOnlyTree implements Tre
 
     @Override
     public TreeLocation getLocation() {
-        // TODO: add implementation
-        throw new UnsupportedOperationException();
+        return new NodeLocation(this);
     }
 
     @Override
@@ -216,4 +216,108 @@ public class ReadOnlyTree implements Tre
         throw new UnsupportedOperationException();
     }
 
+    //-------------------------------------------------------< TreeLocation >---
+
+    private class NodeLocation implements TreeLocation {
+        private final ReadOnlyTree tree;
+
+        private NodeLocation(ReadOnlyTree tree) {
+            this.tree = checkNotNull(tree);
+        }
+
+        @Override
+        public TreeLocation getParent() {
+            return tree.parent == null
+                    ? TreeLocation.NULL
+                    : new NodeLocation(tree.parent);
+        }
+
+        @Override
+        public TreeLocation getChild(String relPath) {
+            checkArgument(!relPath.startsWith("/"));
+            if (relPath.isEmpty()) {
+                return this;
+            }
+
+            ReadOnlyTree child = tree;
+            String parentPath = PathUtils.getParentPath(relPath);
+            for (String name : PathUtils.elements(parentPath)) {
+                child = child.getChild(name);
+                if (child == null) {
+                    return TreeLocation.NULL;
+                }
+            }
+
+            String name = PathUtils.getName(relPath);
+            PropertyState property = child.getProperty(name);
+            if (property != null) {
+                return new PropertyLocation(new NodeLocation(child), name);
+            } else {
+                child = child.getChild(name);
+                return child == null
+                        ? TreeLocation.NULL
+                        : new NodeLocation(child);
+            }
+        }
+
+        @Override
+        public String getPath() {
+            return tree.getPath();
+        }
+
+        @Override
+        public Tree getTree() {
+            return tree;
+        }
+
+        @Override
+        public PropertyState getProperty() {
+            return null;
+        }
+
+        @Override
+        public Status getStatus() {
+            return tree.getStatus();
+        }
+    }
+
+    private class PropertyLocation implements TreeLocation {
+        private final NodeLocation parent;
+        private final String name;
+
+        private PropertyLocation(NodeLocation parent, String name) {
+            this.parent = checkNotNull(parent);
+            this.name = checkNotNull(name);
+        }
+
+        @Override
+        public TreeLocation getParent() {
+            return parent;
+        }
+
+        @Override
+        public TreeLocation getChild(String relPath) {
+            return TreeLocation.NULL;
+        }
+
+        @Override
+        public String getPath() {
+            return PathUtils.concat(parent.getPath(), name);
+        }
+
+        @Override
+        public Tree getTree() {
+            return null;
+        }
+
+        @Override
+        public PropertyState getProperty() {
+            return parent.tree.getProperty(name);
+        }
+
+        @Override
+        public Status getStatus() {
+            return Status.EXISTING;
+        }
+    }
 }

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/ReadOnlyVersionManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/ReadOnlyVersionManager.java?rev=1421137&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/ReadOnlyVersionManager.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/ReadOnlyVersionManager.java Thu Dec 13 08:44:19 2012
@@ -0,0 +1,239 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.version;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+import javax.jcr.RepositoryException;
+import javax.jcr.UnsupportedRepositoryOperationException;
+import javax.jcr.version.VersionManager;
+
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.TreeLocation;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.plugins.identifier.IdentifierManager;
+import org.apache.jackrabbit.oak.plugins.nodetype.ReadOnlyNodeTypeManager;
+import org.apache.jackrabbit.oak.version.VersionConstants;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * <code>ReadOnlyVersionManager</code> provides implementations for read-only
+ * version operations modeled after the ones available in {@link VersionManager}.
+ */
+public abstract class ReadOnlyVersionManager {
+
+    /**
+     * @return the read-only {@link Tree} for the jcr:versionStorage node. The
+     *         returned <code>Tree</code> instance must be up-to-date with the
+     *         <code>Root</code> returned by {@link #getWorkspaceRoot()}.
+     */
+    @Nonnull
+    protected abstract Tree getVersionStorageTree();
+
+    /**
+     * @return the <code>Root</code> of the workspace.
+     */
+    @Nonnull
+    protected abstract Root getWorkspaceRoot();
+
+    /**
+     * @return the node type manager of this repository.
+     */
+    @Nonnull
+    protected abstract ReadOnlyNodeTypeManager getNodeTypeManager();
+
+    /**
+     * Returns <code>true</code> if the tree is checked out; otherwise
+     * <code>false</code>.
+     *
+     * @param tree the tree to check.
+     * @return whether the tree is checked out or not.
+     */
+    public boolean isCheckedOut(@Nonnull Tree tree) {
+        return isCheckedOut(checkNotNull(tree).getLocation());
+    }
+
+    /**
+     * Returns <code>true</code> if the tree at the given absolute Oak path is
+     * checked out; otherwise <code>false</code>.
+     *
+     * @param absOakPath an absolute path.
+     * @return whether the tree at the given location is checked out or not.
+     */
+    public boolean isCheckedOut(@Nonnull String absOakPath) {
+        return isCheckedOut(getWorkspaceRoot().getLocation(checkNotNull(absOakPath)));
+    }
+
+    /**
+     * Returns the tree representing the version history of the given
+     * versionable tree or <code>null</code> if none exists yet.
+     *
+     * @param versionable the versionable tree.
+     * @return the version history or <code>null</code> if none exists yet.
+     * @throws UnsupportedRepositoryOperationException
+     *                             if the versionable tree is not actually
+     *                             versionable.
+     * @throws RepositoryException if an error occurs while checking the node
+     *                             type of the tree.
+     */
+    @CheckForNull
+    public Tree getVersionHistory(@Nonnull Tree versionable)
+            throws UnsupportedRepositoryOperationException,
+            RepositoryException {
+        checkVersionable(versionable);
+        String uuid = versionable.getProperty(
+                VersionConstants.JCR_UUID).getValue(Type.STRING);
+        return getVersionStorageTree().getLocation().getChild(
+                getVersionHistoryPath(uuid)).getTree();
+    }
+
+    /**
+     * Returns the path of the version history for the given <code>uuid</code>.
+     * The returned path is relative to the version storage tree as returned
+     * by {@link #getVersionStorageTree()}.
+     *
+     * @param uuid the uuid of the versionable node
+     * @return the relative path of the version history for the given uuid.
+     */
+    @Nonnull
+    public String getVersionHistoryPath(@Nonnull String uuid) {
+        String relPath = "";
+        for (int i = 0; i < 3; i++) {
+            String name = uuid.substring(i * 2, i * 2 + 2);
+            relPath = PathUtils.concat(relPath, name);
+        }
+        return PathUtils.concat(relPath, uuid);
+    }
+
+    /**
+     * Returns the tree representing the base version of the given versionable
+     * tree or <code>null</code> if none exists yet. This is the case when a
+     * versionable node is created, but is not yet saved.
+     *
+     * @param versionable the versionable tree.
+     * @return the tree representing the base version or <code>null</code>.
+     * @throws UnsupportedRepositoryOperationException
+     *                             if the versionable tree is not actually
+     *                             versionable.
+     * @throws RepositoryException if an error occurs while checking the node
+     *                             type of the tree.
+     */
+    @CheckForNull
+    public Tree getBaseVersion(@Nonnull Tree versionable)
+            throws UnsupportedRepositoryOperationException,
+            RepositoryException {
+        checkVersionable(versionable);
+        PropertyState p = versionable.getProperty(VersionConstants.JCR_BASEVERSION);
+        if (p == null) {
+            // version history does not yet exist
+            return null;
+        }
+        return getIdentifierManager().getTree(p.getValue(Type.STRING));
+    }
+
+    //----------------------------< internal >----------------------------------
+
+    @Nonnull
+    private static String getRelativePath(@Nonnull String absJcrPath) {
+        checkArgument(checkNotNull(absJcrPath).startsWith("/"),
+                "Path is not absolute: " + absJcrPath);
+        String relPath = absJcrPath.substring(1);
+        checkArgument(!relPath.startsWith("/"), "Invalid path: " + absJcrPath);
+        return relPath;
+    }
+
+    /**
+     * @return an identifier manager that is able to resolve identifiers of
+     * nodes in the version storage.
+     */
+    protected IdentifierManager getIdentifierManager() {
+        // FIXME: may need to revise this, because getVersionStorageTree()
+        // is not the same Root as getWorkspaceRoot()
+        return new IdentifierManager(getWorkspaceRoot());
+    }
+
+    protected static boolean isCheckedOut(@Nonnull TreeLocation location) {
+        Tree t = checkNotNull(location).getTree();
+        if (t != null) {
+            PropertyState p = t.getProperty(VersionConstants.JCR_ISCHECKEDOUT);
+            if (p != null) {
+                return p.getValue(Type.BOOLEAN);
+            }
+        } else {
+            // FIXME: this actually means access to the tree is restricted
+            // and may result in wrong isCheckedOut value. This should never
+            // be the case in a commit hook because it operates on non-access-
+            // controlled NodeStates. This means consistency is not at risk
+            // but it may mean oak-jcr sees a node as checked out even though
+            // it is in fact read-only because of a checked-in ancestor.
+        }
+        // otherwise return checkedOut status of parent
+        if (location.getParent() == TreeLocation.NULL) {
+            // root tree
+            return true;
+        } else {
+            return isCheckedOut(location.getParent());
+        }
+    }
+
+    /**
+     * Checks if the given <code>tree</code> is versionable and throws a {@link
+     * UnsupportedRepositoryOperationException} if it is not.
+     *
+     * @param tree the tree to check.
+     * @return the passed tree.
+     * @throws UnsupportedRepositoryOperationException
+     *                             if the tree is not versionable.
+     * @throws RepositoryException if an error occurs while checking the node
+     *                             type of the tree.
+     */
+    @Nonnull
+    protected Tree checkVersionable(@Nonnull Tree tree)
+            throws UnsupportedRepositoryOperationException,
+            RepositoryException {
+        if (!isVersionable(checkNotNull(tree))) {
+            throw new UnsupportedRepositoryOperationException("Node at " +
+                    tree.getPath() + " is not versionable");
+        }
+        return tree;
+    }
+
+    /**
+     * Returns <code>true</code> if the given <code>tree</code> is of type
+     * <code>mix:versionable</code>; <code>false</code> otherwise.
+     *
+     * @param tree the tree to check.
+     * @return whether the <code>tree</code> is versionable.
+     * @throws RepositoryException if an error occurs while checking the node
+     *              type of the tree.
+     */
+    protected boolean isVersionable(@Nonnull Tree tree) throws RepositoryException {
+        checkNotNull(tree);
+        // the first check for the jcr:isCheckedOut property will fail fast
+        // if the node is not versionable. the second check is to make sure
+        // the node is in fact versionable.
+        return tree.hasProperty(VersionConstants.JCR_ISCHECKEDOUT)
+                && getNodeTypeManager().isNodeType(tree, VersionConstants.MIX_VERSIONABLE);
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/ReadOnlyVersionManager.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/ReadOnlyVersionManager.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/ReadWriteVersionManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/ReadWriteVersionManager.java?rev=1421137&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/ReadWriteVersionManager.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/ReadWriteVersionManager.java Thu Dec 13 08:44:19 2012
@@ -0,0 +1,198 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.version;
+
+import java.util.Collections;
+import java.util.GregorianCalendar;
+import java.util.Iterator;
+
+import javax.annotation.Nonnull;
+
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.TreeLocation;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.core.ReadOnlyTree;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.plugins.identifier.IdentifierManager;
+import org.apache.jackrabbit.oak.plugins.nodetype.ReadOnlyNodeTypeManager;
+import org.apache.jackrabbit.oak.plugins.value.Conversions;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.util.TODO;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.apache.jackrabbit.JcrConstants.JCR_BASEVERSION;
+import static org.apache.jackrabbit.JcrConstants.JCR_CREATED;
+import static org.apache.jackrabbit.JcrConstants.JCR_FROZENMIXINTYPES;
+import static org.apache.jackrabbit.JcrConstants.JCR_FROZENNODE;
+import static org.apache.jackrabbit.JcrConstants.JCR_FROZENPRIMARYTYPE;
+import static org.apache.jackrabbit.JcrConstants.JCR_FROZENUUID;
+import static org.apache.jackrabbit.JcrConstants.JCR_ISCHECKEDOUT;
+import static org.apache.jackrabbit.JcrConstants.JCR_MIXINTYPES;
+import static org.apache.jackrabbit.JcrConstants.JCR_PREDECESSORS;
+import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
+import static org.apache.jackrabbit.JcrConstants.JCR_ROOTVERSION;
+import static org.apache.jackrabbit.JcrConstants.JCR_SUCCESSORS;
+import static org.apache.jackrabbit.JcrConstants.JCR_UUID;
+import static org.apache.jackrabbit.JcrConstants.JCR_VERSIONABLEUUID;
+import static org.apache.jackrabbit.JcrConstants.JCR_VERSIONHISTORY;
+import static org.apache.jackrabbit.JcrConstants.JCR_VERSIONLABELS;
+import static org.apache.jackrabbit.JcrConstants.NT_FROZENNODE;
+import static org.apache.jackrabbit.JcrConstants.NT_VERSION;
+import static org.apache.jackrabbit.JcrConstants.NT_VERSIONHISTORY;
+import static org.apache.jackrabbit.JcrConstants.NT_VERSIONLABELS;
+import static org.apache.jackrabbit.oak.version.VersionConstants.REP_VERSIONSTORAGE;
+
+/**
+ * <code>ReadWriteVersionManager</code>...
+ */
+class ReadWriteVersionManager extends ReadOnlyVersionManager {
+
+    private final NodeBuilder versionStorageNode;
+    private final NodeBuilder workspaceRoot;
+
+    ReadWriteVersionManager(NodeBuilder versionStorageNode,
+                            NodeBuilder workspaceRoot) {
+        this.versionStorageNode = checkNotNull(versionStorageNode);
+        this.workspaceRoot = checkNotNull(workspaceRoot);
+    }
+
+    @Nonnull
+    @Override
+    protected Tree getVersionStorageTree() {
+        return new ReadOnlyTree(versionStorageNode.getNodeState());
+    }
+
+    @Nonnull
+    @Override
+    protected Root getWorkspaceRoot() {
+        return new SimpleRoot(new ReadOnlyTree(workspaceRoot.getNodeState()));
+    }
+
+    @Nonnull
+    @Override
+    protected ReadOnlyNodeTypeManager getNodeTypeManager() {
+        return ReadOnlyNodeTypeManager.getInstance(
+                getWorkspaceRoot(), NamePathMapper.DEFAULT);
+    }
+
+    /**
+     * Gets or creates the version history for the given
+     * <code>versionable</code> node.
+     *
+     * @param versionable the versionable node.
+     * @return the version history node.
+     * @throws IllegalArgumentException if the given node does not have a
+     *                                  <code>jcr:uuid</code> property.
+     */
+    @Nonnull
+    NodeBuilder getOrCreateVersionHistory(@Nonnull NodeBuilder versionable) {
+        checkNotNull(versionable);
+        PropertyState p = versionable.getProperty(JCR_UUID);
+        if (p == null) {
+            throw new IllegalArgumentException("Not referenceable");
+        }
+        String vUUID = p.getValue(Type.STRING);
+        String relPath = getVersionHistoryPath(vUUID);
+        NodeBuilder node = versionStorageNode;
+        for (Iterator<String> it = PathUtils.elements(relPath).iterator(); it.hasNext(); ) {
+            String name = it.next();
+            node = node.child(name);
+            if (node.getProperty(JCR_PRIMARYTYPE) == null) {
+                String nt;
+                if (it.hasNext()) {
+                    nt = REP_VERSIONSTORAGE;
+                } else {
+                    // last path element denotes nt:versionHistory node
+                    nt = NT_VERSIONHISTORY;
+                }
+                node.setProperty(JCR_PRIMARYTYPE, nt, Type.NAME);
+            }
+        }
+        // use jcr:versionLabels node to detect if we need to initialize the
+        // version history
+        if (!node.hasChildNode(JCR_VERSIONLABELS)) {
+            // jcr:versionableUuuid property
+            node.setProperty(JCR_VERSIONABLEUUID, vUUID, Type.STRING);
+            node.setProperty(JCR_UUID,
+                    IdentifierManager.generateUUID(), Type.STRING);
+
+            // jcr:versionLabels child node
+            NodeBuilder vLabels = node.child(JCR_VERSIONLABELS);
+            vLabels.setProperty(JCR_PRIMARYTYPE, NT_VERSIONLABELS, Type.NAME);
+
+            // jcr:rootVersion child node
+            NodeBuilder rootVersion = node.child(JCR_ROOTVERSION);
+            rootVersion.setProperty(JCR_UUID,
+                    IdentifierManager.generateUUID(), Type.STRING);
+            rootVersion.setProperty(JCR_PRIMARYTYPE, NT_VERSION, Type.NAME);
+            String now = Conversions.convert(GregorianCalendar.getInstance()).toDate();
+            rootVersion.setProperty(JCR_CREATED, now, Type.DATE);
+            rootVersion.setProperty(JCR_PREDECESSORS,
+                    Collections.<String>emptyList(), Type.REFERENCES);
+            rootVersion.setProperty(JCR_SUCCESSORS,
+                    Collections.<String>emptyList(), Type.REFERENCES);
+
+            // jcr:frozenNode of jcr:rootVersion
+            NodeBuilder frozenNode = rootVersion.child(JCR_FROZENNODE);
+            frozenNode.setProperty(JCR_UUID,
+                    IdentifierManager.generateUUID(), Type.STRING);
+            frozenNode.setProperty(JCR_PRIMARYTYPE, NT_FROZENNODE, Type.NAME);
+            Iterable<String> mixinTypes;
+            if (versionable.getProperty(JCR_MIXINTYPES) != null) {
+                mixinTypes = versionable.getProperty(JCR_MIXINTYPES).getValue(Type.NAMES);
+            } else {
+                mixinTypes = Collections.emptyList();
+            }
+            frozenNode.setProperty(JCR_FROZENMIXINTYPES, mixinTypes, Type.NAMES);
+            frozenNode.setProperty(JCR_FROZENPRIMARYTYPE,
+                    versionable.getProperty(JCR_PRIMARYTYPE).getValue(Type.NAME),
+                    Type.NAME);
+            frozenNode.setProperty(JCR_FROZENUUID, vUUID, Type.STRING);
+
+            // set jcr:isCheckedOut, jcr:versionHistory, jcr:baseVersion and
+            // jcr:predecessors on versionable node
+            versionable.setProperty(JCR_ISCHECKEDOUT, Boolean.TRUE, Type.BOOLEAN);
+            versionable.setProperty(JCR_VERSIONHISTORY,
+                    node.getProperty(JCR_UUID).getValue(Type.STRING), Type.REFERENCE);
+            String rootVersionUUID = rootVersion.getProperty(
+                    JCR_UUID).getValue(Type.STRING);
+            versionable.setProperty(JCR_BASEVERSION, rootVersionUUID, Type.REFERENCE);
+            versionable.setProperty(JCR_PREDECESSORS,
+                    Collections.singletonList(rootVersionUUID), Type.REFERENCES);
+        }
+        return node;
+    }
+
+    public void checkout(NodeBuilder versionable) {
+        TODO.unimplemented();
+    }
+
+    public void checkin(NodeBuilder versionable) {
+        TODO.unimplemented();
+    }
+
+    public void restore(NodeBuilder versionable) {
+        TODO.unimplemented();
+    }
+
+    // TODO: more methods that modify versions
+}

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/ReadWriteVersionManager.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/ReadWriteVersionManager.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/SimpleRoot.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/SimpleRoot.java?rev=1421137&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/SimpleRoot.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/SimpleRoot.java Thu Dec 13 08:44:19 2012
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.version;
+
+import javax.annotation.Nonnull;
+
+import org.apache.jackrabbit.oak.api.BlobFactory;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.api.SessionQueryEngine;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.TreeLocation;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * <code>SimpleRoot</code>...
+ */
+class SimpleRoot implements Root {
+
+    private final Tree rootTree;
+
+    SimpleRoot(Tree rootTree) {
+        checkArgument(rootTree.isRoot());
+        this.rootTree = rootTree;
+    }
+
+    @Override
+    public Tree getTree(String path) {
+        return getLocation(path).getTree();
+    }
+
+    @Nonnull
+    @Override
+    public TreeLocation getLocation(String path) {
+        checkArgument(path.startsWith("/"));
+        return rootTree.getLocation().getChild(path.substring(1));
+    }
+
+    //---------------------< unsupported methods >------------------------------
+
+    @Override
+    public boolean move(String sourcePath, String destPath) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean copy(String sourcePath, String destPath) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void rebase() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void refresh() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void commit() throws CommitFailedException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean hasPendingChanges() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Nonnull
+    @Override
+    public SessionQueryEngine getQueryEngine() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Nonnull
+    @Override
+    public BlobFactory getBlobFactory() {
+        throw new UnsupportedOperationException();
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/SimpleRoot.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/SimpleRoot.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionHook.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionHook.java?rev=1421137&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionHook.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionHook.java Thu Dec 13 08:44:19 2012
@@ -0,0 +1,218 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.version;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.jcr.RepositoryException;
+import javax.jcr.nodetype.ConstraintViolationException;
+import javax.jcr.version.VersionException;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.core.ReadOnlyTree;
+import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeState;
+import org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants;
+import org.apache.jackrabbit.oak.spi.commit.CommitHook;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStateDiff;
+import org.apache.jackrabbit.oak.version.VersionConstants;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * <code>VersionHook</code>...
+ */
+public class VersionHook implements CommitHook {
+
+    @Nonnull
+    @Override
+    public NodeState processCommit(final NodeState before, NodeState after)
+            throws CommitFailedException {
+        NodeBuilder builder = after.builder();
+        // FIXME? depends on InitialContent creating the nodes and setting
+        // the jcr:primaryType accordingly
+        NodeBuilder vsRoot = builder.child(NodeTypeConstants.JCR_SYSTEM)
+                .child(NodeTypeConstants.JCR_VERSIONSTORAGE);
+        ReadWriteVersionManager vMgr = new ReadWriteVersionManager(vsRoot, builder);
+        try {
+            after.compareAgainstBaseState(before,
+                    new VersionDiff(null, vMgr, builder));
+        } catch (UncheckedRepositoryException e) {
+            throw new CommitFailedException(e.getCause());
+        }
+        return builder.getNodeState();
+    }
+
+    private static class VersionDiff implements NodeStateDiff {
+
+        private final VersionDiff parent;
+        private final ReadWriteVersionManager vMgr;
+        private final NodeBuilder nodeAfter;
+        private Boolean isVersionable = null;
+
+        VersionDiff(@Nullable VersionDiff parent,
+                    @Nonnull ReadWriteVersionManager vMgr,
+                    @Nonnull NodeBuilder node) {
+            this.parent = parent;
+            this.vMgr = checkNotNull(vMgr);
+            this.nodeAfter = checkNotNull(node);
+            if (isVersionable()) {
+                vMgr.getOrCreateVersionHistory(node);
+            }
+        }
+
+        @Override
+        public void propertyAdded(PropertyState after) {
+            if (!isVersionable()) {
+                return;
+            }
+            if (wasCheckedIn()) {
+                throwCheckedIn("Cannot add property on checked in node");
+            }
+        }
+
+        @Override
+        public void propertyChanged(PropertyState before, PropertyState after) {
+            if (!isVersionable()) {
+                if (!isVersionProperty(after) && wasCheckedIn()) {
+                    throwProtected("Cannot change property on checked in node");
+                }
+                return;
+            }
+            String propName = after.getName();
+            if (propName.equals(VersionConstants.JCR_ISCHECKEDOUT)) {
+                if (wasCheckedIn()) {
+                    vMgr.checkout(nodeAfter);
+                } else {
+                    vMgr.checkin(nodeAfter);
+                }
+            } else if (propName.equals(VersionConstants.JCR_BASEVERSION)) {
+                vMgr.restore(nodeAfter);
+            } else if (isVersionProperty(after)) {
+                throwProtected(after.getName());
+            } else if (wasCheckedIn()) {
+                throwCheckedIn("Cannot change property on checked in node");
+            }
+        }
+
+        @Override
+        public void propertyDeleted(PropertyState before) {
+            if (!isVersionable()) {
+                if (!isVersionProperty(before) && wasCheckedIn()) {
+                    throwProtected("Cannot delete property on checked in node");
+                }
+            }
+        }
+
+        @Override
+        public void childNodeAdded(String name, NodeState after) {
+            childNodeChanged(name, MemoryNodeState.EMPTY_NODE, after);
+        }
+
+        @Override
+        public void childNodeChanged(String name,
+                                     NodeState before,
+                                     NodeState after) {
+            after.compareAgainstBaseState(before,
+                    new VersionDiff(this, vMgr, nodeAfter.child(name)));
+        }
+
+        @Override
+        public void childNodeDeleted(String name, NodeState before) {
+            NodeState after = MemoryNodeState.EMPTY_NODE;
+            after.compareAgainstBaseState(before,
+                    new VersionDiff(this, vMgr, after.builder()));
+        }
+
+        /**
+         * Returns <code>true</code> if the node of this VersionDiff
+         * is versionable; <code>false</code> otherwise.
+         *
+         * @return whether the node is versionable.
+         */
+        private boolean isVersionable() {
+            try {
+                if (isVersionable == null) {
+                    // this is not 100% correct, because t.getPath() will
+                    // not return the correct path for nodeAfter, but is
+                    // sufficient to check if it is versionable
+                    Tree t = new ReadOnlyTree(nodeAfter.getNodeState());
+                    isVersionable = vMgr.isVersionable(t);
+                }
+                return isVersionable;
+            } catch (RepositoryException e) {
+                throw new UncheckedRepositoryException(e);
+            }
+        }
+
+        private boolean isVersionProperty(PropertyState state) {
+            return VersionConstants.VERSION_PROPERTY_NAMES.contains(state.getName());
+        }
+
+        /**
+         * @return <code>true</code> if this node <b>was</b> checked in. That
+         *         is, this method checks the base state for the
+         *         jcr:isCheckedOut property.
+         */
+        private boolean wasCheckedIn() {
+            NodeState state = nodeAfter.getBaseState();
+            if (state != null) {
+                PropertyState prop = state.getProperty(VersionConstants.JCR_ISCHECKEDOUT);
+                if (prop != null) {
+                    return !prop.getValue(Type.BOOLEAN);
+                }
+            }
+            // new node or not versionable, check parent
+            return parent != null && parent.wasCheckedIn();
+        }
+
+        private static void throwCheckedIn(String msg)
+                throws UncheckedRepositoryException {
+            throwUnchecked(new VersionException(msg));
+        }
+
+        private static void throwProtected(String name)
+                throws UncheckedRepositoryException {
+            throwUnchecked(new ConstraintViolationException(
+                    "Property is protected: " + name));
+        }
+    }
+
+    private static void throwUnchecked(RepositoryException e)
+            throws UncheckedRepositoryException {
+        throw new UncheckedRepositoryException(e);
+    }
+
+    private static class UncheckedRepositoryException extends RuntimeException {
+
+        private static final long serialVersionUID = 5220620245610340169L;
+
+        public UncheckedRepositoryException(RepositoryException cause) {
+            super(cause);
+        }
+
+        public RepositoryException getCause() {
+            return (RepositoryException) super.getCause();
+        }
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionHook.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/version/VersionHook.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/version/VersionConstants.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/version/VersionConstants.java?rev=1421137&r1=1421136&r2=1421137&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/version/VersionConstants.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/version/VersionConstants.java Thu Dec 13 08:44:19 2012
@@ -27,6 +27,9 @@ import org.apache.jackrabbit.JcrConstant
  */
 public interface VersionConstants extends JcrConstants {
 
+    // version storage
+    String REP_VERSIONSTORAGE = "rep:versionStorage";
+
     // activities
     String JCR_ACTIVITY = "jcr:activity";
     String JCR_ACTIVITIES = "jcr:activities";

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/Jcr.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/Jcr.java?rev=1421137&r1=1421136&r2=1421137&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/Jcr.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/Jcr.java Thu Dec 13 08:44:19 2012
@@ -35,6 +35,7 @@ import org.apache.jackrabbit.oak.plugins
 import org.apache.jackrabbit.oak.plugins.nodetype.InitialContent;
 import org.apache.jackrabbit.oak.plugins.nodetype.RegistrationValidatorProvider;
 import org.apache.jackrabbit.oak.plugins.nodetype.TypeValidatorProvider;
+import org.apache.jackrabbit.oak.plugins.version.VersionHook;
 import org.apache.jackrabbit.oak.security.SecurityProviderImpl;
 import org.apache.jackrabbit.oak.spi.commit.CommitHook;
 import org.apache.jackrabbit.oak.spi.commit.ConflictHandler;
@@ -61,6 +62,7 @@ public class Jcr {
         with(new InitialContent());
 
         with(new DefaultTypeEditor());
+        with(new VersionHook());
 
         with(new SecurityProviderImpl());
 

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/NodeDelegate.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/NodeDelegate.java?rev=1421137&r1=1421136&r2=1421137&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/NodeDelegate.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/NodeDelegate.java Thu Dec 13 08:44:19 2012
@@ -57,7 +57,7 @@ public class NodeDelegate extends ItemDe
             : new NodeDelegate(sessionDelegate, location);
     }
 
-    NodeDelegate(SessionDelegate sessionDelegate, Tree tree) {
+    protected NodeDelegate(SessionDelegate sessionDelegate, Tree tree) {
         super(sessionDelegate, tree.getLocation());
     }
 
@@ -232,7 +232,7 @@ public class NodeDelegate extends ItemDe
     //------------------------------------------------------------< internal >---
 
     @Nonnull
-    Tree getTree() throws InvalidItemStateException {
+    protected Tree getTree() throws InvalidItemStateException {
         Tree tree = getLocation().getTree();
         if (tree == null) {
             throw new InvalidItemStateException();

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/NodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/NodeImpl.java?rev=1421137&r1=1421136&r2=1421137&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/NodeImpl.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/NodeImpl.java Thu Dec 13 08:44:19 2012
@@ -54,6 +54,7 @@ import javax.jcr.nodetype.NodeTypeManage
 import javax.jcr.nodetype.PropertyDefinition;
 import javax.jcr.version.Version;
 import javax.jcr.version.VersionHistory;
+import javax.jcr.version.VersionManager;
 
 import com.google.common.base.Function;
 import com.google.common.base.Predicate;
@@ -87,14 +88,14 @@ import static javax.jcr.Property.JCR_LOC
 /**
  * {@code NodeImpl}...
  */
-public class NodeImpl extends ItemImpl<NodeDelegate> implements Node {
+public class NodeImpl<T extends NodeDelegate> extends ItemImpl<T> implements Node {
 
     /**
      * logger instance
      */
     private static final Logger log = LoggerFactory.getLogger(NodeImpl.class);
 
-    public NodeImpl(NodeDelegate dlg) {
+    public NodeImpl(T dlg) {
         super(dlg.getSessionDelegate(), dlg);
     }
 
@@ -116,9 +117,9 @@ public class NodeImpl extends ItemImpl<N
     public Node getParent() throws RepositoryException {
         checkStatus();
 
-        return sessionDelegate.perform(new SessionOperation<NodeImpl>() {
+        return sessionDelegate.perform(new SessionOperation<NodeImpl<NodeDelegate>>() {
             @Override
-            public NodeImpl perform() throws RepositoryException {
+            public NodeImpl<NodeDelegate> perform() throws RepositoryException {
                 if (dlg.isRoot()) {
                     throw new ItemNotFoundException("Root has no parent");
                 } else {
@@ -126,7 +127,7 @@ public class NodeImpl extends ItemImpl<N
                     if (parent == null) {
                         throw new AccessDeniedException();
                     }
-                    return new NodeImpl(parent);
+                    return new NodeImpl<NodeDelegate>(parent);
                 }
             }
         });
@@ -251,7 +252,7 @@ public class NodeImpl extends ItemImpl<N
                     DefinitionProvider dp = sessionDelegate.getDefinitionProvider();
                     try {
                         String childName = sessionDelegate.getOakNameOrThrow(PathUtils.getName(relPath));
-                        NodeDefinition def = dp.getDefinition(new NodeImpl(parent), childName);
+                        NodeDefinition def = dp.getDefinition(new NodeImpl<NodeDelegate>(parent), childName);
                         ntName = def.getDefaultPrimaryTypeName();
                     } catch (RepositoryException e) {
                         throw new ConstraintViolationException(
@@ -272,7 +273,7 @@ public class NodeImpl extends ItemImpl<N
                     throw new ItemExistsException();
                 }
 
-                NodeImpl childNode = new NodeImpl(added);
+                NodeImpl childNode = new NodeImpl<NodeDelegate>(added);
                 childNode.internalSetPrimaryType(ntName);
                 childNode.autoCreateItems();
                 return childNode;
@@ -481,7 +482,7 @@ public class NodeImpl extends ItemImpl<N
                 if (nd == null) {
                     throw new PathNotFoundException(relPath);
                 } else {
-                    return new NodeImpl(nd);
+                    return new NodeImpl<NodeDelegate>(nd);
                 }
             }
         });
@@ -939,7 +940,9 @@ public class NodeImpl extends ItemImpl<N
                 ntm.getNodeType(mixinName); // throws on not found
                 // TODO: END
 
-                return isSupportedMixinName(mixinName);
+                VersionManager vMgr = sessionDelegate.getVersionManager();
+                String path = toJcrPath(dlg.getPath());
+                return isSupportedMixinName(mixinName) && vMgr.isCheckedOut(path);
             }
         });
     }
@@ -1276,7 +1279,7 @@ public class NodeImpl extends ItemImpl<N
                 new Function<NodeDelegate, Node>() {
                     @Override
                     public Node apply(NodeDelegate nodeDelegate) {
-                        return new NodeImpl(nodeDelegate);
+                        return new NodeImpl<NodeDelegate>(nodeDelegate);
                     }
                 });
     }

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/PropertyImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/PropertyImpl.java?rev=1421137&r1=1421136&r2=1421137&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/PropertyImpl.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/PropertyImpl.java Thu Dec 13 08:44:19 2012
@@ -83,7 +83,7 @@ public class PropertyImpl extends ItemIm
                 if (parent == null) {
                     throw new AccessDeniedException();
                 } else {
-                    return new NodeImpl(dlg.getParent());
+                    return new NodeImpl<NodeDelegate>(dlg.getParent());
                 }
             }
         });

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionDelegate.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionDelegate.java?rev=1421137&r1=1421136&r2=1421137&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionDelegate.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionDelegate.java Thu Dec 13 08:44:19 2012
@@ -100,6 +100,7 @@ public class SessionDelegate {
         this.autoRefresh = autoRefresh;
 
         this.root = contentSession.getLatestRoot();
+        // FIXME: do not pass partially initialized 'this'
         this.workspace = new WorkspaceImpl(this);
 
         Map<String, String> namespaces = Maps.newHashMap();

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionImpl.java?rev=1421137&r1=1421136&r2=1421137&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionImpl.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionImpl.java Thu Dec 13 08:44:19 2012
@@ -142,7 +142,7 @@ public class SessionImpl extends Abstrac
                 if (nd == null) {
                     throw new AccessDeniedException("Root node is not accessible.");
                 } else {
-                    return new NodeImpl(nd);
+                    return new NodeImpl<NodeDelegate>(nd);
                 }
             }
         });
@@ -166,7 +166,7 @@ public class SessionImpl extends Abstrac
                 if (d == null) {
                     throw new ItemNotFoundException("Node with id " + id + " does not exist.");
                 }
-                return new NodeImpl(d);
+                return new NodeImpl<NodeDelegate>(d);
             }
         });
     }
@@ -197,7 +197,7 @@ public class SessionImpl extends Abstrac
                 if (d == null) {
                     throw new PathNotFoundException("Node with path " + absPath + " does not exist.");
                 }
-                return new NodeImpl(d);
+                return new NodeImpl<NodeDelegate>(d);
             }
         });
     }

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/WorkspaceImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/WorkspaceImpl.java?rev=1421137&r1=1421136&r2=1421137&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/WorkspaceImpl.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/WorkspaceImpl.java Thu Dec 13 08:44:19 2012
@@ -63,13 +63,14 @@ public class WorkspaceImpl implements Ja
 
     private final SessionDelegate sessionDelegate;
     private final QueryManagerImpl queryManager;
-
     private final LockManager lockManager;
+    private final VersionManagerImpl versionManager;
 
     public WorkspaceImpl(SessionDelegate sessionDelegate) {
         this.sessionDelegate = sessionDelegate;
         this.queryManager = new QueryManagerImpl(sessionDelegate);
-        this.lockManager = new LockManagerImpl(sessionDelegate.getSession());
+        this.lockManager = new LockManagerImpl(sessionDelegate);
+        this.versionManager = new VersionManagerImpl(sessionDelegate);
     }
 
     //----------------------------------------------------------< Workspace >---
@@ -213,8 +214,9 @@ public class WorkspaceImpl implements Ja
     }
 
     @Override
-    public VersionManager getVersionManager() {
-        return new VersionManagerImpl(sessionDelegate);
+    public VersionManager getVersionManager() throws RepositoryException {
+        ensureIsAlive();
+        return versionManager;
     }
 
     @Override

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/lock/LockManagerImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/lock/LockManagerImpl.java?rev=1421137&r1=1421136&r2=1421137&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/lock/LockManagerImpl.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/lock/LockManagerImpl.java Thu Dec 13 08:44:19 2012
@@ -27,6 +27,8 @@ import javax.jcr.lock.Lock;
 import javax.jcr.lock.LockException;
 import javax.jcr.lock.LockManager;
 
+import org.apache.jackrabbit.oak.jcr.SessionDelegate;
+
 /**
  * Simple lock manager implementation that just keeps track of a set of lock
  * tokens and delegates all locking operations back to the {@link Session}
@@ -34,12 +36,12 @@ import javax.jcr.lock.LockManager;
  */
 public class LockManagerImpl implements LockManager {
 
-    private final Session session;
+    private final SessionDelegate sessionDelegate;
 
     private final Set<String> tokens = new HashSet<String>();
 
-    public LockManagerImpl(Session session) {
-        this.session = session;
+    public LockManagerImpl(SessionDelegate sessionDelegate) {
+        this.sessionDelegate = sessionDelegate;
     }
 
     @Override
@@ -64,19 +66,19 @@ public class LockManagerImpl implements 
 
     @Override
     public boolean isLocked(String absPath) throws RepositoryException {
-        return session.getNode(absPath).isLocked();
+        return getSession().getNode(absPath).isLocked();
     }
 
     @Override
     @SuppressWarnings("deprecation")
     public boolean holdsLock(String absPath) throws RepositoryException {
-        return session.getNode(absPath).holdsLock();
+        return getSession().getNode(absPath).holdsLock();
     }
 
     @Override
     @SuppressWarnings("deprecation")
     public Lock getLock(String absPath) throws RepositoryException {
-        return session.getNode(absPath).getLock();
+        return getSession().getNode(absPath).getLock();
     }
 
     @Override
@@ -84,13 +86,16 @@ public class LockManagerImpl implements 
     public Lock lock(
             String absPath, boolean isDeep, boolean isSessionScoped,
             long timeoutHint, String ownerInfo) throws RepositoryException {
-        return session.getNode(absPath).lock(isDeep, isSessionScoped);
+        return getSession().getNode(absPath).lock(isDeep, isSessionScoped);
     }
 
     @Override
     @SuppressWarnings("deprecation")
     public void unlock(String absPath) throws RepositoryException {
-        session.getNode(absPath).unlock();
+        getSession().getNode(absPath).unlock();
     }
 
+    private Session getSession() {
+        return sessionDelegate.getSession();
+    }
 }
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/query/QueryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/query/QueryImpl.java?rev=1421137&r1=1421136&r2=1421137&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/query/QueryImpl.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/query/QueryImpl.java Thu Dec 13 08:44:19 2012
@@ -130,7 +130,7 @@ public class QueryImpl implements Query 
         if (parentDelegate == null) {
             throw new PathNotFoundException("The specified path does not exist: " + parent);
         }
-        Node parentNode = new NodeImpl(parentDelegate);
+        Node parentNode = new NodeImpl<NodeDelegate>(parentDelegate);
         String nodeName = PathUtils.getName(oakPath);
         ValueFactory vf = sessionDelegate.getValueFactory();
         Node n = parentNode.addNode(nodeName, JcrConstants.NT_QUERY);

Added: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/ReadWriteVersionManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/ReadWriteVersionManager.java?rev=1421137&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/ReadWriteVersionManager.java (added)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/ReadWriteVersionManager.java Thu Dec 13 08:44:19 2012
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.jcr.version;
+
+import javax.annotation.Nonnull;
+import javax.jcr.InvalidItemStateException;
+import javax.jcr.RepositoryException;
+import javax.jcr.UnsupportedRepositoryOperationException;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.TreeLocation;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.plugins.nodetype.ReadOnlyNodeTypeManager;
+import org.apache.jackrabbit.oak.plugins.version.ReadOnlyVersionManager;
+import org.apache.jackrabbit.oak.version.VersionConstants;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * <code>ReadWriteVersionManager</code>...
+ */
+public class ReadWriteVersionManager extends ReadOnlyVersionManager {
+
+    private final TreeLocation versionStorageLocation;
+    private final Root workspaceRoot;
+
+    public ReadWriteVersionManager(@Nonnull TreeLocation versionStorageLocation,
+                                   @Nonnull Root workspaceRoot) {
+        this.versionStorageLocation = checkNotNull(versionStorageLocation);
+        this.workspaceRoot = checkNotNull(workspaceRoot);
+    }
+
+    /**
+     * Called by the write methods to refresh the state of the possible
+     * session associated with this instance. The default implementation
+     * of this method does nothing, but a subclass can use this callback
+     * to keep a session in sync with the persisted version changes.
+     *
+     * @throws RepositoryException if the session could not be refreshed
+     */
+    protected void refresh() throws RepositoryException {
+        // do nothing
+    }
+
+    @Override
+    @Nonnull
+    protected Tree getVersionStorageTree() {
+        return versionStorageLocation.getTree();
+    }
+
+    @Override
+    @Nonnull
+    protected Root getWorkspaceRoot() {
+        return workspaceRoot;
+    }
+
+    @Override
+    @Nonnull
+    protected ReadOnlyNodeTypeManager getNodeTypeManager() {
+        return ReadOnlyNodeTypeManager.getInstance(
+                workspaceRoot, NamePathMapper.DEFAULT);
+    }
+
+    /**
+     * Performs a checkin on a versionable tree and returns the tree that
+     * represents the created version.
+     *
+     * @param versionable the versionable node to check in.
+     * @return the created version.
+     * @throws InvalidItemStateException if the current root has pending
+     *                                   changes.
+     * @throws UnsupportedRepositoryOperationException
+     *                                   if the versionable tree isn't actually
+     *                                   versionable.
+     * @throws RepositoryException       if an error occurs while checking the
+     *                                   node type of the tree.
+     */
+    @Nonnull
+    public Tree checkin(@Nonnull Tree versionable)
+            throws RepositoryException, InvalidItemStateException,
+            UnsupportedRepositoryOperationException {
+        if (workspaceRoot.hasPendingChanges()) {
+            throw new InvalidItemStateException("Unable to perform checkin. " +
+                    "Session has pending changes.");
+        }
+        if (!isVersionable(versionable)) {
+            throw new UnsupportedRepositoryOperationException(
+                    versionable.getPath() + " is not versionable");
+        }
+        TreeLocation location = versionable.getLocation();
+        if (isCheckedOut(location)) {
+            versionable.setProperty(VersionConstants.JCR_ISCHECKEDOUT,
+                    Boolean.FALSE, Type.BOOLEAN);
+            try {
+                getWorkspaceRoot().commit();
+                refresh();
+            } catch (CommitFailedException e) {
+                getWorkspaceRoot().refresh();
+                throw new RepositoryException(e);
+            }
+        }
+        return getBaseVersion(location.getTree());
+    }
+
+    /**
+     * Performs a checkout on a versionable tree.
+     *
+     * @param versionable the versionable node to check out.
+     * @throws InvalidItemStateException if the current root has pending
+     *                                   changes.
+     * @throws UnsupportedRepositoryOperationException
+     *                                   if the versionable tree isn't actually
+     *                                   versionable.
+     * @throws RepositoryException       if an error occurs while checking the
+     *                                   node type of the tree.
+     */
+    public void checkout(@Nonnull Tree versionable)
+            throws UnsupportedRepositoryOperationException,
+            InvalidItemStateException, RepositoryException {
+        if (workspaceRoot.hasPendingChanges()) {
+            throw new InvalidItemStateException("Unable to perform checkout. " +
+                    "Session has pending changes.");
+        }
+        if (!isVersionable(versionable)) {
+            throw new UnsupportedRepositoryOperationException(
+                    versionable.getPath() + " is not versionable");
+        }
+        TreeLocation location = versionable.getLocation();
+        if (!isCheckedOut(location)) {
+            versionable.setProperty(VersionConstants.JCR_ISCHECKEDOUT,
+                    Boolean.TRUE, Type.BOOLEAN);
+            try {
+                getWorkspaceRoot().commit();
+                refresh();
+            } catch (CommitFailedException e) {
+                getWorkspaceRoot().refresh();
+                throw new RepositoryException(e);
+            }
+        }
+    }
+
+    // TODO: more methods that modify versions
+}

Propchange: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/ReadWriteVersionManager.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/ReadWriteVersionManager.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Added: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionDelegate.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionDelegate.java?rev=1421137&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionDelegate.java (added)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionDelegate.java Thu Dec 13 08:44:19 2012
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.jcr.version;
+
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.jcr.NodeDelegate;
+import org.apache.jackrabbit.oak.jcr.SessionDelegate;
+
+/**
+ * <code>VersionDelegate</code>...
+ */
+public class VersionDelegate extends NodeDelegate {
+
+    private VersionDelegate(SessionDelegate sessionDelegate, Tree tree) {
+        super(sessionDelegate, tree);
+    }
+
+    static VersionDelegate create(SessionDelegate sessionDelegate, Tree tree) {
+        return new VersionDelegate(sessionDelegate, tree);
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionDelegate.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionDelegate.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Added: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionHistoryDelegate.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionHistoryDelegate.java?rev=1421137&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionHistoryDelegate.java (added)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionHistoryDelegate.java Thu Dec 13 08:44:19 2012
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.jcr.version;
+
+import javax.annotation.Nonnull;
+import javax.jcr.InvalidItemStateException;
+
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.jcr.NodeDelegate;
+import org.apache.jackrabbit.oak.jcr.SessionDelegate;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * <code>VersionHistoryDelegate</code>...
+ */
+public class VersionHistoryDelegate extends NodeDelegate {
+
+    VersionHistoryDelegate(@Nonnull SessionDelegate sessionDelegate,
+                           @Nonnull Tree vhTree) {
+        super(sessionDelegate, checkNotNull(vhTree));
+    }
+
+    public String getVersionableIdentifier() throws InvalidItemStateException {
+        return getTree().getProperty(JcrConstants.JCR_VERSIONABLEUUID).getValue(Type.STRING);
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionHistoryDelegate.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionHistoryDelegate.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Added: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionHistoryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionHistoryImpl.java?rev=1421137&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionHistoryImpl.java (added)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionHistoryImpl.java Thu Dec 13 08:44:19 2012
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.jcr.version;
+
+import javax.jcr.AccessDeniedException;
+import javax.jcr.NodeIterator;
+import javax.jcr.ReferentialIntegrityException;
+import javax.jcr.RepositoryException;
+import javax.jcr.UnsupportedRepositoryOperationException;
+import javax.jcr.version.LabelExistsVersionException;
+import javax.jcr.version.Version;
+import javax.jcr.version.VersionException;
+import javax.jcr.version.VersionHistory;
+import javax.jcr.version.VersionIterator;
+
+import org.apache.jackrabbit.commons.iterator.FrozenNodeIteratorAdapter;
+import org.apache.jackrabbit.oak.jcr.NodeImpl;
+import org.apache.jackrabbit.oak.util.TODO;
+
+/**
+ * <code>VersionHistoryImpl</code>...
+ */
+public class VersionHistoryImpl extends NodeImpl<VersionHistoryDelegate>
+        implements VersionHistory {
+
+    public VersionHistoryImpl(VersionHistoryDelegate dlg) {
+        super(dlg);
+    }
+
+    @Override
+    public String getVersionableUUID() throws RepositoryException {
+        return getVersionableIdentifier();
+    }
+
+    @Override
+    public String getVersionableIdentifier() throws RepositoryException {
+        return dlg.getVersionableIdentifier();
+    }
+
+    @Override
+    public Version getRootVersion() throws RepositoryException {
+        return TODO.unimplemented().returnValue(null);
+    }
+
+    @Override
+    public VersionIterator getAllLinearVersions() throws RepositoryException {
+        return TODO.unimplemented().returnValue(null);
+    }
+
+    @Override
+    public VersionIterator getAllVersions() throws RepositoryException {
+        return TODO.unimplemented().returnValue(null);
+    }
+
+    @Override
+    public NodeIterator getAllLinearFrozenNodes() throws RepositoryException {
+        return new FrozenNodeIteratorAdapter(getAllLinearVersions());
+    }
+
+    @Override
+    public NodeIterator getAllFrozenNodes() throws RepositoryException {
+        return new FrozenNodeIteratorAdapter(getAllVersions());
+    }
+
+    @Override
+    public Version getVersion(String versionName)
+            throws VersionException, RepositoryException {
+        return TODO.unimplemented().returnValue(null);
+    }
+
+    @Override
+    public Version getVersionByLabel(String label)
+            throws VersionException, RepositoryException {
+        return TODO.unimplemented().returnValue(null);
+    }
+
+    @Override
+    public void addVersionLabel(String versionName,
+                                String label,
+                                boolean moveLabel)
+            throws LabelExistsVersionException, VersionException,
+            RepositoryException {
+        TODO.unimplemented();
+    }
+
+    @Override
+    public void removeVersionLabel(String label)
+            throws VersionException, RepositoryException {
+        TODO.unimplemented();
+    }
+
+    @Override
+    public boolean hasVersionLabel(String label) throws RepositoryException {
+        return TODO.unimplemented().returnValue(Boolean.FALSE);
+    }
+
+    @Override
+    public boolean hasVersionLabel(Version version, String label)
+            throws VersionException, RepositoryException {
+        return TODO.unimplemented().returnValue(Boolean.FALSE);
+    }
+
+    @Override
+    public String[] getVersionLabels() throws RepositoryException {
+        return TODO.unimplemented().returnValue(new String[0]);
+    }
+
+    @Override
+    public String[] getVersionLabels(Version version)
+            throws VersionException, RepositoryException {
+        return TODO.unimplemented().returnValue(new String[0]);
+    }
+
+    @Override
+    public void removeVersion(String versionName)
+            throws ReferentialIntegrityException, AccessDeniedException,
+            UnsupportedRepositoryOperationException, VersionException,
+            RepositoryException {
+        TODO.unimplemented();
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionHistoryImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionHistoryImpl.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionImpl.java?rev=1421137&r1=1421136&r2=1421137&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionImpl.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionImpl.java Thu Dec 13 08:44:19 2012
@@ -23,13 +23,14 @@ import javax.jcr.RepositoryException;
 import javax.jcr.version.Version;
 import javax.jcr.version.VersionHistory;
 
+import org.apache.jackrabbit.JcrConstants;
 import org.apache.jackrabbit.oak.jcr.NodeDelegate;
 import org.apache.jackrabbit.oak.jcr.NodeImpl;
 import org.apache.jackrabbit.oak.util.TODO;
 
-class VersionImpl extends NodeImpl implements Version {
+class VersionImpl extends NodeImpl<VersionDelegate> implements Version {
 
-    public VersionImpl(NodeDelegate dlg) {
+    public VersionImpl(VersionDelegate dlg) {
         super(dlg);
     }
 

Added: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionManagerDelegate.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionManagerDelegate.java?rev=1421137&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionManagerDelegate.java (added)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionManagerDelegate.java Thu Dec 13 08:44:19 2012
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.jcr.version;
+
+import javax.annotation.Nonnull;
+import javax.jcr.InvalidItemStateException;
+import javax.jcr.RepositoryException;
+import javax.jcr.UnsupportedRepositoryOperationException;
+
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.TreeLocation;
+import org.apache.jackrabbit.oak.jcr.NodeDelegate;
+import org.apache.jackrabbit.oak.jcr.SessionDelegate;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * <code>VersionManagerDelegate</code>...
+ */
+public class VersionManagerDelegate {
+
+    /**
+     * TODO: this assumes the version store is in the same workspace.
+     */
+    private static final String VERSION_STORAGE_PATH
+            = "/" + JcrConstants.JCR_SYSTEM + "/" + JcrConstants.JCR_VERSIONSTORAGE;
+
+    private final SessionDelegate sessionDelegate;
+
+    private final ReadWriteVersionManager versionManager;
+
+    static VersionManagerDelegate create(SessionDelegate sessionDelegate) {
+        TreeLocation location = sessionDelegate.getRoot().getLocation(VERSION_STORAGE_PATH);
+        return new VersionManagerDelegate(sessionDelegate, location);
+    }
+
+    private VersionManagerDelegate(SessionDelegate sessionDelegate,
+                                   TreeLocation versionStorageLocation) {
+        this.sessionDelegate = sessionDelegate;
+        this.versionManager = new ReadWriteVersionManager(
+                versionStorageLocation,
+                sessionDelegate.getRoot()) {
+            @Override
+            protected void refresh() throws RepositoryException {
+                VersionManagerDelegate.this.sessionDelegate.refresh(true);
+            }
+        };
+    }
+
+    SessionDelegate getSessionDelegate() {
+        return sessionDelegate;
+    }
+
+    @Nonnull
+    public VersionDelegate checkin(@Nonnull NodeDelegate nodeDelegate)
+            throws RepositoryException {
+        return VersionDelegate.create(sessionDelegate,
+                versionManager.checkin(getTree(nodeDelegate)));
+    }
+
+    public void checkout(@Nonnull NodeDelegate nodeDelegate)
+            throws RepositoryException {
+        versionManager.checkout(getTree(nodeDelegate));
+    }
+
+    public boolean isCheckedOut(@Nonnull NodeDelegate nodeDelegate)
+            throws RepositoryException {
+        return versionManager.isCheckedOut(getTree(nodeDelegate));
+    }
+
+    @Nonnull
+    public VersionHistoryDelegate getVersionHistory(@Nonnull NodeDelegate nodeDelegate)
+            throws RepositoryException {
+        Tree vh = versionManager.getVersionHistory(getTree(nodeDelegate));
+        if (vh == null) {
+            throw new UnsupportedRepositoryOperationException("Node does not" +
+                    " have a version history: " + nodeDelegate.getPath());
+        }
+        return new VersionHistoryDelegate(sessionDelegate, vh);
+    }
+
+    @Nonnull
+    public VersionDelegate getBaseVersion(@Nonnull NodeDelegate nodeDelegate)
+            throws RepositoryException {
+        Tree v = versionManager.getBaseVersion(getTree(nodeDelegate));
+        if (v == null) {
+            throw new UnsupportedRepositoryOperationException("Node does not" +
+                    " have a base version: " + nodeDelegate.getPath());
+        }
+        return VersionDelegate.create(sessionDelegate, v);
+    }
+
+    //----------------------------< internal >----------------------------------
+
+    /**
+     * Returns the underlying tree.
+     *
+     * @param nodeDelegate the node delegate.
+     * @return the underlying tree.
+     * @throws InvalidItemStateException if the location points to a stale item.
+     */
+    @Nonnull
+    private static Tree getTree(@Nonnull NodeDelegate nodeDelegate)
+            throws InvalidItemStateException {
+        Tree t = checkNotNull(nodeDelegate).getLocation().getTree();
+        if (t == null) {
+            throw new InvalidItemStateException("Node does not exist: " +
+                    nodeDelegate.getPath());
+        }
+        return t;
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionManagerDelegate.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/version/VersionManagerDelegate.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL



Mime
View raw message