You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by mr...@apache.org on 2013/09/10 11:24:47 UTC

svn commit: r1521392 - in /jackrabbit/oak/trunk/oak-jcr: ./ src/main/java/org/apache/jackrabbit/oak/jcr/ src/main/java/org/apache/jackrabbit/oak/jcr/delegate/ src/test/java/org/apache/jackrabbit/oak/jcr/

Author: mreutegg
Date: Tue Sep 10 09:24:47 2013
New Revision: 1521392

URL: http://svn.apache.org/r1521392
Log:
OAK-915: Workspace#copy of referenceable nodes does not generate new UUIDs

Added:
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/WorkspaceDelegate.java   (with props)
Modified:
    jackrabbit/oak/trunk/oak-jcr/pom.xml
    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/delegate/SessionDelegate.java
    jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/RepositoryTest.java

Modified: jackrabbit/oak/trunk/oak-jcr/pom.xml
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/pom.xml?rev=1521392&r1=1521391&r2=1521392&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/pom.xml (original)
+++ jackrabbit/oak/trunk/oak-jcr/pom.xml Tue Sep 10 09:24:47 2013
@@ -47,7 +47,6 @@
       org.apache.jackrabbit.test.api.NodeUUIDTest#testSaveMovedRefNode                                 <!-- OAK-66 -->
       org.apache.jackrabbit.test.api.SetValueValueFormatExceptionTest#testNodeNotReferenceable
       org.apache.jackrabbit.test.api.NodeSetPrimaryTypeTest#testLocked
-      org.apache.jackrabbit.test.api.WorkspaceCopyReferenceableTest#testCopyNodesNewUUID               <!-- OAK-118 -->
       org.apache.jackrabbit.test.api.WorkspaceCopyVersionableTest#testCopyNodesVersionableAndCheckedIn <!-- OAK-118 -->
       org.apache.jackrabbit.test.api.WorkspaceMoveReferenceableTest#testMoveNodesReferenceableNodesNewUUID  <!-- OAK-118 -->
       org.apache.jackrabbit.test.api.WorkspaceMoveVersionableTest#testMoveNodesVersionableAndCheckedIn <!-- OAK-118 -->

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=1521392&r1=1521391&r2=1521392&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 Tue Sep 10 09:24:47 2013
@@ -39,7 +39,9 @@ import org.apache.jackrabbit.oak.api.Com
 import org.apache.jackrabbit.oak.api.Root;
 import org.apache.jackrabbit.oak.api.Tree;
 import org.apache.jackrabbit.oak.jcr.delegate.SessionDelegate;
+import org.apache.jackrabbit.oak.jcr.delegate.WorkspaceDelegate;
 import org.apache.jackrabbit.oak.jcr.lock.LockManagerImpl;
+import org.apache.jackrabbit.oak.jcr.operation.SessionOperation;
 import org.apache.jackrabbit.oak.jcr.query.QueryManagerImpl;
 import org.apache.jackrabbit.oak.jcr.version.VersionManagerImpl;
 import org.apache.jackrabbit.oak.jcr.xml.ImportHandler;
@@ -60,6 +62,7 @@ public class WorkspaceImpl implements Ja
 
     private final SessionContext sessionContext;
     private final SessionDelegate sessionDelegate;
+    private final WorkspaceDelegate workspaceDelegate;
     private final QueryManagerImpl queryManager;
     private final VersionManagerImpl versionManager;
     private final ReadWriteNodeTypeManager nodeTypeManager;
@@ -67,6 +70,7 @@ public class WorkspaceImpl implements Ja
     public WorkspaceImpl(final SessionContext sessionContext) {
         this.sessionContext = sessionContext;
         this.sessionDelegate = sessionContext.getSessionDelegate();
+        this.workspaceDelegate = new WorkspaceDelegate(sessionContext);
         this.queryManager = new QueryManagerImpl(sessionContext);
         this.versionManager = new VersionManagerImpl(sessionContext);
         this.nodeTypeManager = new ReadWriteNodeTypeManager() {
@@ -122,24 +126,35 @@ public class WorkspaceImpl implements Ja
     }
 
     @Override
-    public void copy(String srcWorkspace, String srcAbsPath, String destAbsPath) throws RepositoryException {
+    public void copy(String srcWorkspace,
+                     String srcAbsPath,
+                     final String destAbsPath) throws RepositoryException {
         final String srcOakPath = getOakPathOrThrowNotFound(srcAbsPath);
         final String destOakPath = getOakPathOrThrowNotFound(destAbsPath);
 
-        // TODO: use perform()
-        ensureIsAlive();
-
         if (!getName().equals(srcWorkspace)) {
             throw new UnsupportedRepositoryOperationException("Not implemented.");
         }
 
-        sessionDelegate.checkProtectedNode(getParentPath(srcOakPath));
-        sessionDelegate.checkProtectedNode(getParentPath(destOakPath));
+        sessionDelegate.perform(new SessionOperation<Object>(true) {
+            @Override
+            public void checkPreconditions() throws RepositoryException {
+                super.checkPreconditions();
+                ensureIsAlive();
+            }
 
-        SessionImpl.checkIndexOnName(sessionContext, destAbsPath);
+            @Override
+            public Object perform() throws RepositoryException {
+                sessionDelegate.checkProtectedNode(getParentPath(srcOakPath));
+                sessionDelegate.checkProtectedNode(getParentPath(destOakPath));
+
+                SessionImpl.checkIndexOnName(sessionContext, destAbsPath);
+
+                workspaceDelegate.copy(srcOakPath, destOakPath);
+                return null;
+            }
+        });
 
-        sessionDelegate.copy(
-                srcOakPath, destOakPath, sessionContext.getAccessManager());
     }
 
     @Override

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/SessionDelegate.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/SessionDelegate.java?rev=1521392&r1=1521391&r2=1521392&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/SessionDelegate.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/SessionDelegate.java Tue Sep 10 09:24:47 2013
@@ -349,46 +349,6 @@ public class SessionDelegate {
     }
 
     /**
-     * Copy a node
-     * @param srcPath  oak path to the source node to copy
-     * @param destPath  oak path to the destination
-     * @throws RepositoryException
-     */
-    public void copy(String srcPath, String destPath, AccessManager accessManager) throws RepositoryException {
-        // check destination
-        Tree dest = root.getTree(destPath);
-        if (dest.exists()) {
-            throw new ItemExistsException(destPath);
-        }
-
-        // check parent of destination
-        String destParentPath = PathUtils.getParentPath(destPath);
-        Tree destParent = root.getTree(destParentPath);
-        if (!destParent.exists()) {
-            throw new PathNotFoundException(PathUtils.getParentPath(destPath));
-        }
-
-        // check source exists
-        Tree src = root.getTree(srcPath);
-        if (!src.exists()) {
-            throw new PathNotFoundException(srcPath);
-        }
-
-        accessManager.checkPermissions(destPath, Permissions.getString(Permissions.NODE_TYPE_MANAGEMENT));
-
-        try {
-            Root currentRoot = contentSession.getLatestRoot();
-            if (!currentRoot.copy(srcPath, destPath)) {
-                throw new RepositoryException("Cannot copy node at " + srcPath + " to " + destPath);
-            }
-            currentRoot.commit();
-            refresh(false);
-        } catch (CommitFailedException e) {
-            throw newRepositoryException(e);
-        }
-    }
-
-    /**
      * Move a node
      * @param srcPath  oak path to the source node to copy
      * @param destPath  oak path to the destination

Added: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/WorkspaceDelegate.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/WorkspaceDelegate.java?rev=1521392&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/WorkspaceDelegate.java (added)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/delegate/WorkspaceDelegate.java Tue Sep 10 09:24:47 2013
@@ -0,0 +1,215 @@
+/*
+ * 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.delegate;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import javax.jcr.ItemExistsException;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+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.Type;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.core.IdentifierManager;
+import org.apache.jackrabbit.oak.jcr.SessionContext;
+import org.apache.jackrabbit.oak.jcr.security.AccessManager;
+import org.apache.jackrabbit.oak.plugins.memory.GenericPropertyState;
+import org.apache.jackrabbit.oak.plugins.memory.MultiGenericPropertyState;
+import org.apache.jackrabbit.oak.spi.security.authorization.permission.Permissions;
+
+import com.google.common.collect.Maps;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.apache.jackrabbit.JcrConstants.JCR_UUID;
+
+/**
+ * Delegate class for workspace operations.
+ */
+public class WorkspaceDelegate {
+
+    private final SessionContext context;
+
+    public WorkspaceDelegate(SessionContext context) {
+        this.context = checkNotNull(context);
+    }
+
+    /**
+     * Copy a node
+     * @param srcPath  oak path to the source node to copy
+     * @param destPath  oak path to the destination
+     * @throws RepositoryException
+     */
+    public void copy(String srcPath, String destPath) throws RepositoryException {
+        SessionDelegate sessionDelegate = context.getSessionDelegate();
+        AccessManager accessManager = context.getAccessManager();
+        Root root = sessionDelegate.getContentSession().getLatestRoot();
+        // check destination
+        Tree dest = root.getTree(destPath);
+        if (dest.exists()) {
+            throw new ItemExistsException(destPath);
+        }
+
+        // check parent of destination
+        String destParentPath = PathUtils.getParentPath(destPath);
+        Tree destParent = root.getTree(destParentPath);
+        if (!destParent.exists()) {
+            throw new PathNotFoundException(PathUtils.getParentPath(destPath));
+        }
+
+        // check source exists
+        Tree src = root.getTree(srcPath);
+        if (!src.exists()) {
+            throw new PathNotFoundException(srcPath);
+        }
+
+        accessManager.checkPermissions(destPath, Permissions.getString(Permissions.NODE_TYPE_MANAGEMENT));
+
+        try {
+            new WorkspaceCopy(root, srcPath, destPath).perform();
+            root.commit();
+            sessionDelegate.refresh(true);
+        } catch (CommitFailedException e) {
+            throw e.asRepositoryException();
+        }
+    }
+
+    //---------------------------< internal >-----------------------------------
+
+    private class WorkspaceCopy {
+
+        private final Map<String, String> translated = Maps.newHashMap();
+        private final String srcPath;
+        private final String destPath;
+        private final Root currentRoot;
+
+        public WorkspaceCopy(Root currentRoot, String srcPath, String destPath) {
+            this.srcPath = checkNotNull(srcPath);
+            this.destPath = checkNotNull(destPath);
+            this.currentRoot = checkNotNull(currentRoot);
+        }
+
+        public void perform() throws RepositoryException {
+            if (!currentRoot.copy(srcPath, destPath)) {
+                throw new RepositoryException("Cannot copy node at " + srcPath + " to " + destPath);
+            }
+            Tree src = currentRoot.getTree(srcPath);
+            Tree dest = currentRoot.getTree(destPath);
+            generateNewIdentifiers(dest);
+            updateReferences(src, dest);
+        }
+
+        public void generateNewIdentifiers(Tree t) throws RepositoryException {
+            if (t.hasProperty(JCR_UUID)) {
+                getNewId(t);
+            }
+            for (Tree c : t.getChildren()) {
+                generateNewIdentifiers(c);
+            }
+        }
+
+        /**
+         * Recursively updates references on the destination tree as defined by
+         * <code>Workspace.copy()</code>.
+         *
+         * @param src  the source tree of the copy operation.
+         * @param dest the unprocessed copy of the tree.
+         */
+        private void updateReferences(Tree src, Tree dest)
+                throws RepositoryException {
+            for (PropertyState prop : src.getProperties()) {
+                Type<?> type = prop.getType();
+                if (type == Type.REFERENCE
+                        || type == Type.REFERENCES
+                        || type == Type.WEAKREFERENCE
+                        || type == Type.WEAKREFERENCES) {
+                    updateProperty(prop, dest);
+                }
+            }
+            for (Tree child : src.getChildren()) {
+                updateReferences(child, dest.getChild(child.getName()));
+            }
+        }
+
+        private void updateProperty(PropertyState prop, Tree dest)
+                throws RepositoryException {
+            boolean multi = prop.isArray();
+            boolean weak = prop.getType() == Type.WEAKREFERENCE
+                    || prop.getType() == Type.WEAKREFERENCES;
+            List<String> ids = new ArrayList<String>();
+            for (int i = 0; i < prop.count(); i++) {
+                String id;
+                if (weak) {
+                    id = prop.getValue(Type.WEAKREFERENCE, i);
+                } else {
+                    id = prop.getValue(Type.REFERENCE, i);
+                }
+                translateId(id, ids);
+            }
+            PropertyState p;
+            if (multi) {
+                if (weak) {
+                    p = MultiGenericPropertyState.weakreferenceProperty(
+                            prop.getName(), ids);
+                } else {
+                    p = MultiGenericPropertyState.referenceProperty(
+                            prop.getName(), ids);
+                }
+            } else {
+                if (weak) {
+                    p = GenericPropertyState.weakreferenceProperty(prop.getName(), ids.get(0));
+                } else {
+                    p = GenericPropertyState.referenceProperty(prop.getName(), ids.get(0));
+                }
+            }
+            dest.setProperty(p);
+        }
+
+        private void translateId(String id, List<String> ids)
+                throws RepositoryException {
+            String newId = translated.get(id);
+            if (newId != null) {
+                ids.add(newId);
+            } else {
+                ids.add(id);
+            }
+        }
+
+        private String getNewId(Tree t) throws RepositoryException {
+            PropertyState uuid = t.getProperty(JCR_UUID);
+            if (uuid == null) {
+                // not referenceable?
+                throw new RepositoryException(
+                        "Node is not referenceable: " + t.getPath());
+            }
+            String targetId = uuid.getValue(Type.STRING);
+            // new id needed?
+            if (!translated.containsKey(targetId)) {
+                String newId = IdentifierManager.generateUUID();
+                translated.put(targetId, newId);
+                t.setProperty(JCR_UUID, newId, Type.STRING);
+            }
+            return translated.get(targetId);
+        }
+    }
+
+}

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

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

Modified: jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/RepositoryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/RepositoryTest.java?rev=1521392&r1=1521391&r2=1521392&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/RepositoryTest.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/RepositoryTest.java Tue Sep 10 09:24:47 2013
@@ -1888,6 +1888,34 @@ public class RepositoryTest extends Abst
         }
     }
 
+    @Test
+    public void workspaceCopyWithReferences() throws RepositoryException {
+        Session session = getAdminSession();
+        ValueFactory vf = session.getValueFactory();
+        Node root = session.getRootNode();
+        Node other = root.addNode("other");
+        other.addMixin("mix:referenceable");
+        Node src = root.addNode("src");
+        Node test = src.addNode("test");
+        test.addMixin("mix:referenceable");
+        src.setProperty("test", test);
+        src.setProperty("other", other);
+        src.setProperty("multi", new Value[]{vf.createValue(test), vf.createValue(other)});
+        session.save();
+        session.getWorkspace().copy("/src", "/dest");
+        Node dest = root.getNode("dest");
+        assertEquals("/dest/test", dest.getProperty("test").getNode().getPath());
+        assertEquals("/other", dest.getProperty("other").getNode().getPath());
+        Value[] refs = dest.getProperty("multi").getValues();
+        assertEquals(2, refs.length);
+        Set<String> paths = new HashSet<String>();
+        for (Value v : refs) {
+            paths.add(session.getNodeByIdentifier(v.getString()).getPath());
+        }
+        assertTrue(paths.contains("/other"));
+        assertTrue(paths.contains("/dest/test"));
+    }
+
     //------------------------------------------------------------< private >---
 
     private Node getNode(String path) throws RepositoryException {