Index: src/test/java/org/apache/jackrabbit/core/NodeImplTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/core/NodeImplTest.java (Revision 800328) +++ src/test/java/org/apache/jackrabbit/core/NodeImplTest.java (Arbeitskopie) @@ -18,14 +18,17 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.apache.jackrabbit.JcrConstants; import org.apache.jackrabbit.test.AbstractJCRTest; import org.apache.jackrabbit.test.NotExecutableException; +import org.apache.jackrabbit.uuid.UUID; import org.apache.jackrabbit.api.jsr283.security.AccessControlManager; import org.apache.jackrabbit.api.jsr283.security.AccessControlPolicyIterator; import org.apache.jackrabbit.api.jsr283.security.AccessControlPolicy; import org.apache.jackrabbit.api.jsr283.security.Privilege; import org.apache.jackrabbit.core.security.authorization.JackrabbitAccessControlList; +import javax.jcr.ItemExistsException; import javax.jcr.Node; import javax.jcr.Session; import javax.jcr.RepositoryException; @@ -125,4 +128,31 @@ changeReadPermission(principal, n, true); } } + + public void testAddNodeUuid() throws RepositoryException, NotExecutableException { + String uuid = "f81d4fae-7dec-11d0-a765-00a0c91e6bf6"; + Node n = testRootNode.addNode(nodeName1); + Node testNode = ((NodeImpl) n).addNodeWithUuid(nodeName2, uuid); + testNode.addMixin(JcrConstants.MIX_REFERENCEABLE); + testRootNode.save(); + assertEquals("Node UUID should be: "+uuid, uuid, testNode.getUUID()); + } + + public void testAddNodeUuidCollision() throws RepositoryException, NotExecutableException { + String uuid = "f81d4fae-7dec-11d0-a765-00a0c91e6bf6"; + Node n = testRootNode.addNode(nodeName1); + Node testNode1 = ((NodeImpl) n).addNodeWithUuid(nodeName2, uuid); + testNode1.addMixin(JcrConstants.MIX_REFERENCEABLE); + testRootNode.save(); + boolean collisionDetected = false; + + try { + Node testNode2 = ((NodeImpl) n).addNodeWithUuid(nodeName2, uuid); + testNode1.addMixin(JcrConstants.MIX_REFERENCEABLE); + testRootNode.save(); + } catch (ItemExistsException iee) { + collisionDetected = true; + } + assertTrue("Node collision detected: "+uuid, collisionDetected); + } } \ No newline at end of file Index: src/main/java/org/apache/jackrabbit/core/NodeImpl.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/NodeImpl.java (Revision 800328) +++ src/main/java/org/apache/jackrabbit/core/NodeImpl.java (Arbeitskopie) @@ -2129,30 +2129,75 @@ * {@inheritDoc} */ public synchronized Node addNode(String relPath) - throws ItemExistsException, PathNotFoundException, VersionException, - ConstraintViolationException, LockException, RepositoryException { - // check state of this instance - sanityCheck(); + throws RepositoryException { + return addNodeWithUuid(relPath, null, null); + } - return internalAddNode(relPath, null); + /** + * Same as {@link Node#addNode(String)} except + * this method takes an additional uuid argument. + * Important Notice: Use this method with caution! + * @param relPath path of the new node + * @param uuid uuid of the new node or null + * @return the newly added node + * @throws RepositoryException if the node can not be added + */ + public synchronized Node addNodeWithUuid(String relPath, String uuid) + throws RepositoryException { + return addNodeWithUuid(relPath, null, uuid); } /** * {@inheritDoc} */ public synchronized Node addNode(String relPath, String nodeTypeName) - throws ItemExistsException, PathNotFoundException, - NoSuchNodeTypeException, VersionException, - ConstraintViolationException, LockException, RepositoryException { + throws RepositoryException { + return addNodeWithUuid(relPath, nodeTypeName, null); + } + + /** + * Same as {@link Node#addNode(String, String)} except + * this method takes an additional uuid argument. + * Important Notice: Use this method with caution! + * @param relPath path of the new node + * @param nodeTypeName name of the new node's node type or null + * if it should be determined automatically + * @param uuid uuid of the new node or null + * @return the newly added node + * @throws RepositoryException if the node can not be added + */ + public synchronized Node addNodeWithUuid( + String relPath, String nodeTypeName, String uuid) + throws RepositoryException { // check state of this instance sanityCheck(); - NodeTypeImpl nt = (NodeTypeImpl) session.getNodeTypeManager().getNodeType(nodeTypeName); - if (nt.isMixin()) { - throw new RepositoryException(nodeTypeName + ": not a primary node type"); + NodeId id = null; + if (uuid != null) { + //if (!isNodeType(NameConstants.MIX_REFERENCEABLE)) { + // throw new UnsupportedRepositoryOperationException(); + //} + // Test for existing UUID + // @see SessionImporter.startNode(NodeInfo nodeInfo, List propInfos) + try { + session.getNodeByUUID(uuid); + throw new ItemExistsException( + "A node with this UUID already exists: " + uuid); + } catch (ItemNotFoundException infe) { + id = new NodeId(new UUID(uuid)); + } } - return internalAddNode(relPath, nt); + NodeType nt = null; + if (nodeTypeName != null) { + nt = session.getNodeTypeManager().getNodeType(nodeTypeName); + if (nt.isMixin()) { + throw new RepositoryException( + "Not a primary node type: " + nodeTypeName); + } + } + + return internalAddNode(relPath, (NodeTypeImpl) nt, id); } /** @@ -3297,9 +3342,11 @@ /** * {@inheritDoc} */ - public Version checkin() - throws VersionException, UnsupportedRepositoryOperationException, - InvalidItemStateException, LockException, RepositoryException { + public Version checkin() throws RepositoryException { + return checkin(null); + } + + public Version checkin(Calendar cal) throws RepositoryException { // check state of this instance sanityCheck(); @@ -3317,7 +3364,7 @@ int options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_HOLD | ItemValidator.CHECK_PENDING_CHANGES_ON_NODE; session.getValidator().checkModify(this, options, Permission.VERSION_MNGMT); - Version v = session.getVersionManager().checkin(this); + Version v = session.getVersionManager().checkin(this, cal); boolean success = false; try { internalSetProperty(NameConstants.JCR_ISCHECKEDOUT, InternalValue.create(false)); @@ -4291,7 +4338,7 @@ } else { // with simple versioning, the node is checked in automatically, // thus not allowing any branches - session.getVersionManager().checkin(this); + session.getVersionManager().checkin(this, null); } // 3. N's jcr:isCheckedOut property is set to false. internalSetProperty(NameConstants.JCR_ISCHECKEDOUT, InternalValue.create(false)); Index: src/main/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java (Revision 800328) +++ src/main/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java (Arbeitskopie) @@ -53,6 +53,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Calendar; import java.util.Collection; import java.util.Iterator; import java.util.List; @@ -276,7 +277,7 @@ * This method must not be synchronized since it could cause deadlocks with * item-reading listeners in the observation thread. */ - public Version checkin(final NodeImpl node) throws RepositoryException { + public Version checkin(final NodeImpl node, final Calendar cal) throws RepositoryException { InternalVersion version = (InternalVersion) escFactory.doSourced((SessionImpl) node.getSession(), new SourcedTarget() { public Object run() throws RepositoryException { @@ -286,11 +287,11 @@ // the property String histUUID = node.getProperty(NameConstants.JCR_VERSIONHISTORY).getString(); vh = getVersionHistory(NodeId.valueOf(histUUID)); - return checkin((InternalVersionHistoryImpl) vh, node, false); + return checkin((InternalVersionHistoryImpl) vh, node, false, cal); } else { // in simple versioning the history id needs to be calculated vh = getVersionHistoryOfNode(node.getNodeId()); - return checkin((InternalVersionHistoryImpl) vh, node, true); + return checkin((InternalVersionHistoryImpl) vh, node, true, cal); } } }); Index: src/main/java/org/apache/jackrabbit/core/version/AbstractVersionManager.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/version/AbstractVersionManager.java (Revision 800328) +++ src/main/java/org/apache/jackrabbit/core/version/AbstractVersionManager.java (Arbeitskopie) @@ -16,6 +16,8 @@ */ package org.apache.jackrabbit.core.version; +import java.util.Calendar; + import org.apache.jackrabbit.core.NodeId; import org.apache.jackrabbit.core.NodeImpl; import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry; @@ -424,17 +426,19 @@ * @param history the version history * @param node node to checkin * @param simple flag indicates simple versioning + * @param cal create time of the new version, or null * @return internal version * @throws javax.jcr.RepositoryException if an error occurs * @see javax.jcr.Node#checkin() */ protected InternalVersion checkin(InternalVersionHistoryImpl history, - NodeImpl node, boolean simple) + NodeImpl node, boolean simple, Calendar cal) throws RepositoryException { WriteOperation operation = startWriteOperation(); try { String versionName = calculateCheckinVersionName(history, node, simple); - InternalVersionImpl v = history.checkin(NameFactoryImpl.getInstance().create("", versionName), node); + InternalVersionImpl v = history.checkin( + NameFactoryImpl.getInstance().create("", versionName), node, cal); operation.save(); return v; } catch (ItemStateException e) { Index: src/main/java/org/apache/jackrabbit/core/version/XAVersionManager.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/version/XAVersionManager.java (Revision 800328) +++ src/main/java/org/apache/jackrabbit/core/version/XAVersionManager.java (Arbeitskopie) @@ -16,6 +16,7 @@ */ package org.apache.jackrabbit.core.version; +import java.util.Calendar; import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -159,7 +160,7 @@ /** * {@inheritDoc} */ - public Version checkin(NodeImpl node) throws RepositoryException { + public Version checkin(NodeImpl node, Calendar cal) throws RepositoryException { if (isInXA()) { InternalVersionHistory vh; InternalVersion version; @@ -168,15 +169,15 @@ // the property String histUUID = node.getProperty(NameConstants.JCR_VERSIONHISTORY).getString(); vh = getVersionHistory(NodeId.valueOf(histUUID)); - version = checkin((InternalVersionHistoryImpl) vh, node, false); + version = checkin((InternalVersionHistoryImpl) vh, node, false, cal); } else { // in simple versioning the history id needs to be calculated vh = getVersionHistoryOfNode(node.getNodeId()); - version = checkin((InternalVersionHistoryImpl) vh, node, true); + version = checkin((InternalVersionHistoryImpl) vh, node, true, cal); } return (Version) ((SessionImpl) node.getSession()).getNodeById(version.getId()); } - return vMgr.checkin(node); + return vMgr.checkin(node, cal); } /** @@ -394,14 +395,14 @@ * Before modifying version history given, make a local copy of it. */ protected InternalVersion checkin(InternalVersionHistoryImpl history, - NodeImpl node, boolean simple) + NodeImpl node, boolean simple, Calendar cal) throws RepositoryException { if (history.getVersionManager() != this) { history = makeLocalCopy(history); xaItems.put(history.getId(), history); } - InternalVersion version = super.checkin(history, node, simple); + InternalVersion version = super.checkin(history, node, simple, cal); NodeId frozenNodeId = version.getFrozenNodeId(); InternalVersionItem frozenNode = createInternalVersionItem(frozenNodeId); if (frozenNode != null) { Index: src/main/java/org/apache/jackrabbit/core/version/VersionManager.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/version/VersionManager.java (Revision 800328) +++ src/main/java/org/apache/jackrabbit/core/version/VersionManager.java (Arbeitskopie) @@ -16,6 +16,8 @@ */ package org.apache.jackrabbit.core.version; +import java.util.Calendar; + import org.apache.jackrabbit.core.NodeImpl; import org.apache.jackrabbit.core.NodeId; import org.apache.jackrabbit.core.state.NodeState; @@ -61,10 +63,11 @@ * newly created version objects. * * @param node node to checkin + * @param cal create time of the new version, or null * @return the newly created version * @throws RepositoryException if an error occurs */ - Version checkin(NodeImpl node) throws RepositoryException; + Version checkin(NodeImpl node, Calendar cal) throws RepositoryException; /** * Removes the specified version from the given version history. Index: src/main/java/org/apache/jackrabbit/core/version/InternalVersionHistoryImpl.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/version/InternalVersionHistoryImpl.java (Revision 800328) +++ src/main/java/org/apache/jackrabbit/core/version/InternalVersionHistoryImpl.java (Arbeitskopie) @@ -489,12 +489,12 @@ * * @param name new version name * @param src source node to version + * @param cal create time of the new version, or null * @return the newly created version * @throws RepositoryException if an error occurs */ - InternalVersionImpl checkin(Name name, NodeImpl src) + InternalVersionImpl checkin(Name name, NodeImpl src, Calendar cal) throws RepositoryException { - // copy predecessors from src node InternalValue[] predecessors; if (src.hasProperty(NameConstants.JCR_PREDECESSORS)) { @@ -527,7 +527,10 @@ NodeStateEx vNode = node.addNode(name, NameConstants.NT_VERSION, versionId, true); // initialize 'created', 'predecessors' and 'successors' - vNode.setPropertyValue(NameConstants.JCR_CREATED, InternalValue.create(getCurrentTime())); + if (cal == null) { + cal = getCurrentTime(); + } + vNode.setPropertyValue(NameConstants.JCR_CREATED, InternalValue.create(cal)); vNode.setPropertyValues(NameConstants.JCR_PREDECESSORS, PropertyType.REFERENCE, predecessors); vNode.setPropertyValues(NameConstants.JCR_SUCCESSORS, PropertyType.REFERENCE, InternalValue.EMPTY_ARRAY);