You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by dp...@apache.org on 2008/04/04 18:02:00 UTC

svn commit: r644747 - in /jackrabbit/trunk/jackrabbit-core/src: main/java/org/apache/jackrabbit/core/NodeImpl.java main/java/org/apache/jackrabbit/core/xml/SessionImporter.java test/java/org/apache/jackrabbit/core/ShareableNodeTest.java

Author: dpfister
Date: Fri Apr  4 09:01:50 2008
New Revision: 644747

URL: http://svn.apache.org/viewvc?rev=644747&view=rev
Log:
JCR-1104 - JSR 283 support
- shareble nodes (work in progress)
- workspace and session import of conflicting shareable nodes

Modified:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/SessionImporter.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/ShareableNodeTest.java

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java?rev=644747&r1=644746&r2=644747&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java Fri Apr  4 09:01:50 2008
@@ -1962,7 +1962,110 @@
         return node;
     }
 
-    //-----------------------------------------------------------------< Item >
+    /**
+     * Create a child node that is a clone of a shareable node.
+     *
+     * @param src shareable source node
+     * @param name name of new node
+     * @return child node
+     * @throws ItemExistsException if there already is a child node with the
+     *             name given and the definition does not allow creating another one
+     * @throws VersionException if this node is not checked out
+     * @throws ConstraintViolationException if no definition is found in this
+     *             node that would allow creating the child node
+     * @throws LockException if this node is locked
+     * @throws RepositoryException if some other error occurs
+     */
+    public synchronized NodeImpl clone(NodeImpl src, Name name)
+            throws ItemExistsException, VersionException,
+                   ConstraintViolationException, LockException,
+                   RepositoryException {
+
+        Path nodePath;
+        try {
+            nodePath = PathFactoryImpl.getInstance().create(getPrimaryPath(), name, true);
+        } catch (MalformedPathException e) {
+            // should never happen
+            String msg = "internal error: invalid path " + safeGetJCRPath();
+            log.debug(msg);
+            throw new RepositoryException(msg, e);
+        }
+
+        // (1) make sure that parent node is checked-out
+        if (!internalIsCheckedOut()) {
+            String msg = safeGetJCRPath()
+                    + ": cannot add a child to a checked-in node";
+            log.debug(msg);
+            throw new VersionException(msg);
+        }
+
+        // (2) check lock status
+        checkLock();
+
+        // (3) check for name collisions
+        NodeDefinitionImpl def;
+        try {
+            def = getApplicableChildNodeDefinition(name, null);
+        } catch (RepositoryException re) {
+            String msg = "no definition found in parent node's node type for new node";
+            log.debug(msg);
+            throw new ConstraintViolationException(msg, re);
+        }
+        NodeState thisState = (NodeState) state;
+        NodeState.ChildNodeEntry cne = thisState.getChildNodeEntry(name, 1);
+        if (cne != null) {
+            // there's already a child node entry with that name;
+            // check same-name sibling setting of new node
+            if (!def.allowsSameNameSiblings()) {
+                throw new ItemExistsException(itemMgr.safeGetJCRPath(nodePath));
+            }
+            // check same-name sibling setting of existing node
+            NodeId newId = cne.getId();
+            if (!((NodeImpl) itemMgr.getItem(newId)).getDefinition().allowsSameNameSiblings()) {
+                throw new ItemExistsException(itemMgr.safeGetJCRPath(nodePath));
+            }
+        }
+
+        // (4) check protected flag of parent (i.e. this) node
+        if (definition.isProtected()) {
+            String msg = safeGetJCRPath() + ": cannot add a child to a protected node";
+            log.debug(msg);
+            throw new ConstraintViolationException(msg);
+        }
+
+        // (5) verify that source is shareable
+        if (!src.isShareable()) {
+            String msg = "Source node at " + src.safeGetJCRPath() + " is not shareable.";
+            log.debug(msg);
+            throw new RepositoryException(msg);
+        }
+
+        // (6) detect share cycle
+        NodeId srcId = src.getNodeId();
+        NodeId parentId = getNodeId();
+        HierarchyManager hierMgr = session.getHierarchyManager();
+        if (parentId.equals(srcId) || hierMgr.isAncestor(srcId, parentId)) {
+            String msg = "This would create a share cycle.";
+            log.debug(msg);
+            throw new RepositoryException(msg);
+        }
+
+        // (7) do clone operation (modify and store affected states)
+        if (!src.addShare(parentId)) {
+            String msg = "Adding a shareable node twice to the same parent is not supported.";
+            log.debug(msg);
+            throw new UnsupportedRepositoryOperationException(msg);
+        }
+
+        // (8) modify the state of 'this', i.e. the parent node
+        thisState = (NodeState) getOrCreateTransientItemState();
+        // add new child node entry
+        thisState.addChildNodeEntry(name, srcId);
+
+        return itemMgr.getNode(srcId, parentId);
+    }
+
+    // -----------------------------------------------------------------< Item >
     /**
      * {@inheritDoc}
      */
@@ -3119,6 +3222,26 @@
         return ((NodeState) state).containsShare(parentId);
     }
 
+    /**
+     * Add a parent to the shared set. This method does not check whether
+     * adding this parent would create a share cycle (and should therefore
+     * strictly be used internally), it does, however detect whether the given
+     * parent is already contained in the shared set.
+     *
+     * @return <code>true</code> if adding succeeded;
+     *         <code>false</code> otherwise.
+     */
+    protected boolean addShare(NodeId parentId) throws RepositoryException {
+        // quickly verify whether the share is already contained before
+        // possibly changing the referenced state
+        NodeState state = (NodeState) this.state;
+        if (state.containsShare(parentId)) {
+            return false;
+        }
+        // now make modifications
+        state = (NodeState) getOrCreateTransientItemState();
+        return state.addShare(parentId);
+    }
 
     /**
      * {@inheritDoc}

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/SessionImporter.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/SessionImporter.java?rev=644747&r1=644746&r2=644747&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/SessionImporter.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/SessionImporter.java Fri Apr  4 09:01:50 2008
@@ -114,6 +114,11 @@
                 refTracker.mappedUUID(nodeInfo.getId().getUUID(), node.getNodeId().getUUID());
             }
         } else if (uuidBehavior == ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW) {
+            // if conflicting node is shareable, then clone it
+            if (conflicting.isNodeType(NameConstants.MIX_SHAREABLE)) {
+                parent.clone(conflicting, nodeInfo.getName());
+                return null;
+            }
             String msg = "a node with uuid " + nodeInfo.getId() + " already exists!";
             log.debug(msg);
             throw new ItemExistsException(msg);
@@ -224,6 +229,12 @@
                 if (conflicting != null) {
                     // resolve uuid conflict
                     node = resolveUUIDConflict(parent, conflicting, nodeInfo);
+                    if (node == null) {
+                        // no new node has been created, so skip this node
+                        parents.push(null); // push null onto stack for skipped node
+                        log.debug("skipping existing node " + nodeInfo.getName());
+                        return;
+                    }
                 } else {
                     // create new with given uuid
                     node = createNode(parent, nodeName, ntName, mixins, id);

Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/ShareableNodeTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/ShareableNodeTest.java?rev=644747&r1=644746&r2=644747&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/ShareableNodeTest.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/ShareableNodeTest.java Fri Apr  4 09:01:50 2008
@@ -485,9 +485,12 @@
     }
 
     /**
-     * Verify import and export (6.13.14).
+     * Verify system view import via workspace (6.13.14). Export a system view
+     * containing a shareable node and verify, that reimporting underneath
+     * a different parent adds another member to the shared set and does not
+     * duplicate children nodes.
      */
-    public void testImportExport() throws Exception {
+    public void testImportSystemViewCollision() throws Exception {
         // setup parent nodes and first child
         Node a1 = testRootNode.addNode("a1");
         Node a2 = testRootNode.addNode("a2");
@@ -525,6 +528,179 @@
         InputStream in = new FileInputStream(tmpFile);
         try {
             workspace.importXML(a3.getPath(), in, ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW);
+        } finally {
+            in.close();
+        }
+
+        // verify there's another element in the shared set
+        Node[] shared = getSharedSet(b1);
+        assertEquals(3, shared.length);
+
+        // verify child c has not been duplicated
+        Node[] children = toArray(b1.getNodes());
+        assertEquals(1, children.length);
+    }
+
+    /**
+     * Verify document view import via workspace (6.13.14). Export a document
+     * view containing a shareable node and verify, that reimporting
+     * underneath a different parent adds another member to the shared set and
+     * does not duplicate children nodes.
+     */
+    public void testImportDocumentViewCollision() throws Exception {
+        // setup parent nodes and first child
+        Node a1 = testRootNode.addNode("a1");
+        Node a2 = testRootNode.addNode("a2");
+        Node a3 = testRootNode.addNode("a3");
+        Node b1 = a1.addNode("b1");
+        testRootNode.save();
+
+        // add mixin
+        b1.addMixin("mix:shareable");
+        b1.save();
+
+        // clone
+        Session session = b1.getSession();
+        Workspace workspace = session.getWorkspace();
+        workspace.clone(workspace.getName(), b1.getPath(),
+                a2.getPath() + "/b2", false);
+
+        // add child c to shareable nodes b1 & b2
+        b1.addNode("c");
+        b1.save();
+
+        // create temp file
+        File tmpFile = File.createTempFile("test", null);
+        tmpFile.deleteOnExit();
+
+        // export system view of /a1/b1
+        OutputStream out = new FileOutputStream(tmpFile);
+        try {
+            session.exportDocumentView(b1.getPath(), out, false, false);
+        } finally {
+            out.close();
+        }
+
+        // and import again underneath /a3
+        InputStream in = new FileInputStream(tmpFile);
+        try {
+            workspace.importXML(a3.getPath(), in, ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW);
+        } finally {
+            in.close();
+        }
+
+        // verify there's another element in the shared set
+        Node[] shared = getSharedSet(b1);
+        assertEquals(3, shared.length);
+
+        // verify child c has not been duplicated
+        Node[] children = toArray(b1.getNodes());
+        assertEquals(1, children.length);
+    }
+
+    /**
+     * Verify system view import via session (6.13.14). Export a system view
+     * containing a shareable node and verify, that reimporting underneath
+     * a different parent adds another member to the shared set and does not
+     * duplicate children nodes.
+     */
+    public void testSessionImportSystemViewCollision() throws Exception {
+        // setup parent nodes and first child
+        Node a1 = testRootNode.addNode("a1");
+        Node a2 = testRootNode.addNode("a2");
+        Node a3 = testRootNode.addNode("a3");
+        Node b1 = a1.addNode("b1");
+        testRootNode.save();
+
+        // add mixin
+        b1.addMixin("mix:shareable");
+        b1.save();
+
+        // clone
+        Session session = b1.getSession();
+        Workspace workspace = session.getWorkspace();
+        workspace.clone(workspace.getName(), b1.getPath(),
+                a2.getPath() + "/b2", false);
+
+        // add child c to shareable nodes b1 & b2
+        b1.addNode("c");
+        b1.save();
+
+        // create temp file
+        File tmpFile = File.createTempFile("test", null);
+        tmpFile.deleteOnExit();
+
+        // export system view of /a1/b1
+        OutputStream out = new FileOutputStream(tmpFile);
+        try {
+            session.exportSystemView(b1.getPath(), out, false, false);
+        } finally {
+            out.close();
+        }
+
+        // and import again underneath /a3
+        InputStream in = new FileInputStream(tmpFile);
+        try {
+            session.importXML(a3.getPath(), in, ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW);
+            session.save();
+        } finally {
+            in.close();
+        }
+
+        // verify there's another element in the shared set
+        Node[] shared = getSharedSet(b1);
+        assertEquals(3, shared.length);
+
+        // verify child c has not been duplicated
+        Node[] children = toArray(b1.getNodes());
+        assertEquals(1, children.length);
+    }
+
+    /**
+     * Verify document view import via session (6.13.14). Export a document
+     * view containing a shareable node and verify, that reimporting
+     * underneath a different parent adds another member to the shared set and
+     * does not duplicate children nodes.
+     */
+    public void testSessionImportDocumentViewCollision() throws Exception {
+        // setup parent nodes and first child
+        Node a1 = testRootNode.addNode("a1");
+        Node a2 = testRootNode.addNode("a2");
+        Node a3 = testRootNode.addNode("a3");
+        Node b1 = a1.addNode("b1");
+        testRootNode.save();
+
+        // add mixin
+        b1.addMixin("mix:shareable");
+        b1.save();
+
+        // clone
+        Session session = b1.getSession();
+        Workspace workspace = session.getWorkspace();
+        workspace.clone(workspace.getName(), b1.getPath(),
+                a2.getPath() + "/b2", false);
+
+        // add child c to shareable nodes b1 & b2
+        b1.addNode("c");
+        b1.save();
+
+        // create temp file
+        File tmpFile = File.createTempFile("test", null);
+        tmpFile.deleteOnExit();
+
+        // export system view of /a1/b1
+        OutputStream out = new FileOutputStream(tmpFile);
+        try {
+            session.exportSystemView(b1.getPath(), out, false, false);
+        } finally {
+            out.close();
+        }
+
+        // and import again underneath /a3
+        InputStream in = new FileInputStream(tmpFile);
+        try {
+            session.importXML(a3.getPath(), in, ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW);
+            session.save();
         } finally {
             in.close();
         }