JCRVLT-104 Access to root node in DocViewSAXImporter and JcrSysViewTransformer

git-svn-id: https://svn.apache.org/repos/asf/jackrabbit/commons/filevault/trunk@1708204 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/ChildNodeStash.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/ChildNodeStash.java
new file mode 100644
index 0000000..9be95cb
--- /dev/null
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/ChildNodeStash.java
@@ -0,0 +1,145 @@
+/*
+ * 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.vault.fs.impl.io;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.jackrabbit.vault.fs.api.ImportInfo;
+import org.apache.jackrabbit.vault.util.JcrConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Helper class isolating the task of temporarily moving child nodes to a
+ * different location in order to be able to recover (and properly merge) them
+ * later on.
+ */
+public class ChildNodeStash {
+
+    static final Logger log = LoggerFactory.getLogger(ChildNodeStash.class);
+
+    private final Session session;
+
+    private Node tmpNode;
+
+    /**
+     * List of potential roots where the transient temporary node will be created.
+     * Note that the node is never persisted.
+     */
+    private static final String[] ROOTS = {"/", "/tmp", "/var", "/etc", "/content"};
+
+
+    /**
+     * Creates a new child node stash utility class
+     * @param session session to operate on
+     */
+    public ChildNodeStash(Session session) {
+        this.session = session;
+    }
+
+    /**
+     * create a transient node for temporarily stash the child nodes
+     * @return the temporary node
+     * @throws RepositoryException if an error occurrs
+     */
+    private Node getOrCreateTemporaryNode() throws RepositoryException {
+        if (tmpNode != null) {
+            return tmpNode;
+        }
+        for (String rootPath: ROOTS) {
+            try {
+                Node root = session.getNode(rootPath);
+                return tmpNode = root.addNode("tmp" + System.currentTimeMillis(), JcrConstants.NT_UNSTRUCTURED);
+            } catch (RepositoryException e) {
+                log.debug("unable to create temporary stash location below {}.", rootPath);
+            }
+        }
+        throw new RepositoryException("Unable to create temporary root below.");
+    }
+
+    /**
+     * Moves the nodes below the given parent path to a temporary location.
+     * @param parentPath the path of the parent node.
+     * @throws RepositoryException if an error occurrs
+     */
+    public void stashChildren(String parentPath) throws RepositoryException {
+        stashChildren(session.getNode(parentPath));
+    }
+
+    /**
+     * Moves the nodes below the given parent to a temporary location.
+     * @param parent the parent node.
+     */
+    public void stashChildren(Node parent) {
+        try {
+            NodeIterator iter = parent.getNodes();
+            while (iter.hasNext()) {
+                Node child = iter.nextNode();
+                Node tmp = getOrCreateTemporaryNode();
+                try {
+                    session.move(child.getPath(), tmp.getPath() + "/" + child.getName());
+                } catch (RepositoryException e) {
+                    log.error("Error while moving child node to temporary location. Child will be removed.", e);
+                }
+            }
+        } catch (RepositoryException e) {
+            log.warn("error while moving child nodes (ignored)", e);
+        }
+    }
+
+    /**
+     * Moves the stashed nodes back below the given parent path.
+     * @param parentPath the path of the new parent node
+     * @throws RepositoryException if an error occurrs
+     */
+    public void recoverChildren(String parentPath) throws RepositoryException {
+        recoverChildren(session.getNode(parentPath), null);
+    }
+
+    /**
+     * Moves the stashed nodes back below the given parent path.
+     * @param parent the new parent node
+     * @throws RepositoryException if an error occurrs
+     */
+    public void recoverChildren(Node parent, ImportInfo importInfo) throws RepositoryException {
+        // move the old child nodes back
+        if (tmpNode != null) {
+            NodeIterator iter = tmpNode.getNodes();
+            boolean hasErrors = false;
+            while (iter.hasNext()) {
+                Node child = iter.nextNode();
+                String newPath = parent.getPath() + "/" + child.getName();
+                try {
+                    session.move(child.getPath(), newPath);
+                } catch (RepositoryException e) {
+                    log.warn("Unable to move child back to new location at {} due to: {}. Node will remain in temporary location: {}",
+                            new Object[]{newPath, e.getMessage(), child.getPath()});
+                    if (importInfo != null) {
+                        importInfo.onError(newPath, e);
+                        hasErrors = true;
+                    }
+                }
+            }
+            if (!hasErrors) {
+                tmpNode.remove();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewSAXImporter.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewSAXImporter.java
index 9db893a..c2ef44e 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewSAXImporter.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewSAXImporter.java
@@ -823,25 +823,9 @@
             // check versionable
             new VersioningState(stack, oldNode).ensureCheckedOut();
 
-            // replace node
-            Node tmpNode = null;
-            try {
-                // if old node exist, try to 'save' the child nodes
-                NodeIterator iter = oldNode.getNodes();
-                while (iter.hasNext()) {
-                    Node child = iter.nextNode();
-                    if (tmpNode == null) {
-                        tmpNode = session.getRootNode().addNode("tmp" + System.currentTimeMillis(), JcrConstants.NT_UNSTRUCTURED);
-                    }
-                    try {
-                        session.move(child.getPath(), tmpNode.getPath() + "/" + child.getName());
-                    } catch (RepositoryException e) {
-                        log.error("Error while moving child node to temporary location. Child will be removed.", e);
-                    }
-                }
-            } catch (RepositoryException e) {
-                log.warn("error while moving child nodes (ignored)", e);
-            }
+            ChildNodeStash recovery = new ChildNodeStash(session);
+            recovery.stashChildren(oldNode);
+
             // ensure that existing binaries are not sourced from a property
             // that is about to be removed
             Map<String, DocViewSAXImporter.BlobInfo> blobs = binaries.get(oldNode.getPath());
@@ -855,26 +839,9 @@
             // now create the new node
             node = createNode(currentNode, ni);
 
-            // move the old child nodes back
-            if (tmpNode != null) {
-                NodeIterator iter = tmpNode.getNodes();
-                boolean hasErrors = false;
-                while (iter.hasNext()) {
-                    Node child = iter.nextNode();
-                    String newPath = node.getPath() + "/" + child.getName();
-                    try {
-                        session.move(child.getPath(), newPath);
-                    } catch (RepositoryException e) {
-                        log.warn("Unable to move child back to new location at {} due to: {}. Node will remain in temporary location: {}",
-                                new Object[]{newPath, e.getMessage(), child.getPath()});
-                        importInfo.onError(newPath, e);
-                        hasErrors = true;
-                    }
-                }
-                if (!hasErrors) {
-                    tmpNode.remove();
-                }
-            }
+            // move the children back
+            recovery.recoverChildren(node, importInfo);
+
             importInfo.onReplaced(node.getPath());
             return new StackElement(node, false);
         }
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/JcrSysViewTransformer.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/JcrSysViewTransformer.java
index 41021f2..5cf5aef 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/JcrSysViewTransformer.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/JcrSysViewTransformer.java
@@ -29,7 +29,6 @@
 import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.vault.util.DocViewNode;
 import org.apache.jackrabbit.vault.util.DocViewProperty;
-import org.apache.jackrabbit.vault.util.JcrConstants;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.xml.sax.ContentHandler;
@@ -55,9 +54,9 @@
     private ContentHandler handler;
 
     /**
-     * temporary node when 'rescuing' the child nodes
+     * temporary recovery helper when 'rescuing' the child nodes
      */
-    private Node tmpNode;
+    private ChildNodeStash recovery;
 
     private String rootName;
 
@@ -87,25 +86,9 @@
 
         this.existingPath = existingPath;
         if (existingPath != null) {
-            Node existingNode = session.getNode(existingPath);
             // check if there is an existing node with the name
-            try {
-                // if old node exist, try to 'save' the child nodes
-                NodeIterator iter = existingNode.getNodes();
-                while (iter.hasNext()) {
-                    Node child = iter.nextNode();
-                    if (tmpNode == null) {
-                        tmpNode = session.getRootNode().addNode("tmp" + System.currentTimeMillis(), JcrConstants.NT_UNSTRUCTURED);
-                    }
-                    try {
-                        session.move(child.getPath(), tmpNode.getPath() + "/" + child.getName());
-                    } catch (RepositoryException e) {
-                        log.error("Error while moving child node to temporary location. Child will be removed.", e);
-                    }
-                }
-            } catch (RepositoryException e) {
-                log.warn("error while moving child nodes (ignored)", e);
-            }
+            recovery = new ChildNodeStash(session);
+            recovery.stashChildren(existingPath);
         }
     }
 
@@ -125,25 +108,13 @@
         }
 
         // check for rescued child nodes
-        // move the old child nodes back
-        if (tmpNode != null) {
+        if (recovery != null) {
             try {
-                Session session = tmpNode.getSession();
-                Node node = session.getNode(existingPath);
-                NodeIterator iter = tmpNode.getNodes();
-                while (iter.hasNext()) {
-                    Node child = iter.nextNode();
-                    String newPath = node.getPath() + "/" + child.getName();
-                    try {
-                        session.move(child.getPath(), newPath);
-                    } catch (RepositoryException e) {
-                        log.warn("Unable to move child back to new location at {} due to: {}. Node will remain in temporary location: {}",
-                                new Object[]{newPath, e.getMessage(), child.getPath()});
-                    }
-                }
-                tmpNode.remove();
+                recovery.recoverChildren(existingPath);
             } catch (RepositoryException e) {
                 log.error("Error while processing rescued child nodes");
+            } finally {
+                recovery = null;
             }
         }