You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by fm...@apache.org on 2008/02/07 16:59:11 UTC

svn commit: r619470 - /incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/loader/Loader.java

Author: fmeschbe
Date: Thu Feb  7 07:59:07 2008
New Revision: 619470

URL: http://svn.apache.org/viewvc?rev=619470&view=rev
Log:
SLING-195 Support JCR system/document view import, see issue for more details

Modified:
    incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/loader/Loader.java

Modified: incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/loader/Loader.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/loader/Loader.java?rev=619470&r1=619469&r2=619470&view=diff
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/loader/Loader.java (original)
+++ incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/loader/Loader.java Thu Feb  7 07:59:07 2008
@@ -18,6 +18,9 @@
  */
 package org.apache.sling.jcr.resource.internal.loader;
 
+import static javax.jcr.ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW;
+
+import java.awt.image.ImagingOpException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.UnsupportedEncodingException;
@@ -35,6 +38,8 @@
 import java.util.Set;
 import java.util.StringTokenizer;
 
+import javax.jcr.ImportUUIDBehavior;
+import javax.jcr.InvalidSerializedDataException;
 import javax.jcr.Item;
 import javax.jcr.Node;
 import javax.jcr.PropertyType;
@@ -46,17 +51,19 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-
 /**
  * The <code>Loader</code> TODO
  */
 public class Loader {
 
-
     public static final String CONTENT_HEADER = "Sling-Initial-Content";
 
     public static final String EXT_XML = ".xml";
+
+    public static final String EXT_JCR_XML = ".jcr.xml";
+
     public static final String EXT_JSON = ".json";
+
     public static final String EXT_XJSON = ".xjson";
 
     // default content type for createFile()
@@ -66,9 +73,13 @@
     private final Logger log = LoggerFactory.getLogger(Loader.class);
 
     private ContentLoaderService jcrContentHelper;
+
     private XmlReader xmlReader;
+
     private JsonReader jsonReader;
+
     private XJsonReader xjsonReader;
+
     private Map<String, List<String>> delayedReferences;
 
     // bundles whose registration failed and should be retried
@@ -93,12 +104,15 @@
     }
 
     public void registerBundle(Session session, Bundle bundle) {
-        log.debug("Registering bundle {} for content loading.", bundle.getSymbolicName());
+        log.debug("Registering bundle {} for content loading.",
+            bundle.getSymbolicName());
         if (this.registerBundleInternal(session, bundle, false)) {
             // handle delayed bundles, might help now
             int currentSize = -1;
-            for (int i=this.delayedBundles.size(); i > 0 && currentSize != this.delayedBundles.size() && !this.delayedBundles.isEmpty(); i--) {
-                for (Iterator<Bundle> di=this.delayedBundles.iterator(); di.hasNext(); ) {
+            for (int i = this.delayedBundles.size(); i > 0
+                && currentSize != this.delayedBundles.size()
+                && !this.delayedBundles.isEmpty(); i--) {
+                for (Iterator<Bundle> di = this.delayedBundles.iterator(); di.hasNext();) {
                     Bundle delayed = di.next();
                     if (this.registerBundleInternal(session, delayed, true)) {
                         di.remove();
@@ -112,19 +126,23 @@
         }
     }
 
-    private boolean registerBundleInternal (Session session, Bundle bundle, boolean isRetry) {
+    private boolean registerBundleInternal(Session session, Bundle bundle,
+            boolean isRetry) {
         try {
             this.installContent(session, bundle);
-            if ( isRetry ) {
+            if (isRetry) {
                 // log success of retry
-                log.info("Retrytring to load initial content for bundle {} succeeded.",
-                        bundle.getSymbolicName());
+                log.info(
+                    "Retrytring to load initial content for bundle {} succeeded.",
+                    bundle.getSymbolicName());
             }
             return true;
         } catch (RepositoryException re) {
-            // if we are retrying we already logged this message once, so we won't log it again
-            if ( !isRetry ) {
-                log.error("Cannot load initial content for bundle " + bundle.getSymbolicName() + " : " + re.getMessage(), re);
+            // if we are retrying we already logged this message once, so we
+            // won't log it again
+            if (!isRetry) {
+                log.error("Cannot load initial content for bundle "
+                    + bundle.getSymbolicName() + " : " + re.getMessage(), re);
             }
         }
 
@@ -132,24 +150,27 @@
     }
 
     public void unregisterBundle(Bundle bundle) {
-        if ( this.delayedBundles.contains(bundle) ) {
+        if (this.delayedBundles.contains(bundle)) {
             this.delayedBundles.remove(bundle);
         } else {
             this.uninstallContent(bundle);
         }
     }
 
-    //---------- internal -----------------------------------------------------
+    // ---------- internal -----------------------------------------------------
 
-    private void installContent(Session session, Bundle bundle) throws RepositoryException {
+    private void installContent(Session session, Bundle bundle)
+            throws RepositoryException {
         String root = (String) bundle.getHeaders().get(CONTENT_HEADER);
         if (root == null) {
-            log.debug("Bundle {} has no initial content", bundle.getSymbolicName());
+            log.debug("Bundle {} has no initial content",
+                bundle.getSymbolicName());
             return;
         }
 
         try {
-            log.debug("Installing initial content from bundle {}", bundle.getSymbolicName());
+            log.debug("Installing initial content from bundle {}",
+                bundle.getSymbolicName());
             StringTokenizer tokener = new StringTokenizer(root, ",");
             while (tokener.hasMoreTokens()) {
                 String path = tokener.nextToken().trim();
@@ -158,7 +179,8 @@
 
             // persist modifications now
             session.save();
-            log.debug("Done installing initial content from bundle {}", bundle.getSymbolicName());
+            log.debug("Done installing initial content from bundle {}",
+                bundle.getSymbolicName());
         } finally {
             try {
                 if (session.hasPendingChanges()) {
@@ -173,7 +195,8 @@
 
     }
 
-    private void install(Bundle bundle, String path, javax.jcr.Node parent) throws RepositoryException {
+    private void install(Bundle bundle, String path, javax.jcr.Node parent)
+            throws RepositoryException {
         @SuppressWarnings("unchecked")
         Enumeration<String> entries = bundle.getEntryPaths(path);
         if (entries == null) {
@@ -187,7 +210,7 @@
             log.debug("Processing initial content entry {}", entry);
             if (entry.endsWith("/")) {
                 // dir, check for node descriptor , else create dir
-                String base = entry.substring(0, entry.length()-1);
+                String base = entry.substring(0, entry.length() - 1);
                 String name = this.getName(base);
 
                 Node node = null;
@@ -202,7 +225,8 @@
                 // if we have a descriptor, which has not been processed yet,
                 // otherwise call crateFolder, which creates an nt:folder or
                 // returns an existing node (created by a descriptor)
-                if (nodeDescriptor != null && !ignoreEntry.contains(nodeDescriptor)) {
+                if (nodeDescriptor != null
+                    && !ignoreEntry.contains(nodeDescriptor)) {
                     node = this.createNode(parent, name, nodeDescriptor);
                     ignoreEntry.add(nodeDescriptor);
                 } else {
@@ -223,9 +247,8 @@
                 }
 
                 // install if it is a descriptor
-                if (entry.endsWith(EXT_XML)
-                        || entry.endsWith(EXT_JSON)
-                        || entry.endsWith(EXT_XJSON)) {
+                if (entry.endsWith(EXT_XML) || entry.endsWith(EXT_JSON)
+                    || entry.endsWith(EXT_XJSON)) {
                     if (this.createNode(parent, this.getName(entry), file) != null) {
                         ignoreEntry.add(file);
                         continue;
@@ -242,7 +265,8 @@
         }
     }
 
-    private javax.jcr.Node createFolder(javax.jcr.Node parent, String name) throws RepositoryException {
+    private javax.jcr.Node createFolder(javax.jcr.Node parent, String name)
+            throws RepositoryException {
         if (parent.hasNode(name)) {
             return parent.getNode(name);
         }
@@ -250,16 +274,23 @@
         return parent.addNode(name, "nt:folder");
     }
 
-    private javax.jcr.Node createNode(Node parent, String name, URL nodeXML) throws RepositoryException {
+    private javax.jcr.Node createNode(Node parent, String name, URL nodeXML)
+            throws RepositoryException {
 
         InputStream ins = null;
         try {
             NodeReader nodeReader;
-            if (nodeXML.getPath().toLowerCase().endsWith(".xml")) {
+            if (nodeXML.getPath().toLowerCase().endsWith(EXT_XML)) {
+                // return immediately if system/document view import succeeds
+                Node childNode = importSystemView(parent, name, nodeXML);
+                if (childNode != null) {
+                    return childNode;
+                }
+                
                 nodeReader = this.getXmlReader();
-            } else if (nodeXML.getPath().toLowerCase().endsWith(".json")) {
+            } else if (nodeXML.getPath().toLowerCase().endsWith(EXT_JSON)) {
                 nodeReader = this.getJsonReader();
-            } else if (nodeXML.getPath().toLowerCase().endsWith(".xjson")) {
+            } else if (nodeXML.getPath().toLowerCase().endsWith(EXT_XJSON)) {
                 nodeReader = this.getXJsonReader();
             } else {
                 // cannot find out the type
@@ -294,12 +325,15 @@
         }
     }
 
-    private Node createNode(Node parentNode, org.apache.sling.jcr.resource.internal.loader.Node clNode) throws RepositoryException {
+    private Node createNode(Node parentNode,
+            org.apache.sling.jcr.resource.internal.loader.Node clNode)
+            throws RepositoryException {
         Node node;
         if (parentNode.hasNode(clNode.getName())) {
             node = parentNode.getNode(clNode.getName());
         } else {
-            node = parentNode.addNode(clNode.getName(), clNode.getPrimaryNodeType());
+            node = parentNode.addNode(clNode.getName(),
+                clNode.getPrimaryNodeType());
 
             if (clNode.getMixinNodeTypes() != null) {
                 for (String mixin : clNode.getMixinNodeTypes()) {
@@ -310,18 +344,21 @@
 
         if (clNode.getProperties() != null) {
             for (Property prop : clNode.getProperties()) {
-                if (node.hasProperty(prop.getName()) && !node.getProperty(prop.getName()).isNew()) {
+                if (node.hasProperty(prop.getName())
+                    && !node.getProperty(prop.getName()).isNew()) {
                     continue;
                 }
 
                 int type = PropertyType.valueFromName(prop.getType());
                 if (prop.isMultiValue()) {
-                    String[] values = prop.getValues().toArray(new String[prop.getValues().size()]);
+                    String[] values = prop.getValues().toArray(
+                        new String[prop.getValues().size()]);
                     node.setProperty(prop.getName(), values, type);
                 } else if (type == PropertyType.REFERENCE) {
                     // need to resolve the reference
                     String propPath = node.getPath() + "/" + prop.getName();
-                    String uuid = this.getUUID(node.getSession(), propPath, prop.getValue());
+                    String uuid = this.getUUID(node.getSession(), propPath,
+                        prop.getValue());
                     if (uuid != null) {
                         node.setProperty(prop.getName(), uuid, type);
                     }
@@ -342,7 +379,8 @@
         return node;
     }
 
-    private void createFile(Node parent, URL source) throws IOException, RepositoryException {
+    private void createFile(Node parent, URL source) throws IOException,
+            RepositoryException {
         String name = this.getName(source.getPath());
         if (parent.hasNode(name)) {
             return;
@@ -376,7 +414,8 @@
         content.setProperty("jcr:data", data);
     }
 
-    private String getUUID(Session session, String propPath, String referencePath) throws RepositoryException {
+    private String getUUID(Session session, String propPath,
+            String referencePath) throws RepositoryException {
         if (session.itemExists(referencePath)) {
             Item item = session.getItem(referencePath);
             if (item.isNode()) {
@@ -423,21 +462,21 @@
     }
 
     /**
-     * Gets and decods the name part of the <code>path</code>. The name is the
-     * part of the path after the last slash (or the complete path if no slash
-     * is contained). To support names containing unsupported characters such
-     * as colon (<code>:</code>), names may be URL encoded (see <code>java.net.URLEncoder</code>)
-     * using the <i>UTF-8</i> character encoding. In this case, this method
-     * decodes the name using the <code>java.netURLDecoder</code> class with
-     * the <i>UTF-8</i> character encoding.
-     *
+     * Gets and decods the name part of the <code>path</code>. The name is
+     * the part of the path after the last slash (or the complete path if no
+     * slash is contained). To support names containing unsupported characters
+     * such as colon (<code>:</code>), names may be URL encoded (see
+     * <code>java.net.URLEncoder</code>) using the <i>UTF-8</i> character
+     * encoding. In this case, this method decodes the name using the
+     * <code>java.netURLDecoder</code> class with the <i>UTF-8</i> character
+     * encoding.
+     * 
      * @param path The path from which to extract the name part.
-     *
      * @return The URL decoded name part.
      */
-    private String getName(String path){
+    private String getName(String path) {
         int lastSlash = path.lastIndexOf('/');
-        String name = (lastSlash < 0) ? path : path.substring(lastSlash+1);
+        String name = (lastSlash < 0) ? path : path.substring(lastSlash + 1);
 
         // check for encoded characters (%xx)
         // has encoded characters, need to decode
@@ -459,7 +498,8 @@
         return name;
     }
 
-    private Node getParentNode(Session session, String path) throws RepositoryException {
+    private Node getParentNode(Session session, String path)
+            throws RepositoryException {
         int lastSlash = path.lastIndexOf('/');
 
         // not an absolute path, cannot find parent
@@ -485,11 +525,13 @@
     private void uninstallContent(Bundle bundle) {
         String root = (String) bundle.getHeaders().get(CONTENT_HEADER);
         if (root == null) {
-            log.debug("Bundle {} has no initial content", bundle.getSymbolicName());
+            log.debug("Bundle {} has no initial content",
+                bundle.getSymbolicName());
             return;
         }
 
-        log.info("Content deinstallation not implemented yet. Keeping content of bundle {}",
+        log.info(
+            "Content deinstallation not implemented yet. Keeping content of bundle {}",
             bundle.getSymbolicName());
     }
 
@@ -519,4 +561,64 @@
         return this.xjsonReader;
     }
 
+    /**
+     * Import the XML file as JCR system or document view import. If the XML
+     * file is not a valid system or document view export/import file,
+     * <code>false</code> is returned.
+     * 
+     * @param parent The parent node below which to import
+     * @param nodeXML The URL to the XML file to import
+     * @return <code>true</code> if the import succeeds, <code>false</code>
+     *         if the import fails due to XML format errors.
+     * @throws IOException If an IO error occurrs reading the XML file.
+     */
+    private Node importSystemView(Node parent, String name, URL nodeXML)
+            throws IOException {
+
+        // only consider ".jcr.xml" files here
+        if (!nodeXML.getPath().toLowerCase().endsWith(EXT_JCR_XML)) {
+            return null;
+        }
+
+        InputStream ins = null;
+        try {
+
+            ins = nodeXML.openStream();
+            Session session = parent.getSession();
+            session.importXML(parent.getPath(), ins, IMPORT_UUID_CREATE_NEW);
+
+            // additionally check whether the expected child node exists
+            if (name.toLowerCase().endsWith(EXT_JCR_XML)) {
+                name = name.substring(0, name.length()-EXT_JCR_XML.length());
+            }
+            return (parent.hasNode(name)) ? parent.getNode(name) : null;
+
+        } catch (InvalidSerializedDataException isde) {
+
+            // the xml might not be System or Document View export, fall back
+            // to old-style XML reading
+            log.info(
+                "importSystemView: XML {} does not seem to be system view export, trying old style",
+                nodeXML);
+            return null;
+
+        } catch (RepositoryException re) {
+
+            // any other repository related issue...
+            log.info(
+                "importSystemView: Repository issue loading XML {}, trying old style",
+                nodeXML);
+            return null;
+
+        } finally {
+            if (ins != null) {
+                try {
+                    ins.close();
+                } catch (IOException ignore) {
+                    // ignore
+                }
+            }
+        }
+
+    }
 }