You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by ju...@apache.org on 2006/10/05 12:03:34 UTC

svn commit: r453169 - /jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/xml/WorkspaceImporter.java

Author: jukka
Date: Thu Oct  5 03:03:32 2006
New Revision: 453169

URL: http://svn.apache.org/viewvc?view=rev&rev=453169
Log:
Oops, reverting the accidental WorkspaceImporter change in revision 453165. Still reviewing patch from Nicolas.

Modified:
    jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/xml/WorkspaceImporter.java

Modified: jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/xml/WorkspaceImporter.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/xml/WorkspaceImporter.java?view=diff&rev=453169&r1=453168&r2=453169
==============================================================================
--- jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/xml/WorkspaceImporter.java (original)
+++ jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/xml/WorkspaceImporter.java Thu Oct  5 03:03:32 2006
@@ -16,23 +16,6 @@
  */
 package org.apache.jackrabbit.core.xml;
 
-import java.util.Iterator;
-import java.util.List;
-import java.util.Stack;
-
-import javax.jcr.AccessDeniedException;
-import javax.jcr.ImportUUIDBehavior;
-import javax.jcr.ItemExistsException;
-import javax.jcr.ItemNotFoundException;
-import javax.jcr.PathNotFoundException;
-import javax.jcr.PropertyType;
-import javax.jcr.RepositoryException;
-import javax.jcr.ValueFormatException;
-import javax.jcr.lock.LockException;
-import javax.jcr.nodetype.ConstraintViolationException;
-import javax.jcr.version.VersionException;
-import javax.jcr.version.VersionHistory;
-
 import org.apache.jackrabbit.core.BatchedItemOperations;
 import org.apache.jackrabbit.core.HierarchyManager;
 import org.apache.jackrabbit.core.NodeId;
@@ -56,23 +39,36 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.jcr.ImportUUIDBehavior;
+import javax.jcr.ItemExistsException;
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.lock.LockException;
+import javax.jcr.nodetype.ConstraintViolationException;
+import javax.jcr.version.VersionException;
+import javax.jcr.version.VersionHistory;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Stack;
+
 /**
- * WorkspaceImporter. It imports the content submitted to it
- * by the Content Handler
- *
+ * <code>WorkspaceImporter</code> ...
  */
 public class WorkspaceImporter implements Importer {
 
     private static Logger log = LoggerFactory.getLogger(WorkspaceImporter.class);
+
     private final NodeState importTarget;
+    private final WorkspaceImpl wsp;
     private final NodeTypeRegistry ntReg;
     private final HierarchyManager hierMgr;
     private final BatchedItemOperations itemOps;
+
     private final int uuidBehavior;
 
-    //It is not useful anymore: we never abort: we raise an exception.
-    // I suggest to delete it. Do you see any issue with this?
-    private boolean aborted = false;
+    private boolean aborted;
     private Stack parents;
 
     /**
@@ -81,31 +77,12 @@
      */
     private final ReferenceChangeTracker refTracker;
 
-    // Unused for now. It will be used in the next iteration on JIRA
-    private boolean raw = false;
-
-    /**
-     * True if we skip the tree with current node as root
-     */
-    private boolean skip = false;
-
     /**
-     * Used to find when stopping skipping
-     */
-    private NodeInfo skipNode;
-
-    /**
-     * True if this node already exist
-     */
-    private NodeState existing = null;
-    private WorkspaceImpl wsp;
-
-    /**
-     * Creates a new <code>sWorkspaceImporter</code> instance.
+     * Creates a new <code>WorkspaceImporter</code> instance.
      *
      * @param parentPath   target path where to add the imported subtree
-     * @param wsp the workspace we want to import content to
-     * @param ntReg the NodeTypeRegistry of the repository
+     * @param wsp
+     * @param ntReg
      * @param uuidBehavior flag that governs how incoming UUIDs are handled
      * @throws PathNotFoundException        if no node exists at
      *                                      <code>parentPath</code> or if the
@@ -121,195 +98,108 @@
      * @throws RepositoryException          if another error occurs
      */
     public WorkspaceImporter(Path parentPath,
-            WorkspaceImpl wsp,
-            NodeTypeRegistry ntReg,
-            int uuidBehavior)
-    throws PathNotFoundException, ConstraintViolationException,
-    VersionException, LockException, RepositoryException {
+                             WorkspaceImpl wsp,
+                             NodeTypeRegistry ntReg,
+                             int uuidBehavior)
+            throws PathNotFoundException, ConstraintViolationException,
+            VersionException, LockException, RepositoryException {
+
         SessionImpl ses = (SessionImpl) wsp.getSession();
         itemOps = new BatchedItemOperations(wsp.getItemStateManager(),
                 ntReg, ses.getLockManager(), ses, wsp.getHierarchyManager(),
                 ses.getNamespaceResolver());
+        hierMgr = wsp.getHierarchyManager();
 
-        this.hierMgr = wsp.getHierarchyManager();
-        //Perform preliminary checks
+        // perform preliminary checks
         itemOps.verifyCanWrite(parentPath);
         importTarget = itemOps.getNodeState(parentPath);
+
         this.wsp = wsp;
         this.ntReg = ntReg;
         this.uuidBehavior = uuidBehavior;
+
         aborted = false;
+
         refTracker = new ReferenceChangeTracker();
+
         parents = new Stack();
         parents.push(importTarget);
     }
 
     /**
-     * Performs some checks to know if the node is importable or not.
-     * If it is a serious issue, raises an exception, else return false.
-     * this subtree.
-     * <br/>
-     * Performs also if needed some remapping.
-     *
-     * @param parent the parent NodeState
-     * @param nodeInfo NodeInfo passed by the ContentHandler
-     * @param propInfo PropInfo passed by the ContentHandler
-     * @return true if the node is fine; else false
-     * @throws RepositoryException if some constraints checks are not OK
-     * @throws ItemExistsException if the item exist
-     * @throws ItemNotFoundException if some constraints checks are not OK (shouldn't happen)
-     * @throws LockException if some constraints checks are not OK
-     * @throws VersionException if some constraints checks are not OK
-     * @throws AccessDeniedException if some constraints checks are not OK
-     * @throws ConstraintViolationException if some constraints checks are not OK
+     * @param parent
+     * @param conflicting
+     * @param nodeInfo
+     * @return
+     * @throws RepositoryException
      */
-    private boolean checkNode(NodeState parent, NodeInfo nodeInfo, List propInfo)
-        throws ConstraintViolationException, AccessDeniedException, VersionException,
-        LockException, ItemNotFoundException, ItemExistsException, RepositoryException {
-        itemOps.checkAddNode(parent, nodeInfo.getName(), nodeInfo.getNodeTypeName(),
-                BatchedItemOperations.CHECK_ACCESS
-                | BatchedItemOperations.CHECK_CONSTRAINTS
-                | BatchedItemOperations.CHECK_LOCK
-                | BatchedItemOperations.CHECK_VERSIONING);
-
-        QName nodeName = nodeInfo.getName();
-        QName ntName = nodeInfo.getNodeTypeName();
-
-        if (parent.hasChildNodeEntry(nodeName)) {
-            // a node with that name already exists...
-            //No need to check for more than one, since if it
-            //is the case we can import it.
-            NodeState.ChildNodeEntry entry =
-                parent.getChildNodeEntry(nodeName, 1);
-            NodeId idExisting = entry.getId();
-            NodeState existing = (NodeState) itemOps.getItemState(idExisting);
-            NodeDef def = ntReg.getNodeDef(existing.getDefinitionId());
-            if (!def.allowsSameNameSiblings()) {
-                // existing doesn't allow same-name siblings,
-                // check for potential conflicts
-                EffectiveNodeType entExisting =
-                    itemOps.getEffectiveNodeType(existing);
-                if (!raw && def.isProtected() && entExisting.includesNodeType(ntName)) {
-                    return false;
-                }
+    protected NodeState resolveUUIDConflict(NodeState parent,
+                                            NodeState conflicting,
+                                            NodeInfo nodeInfo)
+            throws RepositoryException {
 
-                if (def.isAutoCreated() && entExisting.includesNodeType(ntName)) {
-                    // this node has already been auto-created,
-                    // no need to create it
-                    this.existing = existing;
-                } else {
-                    throw new ItemExistsException(itemOps.safeGetJCRPath(existing.getNodeId()));
-                }
+        NodeState node;
+        if (uuidBehavior == ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW) {
+            // create new with new uuid:
+            // check if new node can be added (check access rights &
+            // node type constraints only, assume locking & versioning status
+            // has already been checked on ancestor)
+            itemOps.checkAddNode(parent, nodeInfo.getName(),
+                    nodeInfo.getNodeTypeName(),
+                    BatchedItemOperations.CHECK_ACCESS
+                    | BatchedItemOperations.CHECK_CONSTRAINTS);
+            node = itemOps.createNodeState(parent, nodeInfo.getName(),
+                    nodeInfo.getNodeTypeName(), nodeInfo.getMixinNames(), null);
+            // remember uuid mapping
+            EffectiveNodeType ent = itemOps.getEffectiveNodeType(node);
+            if (ent.includesNodeType(QName.MIX_REFERENCEABLE)) {
+                refTracker.mappedUUID(nodeInfo.getId().getUUID(), node.getNodeId().getUUID());
             }
-        }
-
-        if (parent.hasPropertyName(nodeName)) {
-            /**
-             * a property with the same name already exists; if this property
-             * has been imported as well (e.g. through document view import
-             * where an element can have the same name as one of the attributes
-             * of its parent element) we have to rename the onflicting property;
-             *
-             * see http://issues.apache.org/jira/browse/JCR-61
-             */
-            PropertyId propId = new PropertyId(parent.getNodeId(), nodeName);
-            PropertyState conflicting = itemOps.getPropertyState(propId);
-            if (conflicting.getStatus() == ItemState.STATUS_NEW) {
-                // assume this property has been imported as well;
-                // rename conflicting property
-                // @todo use better reversible escaping scheme to create unique name
-                QName newName = new QName(nodeName.getNamespaceURI(), nodeName.getLocalName() + "_");
-                if (parent.hasPropertyName(newName)) {
-                    newName = new QName(newName.getNamespaceURI(), newName.getLocalName() + "_");
+        } else if (uuidBehavior == ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW) {
+            String msg = "a node with uuid " + nodeInfo.getId()
+                    + " already exists!";
+            log.debug(msg);
+            throw new ItemExistsException(msg);
+        } else if (uuidBehavior == ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING) {
+            // make sure conflicting node is not importTarget or an ancestor thereof
+            Path p0 = hierMgr.getPath(importTarget.getNodeId());
+            Path p1 = hierMgr.getPath(conflicting.getNodeId());
+            try {
+                if (p1.equals(p0) || p1.isAncestorOf(p0)) {
+                    String msg = "cannot remove ancestor node";
+                    log.debug(msg);
+                    throw new ConstraintViolationException(msg);
                 }
-                PropertyState newProp =
-                    itemOps.createPropertyState(parent, newName,
-                            conflicting.getType(), conflicting.getValues().length);
-                newProp.setValues(conflicting.getValues());
-                parent.removePropertyName(nodeName);
-                itemOps.store(parent);
-                itemOps.destroy(conflicting);
+            } catch (MalformedPathException mpe) {
+                // should never get here...
+                String msg = "internal error: failed to determine degree of relationship";
+                log.error(msg, mpe);
+                throw new RepositoryException(msg, mpe);
             }
-        }
-
-        return true;
-    }
-
-    /**
-     * Create propoerties on a specific NodeState
-     * @param myNode the NodeState
-     * @param propInfos PropInfo
-     * @throws ItemNotFoundException if issue in the NodeState
-     * @throws ItemExistsException if issue in the NodeState
-     * @throws ConstraintViolationException if issue in the NodeState
-     * @throws ValueFormatException if issue in the NodeState
-     * @throws RepositoryException if issue in the NodeState
-     */
-    private void createProperties(NodeState myNode, List propInfos)
-        throws ItemNotFoundException, ItemExistsException, ConstraintViolationException,
-                                                ValueFormatException, RepositoryException {
-        // process properties
-        Iterator iter = propInfos.iterator();
-        while (iter.hasNext()) {
-            PropInfo pi = (PropInfo) iter.next();
-            pi.apply(myNode, itemOps, ntReg, refTracker);
-        }
-    }
-
-    /**
-     * Create the specific NodeState
-     * @param parent NodeState
-     * @param nodeInfo NodeInfo
-     * @return newly create NodeState
-     * @throws ConstraintViolationException if we cannot create the NodeState
-     * @throws RepositoryException if we cannot create the NodeState
-     */
-    private NodeState createNode(NodeState parent, NodeInfo nodeInfo) throws ConstraintViolationException, RepositoryException {
-
-        NodeDef def =
-            itemOps.findApplicableNodeDefinition(nodeInfo.getName(), nodeInfo.getNodeTypeName(), parent);
-
-        // potential uuid conflict
-        NodeState conflicting = null;
-        NodeState node;
+            // remove conflicting:
+            // check if conflicting can be removed
+            // (access rights, node type constraints, locking & versioning status)
+            itemOps.checkRemoveNode(conflicting,
+                    BatchedItemOperations.CHECK_ACCESS
+                    | BatchedItemOperations.CHECK_LOCK
+                    | BatchedItemOperations.CHECK_VERSIONING
+                    | BatchedItemOperations.CHECK_CONSTRAINTS);
+            // do remove conflicting (recursive)
+            itemOps.removeNodeState(conflicting);
 
-        try {
-            if (nodeInfo.getId() != null) {
-                conflicting = itemOps.getNodeState(nodeInfo.getId());
-            }
-        } catch (ItemNotFoundException infe) {
-            conflicting = null;
-        }
-        if (conflicting != null) {
-            // resolve uuid conflict
-            node = resolveUUIDConflict(parent, conflicting, nodeInfo);
-        }
-        else {
+            // create new with given uuid:
+            // check if new node can be added (check access rights &
+            // node type constraints only, assume locking & versioning status
+            // has already been checked on ancestor)
+            itemOps.checkAddNode(parent, nodeInfo.getName(),
+                    nodeInfo.getNodeTypeName(),
+                    BatchedItemOperations.CHECK_ACCESS
+                    | BatchedItemOperations.CHECK_CONSTRAINTS);
             // do create new node
-            node = itemOps.createNodeState(parent, nodeInfo.getName(), nodeInfo.getNodeTypeName(),
-                                                                nodeInfo.getMixinNames(), null, def);
-        }
-        return node;
-    }
-
-    /**
-     * Resolve UUID conflict if any.
-     *
-     * @param parent NodeState
-     * @param conflicting NodeState
-     * @param nodeInfo NodeInfo
-     * @return the new conflicting NodeState
-     * @throws ItemExistsException
-     * @throws ConstraintViolationException
-     * @throws IllegalStateException
-     * @throws RepositoryException
-     */
-    private NodeState resolveUUIDConflict(NodeState parent, NodeState conflicting, NodeInfo nodeInfo)
-        throws ItemExistsException, ConstraintViolationException, IllegalStateException, RepositoryException {
-        NodeState node = null;
-        switch (uuidBehavior) {
-
-        case ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING:
+            node = itemOps.createNodeState(parent, nodeInfo.getName(),
+                    nodeInfo.getNodeTypeName(), nodeInfo.getMixinNames(),
+                    nodeInfo.getId());
+        } else if (uuidBehavior == ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING) {
             NodeId parentId = conflicting.getParentId();
             if (parentId == null) {
                 String msg = "root node cannot be replaced";
@@ -335,83 +225,34 @@
                     | BatchedItemOperations.CHECK_CONSTRAINTS);
             // do remove conflicting (recursive)
             itemOps.removeNodeState(conflicting);
-            // do create new node
-            node = itemOps.createNodeState(parent, nodeInfo.getName(),
-                    nodeInfo.getNodeTypeName(), nodeInfo.getMixinNames(),
-                    nodeInfo.getId());
-            break;
-
-        case ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING:
-            // make sure conflicting node is not importTarget or an ancestor thereof
-            Path p0 = hierMgr.getPath(importTarget.getNodeId());
-            Path p1 = hierMgr.getPath(conflicting.getNodeId());
-            try {
-                if (p1.equals(p0) || p1.isAncestorOf(p0)) {
-                    String msg = "cannot remove ancestor node";
-                    log.debug(msg);
-                    throw new ConstraintViolationException(msg);
-                }
-            } catch (MalformedPathException mpe) {
-                // should never get here...
-                String msg = "internal error: failed to determine degree of relationship";
-                log.error(msg, mpe);
-                throw new RepositoryException(msg, mpe);
-            }
-            // remove conflicting:
-            // check if conflicting can be removed
+            // create new with given uuid at same location as conflicting:
+            // check if new node can be added at other location
             // (access rights, node type constraints, locking & versioning status)
-            itemOps.checkRemoveNode(conflicting,
+            itemOps.checkAddNode(parent, nodeInfo.getName(),
+                    nodeInfo.getNodeTypeName(),
                     BatchedItemOperations.CHECK_ACCESS
                     | BatchedItemOperations.CHECK_LOCK
                     | BatchedItemOperations.CHECK_VERSIONING
                     | BatchedItemOperations.CHECK_CONSTRAINTS);
-            // do remove conflicting (recursive)
-            itemOps.removeNodeState(conflicting);
-
-            // create new with given uuid:
             // do create new node
             node = itemOps.createNodeState(parent, nodeInfo.getName(),
                     nodeInfo.getNodeTypeName(), nodeInfo.getMixinNames(),
                     nodeInfo.getId());
-            break;
-
-        case ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW:
-            String msg = "a node with uuid " + nodeInfo.getId()
-            + " already exists!";
+        } else {
+            String msg = "unknown uuidBehavior: " + uuidBehavior;
             log.debug(msg);
-            throw new ItemExistsException(msg);
-
-        case ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW:
-            // create new with new uuid:
-            // check if new node can be added (check access rights &
-            // node type constraints only, assume locking & versioning status
-            // has already been checked on ancestor)
-            node = itemOps.createNodeState(parent, nodeInfo.getName(),
-                    nodeInfo.getNodeTypeName(), nodeInfo.getMixinNames(), null);
-            // remember uuid mapping
-            EffectiveNodeType ent = itemOps.getEffectiveNodeType(node);
-            if (ent.includesNodeType(QName.MIX_REFERENCEABLE)) {
-                refTracker.mappedUUID(nodeInfo.getId().getUUID(), node.getNodeId().getUUID());
-            }
-            break;
-         //No need for default case.
+            throw new RepositoryException(msg);
         }
-        return node;
-    }
 
-    /**
-     * @return true if skip mode is on.
-     */
-    protected boolean isSkipped() {
-        return skip;
+        return node;
     }
 
     /**
      * Post-process imported node (initialize properties with special
      * semantics etc.)
      *
-     * @param node NodeState to postprocess
-     * @throws RepositoryException if issue when postprocessing a node
+     * @param node
+     * @throws RepositoryException
      */
     protected void postProcessNode(NodeState node) throws RepositoryException {
         /**
@@ -478,7 +319,6 @@
         }
     }
 
-
     //-------------------------------------------------------------< Importer >
     /**
      * {@inheritDoc}
@@ -499,58 +339,213 @@
      * {@inheritDoc}
      */
     public void startNode(NodeInfo nodeInfo, List propInfos)
-    throws RepositoryException {
+            throws RepositoryException {
         if (aborted) {
+            // the import has been aborted, get outta here...
             return;
         }
 
-        NodeState parent = (NodeState) parents.peek();
+        boolean succeeded = false;
+        NodeState parent;
+        try {
+            // check sanity of workspace/session first
+            wsp.sanityCheck();
 
-        if (raw && !checkNode(parent, nodeInfo, propInfos)) {
-            skip = true;
-        }
+            parent = (NodeState) parents.peek();
 
-        if (skip) {
-            return;
-        }
+            // process node
+
+            NodeState node = null;
+            NodeId id = nodeInfo.getId();
+            QName nodeName = nodeInfo.getName();
+            QName ntName = nodeInfo.getNodeTypeName();
+            QName[] mixins = nodeInfo.getMixinNames();
+
+            if (parent == null) {
+                // parent node was skipped, skip this child node also
+                parents.push(null); // push null onto stack for skipped node
+                succeeded = true;
+                log.debug("skipping node " + nodeName);
+                return;
+            }
+            if (parent.hasChildNodeEntry(nodeName)) {
+                // a node with that name already exists...
+                NodeState.ChildNodeEntry entry =
+                        parent.getChildNodeEntry(nodeName, 1);
+                NodeId idExisting = entry.getId();
+                NodeState existing = (NodeState) itemOps.getItemState(idExisting);
+                NodeDef def = ntReg.getNodeDef(existing.getDefinitionId());
+
+                if (!def.allowsSameNameSiblings()) {
+                    // existing doesn't allow same-name siblings,
+                    // check for potential conflicts
+                    EffectiveNodeType entExisting =
+                            itemOps.getEffectiveNodeType(existing);
+                    if (def.isProtected() && entExisting.includesNodeType(ntName)) {
+                        // skip protected node
+                        parents.push(null); // push null onto stack for skipped node
+                        succeeded = true;
+                        log.debug("skipping protected node "
+                                + itemOps.safeGetJCRPath(existing.getNodeId()));
+                        return;
+                    }
+                    if (def.isAutoCreated() && entExisting.includesNodeType(ntName)) {
+                        // this node has already been auto-created,
+                        // no need to create it
+                        node = existing;
+                    } else {
+                        throw new ItemExistsException(itemOps.safeGetJCRPath(existing.getNodeId()));
+                    }
+                }
+            }
+
+            if (node == null) {
+                // there's no node with that name...
+                if (id == null) {
+                    // no potential uuid conflict, always create new node
+
+                    NodeDef def =
+                            itemOps.findApplicableNodeDefinition(nodeName, ntName, parent);
+                    if (def.isProtected()) {
+                        // skip protected node
+                        parents.push(null); // push null onto stack for skipped node
+                        succeeded = true;
+                        log.debug("skipping protected node " + nodeName);
+                        return;
+                    }
+
+                    if (parent.hasPropertyName(nodeName)) {
+                        /**
+                         * a property with the same name already exists; if this property
+                         * has been imported as well (e.g. through document view import
+                         * where an element can have the same name as one of the attributes
+                         * of its parent element) we have to rename the onflicting property;
+                         *
+                         * see http://issues.apache.org/jira/browse/JCR-61
+                         */
+                        PropertyId propId = new PropertyId(parent.getNodeId(), nodeName);
+                        PropertyState conflicting = itemOps.getPropertyState(propId);
+                        if (conflicting.getStatus() == ItemState.STATUS_NEW) {
+                            // assume this property has been imported as well;
+                            // rename conflicting property
+                            // @todo use better reversible escaping scheme to create unique name
+                            QName newName = new QName(nodeName.getNamespaceURI(), nodeName.getLocalName() + "_");
+                            if (parent.hasPropertyName(newName)) {
+                                newName = new QName(newName.getNamespaceURI(), newName.getLocalName() + "_");
+                            }
+                            PropertyState newProp =
+                                    itemOps.createPropertyState(parent, newName,
+                                            conflicting.getType(), conflicting.getValues().length);
+                            newProp.setValues(conflicting.getValues());
+                            parent.removePropertyName(nodeName);
+                            itemOps.store(parent);
+                            itemOps.destroy(conflicting);
+                        }
+                    }
+
+                    // check if new node can be added (check access rights &
+                    // node type constraints only, assume locking & versioning status
+                    // has already been checked on ancestor)
+                    itemOps.checkAddNode(parent, nodeName, ntName,
+                            BatchedItemOperations.CHECK_ACCESS
+                            | BatchedItemOperations.CHECK_CONSTRAINTS);
+                    // do create new node
+                    node = itemOps.createNodeState(parent, nodeName, ntName, mixins, null, def);
+                } else {
+                    // potential uuid conflict
+                    NodeState conflicting;
 
-        NodeState myNode;
-        if (existing == null) {
-            myNode = createNode(parent, nodeInfo);
-        }
-        else {
-            myNode = existing;
-            existing = null;
+                    try {
+                        conflicting = itemOps.getNodeState(id);
+                    } catch (ItemNotFoundException infe) {
+                        conflicting = null;
+                    }
+                    if (conflicting != null) {
+                        // resolve uuid conflict
+                        node = resolveUUIDConflict(parent, conflicting, nodeInfo);
+                    } else {
+                        // create new with given uuid
+
+                        NodeDef def =
+                                itemOps.findApplicableNodeDefinition(nodeName, ntName, parent);
+                        if (def.isProtected()) {
+                            // skip protected node
+                            parents.push(null); // push null onto stack for skipped node
+                            succeeded = true;
+                            log.debug("skipping protected node " + nodeName);
+                            return;
+                        }
+
+                        // check if new node can be added (check access rights &
+                        // node type constraints only, assume locking & versioning status
+                        // has already been checked on ancestor)
+                        itemOps.checkAddNode(parent, nodeName, ntName,
+                                BatchedItemOperations.CHECK_ACCESS
+                                | BatchedItemOperations.CHECK_CONSTRAINTS);
+                        // do create new node
+                        node = itemOps.createNodeState(parent, nodeName, ntName, mixins, id, def);
+                    }
+                }
+            }
+
+            // process properties
+
+            Iterator iter = propInfos.iterator();
+            while (iter.hasNext()) {
+                PropInfo pi = (PropInfo) iter.next();
+                pi.apply(node, itemOps, ntReg, refTracker);
+            }
+
+            // store affected nodes
+            itemOps.store(node);
+            itemOps.store(parent);
+
+            // push current node onto stack of parents
+            parents.push(node);
+
+            succeeded = true;
+        } finally {
+            if (!succeeded) {
+                // update operation failed, cancel all modifications
+                aborted = true;
+                itemOps.cancel();
+            }
         }
-        createProperties(myNode, propInfos);
-        parents.push(myNode);
     }
 
     /**
      * {@inheritDoc}
      */
     public void endNode(NodeInfo nodeInfo) throws RepositoryException {
-        //End of skip mode
-        if (skipNode != null && skipNode.equals(nodeInfo)) {
-            skip = false;
-            skipNode = null;
+        if (aborted) {
+            // the import has been aborted, get outta here...
             return;
         }
-
-        if (aborted || skip) {
+        NodeState node = (NodeState) parents.pop();
+        if (node == null) {
+            // node was skipped, nothing to do here
             return;
         }
-
+        boolean succeeded = false;
         try {
-            NodeState node = (NodeState) parents.pop();
+            // check sanity of workspace/session first
+            wsp.sanityCheck();
 
-            if (!raw) {
-                this.postProcessNode(node);
-            }
+            // post-process node (initialize properties with special semantics etc.)
+            postProcessNode(node);
+
+            // make sure node is valid according to its definition
+            itemOps.validate(node);
+
+            // we're done with that node, now store its state
             itemOps.store(node);
-        } catch (IllegalStateException e) {
-            itemOps.cancel();
-            aborted = true;
+            succeeded = true;
+        } finally {
+            if (!succeeded) {
+                // update operation failed, cancel all modifications
+                aborted = true;
+                itemOps.cancel();
+            }
         }
     }
 
@@ -559,54 +554,66 @@
      */
     public void end() throws RepositoryException {
         if (aborted) {
-            itemOps.cancel();
+            // the import has been aborted, get outta here...
             return;
         }
 
-        wsp.sanityCheck();
-        /**
-         * adjust references that refer to uuid's which have been mapped to
-         * newly gererated uuid's on import
-         */
-        Iterator iter = refTracker.getProcessedReferences();
-        while (iter.hasNext()) {
-            PropertyState prop = (PropertyState) iter.next();
-            // being paranoid...
-            if (prop.getType() != PropertyType.REFERENCE) {
-                continue;
-            }
-            boolean modified = false;
-            InternalValue[] values = prop.getValues();
-            InternalValue[] newVals = new InternalValue[values.length];
-            for (int i = 0; i < values.length; i++) {
-                InternalValue val = values[i];
-                UUID original = (UUID) val.internalValue();
-                UUID adjusted = refTracker.getMappedUUID(original);
-                if (adjusted != null) {
-                    newVals[i] = InternalValue.create(adjusted);
-                    modified = true;
-                } else {
-                    // reference doesn't need adjusting, just copy old value
-                    newVals[i] = val;
+        boolean succeeded = false;
+        try {
+            // check sanity of workspace/session first
+            wsp.sanityCheck();
+
+            /**
+             * adjust references that refer to uuid's which have been mapped to
+             * newly gererated uuid's on import
+             */
+            Iterator iter = refTracker.getProcessedReferences();
+            while (iter.hasNext()) {
+                PropertyState prop = (PropertyState) iter.next();
+                // being paranoid...
+                if (prop.getType() != PropertyType.REFERENCE) {
+                    continue;
+                }
+                boolean modified = false;
+                InternalValue[] values = prop.getValues();
+                InternalValue[] newVals = new InternalValue[values.length];
+                for (int i = 0; i < values.length; i++) {
+                    InternalValue val = values[i];
+                    UUID original = (UUID) val.internalValue();
+                    UUID adjusted = refTracker.getMappedUUID(original);
+                    if (adjusted != null) {
+                        newVals[i] = InternalValue.create(adjusted);
+                        modified = true;
+                    } else {
+                        // reference doesn't need adjusting, just copy old value
+                        newVals[i] = val;
+                    }
+                }
+                if (modified) {
+                    prop.setValues(newVals);
+                    itemOps.store(prop);
                 }
             }
-            if (modified) {
-                prop.setValues(newVals);
-                itemOps.store(prop);
-            }
-        }
-        refTracker.clear();
+            refTracker.clear();
 
-        // make sure import target is valid according to its definition
-        itemOps.validate(importTarget);
+            // make sure import target is valid according to its definition
+            itemOps.validate(importTarget);
 
-        // finally store the state of the import target
-        // (the parent of the imported subtree)
-        itemOps.store(importTarget);
+            // finally store the state of the import target
+            // (the parent of the imported subtree)
+            itemOps.store(importTarget);
+            succeeded = true;
+        } finally {
+            if (!succeeded) {
+                // update operation failed, cancel all modifications
+                aborted = true;
+                itemOps.cancel();
+            }
+        }
 
-        // finish update
-        itemOps.update();
+        if (!aborted) {
+            // finish update
+            itemOps.update();
+        }
     }
-
 }
-